Compare commits
16 Commits
a2c52a0bb8
...
b2d259ac71
Author | SHA1 | Date |
---|---|---|
SeanOMik | b2d259ac71 | |
SeanOMik | d001e136d0 | |
SeanOMik | d0e6fc6ecd | |
SeanOMik | 6a47cd2671 | |
SeanOMik | 8e56ee1f0f | |
SeanOMik | 9e9478966b | |
SeanOMik | 624cd5362f | |
SeanOMik | eff6b221e0 | |
SeanOMik | 77ec620adb | |
SeanOMik | 6f65e2ce35 | |
SeanOMik | b90e19161d | |
SeanOMik | e9cbb48653 | |
SeanOMik | 49dfb38da3 | |
SeanOMik | 140ca506d6 | |
SeanOMik | 06a4301c23 | |
SeanOMik | de14b6211b |
|
@ -1,4 +1,4 @@
|
|||
HAS_SETUP_WINDOW = false
|
||||
local is_window_setup = false
|
||||
|
||||
---Return the userdata's name from its metatable.
|
||||
---
|
||||
|
@ -14,10 +14,23 @@ function udname(val)
|
|||
|
||||
return tbl.__name
|
||||
end
|
||||
|
||||
function dump(o)
|
||||
if type(o) == 'table' then
|
||||
local s = '{ '
|
||||
for k,v in pairs(o) do
|
||||
if type(k) ~= 'number' then k = '"'..k..'"' end
|
||||
s = s .. '['..k..'] = ' .. dump(v) .. ','
|
||||
end
|
||||
return s .. '} '
|
||||
else
|
||||
return tostring(o)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function on_init()
|
||||
local cube = world:request_res("../assets/cube-texture-embedded.gltf")
|
||||
local cube = world:request_asset("../assets/cube-texture-embedded.gltf") --[[@as GltfHandle]]
|
||||
print("Loaded textured cube (" .. udname(cube) .. ")")
|
||||
|
||||
cube:wait_until_loaded()
|
||||
|
@ -31,17 +44,31 @@ function on_init()
|
|||
end
|
||||
|
||||
function on_first()
|
||||
if not HAS_SETUP_WINDOW then
|
||||
world:view(function (w)
|
||||
if w.cursor_grab == CursorGrabMode.NONE then
|
||||
w.cursor_grab = CursorGrabMode.LOCKED
|
||||
w.cursor_visible = false
|
||||
return w
|
||||
else
|
||||
HAS_SETUP_WINDOW = true
|
||||
print("Window setup")
|
||||
end
|
||||
end, Window)
|
||||
if not is_window_setup then
|
||||
world:view(
|
||||
---@param w Window
|
||||
function (w)
|
||||
if w.cursor_grab == CursorGrabMode.NONE then
|
||||
w.cursor_grab = CursorGrabMode.LOCKED
|
||||
w.cursor_visible = false
|
||||
return w
|
||||
else
|
||||
is_window_setup = true
|
||||
print("Window setup")
|
||||
end
|
||||
end, Window
|
||||
)
|
||||
end
|
||||
|
||||
---@type EventReader
|
||||
local reader = world:read_event(DeviceEvent)
|
||||
|
||||
---@param ev DeviceEvent
|
||||
for ev in reader:read() do
|
||||
if ev.event.kind == DeviceEventKind.MOTION then
|
||||
local motion_ev = ev.event --[[@as DeviceEventMotion]]
|
||||
print("axis: " .. tostring(motion_ev.axis) .. " = " .. tostring(motion_ev.value))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -66,10 +93,26 @@ function on_update()
|
|||
---@type number
|
||||
local dt = world:resource(DeltaTime)
|
||||
|
||||
world:view(function (t)
|
||||
t:translate(0, 0.15 * dt, 0)
|
||||
return t
|
||||
end, Transform)
|
||||
world:view(
|
||||
---@param t Transform
|
||||
---@param wt WorldTransform
|
||||
function (t, wt)
|
||||
print("Entity is at: " .. tostring(wt))
|
||||
t:translate(0, 0.15 * dt, 0)
|
||||
return t
|
||||
end, Transform, WorldTransform
|
||||
)
|
||||
|
||||
--[[ world:view(
|
||||
---@param c Camera
|
||||
function (c)
|
||||
c.transform:translate(0, 0.15 * dt, 0)
|
||||
|
||||
print("Moving camera to: " .. tostring(c.transform))
|
||||
|
||||
return c
|
||||
end, Camera
|
||||
) ]]
|
||||
end
|
||||
|
||||
--[[ function on_post_update()
|
||||
|
|
|
@ -1,19 +1,10 @@
|
|||
use lyra_engine::{
|
||||
assets::{gltf::Gltf, ResourceManager},
|
||||
game::App,
|
||||
input::{
|
||||
assets::{gltf::Gltf, ResourceManager}, game::App, input::{
|
||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
|
||||
},
|
||||
lua::{LuaScript, LuaScriptingPlugin},
|
||||
math::{self, Transform, Vec3},
|
||||
render::light::directional::DirectionalLight,
|
||||
scene::{
|
||||
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
|
||||
ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN,
|
||||
ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN,
|
||||
},
|
||||
Script, ScriptList,
|
||||
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
||||
self, CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN
|
||||
}, script::{lua::{LuaScript, LuaScriptingPlugin}, Script, ScriptList}
|
||||
};
|
||||
|
||||
#[async_std::main]
|
||||
|
@ -92,6 +83,11 @@ async fn main() {
|
|||
app.with_plugin(setup_scene_plugin);
|
||||
app.with_plugin(action_handler_plugin);
|
||||
app.with_plugin(setup_script_plugin);
|
||||
app.with_system(
|
||||
"update_world_transforms",
|
||||
scene::system_update_world_transforms,
|
||||
&[],
|
||||
);
|
||||
//app.with_plugin(camera_debug_plugin);
|
||||
app.with_plugin(FreeFlyCameraPlugin);
|
||||
app.run();
|
||||
|
|
|
@ -196,7 +196,7 @@ fn setup_scene_plugin(app: &mut App) {
|
|||
|
||||
fn camera_debug_plugin(app: &mut App) {
|
||||
let sys = |handler: Res<ActionHandler>, view: View<&mut CameraComponent>| -> anyhow::Result<()> {
|
||||
if handler.was_action_just_pressed("Debug") {
|
||||
if let Some(true) = handler.was_action_just_pressed("Debug") {
|
||||
for mut cam in view.into_iter() {
|
||||
cam.debug = !cam.debug;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use lyra_engine::{
|
||||
assets::{gltf::Gltf, ResourceManager}, ecs::query::View, game::App, input::{
|
||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
|
||||
}, math::{self, Transform, Vec3}, render::{light::directional::DirectionalLight, window::WindowOptions}, scene::{
|
||||
InputActionPlugin, KeyCode, LayoutId,
|
||||
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
||||
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
|
||||
ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN,
|
||||
ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN,
|
||||
}
|
||||
}, winit::WindowOptions
|
||||
};
|
||||
|
||||
#[async_std::main]
|
||||
|
@ -82,7 +82,6 @@ async fn main() {
|
|||
|
||||
let mut a = App::new();
|
||||
a.with_plugin(lyra_engine::DefaultPlugins)
|
||||
.with_system("mouse_pos_print", mouse_pos_system, &[])
|
||||
.with_plugin(setup_scene_plugin)
|
||||
.with_plugin(action_handler_plugin)
|
||||
//.with_plugin(camera_debug_plugin)
|
||||
|
@ -141,11 +140,3 @@ fn setup_scene_plugin(app: &mut App) {
|
|||
camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5);
|
||||
world.spawn((camera, FreeFlyCamera::default()));
|
||||
}
|
||||
|
||||
fn mouse_pos_system(view: View<&WindowOptions>) -> anyhow::Result<()> {
|
||||
for win in view.iter() {
|
||||
//println!("Mouse pos: {:?}", win.cursor_position());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,7 +1,27 @@
|
|||
use std::ptr::NonNull;
|
||||
|
||||
use lyra_engine::{assets::gltf::Gltf, ecs::{query::{Res, View}, system::{Criteria, CriteriaSchedule, IntoSystem}, Component, World}, game::App, input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput}, math::{self, Quat, Transform, Vec3}, render::{light::{directional::DirectionalLight, PointLight, SpotLight}, window::{CursorGrabMode, WindowOptions}}, scene::{self, CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN}, DeltaTime};
|
||||
use lyra_engine::assets::ResourceManager;
|
||||
use lyra_engine::{
|
||||
assets::gltf::Gltf,
|
||||
ecs::{
|
||||
query::{Res, View},
|
||||
system::{Criteria, CriteriaSchedule, IntoSystem},
|
||||
Component, World,
|
||||
},
|
||||
game::App,
|
||||
input::{
|
||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||
InputActionPlugin, KeyCode, LayoutId, MouseAxis, MouseInput,
|
||||
},
|
||||
math::{self, Quat, Transform, Vec3},
|
||||
render::light::{directional::DirectionalLight, PointLight, SpotLight},
|
||||
scene::{
|
||||
self, CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
|
||||
ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN,
|
||||
ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN,
|
||||
},
|
||||
DeltaTime,
|
||||
};
|
||||
|
||||
struct FixedTimestep {
|
||||
max_tps: u32,
|
||||
|
@ -54,7 +74,7 @@ impl Criteria for FixedTimestep {
|
|||
|
||||
CriteriaSchedule::No
|
||||
}
|
||||
|
||||
|
||||
fn modify_world(&mut self, mut world: NonNull<World>) {
|
||||
let world = unsafe { world.as_mut() };
|
||||
self.old_dt = world.get_resource().map(|r| *r);
|
||||
|
@ -71,9 +91,11 @@ impl Criteria for FixedTimestep {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone)]
|
||||
struct TpsAccumulator(f32);
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Component)]
|
||||
struct CubeFlag;
|
||||
|
||||
|
@ -90,27 +112,29 @@ async fn main() {
|
|||
//let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();
|
||||
//let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
|
||||
//let cube_model = resman.request::<Model>("assets/cube-texture-bin.glb").unwrap();
|
||||
let cube_gltf = resman.request::<Gltf>("../assets/texture-sep/texture-sep.gltf").unwrap();
|
||||
let cube_gltf = resman
|
||||
.request::<Gltf>("../assets/texture-sep/texture-sep.gltf")
|
||||
.unwrap();
|
||||
/*let crate_gltf = resman.request::<Gltf>("assets/crate/crate.gltf").unwrap();
|
||||
|
||||
let separate_gltf = resman.request::<Gltf>("assets/pos-testing/child-node-cubes.glb").unwrap(); */
|
||||
//drop(resman);
|
||||
|
||||
cube_gltf.wait_recurse_dependencies_load();
|
||||
let cube_mesh = &cube_gltf.data_ref()
|
||||
.unwrap().meshes[0];
|
||||
let cube_mesh = &cube_gltf.data_ref().unwrap().meshes[0];
|
||||
/* let crate_mesh = &crate_gltf.data_ref()
|
||||
.unwrap().meshes[0];
|
||||
|
||||
let separate_scene = &separate_gltf.data_ref()
|
||||
.unwrap().scenes[0]; */
|
||||
|
||||
let sponza_model = resman.request::<Gltf>("../assets/sponza/Sponza.gltf").unwrap();
|
||||
let sponza_model = resman
|
||||
.request::<Gltf>("../assets/sponza/Sponza.gltf")
|
||||
.unwrap();
|
||||
drop(resman);
|
||||
|
||||
sponza_model.wait_recurse_dependencies_load();
|
||||
let sponza_scene = &sponza_model.data_ref()
|
||||
.unwrap().scenes[0];
|
||||
let sponza_scene = &sponza_model.data_ref().unwrap().scenes[0];
|
||||
|
||||
world.spawn((
|
||||
sponza_scene.clone(),
|
||||
|
@ -127,8 +151,7 @@ async fn main() {
|
|||
DirectionalLight {
|
||||
enabled: true,
|
||||
color: Vec3::ONE,
|
||||
intensity: 0.35
|
||||
//..Default::default()
|
||||
intensity: 0.35, //..Default::default()
|
||||
},
|
||||
light_tran,
|
||||
));
|
||||
|
@ -136,10 +159,9 @@ async fn main() {
|
|||
|
||||
{
|
||||
let t = Transform::new(
|
||||
//Vec3::new(-5.0, 1.0, -1.28),
|
||||
//Vec3::new(-5.0, 1.0, -1.28),
|
||||
Vec3::new(-5.0, 1.0, -0.0),
|
||||
//Vec3::new(-10.0, 0.94, -0.28),
|
||||
|
||||
//Vec3::new(-10.0, 0.94, -0.28),
|
||||
Quat::IDENTITY,
|
||||
Vec3::new(0.25, 0.25, 0.25),
|
||||
);
|
||||
|
@ -160,8 +182,7 @@ async fn main() {
|
|||
let t = Transform::new(
|
||||
Vec3::new(-3.0, 0.2, -1.5),
|
||||
//Vec3::new(-5.0, 1.0, -0.28),
|
||||
//Vec3::new(-10.0, 0.94, -0.28),
|
||||
|
||||
//Vec3::new(-10.0, 0.94, -0.28),
|
||||
Quat::IDENTITY,
|
||||
Vec3::new(0.15, 0.15, 0.15),
|
||||
);
|
||||
|
@ -182,8 +203,7 @@ async fn main() {
|
|||
let t = Transform::new(
|
||||
Vec3::new(0.0, 0.2, -1.5),
|
||||
//Vec3::new(-5.0, 1.0, -0.28),
|
||||
//Vec3::new(-10.0, 0.94, -0.28),
|
||||
|
||||
//Vec3::new(-10.0, 0.94, -0.28),
|
||||
Quat::IDENTITY,
|
||||
Vec3::new(0.15, 0.15, 0.15),
|
||||
);
|
||||
|
@ -215,7 +235,7 @@ async fn main() {
|
|||
constant: 1.0,
|
||||
linear: 0.09,
|
||||
quadratic: 0.032,
|
||||
|
||||
|
||||
ambient: 0.2,
|
||||
diffuse: 1.0,
|
||||
specular: 1.3,
|
||||
|
@ -224,7 +244,7 @@ async fn main() {
|
|||
cube_mesh.clone(),
|
||||
));
|
||||
} */
|
||||
|
||||
|
||||
/* {
|
||||
let mut light_tran = Transform::from_xyz(-5.0, 2.5, -9.5);
|
||||
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
||||
|
@ -237,7 +257,7 @@ async fn main() {
|
|||
constant: 1.0,
|
||||
linear: 0.045,
|
||||
quadratic: 0.0075,
|
||||
|
||||
|
||||
ambient: 0.1,
|
||||
diffuse: 1.0,
|
||||
specular: 1.3,
|
||||
|
@ -250,35 +270,39 @@ async fn main() {
|
|||
let mut camera = CameraComponent::new_3d();
|
||||
// these values were taken by manually positioning the camera in the scene.
|
||||
camera.transform = Transform::new(
|
||||
Vec3::new(-10.0, 0.94, -0.28),
|
||||
Quat::from_xyzw(0.03375484, -0.7116095, 0.0342693, 0.70092666),
|
||||
Vec3::ONE
|
||||
Vec3::new(-10.0, 0.94, -0.28),
|
||||
Quat::from_xyzw(0.03375484, -0.7116095, 0.0342693, 0.70092666),
|
||||
Vec3::ONE,
|
||||
);
|
||||
//camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5);
|
||||
world.spawn(( camera, FreeFlyCamera::default() ));
|
||||
world.spawn((camera, FreeFlyCamera::default()));
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let camera_debug_plugin = move |app: &mut App| {
|
||||
let sys = |handler: Res<ActionHandler>, view: View<&mut CameraComponent>| -> anyhow::Result<()> {
|
||||
if handler.was_action_just_pressed("Debug") {
|
||||
for mut cam in view.into_iter() {
|
||||
cam.debug = !cam.debug;
|
||||
let sys =
|
||||
|handler: Res<ActionHandler>, view: View<&mut CameraComponent>| -> anyhow::Result<()> {
|
||||
if let Some(true) = handler.was_action_just_pressed("Debug") {
|
||||
for mut cam in view.into_iter() {
|
||||
cam.debug = !cam.debug;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
};
|
||||
Ok(())
|
||||
};
|
||||
|
||||
app.with_system("camera_debug_trigger", sys, &[]);
|
||||
app.with_system("update_world_transforms", scene::system_update_world_transforms, &[]);
|
||||
app.with_system(
|
||||
"update_world_transforms",
|
||||
scene::system_update_world_transforms,
|
||||
&[],
|
||||
);
|
||||
};
|
||||
|
||||
let action_handler_plugin = |app: &mut App| {
|
||||
let action_handler = ActionHandler::builder()
|
||||
.add_layout(LayoutId::from(0))
|
||||
|
||||
.add_action(ACTLBL_MOVE_FORWARD_BACKWARD, Action::new(ActionKind::Axis))
|
||||
.add_action(ACTLBL_MOVE_LEFT_RIGHT, Action::new(ActionKind::Axis))
|
||||
.add_action(ACTLBL_MOVE_UP_DOWN, Action::new(ActionKind::Axis))
|
||||
|
@ -286,41 +310,61 @@ async fn main() {
|
|||
.add_action(ACTLBL_LOOK_UP_DOWN, Action::new(ActionKind::Axis))
|
||||
.add_action(ACTLBL_LOOK_ROLL, Action::new(ActionKind::Axis))
|
||||
.add_action("Debug", Action::new(ActionKind::Button))
|
||||
|
||||
.add_mapping(ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
|
||||
.bind(ACTLBL_MOVE_FORWARD_BACKWARD, &[
|
||||
ActionSource::Keyboard(KeyCode::KeyW).into_binding_modifier(1.0),
|
||||
ActionSource::Keyboard(KeyCode::KeyS).into_binding_modifier(-1.0)
|
||||
])
|
||||
.bind(ACTLBL_MOVE_LEFT_RIGHT, &[
|
||||
ActionSource::Keyboard(KeyCode::KeyA).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::KeyD).into_binding_modifier(1.0)
|
||||
])
|
||||
.bind(ACTLBL_MOVE_UP_DOWN, &[
|
||||
ActionSource::Keyboard(KeyCode::KeyC).into_binding_modifier(1.0),
|
||||
ActionSource::Keyboard(KeyCode::KeyZ).into_binding_modifier(-1.0)
|
||||
])
|
||||
.bind(ACTLBL_LOOK_LEFT_RIGHT, &[
|
||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
||||
ActionSource::Keyboard(KeyCode::ArrowLeft).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::ArrowRight).into_binding_modifier(1.0),
|
||||
//ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickX)).into_binding(),
|
||||
])
|
||||
.bind(ACTLBL_LOOK_UP_DOWN, &[
|
||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
||||
ActionSource::Keyboard(KeyCode::ArrowUp).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::ArrowDown).into_binding_modifier(1.0),
|
||||
//ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickY)).into_binding(),
|
||||
])
|
||||
.bind(ACTLBL_LOOK_ROLL, &[
|
||||
ActionSource::Keyboard(KeyCode::KeyE).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::KeyQ).into_binding_modifier(1.0),
|
||||
])
|
||||
.bind("Debug", &[
|
||||
ActionSource::Keyboard(KeyCode::KeyB).into_binding(),
|
||||
])
|
||||
.finish()
|
||||
).finish();
|
||||
.add_mapping(
|
||||
ActionMapping::builder(LayoutId::from(0), ActionMappingId::from(0))
|
||||
.bind(
|
||||
ACTLBL_MOVE_FORWARD_BACKWARD,
|
||||
&[
|
||||
ActionSource::Keyboard(KeyCode::KeyW).into_binding_modifier(1.0),
|
||||
ActionSource::Keyboard(KeyCode::KeyS).into_binding_modifier(-1.0),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
ACTLBL_MOVE_LEFT_RIGHT,
|
||||
&[
|
||||
ActionSource::Keyboard(KeyCode::KeyA).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::KeyD).into_binding_modifier(1.0),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
ACTLBL_MOVE_UP_DOWN,
|
||||
&[
|
||||
ActionSource::Keyboard(KeyCode::KeyC).into_binding_modifier(1.0),
|
||||
ActionSource::Keyboard(KeyCode::KeyZ).into_binding_modifier(-1.0),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
ACTLBL_LOOK_LEFT_RIGHT,
|
||||
&[
|
||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::X)).into_binding(),
|
||||
ActionSource::Keyboard(KeyCode::ArrowLeft).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::ArrowRight).into_binding_modifier(1.0),
|
||||
//ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickX)).into_binding(),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
ACTLBL_LOOK_UP_DOWN,
|
||||
&[
|
||||
ActionSource::Mouse(MouseInput::Axis(MouseAxis::Y)).into_binding(),
|
||||
ActionSource::Keyboard(KeyCode::ArrowUp).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::ArrowDown).into_binding_modifier(1.0),
|
||||
//ActionSource::Gamepad(GamepadFormat::DualAxis, GamepadInput::Axis(GamepadAxis::RThumbstickY)).into_binding(),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
ACTLBL_LOOK_ROLL,
|
||||
&[
|
||||
ActionSource::Keyboard(KeyCode::KeyE).into_binding_modifier(-1.0),
|
||||
ActionSource::Keyboard(KeyCode::KeyQ).into_binding_modifier(1.0),
|
||||
],
|
||||
)
|
||||
.bind(
|
||||
"Debug",
|
||||
&[ActionSource::Keyboard(KeyCode::KeyB).into_binding()],
|
||||
)
|
||||
.finish(),
|
||||
)
|
||||
.finish();
|
||||
|
||||
let world = &mut app.world;
|
||||
world.add_resource(action_handler);
|
||||
|
@ -341,7 +385,7 @@ async fn main() {
|
|||
world.spawn((scripts,));
|
||||
|
||||
}; */
|
||||
|
||||
|
||||
let mut app = App::new();
|
||||
app.with_plugin(lyra_engine::DefaultPlugins);
|
||||
app.with_startup_system(setup_sys.into_system());
|
||||
|
|
|
@ -43,4 +43,4 @@ fast_poisson = { version = "1.0.0", features = ["single_precision"] }
|
|||
atomic_refcell = "0.1.13"
|
||||
|
||||
[features]
|
||||
tracy = ["dep:tracing-tracy"]
|
||||
tracy = ["dep:tracing-tracy"]
|
|
@ -1,119 +0,0 @@
|
|||
use std::ops::{DerefMut, Deref};
|
||||
|
||||
/// A change tracker. This tracks changes of the data it owns.
|
||||
/// Tracking changes can cause false positives since it actually tracks calls to `deref_mut()`
|
||||
/// If you mutably dereference this, make sure its only when you change the data.
|
||||
pub struct Ct<T> {
|
||||
data: T,
|
||||
changed: bool,
|
||||
}
|
||||
|
||||
impl<T> Ct<T> {
|
||||
pub fn new(data: T) -> Self {
|
||||
Self {
|
||||
data,
|
||||
changed: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new change tracker with data
|
||||
pub fn new_true(data: T) -> Self {
|
||||
Self {
|
||||
data,
|
||||
changed: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if there was a change. Will reset back to false after this is called.
|
||||
pub fn changed(&mut self) -> bool {
|
||||
let before = self.changed;
|
||||
self.reset();
|
||||
before
|
||||
}
|
||||
|
||||
/// Returns true if there was a change, will NOT reset back to false.
|
||||
pub fn peek_changed(&self) -> bool {
|
||||
self.changed
|
||||
}
|
||||
|
||||
/// Resets the changed tracker to be false
|
||||
pub fn reset(&mut self) {
|
||||
self.changed = false;
|
||||
}
|
||||
|
||||
/// Triggers the change tracker to be true
|
||||
pub fn trigger(&mut self) {
|
||||
self.changed = true;
|
||||
}
|
||||
|
||||
/// Silently mutate the inner data, this wont be tracked
|
||||
pub fn silently_mutate(&mut self) -> &mut T {
|
||||
&mut self.data
|
||||
}
|
||||
|
||||
/// Consumes self and returns the data stored inside.
|
||||
pub fn take(self) -> T {
|
||||
self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Ct<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for Ct<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.changed = true;
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Ct;
|
||||
|
||||
#[test]
|
||||
fn new() {
|
||||
let mut c = Ct::new(100);
|
||||
assert!(!c.changed());
|
||||
|
||||
let mut c = Ct::new_true(100);
|
||||
assert!(c.changed());
|
||||
assert!(!c.changed()); // it should've reset itself
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn change_tracking() {
|
||||
let mut c = Ct::new(100);
|
||||
assert!(!c.changed());
|
||||
|
||||
*c = 10;
|
||||
assert!(c.changed());
|
||||
assert!(!c.changed()); // should now be not changed
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn silent_mutate() {
|
||||
let mut c = Ct::new(100);
|
||||
let a = c.silently_mutate();
|
||||
*a = 10;
|
||||
assert!(!c.changed());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reset_trigger() {
|
||||
let mut c = Ct::new(100);
|
||||
*c = 10;
|
||||
c.reset();
|
||||
assert!(!c.changed()); // should not be changed because of reset
|
||||
|
||||
let mut c = Ct::new(100);
|
||||
c.trigger();
|
||||
assert!(c.changed()); // should changed because of trigger
|
||||
assert!(!c.changed()); // should've reset itself
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
use std::{cell::RefCell, rc::Rc, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use lyra_ecs::{query::{ResMut, WorldTick}, system::FnArgFetcher, Tick};
|
||||
|
@ -99,7 +99,7 @@ impl<T: Event> Events<T> {
|
|||
pub fn reader(&self) -> EventReader<T> {
|
||||
EventReader {
|
||||
events: self.events.clone(),
|
||||
cursor: Rc::new(RefCell::new(0)),
|
||||
cursor: Arc::new(AtomicRefCell::new(0)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,11 +112,11 @@ impl<T: Event> Events<T> {
|
|||
|
||||
pub struct EventReader<T: Event> {
|
||||
events: Arc<AtomicRefCell<WaterfallVec<T>>>,
|
||||
cursor: Rc<RefCell<usize>>,
|
||||
cursor: Arc<AtomicRefCell<usize>>,
|
||||
}
|
||||
|
||||
impl<T: Event> EventReader<T> {
|
||||
pub fn read(&mut self) -> Option<atomic_refcell::AtomicRef<T>> {
|
||||
pub fn read(&self) -> Option<atomic_refcell::AtomicRef<T>> {
|
||||
let events = self.events.borrow();
|
||||
|
||||
let mut cursor = self.cursor.borrow_mut();
|
||||
|
@ -136,7 +136,7 @@ pub struct EventWriter<T: Event> {
|
|||
}
|
||||
|
||||
impl<T: Event> EventWriter<T> {
|
||||
pub fn write(&mut self, event: T) {
|
||||
pub fn write(&self, event: T) {
|
||||
let mut events = self.events.borrow_mut();
|
||||
events.push(event);
|
||||
}
|
||||
|
@ -167,12 +167,12 @@ where
|
|||
}
|
||||
|
||||
impl<T: Event> FnArgFetcher for EventReader<T> {
|
||||
type State = Rc<RefCell<usize>>;
|
||||
type State = Arc<AtomicRefCell<usize>>;
|
||||
|
||||
type Arg<'a, 'state> = EventReader<T>;
|
||||
|
||||
fn create_state(_: std::ptr::NonNull<lyra_ecs::World>) -> Self::State {
|
||||
Rc::new(RefCell::new(0))
|
||||
Arc::new(AtomicRefCell::new(0))
|
||||
}
|
||||
|
||||
unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: std::ptr::NonNull<lyra_ecs::World>) -> Self::Arg<'a, 'state> {
|
||||
|
|
|
@ -358,66 +358,61 @@ impl ActionHandler {
|
|||
|
||||
/// Returns true if the action is pressed (or was just pressed).
|
||||
///
|
||||
/// This will panic if the action name does not correspond to an action.
|
||||
pub fn is_action_pressed<L>(&self, action: L) -> bool
|
||||
/// Returns `None` if the action was not found.
|
||||
pub fn is_action_pressed<L>(&self, action: L) -> Option<bool>
|
||||
where
|
||||
L: ActionLabel
|
||||
{
|
||||
let action = self.actions.get(&action.label_hash())
|
||||
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
|
||||
let action = self.actions.get(&action.label_hash())?;
|
||||
|
||||
matches!(action.state, ActionState::Pressed(_) | ActionState::JustPressed(_))
|
||||
Some(matches!(action.state, ActionState::Pressed(_) | ActionState::JustPressed(_)))
|
||||
}
|
||||
|
||||
/// Returns true if the action was just pressed.
|
||||
///
|
||||
/// This will panic if the action name does not correspond to an action.
|
||||
pub fn was_action_just_pressed<L>(&self, action: L) -> bool
|
||||
/// Returns `None` if the action was not found.
|
||||
pub fn was_action_just_pressed<L>(&self, action: L) -> Option<bool>
|
||||
where
|
||||
L: ActionLabel
|
||||
{
|
||||
let action = self.actions.get(&action.label_hash())
|
||||
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
|
||||
let action = self.actions.get(&action.label_hash())?;
|
||||
|
||||
matches!(action.state, ActionState::JustPressed(_))
|
||||
Some(matches!(action.state, ActionState::JustPressed(_)))
|
||||
}
|
||||
|
||||
/// Returns true if the action was just released.
|
||||
///
|
||||
/// This will panic if the action name does not correspond to an action.
|
||||
pub fn was_action_just_released<L>(&self, action: L) -> bool
|
||||
/// Returns `None` if the action was not found.
|
||||
pub fn was_action_just_released<L>(&self, action: L) -> Option<bool>
|
||||
where
|
||||
L: ActionLabel
|
||||
{
|
||||
let action = self.actions.get(&action.label_hash())
|
||||
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
|
||||
let action = self.actions.get(&action.label_hash())?;
|
||||
|
||||
matches!(action.state, ActionState::JustReleased)
|
||||
Some(matches!(action.state, ActionState::JustReleased))
|
||||
}
|
||||
|
||||
/// Returns an action's state.
|
||||
///
|
||||
/// This will panic if the action name does not correspond to an action.
|
||||
pub fn get_action_state<L>(&self, action: L) -> ActionState
|
||||
/// Returns `None` if the action was not found.
|
||||
pub fn get_action_state<L>(&self, action: L) -> Option<ActionState>
|
||||
where
|
||||
L: ActionLabel
|
||||
{
|
||||
let action = self.actions.get(&action.label_hash())
|
||||
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
|
||||
let action = self.actions.get(&action.label_hash())?;
|
||||
|
||||
action.state
|
||||
Some(action.state)
|
||||
}
|
||||
|
||||
/// Returns the action's modifier if it is pressed (or was just pressed).
|
||||
/// Returns `None` if the action's state is not `ActionState::Pressed` or `ActionState::JustPressed`.
|
||||
///
|
||||
/// This will panic if the action name does not correspond to an action.
|
||||
/// Returns `None` if the action's state is not `ActionState::Pressed`, `ActionState::JustPressed`,
|
||||
/// or if the action was not found.
|
||||
pub fn get_pressed_modifier<L>(&self, action: L) -> Option<f32>
|
||||
where
|
||||
L: ActionLabel
|
||||
{
|
||||
let action = self.actions.get(&action.label_hash())
|
||||
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
|
||||
let action = self.actions.get(&action.label_hash())?;
|
||||
|
||||
match action.state {
|
||||
ActionState::Pressed(v) | ActionState::JustPressed(v) => Some(v),
|
||||
|
@ -426,15 +421,14 @@ impl ActionHandler {
|
|||
}
|
||||
|
||||
/// Returns the action's modifier if it was just pressed.
|
||||
/// Returns `None` if the action's state is not `ActionState::JustPressed`.
|
||||
///
|
||||
/// This will panic if the action name does not correspond to an action.
|
||||
/// Returns `None` if the action's state is not `ActionState::JustPressed`,
|
||||
/// or if the action was not found.
|
||||
pub fn get_just_pressed_modifier<L>(&self, action: L) -> Option<f32>
|
||||
where
|
||||
L: ActionLabel
|
||||
{
|
||||
let action = self.actions.get(&action.label_hash())
|
||||
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
|
||||
let action = self.actions.get(&action.label_hash())?;
|
||||
|
||||
match action.state {
|
||||
ActionState::JustPressed(v) => Some(v),
|
||||
|
@ -443,15 +437,14 @@ impl ActionHandler {
|
|||
}
|
||||
|
||||
/// Returns the action's modifier if its an updated axis.
|
||||
/// Returns `None` if the action's state is not `ActionState::Axis`.
|
||||
///
|
||||
/// This will panic if the action name does not correspond to an action.
|
||||
/// Returns `None` if the action's state is not `ActionState::Axis`,
|
||||
/// or if the action was not found.
|
||||
pub fn get_axis_modifier<L>(&self, action: L) -> Option<f32>
|
||||
where
|
||||
L: ActionLabel
|
||||
{
|
||||
let action = self.actions.get(&action.label_hash())
|
||||
.unwrap_or_else(|| panic!("Action {action:?} was not found"));
|
||||
let action = self.actions.get(&action.label_hash())?;
|
||||
|
||||
match action.state {
|
||||
ActionState::Axis(v) => Some(v),
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -6,7 +6,7 @@ use winit::{event::{MouseScrollDelta, WindowEvent}, keyboard::PhysicalKey};
|
|||
|
||||
use crate::{game::GameStages, plugin::Plugin, winit::DeviceEventPair, EventReader, EventWriter};
|
||||
|
||||
use super::{events::*, InputButtons};
|
||||
use super::{events::*, InputButtons, KeyCode};
|
||||
|
||||
fn write_scroll_delta(mouse_scroll_ev: &mut EventWriter<MouseScroll>, delta: &MouseScrollDelta) {
|
||||
let event = match delta {
|
||||
|
@ -21,24 +21,24 @@ fn write_scroll_delta(mouse_scroll_ev: &mut EventWriter<MouseScroll>, delta: &Mo
|
|||
mouse_scroll_ev.write(event);
|
||||
}
|
||||
|
||||
fn write_key_event(key_buttons: &mut ResMut<InputButtons<winit::keyboard::KeyCode>>, physical_key: winit::keyboard::PhysicalKey, state: winit::event::ElementState) {
|
||||
fn write_key_event(key_buttons: &mut ResMut<InputButtons<KeyCode>>, physical_key: PhysicalKey, state: winit::event::ElementState) {
|
||||
if let PhysicalKey::Code(code) = physical_key {
|
||||
key_buttons.add_input_from_winit(code, state);
|
||||
key_buttons.add_input_from_winit(KeyCode::from(code), state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn input_system(
|
||||
mut key_code_res: ResMut<InputButtons<winit::keyboard::KeyCode>>,
|
||||
mut key_code_res: ResMut<InputButtons<KeyCode>>,
|
||||
mut mouse_btn_res: ResMut<InputButtons<MouseButton>>,
|
||||
mut touches_res: ResMut<Touches>,
|
||||
mut window_ev: EventReader<WindowEvent>,
|
||||
mut device_ev: EventReader<DeviceEventPair>,
|
||||
window_ev: EventReader<WindowEvent>,
|
||||
device_ev: EventReader<DeviceEventPair>,
|
||||
mut mouse_scroll_ev: EventWriter<MouseScroll>,
|
||||
mut mouse_btn_ev: EventWriter<MouseButton>,
|
||||
mut mouse_exact_ev: EventWriter<MouseExact>,
|
||||
mut mouse_entered_ev: EventWriter<CursorEnteredWindow>,
|
||||
mut mouse_left_ev: EventWriter<CursorLeftWindow>,
|
||||
mut mouse_motion_ev: EventWriter<MouseMotion>,
|
||||
mouse_btn_ev: EventWriter<MouseButton>,
|
||||
mouse_exact_ev: EventWriter<MouseExact>,
|
||||
mouse_entered_ev: EventWriter<CursorEnteredWindow>,
|
||||
mouse_left_ev: EventWriter<CursorLeftWindow>,
|
||||
mouse_motion_ev: EventWriter<MouseMotion>,
|
||||
) -> anyhow::Result<()> {
|
||||
while let Some(event) = window_ev.read() {
|
||||
match event.deref() {
|
||||
|
@ -122,7 +122,7 @@ pub struct InputPlugin;
|
|||
|
||||
impl Plugin for InputPlugin {
|
||||
fn setup(&mut self, app: &mut crate::game::App) {
|
||||
app.add_resource(InputButtons::<winit::keyboard::KeyCode>::default());
|
||||
app.add_resource(InputButtons::<KeyCode>::default());
|
||||
app.add_resource(InputButtons::<MouseButton>::default());
|
||||
app.add_resource(Touches::default());
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ pub mod input;
|
|||
pub mod winit;
|
||||
pub mod as_any;
|
||||
pub mod plugin;
|
||||
pub mod change_tracker;
|
||||
|
||||
mod event;
|
||||
pub use event::*;
|
||||
|
@ -29,7 +28,4 @@ pub use lyra_ecs as ecs;
|
|||
pub use lyra_math as math;
|
||||
pub use lyra_reflect as reflect;
|
||||
|
||||
#[cfg(feature = "scripting")]
|
||||
pub use lyra_scripting as script;
|
||||
|
||||
pub use plugin::DefaultPlugins;
|
|
@ -1,8 +1,9 @@
|
|||
use lyra_reflect::Reflect;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
use crate::{math::{Angle, OPENGL_TO_WGPU_MATRIX}, scene::CameraComponent};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Reflect)]
|
||||
pub enum CameraProjectionMode {
|
||||
/// 3d camera projection
|
||||
Perspective,
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use lyra_ecs::Component;
|
||||
use lyra_reflect::Reflect;
|
||||
|
||||
use crate::{math::{Angle, Transform}, render::camera::CameraProjectionMode};
|
||||
|
||||
#[derive(Clone, Component)]
|
||||
#[derive(Clone, Component, Reflect)]
|
||||
pub struct CameraComponent {
|
||||
pub transform: Transform,
|
||||
pub fov: Angle,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use glam::{EulerRot, Quat, Vec3};
|
||||
use lyra_ecs::{query::{Res, View}, Component};
|
||||
use lyra_reflect::Reflect;
|
||||
|
||||
use crate::{game::App, input::ActionHandler, plugin::Plugin, DeltaTime};
|
||||
|
||||
|
@ -12,36 +13,29 @@ pub const ACTLBL_LOOK_LEFT_RIGHT: &str = "LookLeftRight";
|
|||
pub const ACTLBL_LOOK_UP_DOWN: &str = "LookUpDown";
|
||||
pub const ACTLBL_LOOK_ROLL: &str = "LookRoll";
|
||||
|
||||
#[derive(Clone, Component)]
|
||||
#[derive(Clone, Component, Reflect)]
|
||||
pub struct FreeFlyCamera {
|
||||
pub speed: f32,
|
||||
pub slow_speed_factor: f32,
|
||||
pub look_speed: f32,
|
||||
pub mouse_sensitivity: f32,
|
||||
pub look_with_keys: bool,
|
||||
}
|
||||
|
||||
impl Default for FreeFlyCamera {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
speed: 4.0,
|
||||
slow_speed_factor: 0.25,
|
||||
look_speed: 0.5,
|
||||
mouse_sensitivity: 0.9,
|
||||
look_with_keys: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FreeFlyCamera {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(speed: f32, slow_speed_factor: f32, look_speed: f32, mouse_sensitivity: f32, look_with_keys: bool) -> Self {
|
||||
pub fn new(speed: f32, look_speed: f32, mouse_sensitivity: f32) -> Self {
|
||||
Self {
|
||||
speed,
|
||||
slow_speed_factor,
|
||||
look_speed,
|
||||
mouse_sensitivity,
|
||||
look_with_keys,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
use lyra_ecs::Component;
|
||||
use lyra_reflect::Reflect;
|
||||
use lyra_resource::{gltf::Mesh, ResHandle};
|
||||
|
||||
#[derive(Clone, Component, Reflect)]
|
||||
pub struct MeshComponent {
|
||||
#[reflect(skip)]
|
||||
pub mesh: ResHandle<Mesh>,
|
||||
}
|
||||
|
||||
impl From<ResHandle<Mesh>> for MeshComponent {
|
||||
fn from(value: ResHandle<Mesh>) -> Self {
|
||||
Self {
|
||||
mesh: value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for MeshComponent {
|
||||
type Target = ResHandle<Mesh>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.mesh
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for MeshComponent {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.mesh
|
||||
}
|
||||
}
|
||||
|
||||
impl MeshComponent {
|
||||
pub fn new(mesh: ResHandle<Mesh>) -> Self {
|
||||
Self::from(mesh)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,3 @@
|
|||
pub mod mesh;
|
||||
pub use mesh::*;
|
||||
|
||||
pub mod camera;
|
||||
pub use camera::*;
|
||||
|
||||
|
|
|
@ -4,3 +4,5 @@ pub use plugin::*;
|
|||
|
||||
mod window;
|
||||
pub use window::*;
|
||||
|
||||
pub use winit::dpi as dpi;
|
|
@ -3,6 +3,7 @@ use std::{collections::VecDeque, sync::Arc};
|
|||
use async_std::task::block_on;
|
||||
use glam::{DVec2, IVec2, UVec2};
|
||||
use lyra_ecs::Entity;
|
||||
use lyra_reflect::Reflect;
|
||||
use rustc_hash::FxHashMap;
|
||||
use tracing::{debug, error, warn};
|
||||
use winit::{
|
||||
|
@ -12,6 +13,9 @@ use winit::{
|
|||
window::{Window, WindowAttributes, WindowId},
|
||||
};
|
||||
|
||||
pub use winit::event::{DeviceId, DeviceEvent, MouseScrollDelta, ElementState, RawKeyEvent};
|
||||
pub use winit::keyboard::PhysicalKey;
|
||||
|
||||
use crate::{
|
||||
game::{App, WindowState},
|
||||
plugin::Plugin,
|
||||
|
@ -22,10 +26,12 @@ use super::WindowOptions;
|
|||
|
||||
/// A struct that contains a [`DeviceEvent`](winit::event::DeviceEvent) with its source
|
||||
/// [`DeviceId`](winit::event::DeviceId).
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Reflect)]
|
||||
pub struct DeviceEventPair {
|
||||
pub device_src: winit::event::DeviceId,
|
||||
pub event: winit::event::DeviceEvent,
|
||||
#[reflect(skip)]
|
||||
pub device_src: DeviceId,
|
||||
#[reflect(skip)]
|
||||
pub event: DeviceEvent,
|
||||
}
|
||||
|
||||
pub struct WinitPlugin {
|
||||
|
|
|
@ -28,10 +28,6 @@ impl Default for Transform {
|
|||
|
||||
// TODO: https://www.brainvoyager.com/bv/doc/UsersGuide/CoordsAndTransforms/SpatialTransformationMatrices.html
|
||||
|
||||
#[allow(dead_code)]
|
||||
const ZERO_V3: Vec3 = Vec3::new(0.0, 0.0, 0.0);
|
||||
const ONE_V3: Vec3 = Vec3::new(1.0, 1.0, 1.0);
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Transform {
|
||||
pub fn new(translation: Vec3, rotation: Quat, scale: Vec3) -> Self {
|
||||
|
@ -43,33 +39,42 @@ impl Transform {
|
|||
}
|
||||
|
||||
pub fn from_translation(translation: Vec3) -> Self {
|
||||
Self::new(translation, Quat::IDENTITY, ONE_V3)
|
||||
Self::new(translation, Quat::IDENTITY, Vec3::ONE)
|
||||
}
|
||||
|
||||
pub fn from_xyz(x: f32, y: f32, z: f32) -> Self {
|
||||
Self::new(Vec3::new(x, y, z), Quat::IDENTITY, ONE_V3)
|
||||
Self::new(Vec3::new(x, y, z), Quat::IDENTITY, Vec3::ONE)
|
||||
}
|
||||
|
||||
pub fn calculate_mat4(&self) -> Mat4 {
|
||||
Mat4::from_scale_rotation_translation(self.scale, self.rotation, self.translation)
|
||||
}
|
||||
|
||||
/// Get the forward vector of the Transform.
|
||||
/// Returns a normalized vector pointing in the direction the Transform is facing.
|
||||
///
|
||||
/// This represents the front of the object can be used for movement, camera orientation, and
|
||||
/// other directional calculations.
|
||||
pub fn forward(&self) -> Vec3 {
|
||||
(self.rotation * -Vec3::Z).normalize()
|
||||
}
|
||||
|
||||
/// Get the left vector of the Transform.
|
||||
/// Returns a normalized vector pointing to the left side of the Transform.
|
||||
///
|
||||
/// The vector is in local space. This represents the direction that is
|
||||
/// perpendicular to the object's forward direction.
|
||||
pub fn left(&self) -> Vec3 {
|
||||
(self.rotation * Vec3::X).normalize()
|
||||
}
|
||||
|
||||
/// Get the up vector of the Transform.
|
||||
/// Returns a normalized vector that indicates the upward direction of the Transform.
|
||||
///
|
||||
/// This vector is commonly used to define an object's orientation and is essential for maintaining
|
||||
/// consistent vertical alignment in 3D environments, such as for camera positioning and object alignment.
|
||||
pub fn up(&self) -> Vec3 {
|
||||
(self.rotation * Vec3::Y).normalize()
|
||||
}
|
||||
|
||||
/// Rotate this transform using a Quaternion
|
||||
/// Rotate this transform using a Quaternion.
|
||||
pub fn rotate(&mut self, rotation: Quat) {
|
||||
self.rotation = (rotation * self.rotation).normalize();
|
||||
}
|
||||
|
@ -110,7 +115,7 @@ impl Transform {
|
|||
let mut res = *self;
|
||||
res.translation = self.translation.lerp(rhs.translation, alpha);
|
||||
// normalize rotation here to avoid panics
|
||||
res.rotation = self.rotation.lerp(rhs.rotation.normalize(), alpha);
|
||||
res.rotation = self.rotation.normalize().lerp(rhs.rotation.normalize(), alpha);
|
||||
res.scale = self.scale.lerp(rhs.scale, alpha);
|
||||
res
|
||||
} else {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use lyra_math::Angle;
|
||||
use lyra_reflect_derive::{impl_reflect_simple_struct, impl_reflect_trait_value};
|
||||
|
||||
use crate::{lyra_engine, Method, Reflect};
|
||||
use crate::{lyra_engine, Enum, Method, Reflect, ReflectMut, ReflectRef};
|
||||
|
||||
impl_reflect_simple_struct!(lyra_math::Vec2, fields(x = f32, y = f32));
|
||||
impl_reflect_simple_struct!(lyra_math::Vec3, fields(x = f32, y = f32, z = f32));
|
||||
|
@ -17,3 +18,131 @@ impl_reflect_simple_struct!(
|
|||
);
|
||||
|
||||
impl_reflect_trait_value!(lyra_math::Mat4);
|
||||
|
||||
impl Reflect for Angle {
|
||||
fn name(&self) -> String {
|
||||
"Angle".into()
|
||||
}
|
||||
|
||||
fn type_id(&self) -> std::any::TypeId {
|
||||
std::any::TypeId::of::<Self>()
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_boxed_any(self: Box<Self>) -> Box<dyn std::any::Any> {
|
||||
self
|
||||
}
|
||||
|
||||
fn apply(&mut self, val: &dyn Reflect) {
|
||||
if let ReflectRef::Enum(e) = val.reflect_ref() {
|
||||
let s = e.as_any().downcast_ref::<Self>()
|
||||
.expect("cannot apply mismatched reflected enum");
|
||||
*self = *s;
|
||||
} else {
|
||||
panic!("Provided value was not an enum!");
|
||||
}
|
||||
}
|
||||
|
||||
fn clone_inner(&self) -> Box<dyn Reflect> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn reflect_ref(&self) -> crate::ReflectRef {
|
||||
ReflectRef::Enum(self)
|
||||
}
|
||||
|
||||
fn reflect_mut(&mut self) -> crate::ReflectMut {
|
||||
ReflectMut::Enum(self)
|
||||
}
|
||||
|
||||
fn reflect_val(&self) -> &dyn Reflect {
|
||||
self
|
||||
}
|
||||
|
||||
fn reflect_val_mut(&mut self) -> &mut dyn Reflect {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Enum for Angle {
|
||||
fn field(&self, _: &str) -> Option<&dyn Reflect> {
|
||||
// no struct variants
|
||||
None
|
||||
}
|
||||
|
||||
fn field_mut(&mut self, _: &str) -> Option<&mut dyn Reflect> {
|
||||
// no struct variants
|
||||
None
|
||||
}
|
||||
|
||||
fn field_at(&self, idx: usize) -> Option<&dyn Reflect> {
|
||||
// all variants only have one tuple field
|
||||
if idx != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
match self {
|
||||
Angle::Degrees(v) => Some(v),
|
||||
Angle::Radians(v) => Some(v),
|
||||
}
|
||||
}
|
||||
|
||||
fn field_at_mut(&mut self, idx: usize) -> Option<&mut dyn Reflect> {
|
||||
// all variants only have one tuple field
|
||||
if idx != 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
match self {
|
||||
Angle::Degrees(v) => Some(v),
|
||||
Angle::Radians(v) => Some(v),
|
||||
}
|
||||
}
|
||||
|
||||
fn field_name_at(&self, _: usize) -> Option<String> {
|
||||
// no struct variants
|
||||
None
|
||||
}
|
||||
|
||||
fn has_field(&self, _: &str) -> bool {
|
||||
// no struct variants
|
||||
false
|
||||
}
|
||||
|
||||
fn fields_len(&self) -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn variants_len(&self) -> usize {
|
||||
2
|
||||
}
|
||||
|
||||
fn variant_name(&self) -> String {
|
||||
match self {
|
||||
Angle::Degrees(_) => "degrees".into(),
|
||||
Angle::Radians(_) => "radians".into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn variant_index(&self) -> usize {
|
||||
match self {
|
||||
Angle::Degrees(_) => 0,
|
||||
Angle::Radians(_) => 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_variant_name(&self, name: &str) -> bool {
|
||||
self.variant_name() == name
|
||||
}
|
||||
|
||||
fn variant_type(&self) -> crate::EnumType {
|
||||
crate::EnumType::Tuple
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ use std::ops::Deref;
|
|||
|
||||
use lyra_ecs::{query::{filter::{Has, Not}, Entities, View}, relation::{ChildOf, RelationOriginComponent}, Component, Entity, World};
|
||||
use lyra_math::Transform;
|
||||
use lyra_reflect::Reflect;
|
||||
use crate::lyra_engine;
|
||||
|
||||
/// The world transform of an entity.
|
||||
|
@ -11,7 +12,7 @@ use crate::lyra_engine;
|
|||
/// you should use its [`Transform`]. You cannot mutate [`WorldTransform`] as its managed completey
|
||||
/// by the [`system_update_world_transforms`] system. For the WorldTransform to work properly, you
|
||||
/// must have both a [`Transform`] and [`WorldTransform`] on the entities in the scene.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Default, Component)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Default, Component, Reflect)]
|
||||
pub struct WorldTransform(pub(crate) Transform);
|
||||
|
||||
impl Deref for WorldTransform {
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
use syn::{parenthesized, token, Token};
|
||||
|
||||
pub(crate) enum FieldType {
|
||||
Unknown,
|
||||
Type(syn::Path),
|
||||
Wrapped(syn::Path),
|
||||
}
|
||||
|
||||
impl FieldType {
|
||||
pub fn is_unknown(&self) -> bool {
|
||||
matches!(self, FieldType::Unknown)
|
||||
}
|
||||
|
||||
pub fn is_wrapped(&self) -> bool {
|
||||
matches!(self, FieldType::Wrapped(_))
|
||||
}
|
||||
|
||||
pub fn get_type_path(&self) -> Option<&syn::Path> {
|
||||
match self {
|
||||
FieldType::Unknown => None,
|
||||
FieldType::Type(path) => Some(path),
|
||||
FieldType::Wrapped(path) => Some(path),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct Field {
|
||||
pub field: syn::Ident,
|
||||
pub field_ty: FieldType,
|
||||
pub skip_setter: bool,
|
||||
pub setter: Option<syn::Block>,
|
||||
pub getter: Option<syn::Block>,
|
||||
}
|
||||
|
||||
impl Field {
|
||||
fn parse_extended(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let field_name = input.parse()?;
|
||||
|
||||
let fty = if input.peek(Token![:]) {
|
||||
let _col: Token![:] = input.parse()?;
|
||||
let s: syn::Path = input.parse()?;
|
||||
let mut fty = FieldType::Type(s.clone());
|
||||
|
||||
if let Some(ident) = s.get_ident() {
|
||||
if ident.to_string() == "wrap" {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
fty = FieldType::Wrapped(content.parse()?);
|
||||
}
|
||||
}
|
||||
|
||||
fty
|
||||
} else {
|
||||
FieldType::Unknown
|
||||
};
|
||||
|
||||
let mut s = Self {
|
||||
field: field_name,
|
||||
field_ty: fty,
|
||||
skip_setter: false,
|
||||
setter: None,
|
||||
getter: None,
|
||||
};
|
||||
|
||||
while input.peek(Token![,]) {
|
||||
let _: Token![,] = input.parse()?;
|
||||
|
||||
if input.peek(syn::Ident) {
|
||||
let ident: syn::Ident = input.parse()?;
|
||||
let ident_str = ident.to_string();
|
||||
let ident_str = ident_str.as_str();
|
||||
|
||||
match ident_str {
|
||||
"skip_set" => {
|
||||
s.skip_setter = true;
|
||||
}
|
||||
"set" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
|
||||
s.setter = Some(input.parse()?);
|
||||
}
|
||||
"get" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
|
||||
s.getter = Some(input.parse()?);
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(ident, "unknown wrapper command"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s.getter.is_some() || s.setter.is_some()) && s.field_ty.is_wrapped() {
|
||||
return Err(syn::Error::new(
|
||||
input.span(),
|
||||
"cannot specify custom getter or setter \
|
||||
with wrapped type",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for Field {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
Self::parse_extended(&content)
|
||||
} else {
|
||||
let field_name = input.parse()?;
|
||||
|
||||
let fty = if input.peek(Token![:]) {
|
||||
let _col: Token![:] = input.parse()?;
|
||||
let s: syn::Path = input.parse()?;
|
||||
let mut fty = FieldType::Type(s.clone());
|
||||
|
||||
if let Some(ident) = s.get_ident() {
|
||||
if ident.to_string() == "wrap" {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
fty = FieldType::Wrapped(content.parse()?);
|
||||
}
|
||||
}
|
||||
|
||||
fty
|
||||
} else {
|
||||
FieldType::Unknown
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
field: field_name,
|
||||
field_ty: fty,
|
||||
skip_setter: false,
|
||||
setter: None,
|
||||
getter: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
use quote::{format_ident, quote};
|
||||
use syn::{braced, parenthesized, parse_macro_input, token, Ident, Path, Token};
|
||||
|
||||
struct FieldGetter {
|
||||
field: Ident,
|
||||
body: Option<syn::Block>,
|
||||
wrapper_type: Option<syn::Path>,
|
||||
pub(crate) struct FieldGetter {
|
||||
pub field: Ident,
|
||||
pub body: Option<syn::Block>,
|
||||
pub wrapper_type: Option<syn::Path>,
|
||||
}
|
||||
|
||||
impl FieldGetter {
|
||||
|
@ -131,6 +131,7 @@ impl syn::parse::Parse for HandleWrapUsage {
|
|||
pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as HandleWrapUsage);
|
||||
|
||||
let handle_path = &input.type_path;
|
||||
let handle_name = &input.type_path.segments.last().unwrap().ident;
|
||||
|
||||
let base_name = input.override_name.unwrap_or_else(|| handle_name.clone());
|
||||
|
@ -145,7 +146,7 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
|||
|
||||
let field_creator = match &g.wrapper_type {
|
||||
Some(wrap) => {
|
||||
quote!(#wrap(data.#field).into_lua(lua))
|
||||
quote!(#wrap(data.#field.clone()).into_lua(lua))
|
||||
},
|
||||
None => match &g.body {
|
||||
Some(body) => {
|
||||
|
@ -170,18 +171,18 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
|||
|
||||
quote! {
|
||||
#[derive(Clone, Reflect)]
|
||||
pub struct #wrapper_name(pub ResHandle<#handle_name>);
|
||||
pub struct #wrapper_name(pub ResHandle<#handle_path>);
|
||||
|
||||
impl Deref for #wrapper_name {
|
||||
type Target = ResHandle<#handle_name>;
|
||||
type Target = ResHandle<#handle_path>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ResHandle<#handle_name>> for #wrapper_name {
|
||||
fn from(value: ResHandle<#handle_name>) -> Self {
|
||||
impl From<ResHandle<#handle_path>> for #wrapper_name {
|
||||
fn from(value: ResHandle<#handle_path>) -> Self {
|
||||
#wrapper_name(value)
|
||||
}
|
||||
}
|
||||
|
@ -224,7 +225,7 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
|||
});
|
||||
|
||||
methods.add_function(FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
|
||||
Ok(ScriptBorrow::from_component::<ResHandle<#handle_name>>(None))
|
||||
Ok(ScriptBorrow::from_component::<ResHandle<#handle_path>>(None))
|
||||
});
|
||||
methods.add_method(FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
|
||||
Ok(ScriptBorrow::from_component(Some(this.0.clone())))
|
||||
|
@ -246,8 +247,22 @@ pub(crate) fn lua_wrap_handle_impl(input: proc_macro::TokenStream) -> proc_macro
|
|||
}
|
||||
|
||||
impl LuaWrapper for #wrapper_name {
|
||||
type Wrap = ResHandle<#handle_path>;
|
||||
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
TypeId::of::<ResHandle<#handle_name>>()
|
||||
TypeId::of::<ResHandle<#handle_path>>()
|
||||
}
|
||||
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaHandleWrapper for #wrapper_name {
|
||||
type ResourceType = #handle_path;
|
||||
|
||||
fn from_handle(handle: ResHandle<Self::ResourceType>) -> Self {
|
||||
Self(handle)
|
||||
}
|
||||
}
|
||||
}.into()
|
||||
|
|
|
@ -5,9 +5,12 @@ use syn::{parse_macro_input, Token};
|
|||
|
||||
mod mat_wrapper;
|
||||
mod vec_wrapper;
|
||||
use to_lua_macro::to_lua_struct_impl;
|
||||
use vec_wrapper::VecWrapper;
|
||||
|
||||
mod lua_macro;
|
||||
mod to_lua_macro;
|
||||
mod field;
|
||||
|
||||
mod handle_macro;
|
||||
|
||||
|
@ -24,6 +27,11 @@ pub fn lua_wrap_handle(input: proc_macro::TokenStream) -> proc_macro::TokenStrea
|
|||
lua_wrap_handle_impl(input)
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
pub fn to_lua_convert(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
to_lua_struct_impl(input)
|
||||
}
|
||||
|
||||
pub(crate) struct VecExtensionInputs {
|
||||
#[allow(dead_code)]
|
||||
pub type_path: syn::Path,
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
use proc_macro2::Span;
|
||||
use quote::quote;
|
||||
use syn::{parenthesized, parse_macro_input, punctuated::Punctuated, token, Ident, Path, Token};
|
||||
use syn::{
|
||||
braced, parenthesized, parse_macro_input, punctuated::Punctuated, token, Ident, Path, Token,
|
||||
};
|
||||
|
||||
use crate::{FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE};
|
||||
use crate::{field::{Field, FieldType}, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
enum SkipType {
|
||||
/// Skips implementing
|
||||
LuaReflect
|
||||
LuaReflect,
|
||||
LuaWrapper,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for SkipType {
|
||||
|
@ -17,9 +20,8 @@ impl syn::parse::Parse for SkipType {
|
|||
|
||||
match name_str.as_str() {
|
||||
"lua_reflect" => Ok(Self::LuaReflect),
|
||||
_ => {
|
||||
Err(syn::Error::new_spanned(name, "unknown skip type"))
|
||||
}
|
||||
"lua_wrapper" => Ok(Self::LuaWrapper),
|
||||
_ => Err(syn::Error::new_spanned(name, "unknown skip type")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,22 +45,21 @@ impl MetaMethod {
|
|||
let mm_str = mm_str.as_str();
|
||||
match mm_str {
|
||||
"Add" | "Sub" | "Div" | "Mul" | "Mod" | "Eq" | "Shl" | "Shr" | "BAnd" | "BOr"
|
||||
| "BXor" => {
|
||||
true
|
||||
},
|
||||
"Unm" | "BNot" | "ToString" => {
|
||||
false
|
||||
},
|
||||
| "BXor" => true,
|
||||
"Unm" | "BNot" | "ToString" => false,
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// returns the tokens of the body of the metamethod
|
||||
///
|
||||
///
|
||||
/// Parameters
|
||||
/// * `metamethod` - The ident of the metamethod that is being implemented.
|
||||
/// * `other` - The tokens of the argument used in the metamethod.
|
||||
fn get_method_body(metamethod: &Ident, other: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
||||
fn get_method_body(
|
||||
metamethod: &Ident,
|
||||
other: proc_macro2::TokenStream,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let mm_str = metamethod.to_string();
|
||||
let mm_str = mm_str.as_str();
|
||||
match mm_str {
|
||||
|
@ -75,17 +76,17 @@ impl MetaMethod {
|
|||
quote! {
|
||||
Ok(Self(this.0 #symbol #other))
|
||||
}
|
||||
},
|
||||
}
|
||||
"Unm" => {
|
||||
quote! {
|
||||
Ok(Self(-this.0))
|
||||
}
|
||||
},
|
||||
}
|
||||
"Eq" => {
|
||||
quote! {
|
||||
Ok(this.0 == #other)
|
||||
}
|
||||
},
|
||||
}
|
||||
"Shl" => {
|
||||
quote! {
|
||||
Ok(Self(this.0 << #other))
|
||||
|
@ -95,41 +96,47 @@ impl MetaMethod {
|
|||
quote! {
|
||||
Ok(Self(this.0 >> #other))
|
||||
}
|
||||
},
|
||||
}
|
||||
"BAnd" | "BOr" | "BXor" => {
|
||||
let symbol = match mm_str {
|
||||
"BAnd" => {
|
||||
quote!(&)
|
||||
},
|
||||
}
|
||||
"BOr" => {
|
||||
quote!(|)
|
||||
},
|
||||
}
|
||||
"BXor" => {
|
||||
quote!(^)
|
||||
},
|
||||
_ => unreachable!() // the string was just checked to be one of these
|
||||
}
|
||||
_ => unreachable!(), // the string was just checked to be one of these
|
||||
};
|
||||
|
||||
quote! {
|
||||
Ok(Self(this.0 #symbol #other))
|
||||
}
|
||||
},
|
||||
}
|
||||
"BNot" => {
|
||||
quote! {
|
||||
Ok(Self(!this.0))
|
||||
}
|
||||
},
|
||||
}
|
||||
"ToString" => {
|
||||
quote! {
|
||||
Ok(format!("{:?}", this.0))
|
||||
}
|
||||
},
|
||||
_ => syn::Error::new_spanned(metamethod,
|
||||
"unsupported auto implementation of metamethod").to_compile_error(),
|
||||
}
|
||||
_ => {
|
||||
syn::Error::new_spanned(metamethod, "unsupported auto implementation of metamethod")
|
||||
.to_compile_error()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_body_for_arg(mt_ident: &Ident, arg_ident: &Ident, arg_param: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
||||
fn get_body_for_arg(
|
||||
mt_ident: &Ident,
|
||||
arg_ident: &Ident,
|
||||
arg_param: proc_macro2::TokenStream,
|
||||
) -> proc_macro2::TokenStream {
|
||||
let other: proc_macro2::TokenStream = if Self::is_arg_wrapper(arg_ident) {
|
||||
// Lua wrappers must be dereferenced
|
||||
quote! {
|
||||
|
@ -167,7 +174,6 @@ impl MetaMethod {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
} else if self.arg.len() == 1 {
|
||||
let first = self.arg.iter().next().unwrap();
|
||||
let body = Self::get_body_for_arg(&self.name, first, quote!(v));
|
||||
|
@ -184,38 +190,41 @@ impl MetaMethod {
|
|||
let is = i.to_string();
|
||||
let is = is.as_str();
|
||||
match is {
|
||||
"u8" | "u16" | "u32" | "u64" | "u128"
|
||||
| "i8" | "i16" | "i32" | "i64" | "i128"
|
||||
| "f32" | "f64" => true,
|
||||
"u8" | "u16" | "u32" | "u64" | "u128" | "i8" | "i16" | "i32" | "i64"
|
||||
| "i128" | "f32" | "f64" => true,
|
||||
_ => false,
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(num_ident) = num_ident {
|
||||
let body = Self::get_body_for_arg(&self.name, num_ident, quote!(n as #num_ident));
|
||||
let body =
|
||||
Self::get_body_for_arg(&self.name, num_ident, quote!(n as #num_ident));
|
||||
|
||||
quote! {
|
||||
mlua::Value::Number(n) => {
|
||||
#body
|
||||
},
|
||||
}
|
||||
} else { quote!() }
|
||||
} else {
|
||||
quote!()
|
||||
}
|
||||
};
|
||||
|
||||
let userdata_arm = {
|
||||
let wrappers: Vec<&Ident> = self.arg.iter()
|
||||
let wrappers: Vec<&Ident> = self
|
||||
.arg
|
||||
.iter()
|
||||
.filter(|i| Self::is_arg_wrapper(i))
|
||||
.collect();
|
||||
|
||||
let if_statements = wrappers.iter().map(|i| {
|
||||
let body = Self::get_method_body(&self.name, quote!(other.0));
|
||||
|
||||
|
||||
quote! {
|
||||
if let Ok(other) = ud.borrow::<#i>() {
|
||||
#body
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
quote! {
|
||||
|
@ -226,7 +235,7 @@ impl MetaMethod {
|
|||
// try to get the name of the userdata for the error message
|
||||
if let Ok(mt) = ud.metatable() {
|
||||
if let Ok(name) = mt.get::<String>("__name") {
|
||||
return Err(mlua::Error::BadArgument {
|
||||
return Err(mlua::Error::BadArgument {
|
||||
to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
|
||||
pos: 2,
|
||||
name: Some("rhs".to_string()),
|
||||
|
@ -236,8 +245,8 @@ impl MetaMethod {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
Err(mlua::Error::BadArgument {
|
||||
|
||||
Err(mlua::Error::BadArgument {
|
||||
to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
|
||||
pos: 2,
|
||||
name: Some("rhs".to_string()),
|
||||
|
@ -249,13 +258,13 @@ impl MetaMethod {
|
|||
},
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
quote! {
|
||||
methods.add_meta_method(mlua::MetaMethod::#mt_ident, |_, this, (v,): (mlua::Value,)| {
|
||||
match v {
|
||||
#number_arm
|
||||
#userdata_arm
|
||||
_ => Err(mlua::Error::BadArgument {
|
||||
_ => Err(mlua::Error::BadArgument {
|
||||
to: Some(format!("{}.__{}", #wrapped_str, #mt_lua_name)),
|
||||
pos: 2,
|
||||
name: Some("rhs".to_string()),
|
||||
|
@ -274,17 +283,15 @@ impl syn::parse::Parse for MetaMethod {
|
|||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let name: Ident = input.parse()?;
|
||||
|
||||
let mut s = Self {
|
||||
name,
|
||||
arg: vec![],
|
||||
};
|
||||
let mut s = Self { name, arg: vec![] };
|
||||
|
||||
// try to parse args
|
||||
if input.peek(syn::token::Paren) {
|
||||
let content;
|
||||
let _parens: syn::token::Paren = parenthesized!(content in input);
|
||||
|
||||
let arg: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
|
||||
let arg: Punctuated<Ident, Token![,]> =
|
||||
content.parse_terminated(Ident::parse, Token![,])?;
|
||||
s.arg = arg.into_iter().collect(); // convert to Vec<Ident>
|
||||
}
|
||||
|
||||
|
@ -296,7 +303,7 @@ struct WrapUsage {
|
|||
type_path: syn::Path,
|
||||
/// The extra derives of the type.
|
||||
override_name: Option<Ident>,
|
||||
auto_fields: Vec<Ident>,
|
||||
auto_fields: Vec<Field>,
|
||||
auto_derives: Vec<Ident>,
|
||||
auto_new: bool,
|
||||
meta_methods: Vec<MetaMethod>,
|
||||
|
@ -322,7 +329,7 @@ impl syn::parse::Parse for WrapUsage {
|
|||
};
|
||||
|
||||
let mut new_ident = None;
|
||||
|
||||
|
||||
while input.peek(Token![,]) {
|
||||
let _: Token![,] = input.parse()?;
|
||||
|
||||
|
@ -337,62 +344,72 @@ impl syn::parse::Parse for WrapUsage {
|
|||
|
||||
let name: Ident = input.parse()?;
|
||||
s.override_name = Some(name);
|
||||
},
|
||||
}
|
||||
"new" => {
|
||||
s.auto_new = true;
|
||||
new_ident = Some(ident.clone());
|
||||
},
|
||||
}
|
||||
"skip" => {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let terminated = content.parse_terminated(SkipType::parse, Token![,])?;
|
||||
s.skips = terminated.into_iter().collect();
|
||||
},
|
||||
}
|
||||
"derives" => {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let derives: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
|
||||
|
||||
let derives: Punctuated<Ident, Token![,]> =
|
||||
content.parse_terminated(Ident::parse, Token![,])?;
|
||||
s.auto_derives = derives.into_iter().collect();
|
||||
}
|
||||
},
|
||||
}
|
||||
"fields" => {
|
||||
if input.peek(token::Paren) {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
|
||||
if input.peek(token::Brace) {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let fields: Punctuated<Ident, Token![,]> = content.parse_terminated(Ident::parse, Token![,])?;
|
||||
s.auto_fields = fields.into_iter().collect();
|
||||
let _braced: token::Brace = braced!(content in input);
|
||||
|
||||
let terminated = content.parse_terminated(Field::parse, Token![,])?;
|
||||
s.auto_fields.extend(terminated.into_iter());
|
||||
}
|
||||
},
|
||||
}
|
||||
"metamethods" => {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _bracket: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let meta_methods: Punctuated<MetaMethod, Token![,]> = content.parse_terminated(MetaMethod::parse, Token![,])?;
|
||||
|
||||
let meta_methods: Punctuated<MetaMethod, Token![,]> =
|
||||
content.parse_terminated(MetaMethod::parse, Token![,])?;
|
||||
s.meta_methods = meta_methods.into_iter().collect();
|
||||
}
|
||||
},
|
||||
}
|
||||
"extra_fields" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
s.extra_fields = Some(input.parse::<syn::Block>()?);
|
||||
},
|
||||
}
|
||||
"extra_methods" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
s.extra_methods = Some(input.parse::<syn::Block>()?);
|
||||
},
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(ident, format!("unknown wrapper command: '{}'", ident_str)));
|
||||
return Err(syn::Error::new_spanned(
|
||||
ident,
|
||||
format!("unknown wrapper command: '{}'", ident_str),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.auto_new && s.auto_fields.is_empty() {
|
||||
return Err(syn::Error::new_spanned(new_ident.unwrap(), "must specify 'fields' when auto creating new function"));
|
||||
return Err(syn::Error::new_spanned(
|
||||
new_ident.unwrap(),
|
||||
"must specify 'fields' when auto creating new function",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
|
@ -404,51 +421,50 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
let input = parse_macro_input!(input as WrapUsage);
|
||||
|
||||
let path: Path = input.type_path;
|
||||
let type_name = &path.segments.last()
|
||||
let type_name = &path
|
||||
.segments
|
||||
.last()
|
||||
.expect("Failure to find typename in macro usage!")
|
||||
.ident;
|
||||
let wrapper_typename = input.override_name
|
||||
let wrapper_typename = input
|
||||
.override_name
|
||||
.unwrap_or_else(|| Ident::new(&format!("Lua{}", type_name), Span::call_site()));
|
||||
|
||||
let derive_idents_iter = input.auto_derives.iter();
|
||||
let extra_fields = input.extra_fields;
|
||||
let extra_methods = input.extra_methods;
|
||||
|
||||
let field_get_set_pairs = input.auto_fields.iter().map(|i| {
|
||||
let is = i.to_string();
|
||||
quote! {
|
||||
fields.add_field_method_get(#is, |_, this| {
|
||||
Ok(this.#i)
|
||||
});
|
||||
fields.add_field_method_set(#is, |_, this, #i| {
|
||||
this.#i = #i;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// the tokens for the new function
|
||||
let new_func_tokens = if input.auto_new {
|
||||
let arg_names = input.auto_fields.iter().map(|i| {
|
||||
Ident::new(&i.to_string().to_lowercase(), Span::call_site())
|
||||
});
|
||||
let arg_names = input
|
||||
.auto_fields
|
||||
.iter()
|
||||
.map(|i| Ident::new(&i.field.to_string().to_lowercase(), Span::call_site()));
|
||||
|
||||
let arg_types = input
|
||||
.auto_fields
|
||||
.iter()
|
||||
.map(|i| i.field_ty.get_type_path().unwrap());
|
||||
|
||||
let arg_names_clone = arg_names.clone();
|
||||
let arg_types_clone = arg_types.clone();
|
||||
|
||||
quote! {
|
||||
// arguments for function are not specified since they can be implied from the call
|
||||
// to new(...)
|
||||
methods.add_function("new", |_, ( #(#arg_names_clone),* )| {
|
||||
methods.add_function("new", |_, ( #(#arg_names_clone),* ): ( #(#arg_types_clone),* ) | {
|
||||
Ok(#wrapper_typename(#path::new( #(#arg_names),* )))
|
||||
});
|
||||
}
|
||||
|
||||
} else { quote!() };
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
let meta_methods_tokens = {
|
||||
let method_tokens = input.meta_methods.iter().map(|mm| {
|
||||
mm.to_tokens(&wrapper_typename)
|
||||
});
|
||||
let method_tokens = input
|
||||
.meta_methods
|
||||
.iter()
|
||||
.map(|mm| mm.to_tokens(&wrapper_typename));
|
||||
|
||||
quote! {
|
||||
#(#method_tokens)*
|
||||
|
@ -462,25 +478,113 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
methods.add_method(#FN_NAME_INTERNAL_REFLECT, |_, this, ()| {
|
||||
Ok(crate::ScriptBorrow::from_component::<#path>(Some(this.0.clone())))
|
||||
});
|
||||
|
||||
|
||||
methods.add_function(#FN_NAME_INTERNAL_REFLECT_TYPE, |_, ()| {
|
||||
Ok(crate::ScriptBorrow::from_component::<#path>(None))
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let lua_wrapper = if input.skips.contains(&SkipType::LuaWrapper) {
|
||||
quote!()
|
||||
} else {
|
||||
quote! {
|
||||
impl lyra_scripting::lua::LuaWrapper for #wrapper_typename {
|
||||
type Wrap = #path;
|
||||
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<#path>()
|
||||
}
|
||||
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for field in input.auto_fields.iter() {
|
||||
if field.field_ty.is_unknown() && !field.skip_setter {
|
||||
return syn::Error::new(
|
||||
field.field.span(),
|
||||
"missing type of field, must be specified to generate setters",
|
||||
)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
}
|
||||
|
||||
let fields = input.auto_fields.iter().map(|g| {
|
||||
let field = &g.field;
|
||||
|
||||
let field_getter = match &g.field_ty {
|
||||
FieldType::Wrapped(wrap) => {
|
||||
quote!(#wrap(this.#field.clone()).into_lua(lua))
|
||||
}
|
||||
_ => match &g.getter {
|
||||
Some(body) => {
|
||||
quote!(#body)
|
||||
}
|
||||
None => {
|
||||
quote!(this.#field.clone().into_lua(lua))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let field_setter = if g.skip_setter {
|
||||
quote! {}
|
||||
} else {
|
||||
let fty = g
|
||||
.field_ty
|
||||
.get_type_path()
|
||||
// should be unreachable due to the checks right before this closure
|
||||
.expect("no field type specified");
|
||||
let s = if g.field_ty.is_wrapped() {
|
||||
quote! {
|
||||
this.#field = #field.0.clone();
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
match &g.setter {
|
||||
Some(body) => {
|
||||
quote!(#body)
|
||||
}
|
||||
None => {
|
||||
quote! {
|
||||
this.#field = #field.clone();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
fields.add_field_method_set(stringify!(#field), |_, this, #field: #fty| {
|
||||
#s
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
fields.add_field_method_get(stringify!(#field), |lua, this| {
|
||||
#field_getter
|
||||
});
|
||||
#field_setter
|
||||
}
|
||||
});
|
||||
|
||||
proc_macro::TokenStream::from(quote! {
|
||||
#[derive(Clone, lyra_reflect::Reflect, #(#derive_idents_iter),*)]
|
||||
pub struct #wrapper_typename(#[reflect(skip)] pub(crate) #path);
|
||||
|
||||
impl std::ops::Deref for #wrapper_typename {
|
||||
type Target = #path;
|
||||
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl std::ops::DerefMut for #wrapper_typename {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
|
@ -498,12 +602,18 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
|
||||
impl mlua::UserData for #wrapper_typename {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
#(#field_get_set_pairs)*
|
||||
use mlua::IntoLua;
|
||||
use mlua::FromLua;
|
||||
|
||||
#(#fields)*
|
||||
|
||||
#extra_fields
|
||||
}
|
||||
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
use mlua::IntoLua;
|
||||
use mlua::FromLua;
|
||||
|
||||
#lua_reflects
|
||||
|
||||
#new_func_tokens
|
||||
|
@ -512,10 +622,6 @@ pub fn wrap_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::Token
|
|||
}
|
||||
}
|
||||
|
||||
impl lyra_scripting::lua::LuaWrapper for #wrapper_typename {
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<#path>()
|
||||
}
|
||||
}
|
||||
#lua_wrapper
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,362 @@
|
|||
use proc_macro2::Span;
|
||||
use syn::{braced, parenthesized, parse_macro_input, punctuated::Punctuated, token, Token};
|
||||
use quote::{quote, ToTokens};
|
||||
use crate::{field::Field, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE};
|
||||
|
||||
fn field_table_setter(field: &Field) -> proc_macro2::TokenStream {
|
||||
let ident = &field.field;
|
||||
|
||||
match &field.setter {
|
||||
Some(set) => {
|
||||
quote! {
|
||||
table.set(stringify!(#ident), #set)?;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let ty = field.field_ty.get_type_path()
|
||||
.expect("no field type specified");
|
||||
|
||||
let arg = if field.field_ty.is_wrapped() {
|
||||
quote!(#ty(self.#ident))
|
||||
} else { quote!(self.#ident) };
|
||||
|
||||
quote! {
|
||||
table.set(stringify!(#ident), #arg)?;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn field_table_getter(field: &Field) -> proc_macro2::TokenStream {
|
||||
let ident = &field.field;
|
||||
|
||||
match &field.getter {
|
||||
Some(get) => {
|
||||
quote! {
|
||||
let #ident = #get;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
let ty = field.field_ty.get_type_path()
|
||||
.expect("no field type specified");
|
||||
|
||||
quote! {
|
||||
let #ident: #ty = table.get(stringify!(#ident))?;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn wrapper_creation(wrapper: &syn::Ident, type_path: &syn::Path, struct_type: StructType, create: Option<&syn::Block>, fields: &Vec<Field>) -> proc_macro2::TokenStream {
|
||||
|
||||
match create {
|
||||
Some(b) => quote!(#b),
|
||||
None => {
|
||||
/* let field_iter = fields.iter().map(|f| match &f.field_ty {
|
||||
crate::field::FieldType::Type(path) => quote!(#path),
|
||||
crate::field::FieldType::Wrapped(path) => quote!(*#path),
|
||||
_ => todo!()
|
||||
}); */
|
||||
let field_iter = fields.iter().map(|f| {
|
||||
let ident = &f.field;
|
||||
if f.field_ty.is_wrapped() && struct_type == StructType::Fields {
|
||||
quote!(#ident: (*#ident).clone())
|
||||
} else {
|
||||
quote!(#ident)
|
||||
}
|
||||
});
|
||||
|
||||
match struct_type {
|
||||
StructType::Fields => {
|
||||
quote! {
|
||||
#wrapper(#type_path {
|
||||
#(
|
||||
#field_iter
|
||||
),*
|
||||
})
|
||||
}
|
||||
},
|
||||
StructType::Tuple => {
|
||||
quote! {
|
||||
#wrapper(#type_path( #(#field_iter),* ))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn get_reflect_lua_functions(ty: &ReflectType, type_path: &syn::Path, set_data: bool) -> proc_macro2::TokenStream {
|
||||
let data = if set_data {
|
||||
quote!(Some(this.into_wrapped()))
|
||||
} else { quote!(None) };
|
||||
|
||||
match ty {
|
||||
ReflectType::Component => {
|
||||
quote! {
|
||||
Ok(ScriptBorrow::from_component::<#type_path>(#data))
|
||||
}
|
||||
},
|
||||
ReflectType::Resource => {
|
||||
quote! {
|
||||
Ok(ScriptBorrow::from_component::<#type_path>(#data))
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ReflectType {
|
||||
//Unknown,
|
||||
Component,
|
||||
Resource,
|
||||
}
|
||||
|
||||
/// The type of the wrapping struct
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
enum StructType {
|
||||
#[default]
|
||||
Fields,
|
||||
Tuple,
|
||||
}
|
||||
|
||||
struct IntoLuaUsage {
|
||||
type_path: syn::Path,
|
||||
struct_type: StructType,
|
||||
override_name: Option<syn::Ident>,
|
||||
table_name: String,
|
||||
derives: Vec<syn::Ident>,
|
||||
fields: Vec<Field>,
|
||||
create: Option<syn::Block>,
|
||||
reflection_type: Option<ReflectType>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for IntoLuaUsage {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let type_path: syn::Path = input.parse()?;
|
||||
let type_ident = &type_path
|
||||
.segments
|
||||
.last()
|
||||
.expect("Failure to find typename in macro usage!")
|
||||
.ident;
|
||||
let lua_name = type_ident.to_string();
|
||||
|
||||
let mut s = Self {
|
||||
type_path,
|
||||
struct_type: StructType::Fields,
|
||||
override_name: None,
|
||||
table_name: lua_name,
|
||||
derives: vec![],
|
||||
fields: vec![],
|
||||
create: None,
|
||||
reflection_type: None,
|
||||
};
|
||||
|
||||
while input.peek(Token![,]) {
|
||||
let _: Token![,] = input.parse()?;
|
||||
|
||||
if input.peek(syn::Ident) {
|
||||
let ident: syn::Ident = input.parse()?;
|
||||
let ident_str = ident.to_string();
|
||||
let ident_str = ident_str.as_str();
|
||||
|
||||
match ident_str {
|
||||
"name" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
|
||||
let name: syn::Ident = input.parse()?;
|
||||
s.override_name = Some(name);
|
||||
},
|
||||
"struct_type" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
|
||||
let st_token = input.parse::<syn::LitStr>()?;
|
||||
let st_str = st_token.value().to_lowercase();
|
||||
let st_str = st_str.as_str();
|
||||
|
||||
let st = match st_str {
|
||||
"fields" => StructType::Fields,
|
||||
"tuple" => StructType::Tuple,
|
||||
_ => return Err(syn::Error::new_spanned(
|
||||
st_token,
|
||||
format!("unknown struct type: '{}', expected 'fields', or `tuple`", st_str),
|
||||
)),
|
||||
};
|
||||
s.struct_type = st;
|
||||
},
|
||||
"lua_name" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
s.table_name = input.parse::<syn::LitStr>()?.value();
|
||||
},
|
||||
"derives" => {
|
||||
if input.peek(token::Paren) {
|
||||
let content;
|
||||
let _parens: token::Paren = parenthesized!(content in input);
|
||||
|
||||
let derives: Punctuated<syn::Ident, Token![,]> =
|
||||
content.parse_terminated(syn::Ident::parse, Token![,])?;
|
||||
s.derives = derives.into_iter().collect();
|
||||
}
|
||||
},
|
||||
"fields" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
|
||||
if input.peek(token::Brace) {
|
||||
let content;
|
||||
let _braced: token::Brace = braced!(content in input);
|
||||
|
||||
let terminated = content.parse_terminated(Field::parse, Token![,])?;
|
||||
s.fields.extend(terminated.into_iter());
|
||||
}
|
||||
},
|
||||
"create" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
s.create = Some(input.parse()?);
|
||||
},
|
||||
"reflect" => {
|
||||
let _eq: Token![=] = input.parse()?;
|
||||
let ty: syn::Ident = input.parse()?;
|
||||
let ty_str = ty.to_string();
|
||||
let ty_str = ty_str.as_str();
|
||||
|
||||
let ty = match ty_str {
|
||||
"component" => ReflectType::Component,
|
||||
"resource" => ReflectType::Resource,
|
||||
_ => return Err(syn::Error::new_spanned(
|
||||
ident,
|
||||
format!("unknown wrapper type: '{}', expected 'component' or 'resource'", ty_str),
|
||||
)),
|
||||
};
|
||||
|
||||
s.reflection_type = Some(ty);
|
||||
},
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
ident,
|
||||
format!("unknown wrapper command: '{}'", ident_str),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if s.reflection_type.is_none() {
|
||||
return Err(syn::Error::new(
|
||||
input.span(),
|
||||
format!("Wrapper type not specified! Expected 'type=component' or 'type=resource'"),
|
||||
));
|
||||
}
|
||||
|
||||
if s.table_name.is_empty() {
|
||||
return Err(syn::Error::new(
|
||||
input.span(),
|
||||
format!("No lua table specified. Use 'lua_name=\"Camera\"'"),
|
||||
))
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_lua_struct_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
let input = parse_macro_input!(input as IntoLuaUsage);
|
||||
|
||||
// unwrap is fine since `Some` is ensured in parse impl
|
||||
let reflect_type = input.reflection_type.as_ref().unwrap();
|
||||
let type_path = &input.type_path;
|
||||
let type_name = &type_path
|
||||
.segments
|
||||
.last()
|
||||
.expect("Failure to find typename in macro usage!")
|
||||
.ident;
|
||||
let wrapper = input.override_name
|
||||
.unwrap_or_else(|| syn::Ident::new(&format!("Lua{}", type_name), Span::call_site()));
|
||||
|
||||
let derives_iter = input.derives.into_iter();
|
||||
|
||||
let lua_name = &input.table_name;
|
||||
let field_getters_iter = input.fields.iter().map(|f| field_table_getter(f));
|
||||
let field_setters_iter = input.fields.iter().map(|f| field_table_setter(f));
|
||||
let struct_creator = wrapper_creation(&wrapper, type_path, input.struct_type, input.create.as_ref(), &input.fields);
|
||||
let reflect_fn = get_reflect_lua_functions(reflect_type, &input.type_path, true);
|
||||
let reflect_type_fn = get_reflect_lua_functions(reflect_type, &input.type_path, false);
|
||||
|
||||
quote! {
|
||||
#[derive(Clone, #(#derives_iter),*)]
|
||||
pub struct #wrapper(pub(crate) #type_path);
|
||||
|
||||
impl std::ops::Deref for #wrapper {
|
||||
type Target = #type_path;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for #wrapper {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::FromLua for #wrapper {
|
||||
fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let ty = val.type_name();
|
||||
let table = val.as_table().ok_or(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "Table".into(),
|
||||
message: Some("expected Table".into()),
|
||||
})?;
|
||||
|
||||
#(
|
||||
#field_getters_iter
|
||||
)*
|
||||
|
||||
Ok(#struct_creator)
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::IntoLua for #wrapper {
|
||||
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
|
||||
let table = lua.create_table()?;
|
||||
#(
|
||||
#field_setters_iter
|
||||
)*
|
||||
|
||||
table.set(
|
||||
#FN_NAME_INTERNAL_REFLECT,
|
||||
lua.create_function(|_, this: Self| {
|
||||
#reflect_fn
|
||||
})?,
|
||||
)?;
|
||||
|
||||
table.set(
|
||||
#FN_NAME_INTERNAL_REFLECT_TYPE,
|
||||
lua.create_function(|_, ()| {
|
||||
#reflect_type_fn
|
||||
})?,
|
||||
)?;
|
||||
|
||||
table.set(mlua::MetaMethod::Type.name(), #lua_name)?;
|
||||
|
||||
Ok(mlua::Value::Table(table))
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaWrapper for #wrapper {
|
||||
type Wrap = #type_path;
|
||||
|
||||
#[inline(always)]
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<#type_path>()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
}.into_token_stream().into()
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
---@enum WindowMode
|
||||
WindowMode = {
|
||||
WNDOWED = "windowed",
|
||||
BORDERLESS_FULLSCREEN = "borderless_fullscreen",
|
||||
SIZED_FULLSCREEN = "sized_fullscreen",
|
||||
FULLSCREEN = "fullscreen",
|
||||
}
|
||||
|
||||
---@enum CursorGrabMode
|
||||
CursorGrabMode = {
|
||||
NONE = "none",
|
||||
CONFINED = "confined",
|
||||
LOCKED = "locked",
|
||||
}
|
||||
|
||||
---@enum WindowTheme
|
||||
WindowTheme = {
|
||||
LIGHT = "light",
|
||||
DARK = "dark",
|
||||
}
|
||||
|
||||
---@enum WindowLevel
|
||||
WindowLevel = {
|
||||
ALWAYS_ON_BOTTOM = "always_on_bottom",
|
||||
NORMAL = "normal",
|
||||
ALWAYS_ON_TOP = "always_on_top",
|
||||
}
|
||||
|
||||
---@enum HandleState
|
||||
HandleState = {
|
||||
LOADING = "loading",
|
||||
READY = "ready",
|
||||
ERROR = "error",
|
||||
}
|
||||
|
||||
---@enum ActionKind
|
||||
ActionKind = {
|
||||
BUTTON = "button",
|
||||
AXIS = "axis",
|
||||
}
|
||||
|
||||
---@enum ActionState
|
||||
ActionState = {
|
||||
IDLE = "idle",
|
||||
PRESSED = "pressed",
|
||||
JUST_PRESSED = "just_pressed",
|
||||
JUST_RELEASED = "just_released",
|
||||
AXIS = "axis",
|
||||
OTHER = "other",
|
||||
}
|
||||
|
||||
---@enum FilterMode
|
||||
FilterMode = {
|
||||
NEAREST = "nearest",
|
||||
LINEAR = "linear",
|
||||
}
|
||||
|
||||
---@enum WrappingMode
|
||||
WrappingMode = {
|
||||
CLAMP_TO_EDGE = "clamp_to_edge",
|
||||
MIRRORED_REPEAT = "mirrored_repeat",
|
||||
REPEAT = "repeat",
|
||||
}
|
||||
|
||||
---@enum CameraProjectionMode
|
||||
CameraProjectionMode = {
|
||||
PERSPECTIVE = "perspective",
|
||||
ORTHOGRAPHIC = "orthographic",
|
||||
}
|
||||
|
||||
---@enum DeviceEventKind
|
||||
DeviceEventKind = {
|
||||
ADDED = "added",
|
||||
REMOVED = "removed",
|
||||
MOUSE_MOTION = "mouse_motion",
|
||||
MOUSE_WHEEL = "mouse_wheel",
|
||||
MOTION = "motion",
|
||||
BUTTON = "button",
|
||||
KEY = "key",
|
||||
}
|
||||
|
||||
---@enum NativeKeyCodeKind
|
||||
NativeKeyCodeKind = {
|
||||
ANDROID = "android",
|
||||
MACOS = "macos",
|
||||
WINDOWS = "windows",
|
||||
XKB = "xkb",
|
||||
}
|
||||
|
||||
---@enum ElementState
|
||||
ElementState = {
|
||||
PRESSED = "pressed",
|
||||
RELEASED = "released",
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
---@class Quat
|
||||
---@field x number
|
||||
---@field y number
|
||||
---@field z number
|
||||
---@field w number
|
||||
Quat = { x = 0.0, y = 0.0, z = 0.0, w = 0.0 }
|
||||
Quat.__index = Quat
|
||||
Quat.__name = "Quat"
|
||||
|
||||
--- Constructs a new Quaternion from x, y, z, and w.
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param z number
|
||||
---@param w number
|
||||
---@return Quat
|
||||
function Quat:new(x, y, z, w)
|
||||
local q = {}
|
||||
setmetatable(q, Quat)
|
||||
|
||||
q.x = x
|
||||
q.y = y
|
||||
q.z = z
|
||||
q.w = w
|
||||
|
||||
return q
|
||||
end
|
||||
|
||||
Quat.IDENTITY = Quat:new(0, 0, 0, 1)
|
||||
|
||||
function Quat:clone()
|
||||
return Quat:new(self.x, self.y, self.z, self.w)
|
||||
end
|
||||
|
||||
--- Creates a quaternion from the angle, in radians, around the x axis.
|
||||
--- @param rad number
|
||||
--- @return Quat
|
||||
function Quat:from_rotation_x(rad)
|
||||
local sin = math.sin(rad * 0.5)
|
||||
local cos = math.cos(rad * 0.5)
|
||||
return Quat:new(sin, 0, 0, cos)
|
||||
end
|
||||
|
||||
--- Creates a quaternion from the angle, in radians, around the y axis.
|
||||
--- @param rad number
|
||||
--- @return Quat
|
||||
function Quat:from_rotation_y(rad)
|
||||
local sin = math.sin(rad * 0.5)
|
||||
local cos = math.cos(rad * 0.5)
|
||||
return Quat:new(0, sin, 0, cos)
|
||||
end
|
||||
|
||||
--- Creates a quaternion from the angle, in radians, around the z axis.
|
||||
--- @param rad number
|
||||
--- @return Quat
|
||||
function Quat:from_rotation_z(rad)
|
||||
local sin = math.sin(rad * 0.5)
|
||||
local cos = math.cos(rad * 0.5)
|
||||
return Quat:new(0, 0, sin, cos)
|
||||
end
|
||||
|
||||
--- Computes the dot product of `self`.
|
||||
---@param rhs Quat
|
||||
---@return number
|
||||
function Quat:dot(rhs)
|
||||
return (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z) + (self.w * rhs.w)
|
||||
end
|
||||
|
||||
--- Computes the length of `self`.
|
||||
---@return number
|
||||
function Quat:length()
|
||||
return math.sqrt(self:dot(self))
|
||||
end
|
||||
|
||||
--- Compute the length of `self` squared.
|
||||
---@return number
|
||||
function Quat:length_squared()
|
||||
return self:length() ^ 2
|
||||
end
|
||||
|
||||
--- Normalizes `self` and returns the new Quat
|
||||
---@return unknown
|
||||
function Quat:normalize()
|
||||
local length = self:length()
|
||||
return self / length
|
||||
end
|
||||
|
||||
--- Multiplies two Quaternions together. Keep in mind that Quaternion multiplication is NOT
|
||||
--- commutative so the order in which you multiply the quaternions matters.
|
||||
---@param rhs Quat
|
||||
---@return Quat
|
||||
function Quat:mult_quat(rhs)
|
||||
local x1, y1, z1, w1 = self.x, self.y, self.z, self.w
|
||||
local x2, y2, z2, w2 = rhs.x, rhs.y, rhs.z, rhs.w
|
||||
|
||||
local x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
|
||||
local y = w1 * y2 - x1 * z2 + y1 * w2 + z1 * x2
|
||||
local z = w1 * z2 + x1 * y2 - y1 * x2 + z1 * w2
|
||||
local w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * x2
|
||||
|
||||
return Quat:new(x, y, z, w)
|
||||
end
|
||||
|
||||
--- Multiplies `self` by a Vec3, returning the rotated Vec3
|
||||
---@param vec Vec3
|
||||
---@return Vec3
|
||||
function Quat:mult_vec3(vec)
|
||||
local vec_quat = Quat:new(vec.x, vec.y, vec.z, 0)
|
||||
local quat = self:mult_quat(vec_quat)
|
||||
return Vec3:new(quat.x, quat.y, quat.z)
|
||||
end
|
||||
|
||||
--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`.
|
||||
--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result
|
||||
--- will be equal to `rhs`
|
||||
--- @param rhs Quat
|
||||
--- @param alpha number
|
||||
--- @return Quat
|
||||
function Quat:lerp(rhs, alpha)
|
||||
-- ensure alpha is [0, 1]
|
||||
local alpha = math.max(0, math.min(1, alpha))
|
||||
|
||||
local x1, y1, z1, w1 = self.x, self.y, self.z, self.w
|
||||
local x2, y2, z2, w2 = rhs.x, rhs.y, rhs.z, rhs.w
|
||||
|
||||
local x = (1 - alpha) * x1 + alpha * x2
|
||||
local y = (1 - alpha) * y1 + alpha * y2
|
||||
local z = (1 - alpha) * z1 + alpha * z2
|
||||
local w = (1 - alpha) * w1 + alpha * w2
|
||||
|
||||
return Quat:new(x, y, z, w):normalize()
|
||||
end
|
||||
|
||||
function Quat:__add(rhs)
|
||||
return Quat:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z, self.w + rhs.w)
|
||||
end
|
||||
|
||||
function Quat:__sub(rhs)
|
||||
return Quat:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z, self.w - rhs.w)
|
||||
end
|
||||
|
||||
function Quat:__mul(rhs)
|
||||
if type(rhs) == "number" then
|
||||
return Quat:new(self.x * rhs, self.y * rhs, self.z * rhs, self.w * rhs)
|
||||
elseif type(rhs) == "table" then
|
||||
local name = rhs.__name
|
||||
|
||||
if name == "Vec3" then
|
||||
return self:mult_vec3(rhs)
|
||||
elseif name == "Quat" then
|
||||
return self:mult_quat(rhs)
|
||||
else
|
||||
assert(false, "Unknown usertype of rhs" .. name)
|
||||
end
|
||||
else
|
||||
assert(false, "Unknown type of rhs" .. type(rhs))
|
||||
end
|
||||
end
|
||||
|
||||
function Quat:__div(rhs)
|
||||
if type(rhs) == "number" then
|
||||
return Quat:new(self.x / rhs, self.y / rhs, self.z / rhs, self.w / rhs)
|
||||
else
|
||||
assert(rhs.__name == "Quat", "Attempted to divide Quat by unknown type " .. rhs.__name)
|
||||
return Quat:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z, self.w / rhs.w)
|
||||
end
|
||||
end
|
||||
|
||||
function Quat:__eq(rhs)
|
||||
return self.x == rhs.x and self.y == rhs.y and self.z == rhs.z and self.w == rhs.w
|
||||
end
|
||||
|
||||
function Quat:__lt(rhs)
|
||||
return self.x < rhs.x and self.y < rhs.y and self.z < rhs.z and self.w < rhs.w
|
||||
end
|
||||
|
||||
function Quat:__le(rhs)
|
||||
return self.x <= rhs.x and self.y <= rhs.y and self.z <= rhs.z and self.w <= rhs.w
|
||||
end
|
||||
|
||||
function Quat:__tostring()
|
||||
return "Quat(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ", " .. self.w .. ")"
|
||||
end
|
|
@ -1,95 +0,0 @@
|
|||
---@class Transform
|
||||
---@field translation Vec3
|
||||
---@field rotation Quat
|
||||
---@field Scale Vec3
|
||||
Transform = { translation = Vec3.ZERO, rotation = Quat.IDENTITY, scale = Vec3.ONE }
|
||||
Transform.__index = Transform
|
||||
Transform.__name = "Transform"
|
||||
|
||||
function Transform:new(translation, rotation, scale)
|
||||
local t = {}
|
||||
setmetatable(t, Transform)
|
||||
|
||||
t.translation = translation
|
||||
t.rotation = rotation
|
||||
t.scale = scale
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function Transform:clone()
|
||||
return Transform:new(self.translation:clone(), self.rotation:clone(), self.scale:clone())
|
||||
end
|
||||
|
||||
--- Creates a new Transform with the translation at the vec3
|
||||
--- @param pos Vec3
|
||||
function Transform:from_vec3(pos)
|
||||
local t = Transform:clone() -- copy of default transform
|
||||
t.translation = pos
|
||||
return t
|
||||
end
|
||||
|
||||
function Transform:from_xyz(x, y, z)
|
||||
Transform:from_vec3(Vec3:new(x, y, z))
|
||||
end
|
||||
|
||||
--- Calculates the forward vector of the Transform.
|
||||
--- @return Vec3
|
||||
function Transform:forward()
|
||||
return (self.rotation * Vec3.NEG_Z):normalize()
|
||||
end
|
||||
|
||||
--- Calculates the left vector of the Transform.
|
||||
--- @return Vec3
|
||||
function Transform:left()
|
||||
return (self.rotation * Vec3.X):normalize()
|
||||
end
|
||||
|
||||
--- Calculates the up vector of the Transform.
|
||||
--- @return Vec3
|
||||
function Transform:up()
|
||||
return (self.rotation * Vec3.Y):normalize()
|
||||
end
|
||||
|
||||
--- Rotates `self` using a Quaternion
|
||||
--- @param quat Quat
|
||||
function Transform:rotate(quat)
|
||||
self.rotation = (quat * self.rotation):normalize()
|
||||
end
|
||||
|
||||
--- Rotates `self` around the x-axis
|
||||
--- @param rad number
|
||||
function Transform:rotate_x(rad)
|
||||
self:rotate(Quat:from_rotation_x(rad))
|
||||
end
|
||||
|
||||
--- Rotates `self` around the y-axis
|
||||
--- @param rad number
|
||||
function Transform:rotate_y(rad)
|
||||
self:rotate(Quat:from_rotation_y(rad))
|
||||
end
|
||||
|
||||
--- Rotates `self` around the z-axis
|
||||
--- @param rad number
|
||||
function Transform:rotate_z(rad)
|
||||
self:rotate(Quat:from_rotation_z(rad))
|
||||
end
|
||||
|
||||
--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`.
|
||||
--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result
|
||||
--- will be equal to `rhs`
|
||||
--- @param rhs Transform
|
||||
--- @param alpha number
|
||||
--- @return Transform
|
||||
function Transform:lerp(rhs, alpha)
|
||||
local res = self:clone()
|
||||
res.translation = self.translation:lerp(rhs.translation, alpha)
|
||||
res.rotation = self.rotation:lerp(rhs.rotation, alpha)
|
||||
res.scale = self.scale:lerp(rhs.scale, alpha)
|
||||
return res
|
||||
end
|
||||
|
||||
function Transform:__tostring()
|
||||
return "Transform(pos=" .. tostring(self.translation) .. ", rot="
|
||||
.. tostring(self.rotation) .. ", scale=" .. tostring(self.scale) .. ")"
|
||||
end
|
|
@ -1,187 +0,0 @@
|
|||
---@class Vec3
|
||||
---@field x number
|
||||
---@field y number
|
||||
---@field z number
|
||||
Vec3 = { x = 0.0, y = 0.0, z = 0.0 }
|
||||
Vec3.__index = Vec3
|
||||
Vec3.__name = "Vec3"
|
||||
|
||||
--- Constructs a new vector
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param z number
|
||||
---@return Vec3
|
||||
function Vec3:new(x, y, z)
|
||||
local v = {}
|
||||
setmetatable(v, Vec3)
|
||||
|
||||
v.x = x
|
||||
v.y = y
|
||||
v.z = z
|
||||
|
||||
return v
|
||||
end
|
||||
|
||||
---Creates a copy of self
|
||||
---@return Vec3
|
||||
function Vec3:clone()
|
||||
return Vec3:new(self.x, self.y, self.z)
|
||||
end
|
||||
|
||||
--- Constructs a vector with all elements as parameter `x`.
|
||||
---@param x number
|
||||
---@return Vec3
|
||||
function Vec3:all(x)
|
||||
return Vec3:new(x, x, x)
|
||||
end
|
||||
|
||||
--- A unit-length vector pointing alongside the positive X axis.
|
||||
Vec3.X = Vec3:new(1, 0, 0)
|
||||
--- A unit-length vector pointing alongside the positive Y axis.
|
||||
Vec3.Y = Vec3:new(0, 1, 0)
|
||||
--- A unit-length vector pointing alongside the positive Z axis.
|
||||
Vec3.Z = Vec3:new(0, 0, 1)
|
||||
|
||||
--- A unit-length vector pointing alongside the negative X axis.
|
||||
Vec3.NEG_X = Vec3:new(-1, 0, 0)
|
||||
--- A unit-length vector pointing alongside the negative Y axis.
|
||||
Vec3.NEG_Y = Vec3:new(0, -1, 0)
|
||||
--- A unit-length vector pointing alongside the negative Z axis.
|
||||
Vec3.NEG_Z = Vec3:new(0, 0, -1)
|
||||
|
||||
--- A vector of all zeros
|
||||
Vec3.ZERO = Vec3:new(0, 0, 0)
|
||||
--- A vector of all ones
|
||||
Vec3.ONE = Vec3:new(1, 1, 1)
|
||||
|
||||
--- Computes the absolute value of `self`.
|
||||
function Vec3:abs()
|
||||
self.x = math.abs(self.x)
|
||||
self.y = math.abs(self.y)
|
||||
self.z = math.abs(self.z)
|
||||
end
|
||||
|
||||
--- Computes the length of `self`.
|
||||
---@return number
|
||||
function Vec3:length()
|
||||
return math.sqrt(self:dot(self))
|
||||
end
|
||||
|
||||
---Moves `self` by the provided coordinates
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param z number
|
||||
function Vec3:move_by(x, y, z)
|
||||
self.x = self.x + x
|
||||
self.y = self.y + y
|
||||
self.z = self.z + z
|
||||
end
|
||||
|
||||
--- Computes the dot product of `self` and `rhs`.
|
||||
---@param rhs Vec3
|
||||
---@return number
|
||||
function Vec3:dot(rhs)
|
||||
assert(rhs.__name == "Vec3")
|
||||
|
||||
return (self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
|
||||
end
|
||||
|
||||
--- Returns a vector that has the minimum value of each element of `self` and `rhs`
|
||||
---@param rhs Vec3
|
||||
---@return Vec3
|
||||
function Vec3:min(rhs)
|
||||
local x = math.min(self.x, rhs.x)
|
||||
local y = math.min(self.y, rhs.y)
|
||||
local z = math.min(self.z, rhs.z)
|
||||
|
||||
return Vec3:new(x, y, z)
|
||||
end
|
||||
|
||||
--- Modifies `self` to be normalized to a length 1.
|
||||
function Vec3:normalize()
|
||||
local len_recip = 1.0 / self:length()
|
||||
self.x = self.x * len_recip
|
||||
self.y = self.y * len_recip
|
||||
self.z = self.z * len_recip
|
||||
end
|
||||
|
||||
--- Calculates the linear iterpolation between `self` and `rhs` based on the `alpha`.
|
||||
--- When `alpha` is `0`, the result will be equal to `self`. When `s` is `1`, the result
|
||||
--- will be equal to `rhs`
|
||||
--- @param rhs Vec3
|
||||
--- @param alpha number
|
||||
--- @return Vec3
|
||||
function Vec3:lerp(rhs, alpha)
|
||||
-- ensure alpha is [0, 1]
|
||||
local alpha = math.max(0, math.min(1, alpha))
|
||||
|
||||
local res = self:clone()
|
||||
res = res + ((rhs - res) * alpha)
|
||||
return res
|
||||
end
|
||||
|
||||
function Vec3:__add(rhs)
|
||||
if type(rhs) == "Vec3" then
|
||||
return Vec3:new(self.x + rhs.x, self.y + rhs.y, self.z + rhs.z)
|
||||
else
|
||||
return Vec3:new(self.x + rhs, self.y + rhs, self.z + rhs)
|
||||
end
|
||||
end
|
||||
|
||||
function Vec3:__sub(rhs)
|
||||
if type(rhs) == "Vec3" then
|
||||
return Vec3:new(self.x - rhs.x, self.y - rhs.y, self.z - rhs.z)
|
||||
else
|
||||
return Vec3:new(self.x - rhs, self.y - rhs, self.z - rhs)
|
||||
end
|
||||
end
|
||||
|
||||
function Vec3:__mul(rhs)
|
||||
if type(rhs) == "Vec3" then
|
||||
return Vec3:new(self.x * rhs.x, self.y * rhs.y, self.z * rhs.z)
|
||||
else
|
||||
return Vec3:new(self.x * rhs, self.y * rhs, self.z * rhs)
|
||||
end
|
||||
end
|
||||
|
||||
function Vec3:__div(rhs)
|
||||
if type(rhs) == "Vec3" then
|
||||
return Vec3:new(self.x / rhs.x, self.y / rhs.y, self.z / rhs.z)
|
||||
else
|
||||
return Vec3:new(self.x / rhs, self.y / rhs, self.z / rhs)
|
||||
end
|
||||
end
|
||||
|
||||
function Vec3:__idiv(rhs)
|
||||
if type(rhs) == "Vec3" then
|
||||
return Vec3:new(self.x // rhs.x, self.y // rhs.y, self.z // rhs.z)
|
||||
else
|
||||
return Vec3:new(self.x // rhs, self.y // rhs, self.z // rhs)
|
||||
end
|
||||
end
|
||||
|
||||
function Vec3:__unm()
|
||||
return Vec3:new(-self.x, -self.y, -self.z)
|
||||
end
|
||||
|
||||
function Vec3:__pow(rhs)
|
||||
if type(rhs) == "number" then
|
||||
return Vec3:new(self.x ^ rhs, self.y ^ rhs, self.z ^ rhs)
|
||||
end
|
||||
end
|
||||
|
||||
function Vec3:__eq(rhs)
|
||||
return self.x == rhs.x and self.y == rhs.y and self.z == rhs.z
|
||||
end
|
||||
|
||||
function Vec3:__lt(rhs)
|
||||
return self.x < rhs.x and self.y < rhs.y and self.z < rhs.z
|
||||
end
|
||||
|
||||
function Vec3:__le(rhs)
|
||||
return self.x <= rhs.x and self.y <= rhs.y and self.z <= rhs.z
|
||||
end
|
||||
|
||||
function Vec3:__tostring()
|
||||
return "Vec3(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ")"
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
---@meta
|
||||
|
||||
---@class GltfHandle: Handle
|
||||
---
|
||||
---A handle to a GLTF asset.
|
||||
GltfHandle = {
|
||||
|
||||
}
|
||||
|
||||
---Get a list of scenes in the GLTF file.
|
||||
---
|
||||
---@return SceneHandle[]
|
||||
function GltfHandle:scenes() end
|
||||
|
||||
---Get a list of materials in the GLTF file.
|
||||
---
|
||||
---@return MaterialHandle[]
|
||||
function GltfHandle:materials() end
|
||||
|
||||
---Get a list of meshes in the GLTF file.
|
||||
---
|
||||
---@return MeshHandle[]
|
||||
function GltfHandle:meshes() end
|
|
@ -0,0 +1,43 @@
|
|||
---@meta
|
||||
|
||||
---@class Handle: userdata
|
||||
---
|
||||
---A handle to an asset. Assets are loaded asynchronously, so you cannot immediately
|
||||
---use them after you request them from the World.
|
||||
Handle = {
|
||||
---The path the asset was loaded from.
|
||||
---
|
||||
---@type string
|
||||
path = nil,
|
||||
|
||||
---The version of the resource.
|
||||
---
|
||||
---Increments every time a resource is loaded.
|
||||
---
|
||||
---@type number
|
||||
version = nil,
|
||||
|
||||
---The unique id of the resource.
|
||||
---
|
||||
---This is not changed for the entire lifetime of the handle, it does not change
|
||||
---when an asset is reloaded.
|
||||
---
|
||||
---@type string
|
||||
uuid = nil,
|
||||
|
||||
---Current state of the asset handle.
|
||||
---@type HandleState
|
||||
state = nil,
|
||||
}
|
||||
|
||||
---Returns true if the asset is watched for auto-reloading.
|
||||
---
|
||||
---@return boolean
|
||||
function Handle:is_watched() end
|
||||
|
||||
---Returns true if the asset is loaded.
|
||||
---@return boolean
|
||||
function Handle:is_loaded() end
|
||||
|
||||
---Blocks execution until the asset and its dependencies are loaded.
|
||||
function Handle:wait_until_loaded() end
|
|
@ -0,0 +1,102 @@
|
|||
---@meta
|
||||
|
||||
---@class MaterialHandle: Handle
|
||||
---
|
||||
---A handle to a material asset in a GLTF file.
|
||||
MaterialHandle = {
|
||||
---The unique id of the GPU shader.
|
||||
---@type number?
|
||||
shader_uuid = nil,
|
||||
|
||||
---The name of the material from GLTF.
|
||||
---@type string?
|
||||
name = nil,
|
||||
|
||||
---@type boolean
|
||||
double_sided = nil,
|
||||
|
||||
---The RGBA base color of the model.
|
||||
---
|
||||
---If a texture is supplied with `base_color_texture`, this value will tint the
|
||||
---texture. If a texture is not provided, this value would be the color of
|
||||
---the Material.
|
||||
---
|
||||
---@type Vec4
|
||||
base_color = nil,
|
||||
|
||||
---The metalness of the material
|
||||
---
|
||||
---From 0.0 (non-metal) to 1.0 (metal).
|
||||
---
|
||||
---@type number
|
||||
metallic = nil,
|
||||
|
||||
---The roughness of the material
|
||||
---
|
||||
---From 0.0 (smooth) to 1.0 (rough)
|
||||
---
|
||||
---@type number
|
||||
roughness = nil,
|
||||
|
||||
---The base color texture of the model.
|
||||
---@type TextureHandle?
|
||||
base_color_texture = nil,
|
||||
|
||||
---The metallic-roughness texture.
|
||||
---
|
||||
---The metalness values are sampled from the B channel. The roughness values are sampled from
|
||||
---the G channel. These values are linear. If other channels are present (R or A), they are
|
||||
---ignored for metallic-roughness calculations.
|
||||
---@type TextureHandle?
|
||||
metallic_roughness_texture = nil,
|
||||
|
||||
---A set of parameter values that are used to define the specular-glossiness material model
|
||||
---from Physically-Based Rendering (PBR) methodology.
|
||||
---GLTF extension: [KHR_materials_pbrSpecularGlossiness](https://kcoley.github.io/glTF/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness)
|
||||
---@type TextureHandle?
|
||||
pbr_glossiness = nil,
|
||||
|
||||
---The optional alpha cutoff value of the material.
|
||||
---
|
||||
---This will be used instead of the renderer's default.
|
||||
---
|
||||
---@type number?
|
||||
alpha_cutoff = nil,
|
||||
|
||||
---The alpha rendering mode of the material.
|
||||
---
|
||||
---The material's alpha rendering
|
||||
---mode enumeration specifying the interpretation of the alpha value of the main
|
||||
---factor and texture.
|
||||
---
|
||||
---* In `Opaque` mode (default) the alpha value is ignored
|
||||
--- and the rendered output is fully opaque.
|
||||
---* In `Mask` mode, the rendered
|
||||
--- output is either fully opaque or fully transparent depending on the alpha
|
||||
--- value and the specified alpha cutoff value.
|
||||
---* In `Blend` mode, the alpha value is used to composite the source and
|
||||
--- destination areas and the rendered output is combined with the background
|
||||
--- using the normal painting operation (i.e. the Porter and Duff over
|
||||
--- operator).
|
||||
---
|
||||
---@type AlphaMode
|
||||
alpha_mode = nil,
|
||||
|
||||
---@type Specular?
|
||||
specular = nil,
|
||||
}
|
||||
|
||||
---@enum AlphaMode
|
||||
AlphaMode = {
|
||||
OPAQUE = "opaque",
|
||||
MASK = "mask",
|
||||
BLEND = "blend",
|
||||
}
|
||||
|
||||
---@class PbrGlossiness
|
||||
---TODO: implement
|
||||
PbrGlossiness = {}
|
||||
|
||||
---@class Specular
|
||||
---TODO: implement
|
||||
Specular = {}
|
|
@ -0,0 +1,14 @@
|
|||
---@meta
|
||||
|
||||
---@class MeshHandle: Handle
|
||||
---
|
||||
---A handle to a mesh in a GLTF file.
|
||||
MeshHandle = {
|
||||
---The material of the mesh
|
||||
---@type MaterialHandle
|
||||
material = nil,
|
||||
}
|
||||
|
||||
---Get the indices in the mesh.
|
||||
---@return number[]
|
||||
function MeshHandle:indices() end
|
|
@ -0,0 +1,9 @@
|
|||
---@meta
|
||||
|
||||
---@class SceneHandle: Handle
|
||||
---
|
||||
---A handle to a scene asset in a GLTF file.
|
||||
SceneHandle = {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
---@meta
|
||||
|
||||
---@class TextureHandle
|
||||
TextureHandle = {}
|
|
@ -0,0 +1,125 @@
|
|||
---@meta
|
||||
|
||||
---@class ActionHandler: userdata
|
||||
ActionHandler = {}
|
||||
|
||||
--- Create a new `ActionHandler`.
|
||||
---
|
||||
--- ```lua
|
||||
--- local handler = ActionHandler.new {
|
||||
--- -- A list of layout IDs
|
||||
--- layouts = { 0 },
|
||||
--- actions = {
|
||||
--- -- A list of action names and the `ActionKind`s.
|
||||
--- -- Actions can be buttons or axes.
|
||||
--- MoveForwardBackward = ActionKind.AXIS,
|
||||
--- MoveLeftRight = ActionKind.AXIS,
|
||||
--- MoveUpDown = ActionKind.AXIS,
|
||||
--- LookLeftRight = ActionKind.AXIS,
|
||||
--- LookUpDown = ActionKind.AXIS,
|
||||
--- LookRoll = ActionKind.AXIS,
|
||||
--- ObjectsMoveUpDown = ActionKind.AXIS
|
||||
--- },
|
||||
--- mappings = {
|
||||
--- -- Each entry here is a mapping of actions for a layout.
|
||||
--- -- This can be used so that when the current layout is changed,
|
||||
--- -- the mapping would also change.
|
||||
--- {
|
||||
--- -- Specify the layout id that this mapping is for.
|
||||
--- layout = 0,
|
||||
--- binds = {
|
||||
--- -- This is an Action bind. A bind is used to bind an input to an action.
|
||||
--- -- These actions are defined above in "actions".
|
||||
--- MoveForwardBackward = {
|
||||
--- -- This is how you bind a button. In this case the button is a key.
|
||||
--- -- "key" is the device the bind comes from, then after the colon is the
|
||||
--- -- input name, in this case a specific key. We specify a modifier to the bind
|
||||
--- -- after the equal sign.
|
||||
--- "key:w=1.0",
|
||||
--- "key:s=-1.0"
|
||||
--- },
|
||||
--- MoveLeftRight = {
|
||||
--- "key:a=-1.0", "key:d=1.0"
|
||||
--- },
|
||||
--- MoveUpDown = {
|
||||
--- "key:c=1.0", "key:z=-1.0"
|
||||
--- },
|
||||
--- LookLeftRight = {
|
||||
--- "key:left=-1.0", "key:right=1.0",
|
||||
--- -- Here is a bind to an axis.
|
||||
--- -- We use "mouse", for the device the bind is from, then "axis" to specify
|
||||
--- -- that we want to bind a specific axis, then we use the name of the axis,
|
||||
--- -- in this case "x".
|
||||
--- -- So this binds to the x axis of the mouse.
|
||||
--- "mouse:axis:x"
|
||||
--- },
|
||||
--- LookUpDown = {
|
||||
--- "key:up=-1.0", "key:down=1.0",
|
||||
--- -- Here we bind to the y axis of the mouse.
|
||||
--- "mouse:axis:y",
|
||||
--- },
|
||||
--- LookRoll = {
|
||||
--- "key:e=-1.0", "key:q=1.0",
|
||||
--- },
|
||||
--- ObjectsMoveUpDown = {
|
||||
--- "key:u=1.0", "key:j=-1.0"
|
||||
--- }
|
||||
--- }
|
||||
--- }
|
||||
--- }
|
||||
--- }
|
||||
---
|
||||
--- -- Add the handler to the world so the host will process it.
|
||||
--- world:add_resource(handler)
|
||||
--- ```
|
||||
---
|
||||
---@param table table See above example to see the format of this table.
|
||||
function ActionHandler.new(table) end
|
||||
|
||||
---Returns the action's modifier if its an updated axis.
|
||||
---
|
||||
---Returns `nil` if the action's state is not `ActionState::Axis`, or if the
|
||||
---action was not found.
|
||||
---@param action string
|
||||
---@return number?
|
||||
function ActionHandler:get_axis(action) end
|
||||
|
||||
---Returns true if the action is pressed (or was just pressed).
|
||||
---
|
||||
---Returns `nil` if the action's was not found.
|
||||
---@param action string
|
||||
---@return boolean?
|
||||
function ActionHandler:is_pressed(action) end
|
||||
|
||||
---Returns true if the action was **just** pressed.
|
||||
---
|
||||
---Returns `nil` if the action was not found
|
||||
---
|
||||
---@param action string
|
||||
---@return boolean?
|
||||
function ActionHandler:was_just_pressed(action) end
|
||||
|
||||
---Returns true if the action was just released.
|
||||
---
|
||||
---Returns `nil` if the action was not found
|
||||
---
|
||||
---@param action string
|
||||
---@return boolean?
|
||||
function ActionHandler:was_just_released(action) end
|
||||
|
||||
---Returns the action's modifier if it was just pressed.
|
||||
---
|
||||
---Returns `nil` if the action's state is not `ActionState.JUST_PRESSED`,
|
||||
---or if the action was not found.
|
||||
---
|
||||
---@param action string
|
||||
---@return number?
|
||||
function ActionHandler:get_just_pressed(action) end
|
||||
|
||||
---Returns the current state of the action.
|
||||
---
|
||||
---The first element in the returned tuple is the state enum, and the second
|
||||
---is the state modifier. The modifer will be `nil` if the state is "idle"
|
||||
---
|
||||
---@return [ActionState, number?]
|
||||
function ActionHandler:get_action_state(action) end
|
|
@ -0,0 +1,18 @@
|
|||
---@meta
|
||||
|
||||
---@class Camera: userdata
|
||||
Camera = {
|
||||
---The position of the camera
|
||||
---@type Transform
|
||||
transform = nil,
|
||||
---The field of view of the camera
|
||||
---@type Angle
|
||||
fov = nil,
|
||||
---The projection mode the camera.
|
||||
---Can be used to specify if the camera is 2D (orthographic), or 3D (perspective).
|
||||
---@type CameraProjectionMode
|
||||
mode = nil,
|
||||
---Flag to enable some debug rendering stuff.
|
||||
---@type boolean
|
||||
debug = nil,
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
---@meta
|
||||
|
||||
---@class DeltaTime: userdata
|
||||
---
|
||||
---DeltaTime is an ECS world resource. When its requested from the world, a `number`
|
||||
---is returned.
|
||||
---
|
||||
---Example:
|
||||
---```lua
|
||||
------@type number
|
||||
---local dt = world:resource(DeltaTime)
|
||||
---
|
||||
---print(type(dt)) --> number
|
||||
---```
|
||||
DeltaTime = {}
|
|
@ -0,0 +1,4 @@
|
|||
---@meta
|
||||
|
||||
---@class Entity: userdata
|
||||
Entity = {}
|
|
@ -0,0 +1,17 @@
|
|||
---@meta
|
||||
|
||||
---@class FreeFlyCamera: userdata
|
||||
FreeFlyCamera = {
|
||||
---Movement speed of the camera.
|
||||
---@type number
|
||||
speed = nil,
|
||||
---The speed of the camera rotation.
|
||||
---@type number
|
||||
look_speed = nil,
|
||||
---The sensitivity of the mouse when looking.
|
||||
---
|
||||
---This is additional to `look_speed`, but onyl applied to mouse movement.
|
||||
---
|
||||
---@type number
|
||||
mouse_sensitivity = nil,
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
---@meta
|
||||
|
||||
---@class Window: userdata
|
||||
Window = {
|
||||
---Gets or sets the window's focus.
|
||||
---@type boolean
|
||||
focused = nil,
|
||||
---Gets or sets the window mode.
|
||||
---@type WindowMode
|
||||
window_mode = nil,
|
||||
---Gets or sets the position of the top-left corner of the window.
|
||||
---
|
||||
---The top-left hand corner of the desktop is not necessarily the same
|
||||
---as the screen. If the user uses a desktop with multiple monitors, the top-left
|
||||
---hand corner of the desktop is the top-left hand corner of the monitor at the
|
||||
---top-left of the desktop.
|
||||
---
|
||||
---If this is `nil`, the position will be chosen by the windowing manager at creation,
|
||||
---then set when the window is created.
|
||||
---
|
||||
---@type Vec2?
|
||||
position = nil,
|
||||
---@type Vec2
|
||||
physical_size = nil,
|
||||
---@type Vec2
|
||||
size = nil,
|
||||
---Gets/sets if the window has decorations.
|
||||
---@type boolean
|
||||
decorated = nil,
|
||||
---Gets/sets the window's current maximized state.
|
||||
---@type boolean
|
||||
maximized = nil,
|
||||
---Gets/sets the window's current minimized state.
|
||||
---
|
||||
---Is `nil` if the minimized state could not be determined.
|
||||
---
|
||||
---@type boolean?
|
||||
minimized = nil,
|
||||
---Gets/sets the window's current resizable state
|
||||
---@type boolean
|
||||
resizable = nil,
|
||||
---Gets/sets the window's current visibility state.
|
||||
---
|
||||
---Is `nil` when it could not be determined.
|
||||
---
|
||||
---@type boolean?
|
||||
visible = nil,
|
||||
|
||||
--TODO: resize_increments
|
||||
|
||||
---Gets the scale factor.
|
||||
---
|
||||
---You cannot set this field.
|
||||
---
|
||||
---@type number
|
||||
scale_factor = nil,
|
||||
---Gets/sets the window's blur state.
|
||||
---@type boolean
|
||||
blur = nil,
|
||||
|
||||
--TODO: cursor appearance
|
||||
|
||||
---Gets/sets the window's cursor grab mode.
|
||||
---@type CursorGrabMode
|
||||
cursor_grab = nil,
|
||||
---Gets/sets whether the window catches cursor events.
|
||||
---@type boolean
|
||||
cursor_hittest = nil,
|
||||
---Gets/sets the cursor's visibility.
|
||||
---@type boolean
|
||||
cursor_visible = nil,
|
||||
---Sets whether the window should get IME events.
|
||||
---
|
||||
---When IME is allowed, the window will receive Ime events, and during the preedit phase
|
||||
---the window will NOT get KeyboardInput events. The window should allow IME while
|
||||
---it is expecting text input.
|
||||
---
|
||||
---When IME is not allowed, the window won’t receive window ime events, and will receive
|
||||
---KeyboardInput events for every keypress instead. Not allowing IME is useful for games
|
||||
---for example. IME is not allowed by default.
|
||||
---
|
||||
---@type boolean
|
||||
ime_allowed = nil,
|
||||
---Gets/sets the minimum size of the window.
|
||||
---@type Vec2?
|
||||
min_size = nil,
|
||||
---Gets/sets the maximum size of the window.
|
||||
---@type Vec2?
|
||||
max_size = nil,
|
||||
---Gets/sets the current window theme.
|
||||
---
|
||||
---Specify `nil` to reset the theme to the system default. May also be `nil` on
|
||||
---unsupported platforms.
|
||||
---
|
||||
---@type WindowTheme?
|
||||
theme = nil,
|
||||
---Gets/sets the title of the window.
|
||||
---@type string
|
||||
title = nil,
|
||||
---Gets/sets the window's transparency state.
|
||||
---@type boolean
|
||||
transparent = nil,
|
||||
|
||||
--TODO: window_icon
|
||||
|
||||
---Change the window level.
|
||||
---@type WindowLevel
|
||||
window_level = nil,
|
||||
---Gets the window's occluded state (completely hidden from view).
|
||||
---@type boolean
|
||||
occluded = nil,
|
||||
---Gets/sets the cursor position in the window in logical coordinates.
|
||||
---
|
||||
---The value is `nil` when the cursor is not in the window.
|
||||
---
|
||||
---@type Vec2?
|
||||
cursor_position = nil,
|
||||
---Gets/sets the cursor position in the window in physical coordinates.
|
||||
---
|
||||
---The value is `nil` when the cursor is not in the window.
|
||||
---
|
||||
---@type Vec2?
|
||||
physical_cursor_position = nil,
|
||||
---Checks if the mouse is inside the window
|
||||
---@param self Window
|
||||
---@return boolean
|
||||
is_mouse_inside = function (self) return false end,
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
---@meta
|
||||
|
||||
---@class World: userdata
|
||||
World = {}
|
||||
|
||||
---Spawn an entity with components.
|
||||
---
|
||||
---@param ... userdata The components to spawn on the new entity, currently must be userdata.
|
||||
---@return Entity
|
||||
function World:spawn(...) end
|
||||
|
||||
--- Query components from the world.
|
||||
---
|
||||
--- The `system` parameter is a function with the requested components. The function
|
||||
--- is ran every time for an entity. If you modify a component and want the changes to be
|
||||
--- stored, return it in the function. The order of the returned components do not matter.
|
||||
---
|
||||
--- Example:
|
||||
--- ```lua
|
||||
--- ---@type number
|
||||
--- local dt = world:resource(DeltaTime)
|
||||
---
|
||||
--- world:view(
|
||||
--- ---@param t Transform
|
||||
--- function (t)
|
||||
--- -- Move the transform of the entity a bit
|
||||
--- t:translate(0, 0.15 * dt, 0)
|
||||
--- -- Since the transform was modified, it must be returned so
|
||||
--- -- the engine can store the changes.
|
||||
--- return t
|
||||
--- end,
|
||||
--- -- Specify the requested components here
|
||||
--- Transform
|
||||
--- )
|
||||
--- ```
|
||||
---
|
||||
---@param system fun(...): ...
|
||||
---@param ... userdata
|
||||
function World:view(system, ...) end
|
||||
|
||||
---Get an ECS resource.
|
||||
---
|
||||
---Returns `nil` if the resource was not found in the world. Many resources will
|
||||
---return userdata, however some may return Lua types like `DeltaTime`
|
||||
---returning a `number`.
|
||||
---
|
||||
---Example:
|
||||
---```lua
|
||||
------@type number
|
||||
---local dt = world:resource(DeltaTime)
|
||||
---
|
||||
---print(type(dt)) --> number
|
||||
---```
|
||||
---
|
||||
---@param resource userdata This shouldn't be an instance of userdata.
|
||||
---@return any?
|
||||
function World:resource(resource) end
|
||||
|
||||
---Add a resource to the world.
|
||||
---
|
||||
---If the resource already exists, it will be overwritten.
|
||||
---
|
||||
---@param resource userdata
|
||||
function World:add_resource(resource) end
|
||||
|
||||
---Request an asset.
|
||||
---
|
||||
---Assets are loaded asyncronously, so you must wait before trying to access fields on
|
||||
---the asset. You can spawn an entity with it when its still loading.
|
||||
---
|
||||
---Returns an asset handle to the requested resource type
|
||||
---
|
||||
---@param path string
|
||||
---@return Handle asset An asset handle to the requested resource type.
|
||||
function World:request_asset(path) end
|
|
@ -0,0 +1,9 @@
|
|||
---@meta
|
||||
|
||||
--- A Transform represents the relative position of the entity to its parent entity, while
|
||||
--- a world transform is the position relative to the World. When wanting to move an entity,
|
||||
--- you should use its [`Transform`]. You cannot mutate [`WorldTransform`] as its managed completey
|
||||
--- by the [`system_update_world_transforms`] system. For the WorldTransform to work properly, you
|
||||
--- must have both a [`Transform`] and [`WorldTransform`] on the entities in the scene.
|
||||
---@alias WorldTransform Transform The world transform of an entity.
|
||||
WorldTransform = {}
|
|
@ -0,0 +1,140 @@
|
|||
---@meta
|
||||
|
||||
---@class DeviceEventAdded: table
|
||||
DeviceEventAdded = {
|
||||
---@type DeviceEventKind
|
||||
kind = DeviceEventKind.ADDED,
|
||||
}
|
||||
|
||||
---@class DeviceEventRemoved: table
|
||||
DeviceEventRemoved = {
|
||||
---@type DeviceEventKind
|
||||
kind = DeviceEventKind.REMOVED,
|
||||
}
|
||||
|
||||
---@class DeviceEventMouseMotion: table
|
||||
DeviceEventMouseMotion = {
|
||||
---@type DeviceEventKind
|
||||
kind = DeviceEventKind.MOUSE_MOTION,
|
||||
|
||||
---The change in physical position of a pointing device.
|
||||
---
|
||||
---This represents raw, unfiltered physical motion.
|
||||
---
|
||||
---@type Vec2
|
||||
delta = nil,
|
||||
}
|
||||
|
||||
---A physical scroll event from a device.
|
||||
---
|
||||
---`line_delta` and `pixel_delta` are exclusive, only one is non-nil at a time.
|
||||
---
|
||||
---@class DeviceEventMouseWheel: table
|
||||
DeviceEventMouseWheel = {
|
||||
---@type DeviceEventKind
|
||||
kind = DeviceEventKind.MOUSE_WHEEL,
|
||||
|
||||
---Amount in lines or rows to scroll.
|
||||
---
|
||||
---Positive values indicate that the content that is being scrolled should move right
|
||||
---and down (revealing more content left and up).
|
||||
---
|
||||
---@type Vec2?
|
||||
line_delta = nil,
|
||||
|
||||
---Amount in pixels to scroll in the horizontal and vertical direction.
|
||||
---
|
||||
---Positive values indicate that the content being scrolled should move right/down.
|
||||
---
|
||||
---@type Vec2?
|
||||
pixel_delta = nil,
|
||||
}
|
||||
|
||||
---Device event that was triggered by motion on an analog axis.
|
||||
---@class DeviceEventMotion: table
|
||||
DeviceEventMotion = {
|
||||
---@type DeviceEventKind
|
||||
kind = DeviceEventKind.MOTION,
|
||||
|
||||
---The analog axis that motion was triggered from.
|
||||
---@type number
|
||||
axis = nil,
|
||||
|
||||
---The amount of motion.
|
||||
---@type number
|
||||
value = nil,
|
||||
}
|
||||
|
||||
---@class DeviceEventButton: table
|
||||
DeviceEventButton = {
|
||||
---@type DeviceEventKind
|
||||
kind = DeviceEventKind.BUTTON,
|
||||
|
||||
---The button id that the event is from.
|
||||
---@type number
|
||||
button = nil,
|
||||
|
||||
---The state of the button.
|
||||
---@type ElementState
|
||||
state = nil,
|
||||
}
|
||||
|
||||
---A device event triggered from a key press.
|
||||
---
|
||||
---The field `code` will be nil if the key code identifier is unknown.
|
||||
---When the identifier is unknown, it can be retrieved with `native_code`. The field
|
||||
---`native` specifies the kind of platform the code is from.
|
||||
---
|
||||
---@class DeviceEventKey: table
|
||||
DeviceEventKey = {
|
||||
---@type DeviceEventKind
|
||||
kind = DeviceEventKind.KEY,
|
||||
|
||||
---@type ElementState
|
||||
state = nil,
|
||||
|
||||
---The known key name.
|
||||
---
|
||||
---This is `nil` if `native` or `native_code` is specified.
|
||||
---
|
||||
---@type string
|
||||
code = nil,
|
||||
|
||||
---The kind of native platform this code is for.
|
||||
---
|
||||
---This is `nil` if `code` is specified.
|
||||
---
|
||||
---@type NativeKeyCodeKind
|
||||
native = nil,
|
||||
|
||||
---The platform-native physical key identifier.
|
||||
---
|
||||
---This is `nil` if `code` is specified.
|
||||
---
|
||||
---@type number
|
||||
native_code = nil,
|
||||
}
|
||||
|
||||
---@alias DeviceEventRaw
|
||||
---| DeviceEventAdded
|
||||
---| DeviceEventRemoved
|
||||
---| DeviceEventMotion
|
||||
---| DeviceEventMouseMotion
|
||||
---| DeviceEventMouseWheel
|
||||
---| DeviceEventButton
|
||||
---| DeviceEventKey
|
||||
DeviceEventRaw = { }
|
||||
|
||||
---@class DeviceId: userdata
|
||||
DeviceId = {}
|
||||
|
||||
---@class DeviceEvent: table
|
||||
DeviceEvent = {
|
||||
---The device id that the event came from
|
||||
---@type DeviceId
|
||||
source = nil,
|
||||
---The actual device event
|
||||
---@type DeviceEventRaw
|
||||
event = nil,
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
---@meta
|
||||
|
||||
---@class EventReader: userdata
|
||||
EventReader = {}
|
||||
|
||||
---Returns an iterator over the events in the reader.
|
||||
---@return fun(): any
|
||||
function EventReader:read() end
|
||||
|
||||
---@class EventWriter: userdata
|
||||
EventWriter = {}
|
||||
|
||||
---Writes an event.
|
||||
---
|
||||
---The event must be the same type that this writer wraps.
|
||||
---
|
||||
---@param event any
|
||||
function EventWriter:write(event) end
|
|
@ -0,0 +1,5 @@
|
|||
---@meta
|
||||
|
||||
---The world global that is provided to every Lua script.
|
||||
---@type World
|
||||
world = nil
|
|
@ -0,0 +1,10 @@
|
|||
require "math.vec2"
|
||||
require "math.vec3"
|
||||
require "math.vec4"
|
||||
require "math.quat"
|
||||
require "math.transform"
|
||||
|
||||
require "ecs.window"
|
||||
require "ecs.delta_time"
|
||||
|
||||
require "asset.handle"
|
|
@ -0,0 +1,25 @@
|
|||
---@meta
|
||||
|
||||
---@class Angle: userdata
|
||||
Angle = {}
|
||||
|
||||
---Create a new angle in degrees.
|
||||
---@param deg number
|
||||
function Angle.new_degrees(deg) end
|
||||
---Create a new angle in radians.
|
||||
---@param rad number
|
||||
function Angle.new_radians(rad) end
|
||||
|
||||
---Get the angle in radians.
|
||||
---
|
||||
---Will convert from degrees automatically.
|
||||
---
|
||||
---@return number radians
|
||||
function Angle:radians() end
|
||||
|
||||
---Get the angle in degrees.
|
||||
---
|
||||
---Will convert from radians automatically.
|
||||
---
|
||||
---@return number degrees
|
||||
function Angle:degrees() end
|
|
@ -0,0 +1,188 @@
|
|||
---@meta
|
||||
|
||||
---@class Quat: userdata
|
||||
---This is a Lua export of [`glam::Quat`](https://docs.rs/glam/latest/glam/f32/struct.Quat.html)
|
||||
---
|
||||
---@operator add(self): self
|
||||
---@operator sub(self): self
|
||||
---@operator div(number): self
|
||||
---@operator mul(self|Vec3|number): self
|
||||
---@diagnostic disable-next-line: unknown-operator
|
||||
---@operator eq: self
|
||||
Quat = {
|
||||
---The x coordinate
|
||||
---@type number
|
||||
x = nil,
|
||||
|
||||
---The y coordinate
|
||||
---@type number
|
||||
y = nil,
|
||||
|
||||
---The z coordinate
|
||||
---@type number
|
||||
z = nil,
|
||||
|
||||
---The w coordinate
|
||||
---@type number
|
||||
w = nil,
|
||||
}
|
||||
|
||||
---Create a new `Quat`
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param z number
|
||||
---@param w number
|
||||
---@return self
|
||||
function Quat.new(x, y, z, w) end
|
||||
|
||||
---Creates a quaternion from the angle (in radians) around the x axis.
|
||||
---@param rad number
|
||||
---@return self
|
||||
function Quat.from_rotation_x(rad) end
|
||||
|
||||
---Creates a quaternion from the angle (in radians) around the y axis.
|
||||
---@param rad number
|
||||
---@return self
|
||||
function Quat.from_rotation_y(rad) end
|
||||
|
||||
---Creates a quaternion from the angle (in radians) around the z axis.
|
||||
---@param rad number
|
||||
---@return self
|
||||
function Quat.from_rotation_z(rad) end
|
||||
|
||||
---Creates a quaternion from a `Vec4`.
|
||||
---@param vec4 Vec4
|
||||
---@return self
|
||||
function Quat.from_vec4(vec4) end
|
||||
|
||||
---Create a quaternion for a normalized rotation axis and angle (in radians).
|
||||
---
|
||||
---The axis must be a unit vector.
|
||||
---
|
||||
---@param axis Vec3
|
||||
---@param rad number
|
||||
---@return self
|
||||
function Quat.from_axis_angle(axis, rad) end
|
||||
|
||||
---Computes the dot product of self and rhs.
|
||||
---
|
||||
---The dot product is equal to the cosine of the angle between two
|
||||
---quaternion rotations.
|
||||
---
|
||||
---@param rhs Quat
|
||||
---@return number
|
||||
function Quat:dot(rhs) end
|
||||
|
||||
---Computes the length of self.
|
||||
---
|
||||
---@return number
|
||||
function Quat:length() end
|
||||
|
||||
---Computes the squared length of self.
|
||||
---
|
||||
---This is generally faster than length() as it avoids a square root operation.
|
||||
---
|
||||
---@return number
|
||||
function Quat:length_squared() end
|
||||
|
||||
---Computes 1.0 / length().
|
||||
---
|
||||
---For valid results, self must not be of length zero.
|
||||
---@return number
|
||||
function length_recip() end
|
||||
|
||||
---Returns `self` normalized to length `1.0`.
|
||||
---
|
||||
---For valid results, `self` must not be of length zero.
|
||||
---
|
||||
---@return self
|
||||
function Quat:normalize() end
|
||||
|
||||
---Multipies `self` with a `Quat`
|
||||
---@param rhs Quat
|
||||
function Quat:mult_quat(rhs) end
|
||||
|
||||
---Multiplies `self` with a `Vec3`
|
||||
---@param rhs Vec3
|
||||
function Quat:mult_vec3(rhs) end
|
||||
|
||||
---Performs a linear interpolation between `self` and `rhs` based on `alpha`.
|
||||
---
|
||||
---Both `Quat`s must be normalized.
|
||||
---
|
||||
---When `alpha` is `0.0`, the result will be equal to `self`. When `alpha` is `1.0`,
|
||||
---the result will be equal to `rhs`.
|
||||
---
|
||||
---@param rhs Quat
|
||||
---@param alpha number
|
||||
function Quat:lerp(rhs, alpha) end
|
||||
|
||||
---Performs a spherical linear interpolation between `self` and `rhs` based on `alpha`.
|
||||
---
|
||||
---Both `Quat`s must be normalized.
|
||||
---
|
||||
---When `alpha` is `0.0`, the result will be equal to `self`. When `alpha` is `1.0`,
|
||||
---the result will be equal to `rhs`.
|
||||
---
|
||||
---@param rhs Quat
|
||||
---@param alpha number
|
||||
function Quat:slerp(rhs, alpha) end
|
||||
|
||||
|
||||
---Returns the inverse of a normalized quaternion.
|
||||
---
|
||||
---Typically quaternion inverse returns the conjugate of a normalized quaternion.
|
||||
---Because `self` is assumed to already be unit length this method does not
|
||||
---normalize before returning the conjugate.
|
||||
---@return self
|
||||
function Quat:inverse() end
|
||||
|
||||
---Returns `true` if, and only if, all elements are finite. If any element is either
|
||||
---`NaN`, positive or negative infinity, this will return `false`.
|
||||
---
|
||||
---@return boolean
|
||||
function Quat:is_finite() end
|
||||
|
||||
---@return boolean
|
||||
function Quat:is_nan() end
|
||||
|
||||
---Returns whether `self` is of length `1.0` or not.
|
||||
---
|
||||
---Uses a precision threshold of `1e-6`.
|
||||
---@return boolean
|
||||
function Quat:is_normalized() end
|
||||
|
||||
---@return boolean
|
||||
function Quat:is_near_identity() end
|
||||
|
||||
---Returns the angle (in radians) for the minimal rotation for transforming
|
||||
---this quaternion into another.
|
||||
---
|
||||
---Both quaternions must be normalized.
|
||||
---@return number
|
||||
function Quat:angle_between(rhs) end
|
||||
|
||||
---Rotates towards `rhs` up to `max_angle` (in radians).
|
||||
---
|
||||
---When `max_angle` is `0.0`, the result will be equal to `self`. When `max_angle`
|
||||
---is equal to `self.angle_between(rhs)`, the result will be equal to `rhs`.
|
||||
---If `max_angle` is negative, rotates towards the exact opposite of `rhs`.
|
||||
---Will not go past the target.
|
||||
---
|
||||
---Both quaternions must be normalized.
|
||||
---@return self
|
||||
function Quat:rotate_towards(rhs, max_angle) end
|
||||
|
||||
---Returns true if the absolute difference of all elements between `self` and `rhs` is less
|
||||
---than or equal to `max_abs_diff`.
|
||||
---
|
||||
---This can be used to compare if two quaternions contain similar elements. It works best when
|
||||
---comparing with a known value. The `max_abs_diff` that should be used used depends on the
|
||||
---values being compared against.
|
||||
---
|
||||
---For more see [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
|
||||
---
|
||||
---@param rhs Quat
|
||||
---@param max_abs_diff number
|
||||
---@return boolean
|
||||
function Quat:abs_diff_eq(rhs, max_abs_diff) end
|
|
@ -0,0 +1,134 @@
|
|||
---@meta
|
||||
|
||||
---@class Transform: userdata
|
||||
---
|
||||
---A Transform represents a transformation of an object. A transform includes the position
|
||||
---(called translation here), rotation, and scale. Rotation is represented using a Quaternion
|
||||
---(or Quat for short).
|
||||
---
|
||||
---Although Quats can be scary, they are much more robust than euler angles for games
|
||||
---since they do not suffer from things like
|
||||
---[gimbal-lock](https://en.wikipedia.org/wiki/Gimbal_lock).
|
||||
---
|
||||
---This is a Lua export of [`lyra_math::Transform`].
|
||||
---
|
||||
---@operator add(Quat): self
|
||||
---@operator mul(Vec3): self
|
||||
---@diagnostic disable-next-line: unknown-operator
|
||||
---@operator eq: self
|
||||
Transform = {
|
||||
---The translation/position of the transform.
|
||||
---@type Vec3
|
||||
translation = nil,
|
||||
---The rotation of the transform.
|
||||
---@type Quat
|
||||
rotation = nil,
|
||||
---The scale of the transform.
|
||||
---@type Vec3
|
||||
scale = nil,
|
||||
}
|
||||
|
||||
function Transform:__tostring() end
|
||||
|
||||
---@return self
|
||||
function Transform.default() end
|
||||
|
||||
---Create a new transform with its components.
|
||||
---
|
||||
---@param translation Vec3
|
||||
---@param rotation Quat
|
||||
---@param scale Vec3
|
||||
---@return self
|
||||
function Transform.new(translation, rotation, scale) end
|
||||
|
||||
---Create a new transform with a `Vec3` translation.
|
||||
---@param translation Vec3
|
||||
---@return self
|
||||
function Transform.from_translation(translation) end
|
||||
|
||||
---Create a new transform with a translation of `x`, `y`, and `z`.
|
||||
---
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param z number
|
||||
---@return self
|
||||
function Transform.from_translation(x, y, z) end
|
||||
|
||||
---Create a clone of `self`
|
||||
---@return self
|
||||
function Transform:clone() end
|
||||
|
||||
---Returns a normalized vector pointing in the direction the Transform is facing.
|
||||
---
|
||||
---This represents the front of the object can be used for movement, camera orientation, and
|
||||
---other directional calculations.
|
||||
---
|
||||
---@return Vec3
|
||||
function Transform:forward() end
|
||||
|
||||
---Returns a normalized vector pointing to the left side of the Transform.
|
||||
---
|
||||
---The vector is in local space. This represents the direction that is
|
||||
---perpendicular to the object's forward direction.
|
||||
---
|
||||
---@return Vec3
|
||||
function Transform:left() end
|
||||
|
||||
---Returns a normalized vector that indicates the upward direction of the Transform.
|
||||
---
|
||||
---This vector is commonly used to define an object's orientation and is essential for maintaining
|
||||
---consistent vertical alignment in 3D environments, such as for camera positioning and object alignment.
|
||||
---@return Vec3
|
||||
function Transform:up() end
|
||||
|
||||
---Rotate `self` using a quaternion
|
||||
---@param quat Quat
|
||||
function Transform:rotate(quat) end
|
||||
|
||||
---Rotate `self` around the x axis by **degrees**.
|
||||
---
|
||||
---@param deg number The amount of **degrees** to rotate by.
|
||||
function Transform:rotate_x(deg) end
|
||||
|
||||
---Rotate `self` around the y axis by **degrees**.
|
||||
---
|
||||
---@param deg number The amount of **degrees** to rotate by.
|
||||
function Transform:rotate_y(deg) end
|
||||
|
||||
---Rotate `self` around the z axis by **degrees**.
|
||||
---
|
||||
---@param deg number The amount of **degrees** to rotate by.
|
||||
function Transform:rotate_z(deg) end
|
||||
|
||||
---Rotate `self` around the x axis by **radians** .
|
||||
---
|
||||
---@param rad number The amount of **radians** to rotate by.
|
||||
function Transform:rotate_x_rad(rad) end
|
||||
|
||||
---Rotate `self` around the y axis by **radians** .
|
||||
---
|
||||
---@param rad number The amount of **radians** to rotate by.
|
||||
function Transform:rotate_y_rad(rad) end
|
||||
|
||||
---Rotate `self` around the z axis by **radians** .
|
||||
---
|
||||
---@param rad number The amount of **radians** to rotate by.
|
||||
function Transform:rotate_z_rad(rad) end
|
||||
|
||||
---Move `self` by `x`, `y`, and `z`.
|
||||
---
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param z number
|
||||
function Transform:translate(x, y, z) end
|
||||
|
||||
---Performs a linear interpolation between `self` and `rhs` based on `alpha`.
|
||||
---
|
||||
---This will normalize the rotation `Quat`.
|
||||
---
|
||||
---When `alpha` is `0.0`, the result will be equal to `self`. When `alpha` is `1.0`,
|
||||
---the result will be equal to `rhs`.
|
||||
---
|
||||
---@param rhs Transform
|
||||
---@param alpha number
|
||||
function Transform:lerp(rhs, alpha) end
|
|
@ -0,0 +1,125 @@
|
|||
---@meta
|
||||
|
||||
---@class Vec2: userdata
|
||||
---This is a Lua export of [`glam::Vec2`](https://docs.rs/glam/latest/glam/f32/struct.Vec2.html)
|
||||
---
|
||||
---@operator add(self|number): self
|
||||
---@operator sub(self|number): self
|
||||
---@operator div(self|number): self
|
||||
---@operator mul(self|number): self
|
||||
---@operator mod(self|number): self
|
||||
---@operator unm: self
|
||||
---@diagnostic disable-next-line: unknown-operator
|
||||
---@operator eq: self
|
||||
Vec2 = {
|
||||
---The x coordinate
|
||||
---@type number
|
||||
x = nil,
|
||||
|
||||
---The y coordinate
|
||||
---@type number
|
||||
y = nil,
|
||||
|
||||
---A constant `Vec2` with coordinates as `f32::NAN`.
|
||||
---@type Vec2
|
||||
NAN = nil,
|
||||
|
||||
---A constant `Vec2` with `x` as `-1.0`.
|
||||
---@type Vec2
|
||||
NEG_X = nil,
|
||||
|
||||
---A constant `Vec2` with `y` as `-1.0`.
|
||||
---@type Vec2
|
||||
NEG_Y = nil,
|
||||
|
||||
---A constant `Vec2` with both components as `-1.0`.
|
||||
---@type Vec2
|
||||
NEG_ONE = nil,
|
||||
|
||||
---A constant `Vec2` with `x` as `1.0`.
|
||||
---@type Vec2
|
||||
POS_X = nil,
|
||||
|
||||
---A constant `Vec2` with `y` as `1.0`.
|
||||
---@type Vec2
|
||||
POS_Y = nil,
|
||||
|
||||
---A constant `Vec2` with both components as `1.0`.
|
||||
---@type Vec2
|
||||
ONE = nil,
|
||||
|
||||
---A constant `Vec2` with both components as `0.0`.
|
||||
---@type Vec2
|
||||
ZERO = nil,
|
||||
}
|
||||
|
||||
function Vec2:__tostring() end
|
||||
|
||||
---Create a new `Vec2`
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@return self
|
||||
function Vec2.new(x, y) end
|
||||
|
||||
---Returns a vector with a length no less than min and no more than max.
|
||||
---@param min number the minimum value to clamp the length to
|
||||
---@param max number the maximum value to clamp the length to
|
||||
---@return self
|
||||
function Vec2:clamp_length(min, max) end
|
||||
|
||||
---Returns true if the absolute difference of all elements between `self` and `rhs` is less
|
||||
---than or equal to `max_abs_diff`.
|
||||
---
|
||||
---This can be used to compare if two vectors contain similar elements. It works best when
|
||||
---comparing with a known value. The `max_abs_diff` that should be used used depends on the
|
||||
---values being compared against.
|
||||
---
|
||||
---For more see [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
|
||||
---
|
||||
---@param rhs Vec2 The other `Vec2` to compare to.
|
||||
---@param max_abs_diff number Maximum absolute difference between `self` and `rhs`.
|
||||
---@return boolean
|
||||
function Vec2:abs_diff_eq(rhs, max_abs_diff) end
|
||||
|
||||
---Returns a vector containing the smallest integer greater than or equal to a number for each
|
||||
---element of self.
|
||||
---@return self
|
||||
function Vec2:ceil() end
|
||||
|
||||
---Returns the angle of rotation (in radians) from `self` to `rhs` in the range [-π, +π].
|
||||
---
|
||||
---The inputs do not need to be unit vectors however they must be non-zero.
|
||||
---
|
||||
---@param rhs Vec2 The other `Vec2` to get the angle to.
|
||||
---@return number
|
||||
function Vec2:angle_to(rhs) end
|
||||
|
||||
---Returns a vector containing the absolute value of each element of `self`.
|
||||
---
|
||||
---@return self
|
||||
function Vec2:abs() end
|
||||
|
||||
---Component-wise clamping of values.
|
||||
---
|
||||
---Each element in `min` must be less-or-equal to the corresponding element in `max`.
|
||||
---
|
||||
---@param min self The minimum `Vec2` components to clamp the components of `self` to.
|
||||
---@param max self The maximum `Vec2` components to clamp the components of `self` to.
|
||||
---@return self
|
||||
function Vec2:clamp(min, max) end
|
||||
|
||||
---Converts `self` to an array `[x, y]`
|
||||
---
|
||||
---@return number[]
|
||||
function Vec2:to_array() end
|
||||
|
||||
---Move `self` by `x` and `y` values.
|
||||
---
|
||||
---@param x number
|
||||
---@param y number
|
||||
function Vec2:move_by(x, y) end
|
||||
|
||||
---Move `self` by a `Vec2`.
|
||||
---
|
||||
---@param rhs Vec2
|
||||
function Vec2:move_by(rhs) end
|
|
@ -0,0 +1,139 @@
|
|||
---@meta
|
||||
|
||||
---@class Vec3: userdata
|
||||
---This is a Lua export of [`glam::Vec3`](https://docs.rs/glam/latest/glam/f32/struct.Vec3.html)
|
||||
---
|
||||
---@operator add(self|number): self
|
||||
---@operator sub(self|number): self
|
||||
---@operator div(self|number): self
|
||||
---@operator mul(self|number): self
|
||||
---@operator mod(self|number): self
|
||||
---@operator unm: self
|
||||
---@diagnostic disable-next-line: unknown-operator
|
||||
---@operator eq: self
|
||||
Vec3 = {
|
||||
---The x coordinate
|
||||
---@type number
|
||||
x = nil,
|
||||
|
||||
---The y coordinate
|
||||
---@type number
|
||||
y = nil,
|
||||
|
||||
---The z coordinate
|
||||
---@type number
|
||||
z = nil,
|
||||
|
||||
---A constant `Vec3` with coordinates as `f32::NAN`.
|
||||
---@type Vec3
|
||||
NAN = nil,
|
||||
|
||||
---A constant `Vec3` with `x` as `-1.0`.
|
||||
---@type Vec3
|
||||
NEG_X = nil,
|
||||
|
||||
---A constant `Vec3` with `y` as `-1.0`.
|
||||
---@type Vec3
|
||||
NEG_Y = nil,
|
||||
|
||||
---A constant `Vec3` with `z` as `-1.0`.
|
||||
---@type Vec3
|
||||
NEG_Z = nil,
|
||||
|
||||
---A constant `Vec3` with all components as `-1.0`.
|
||||
---@type Vec3
|
||||
NEG_ONE = nil,
|
||||
|
||||
---A constant `Vec3` with `x` as `1.0`.
|
||||
---@type Vec3
|
||||
POS_X = nil,
|
||||
|
||||
---A constant `Vec3` with `y` as `1.0`.
|
||||
---@type Vec3
|
||||
POS_Y = nil,
|
||||
|
||||
---A constant `Vec3` with `z` as `1.0`.
|
||||
---@type Vec3
|
||||
POS_Z = nil,
|
||||
|
||||
---A constant `Vec3` with all components as `1.0`.
|
||||
---@type Vec3
|
||||
ONE = nil,
|
||||
|
||||
---A constant `Vec3` with all components as `0.0`.
|
||||
---@type Vec3
|
||||
ZERO = nil,
|
||||
}
|
||||
|
||||
function Vec3:__tostring() end
|
||||
|
||||
---Create a new `Vec3`
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param z number
|
||||
---@return self
|
||||
function Vec3.new(x, y, z) end
|
||||
|
||||
---Returns a vector with a length no less than min and no more than max.
|
||||
---@param min number the minimum value to clamp the length to
|
||||
---@param max number the maximum value to clamp the length to
|
||||
---@return self
|
||||
function Vec3:clamp_length(min, max) end
|
||||
|
||||
---Returns true if the absolute difference of all elements between `self` and `rhs` is less
|
||||
---than or equal to `max_abs_diff`.
|
||||
---
|
||||
---This can be used to compare if two vectors contain similar elements. It works best when
|
||||
---comparing with a known value. The `max_abs_diff` that should be used used depends on the
|
||||
---values being compared against.
|
||||
---
|
||||
---For more see [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
|
||||
---
|
||||
---@param rhs Vec3 The other `Vec3` to compare to.
|
||||
---@param max_abs_diff number Maximum absolute difference between `self` and `rhs`.
|
||||
---@return boolean
|
||||
function Vec3:abs_diff_eq(rhs, max_abs_diff) end
|
||||
|
||||
---Returns a vector containing the smallest integer greater than or equal to a number for each
|
||||
---element of self.
|
||||
---@return self
|
||||
function Vec3:ceil() end
|
||||
|
||||
---Returns the angle (in radians) between two vectors in the range [-π, +π].
|
||||
---
|
||||
---The inputs do not need to be unit vectors however they must be non-zero.
|
||||
---
|
||||
---@param rhs Vec3 The other `Vec3` to get the angle to.
|
||||
---@return number
|
||||
function Vec3:angle_between(rhs) end
|
||||
|
||||
---Returns a vector containing the absolute value of each element of `self`.
|
||||
---
|
||||
---@return self
|
||||
function Vec3:abs() end
|
||||
|
||||
---Component-wise clamping of values.
|
||||
---
|
||||
---Each element in `min` must be less-or-equal to the corresponding element in `max`.
|
||||
---
|
||||
---@param min self The minimum `Vec3` components to clamp the components of `self` to.
|
||||
---@param max self The maximum `Vec3` components to clamp the components of `self` to.
|
||||
---@return self
|
||||
function Vec3:clamp(min, max) end
|
||||
|
||||
---Converts `self` to an array `[x, y, z]`
|
||||
---
|
||||
---@return number[]
|
||||
function Vec3:to_array() end
|
||||
|
||||
---Move `self` by `x`, `y`, and `z` values.
|
||||
---
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param z number
|
||||
function Vec3:move_by(x, y, z) end
|
||||
|
||||
---Move `self` by a `Vec3`.
|
||||
---
|
||||
---@param rhs Vec3
|
||||
function Vec3:move_by(rhs) end
|
|
@ -0,0 +1,132 @@
|
|||
---@meta
|
||||
|
||||
---@class Vec4: userdata
|
||||
---This is a Lua export of [`glam::Vec4`](https://docs.rs/glam/latest/glam/f32/struct.Vec4.html)
|
||||
---
|
||||
---@operator add(self|number): self
|
||||
---@operator sub(self|number): self
|
||||
---@operator div(self|number): self
|
||||
---@operator mul(self|number): self
|
||||
---@operator mod(self|number): self
|
||||
---@operator unm: self
|
||||
---@diagnostic disable-next-line: unknown-operator
|
||||
---@operator eq: self
|
||||
Vec4 = {
|
||||
---The x coordinate
|
||||
---@type number
|
||||
x = nil,
|
||||
|
||||
---The y coordinate
|
||||
---@type number
|
||||
y = nil,
|
||||
|
||||
---The z coordinate
|
||||
---@type number
|
||||
z = nil,
|
||||
|
||||
---The w coordinate
|
||||
---@type number
|
||||
w = nil,
|
||||
|
||||
---A constant `Vec4` with coordinates as `f32::NAN`.
|
||||
---@type Vec4
|
||||
NAN = nil,
|
||||
|
||||
---A constant `Vec4` with `x` as `-1.0`.
|
||||
---@type Vec4
|
||||
NEG_X = nil,
|
||||
|
||||
---A constant `Vec4` with `y` as `-1.0`.
|
||||
---@type Vec4
|
||||
NEG_Y = nil,
|
||||
|
||||
---A constant `Vec4` with `z` as `-1.0`.
|
||||
---@type Vec4
|
||||
NEG_Z = nil,
|
||||
|
||||
---A constant `Vec4` with `w` as `-1.0`.
|
||||
---@type Vec4
|
||||
NEG_W = nil,
|
||||
|
||||
---A constant `Vec4` with all components as `-1.0`.
|
||||
---@type Vec4
|
||||
NEG_ONE = nil,
|
||||
|
||||
---A constant `Vec4` with `x` as `1.0`.
|
||||
---@type Vec4
|
||||
POS_X = nil,
|
||||
|
||||
---A constant `Vec4` with `y` as `1.0`.
|
||||
---@type Vec4
|
||||
POS_Y = nil,
|
||||
|
||||
---A constant `Vec4` with `z` as `1.0`.
|
||||
---@type Vec4
|
||||
POS_Z = nil,
|
||||
|
||||
---A constant `Vec4` with `w` as `1.0`.
|
||||
---@type Vec4
|
||||
POS_W = nil,
|
||||
|
||||
---A constant `Vec4` with all components as `1.0`.
|
||||
---@type Vec4
|
||||
ONE = nil,
|
||||
|
||||
---A constant `Vec4` with all components as `0.0`.
|
||||
---@type Vec4
|
||||
ZERO = nil,
|
||||
}
|
||||
|
||||
function Vec4:__tostring() end
|
||||
|
||||
---Create a new `Vec4`
|
||||
---@param x number
|
||||
---@param y number
|
||||
---@param z number
|
||||
---@param w number
|
||||
---@return self
|
||||
function Vec4.new(x, y, z, w) end
|
||||
|
||||
---Returns a vector with a length no less than min and no more than max.
|
||||
---@param min number the minimum value to clamp the length to
|
||||
---@param max number the maximum value to clamp the length to
|
||||
---@return self
|
||||
function Vec4:clamp_length(min, max) end
|
||||
|
||||
---Returns true if the absolute difference of all elements between `self` and `rhs` is less
|
||||
---than or equal to `max_abs_diff`.
|
||||
---
|
||||
---This can be used to compare if two vectors contain similar elements. It works best when
|
||||
---comparing with a known value. The `max_abs_diff` that should be used used depends on the
|
||||
---values being compared against.
|
||||
---
|
||||
---For more see [comparing floating point numbers](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/).
|
||||
---
|
||||
---@param rhs Vec4 The other `Vec4` to compare to.
|
||||
---@param max_abs_diff number Maximum absolute difference between `self` and `rhs`.
|
||||
---@return boolean
|
||||
function Vec4:abs_diff_eq(rhs, max_abs_diff) end
|
||||
|
||||
---Returns a vector containing the smallest integer greater than or equal to a number for each
|
||||
---element of self.
|
||||
---@return self
|
||||
function Vec4:ceil() end
|
||||
|
||||
---Returns a vector containing the absolute value of each element of `self`.
|
||||
---
|
||||
---@return self
|
||||
function Vec4:abs() end
|
||||
|
||||
---Component-wise clamping of values.
|
||||
---
|
||||
---Each element in `min` must be less-or-equal to the corresponding element in `max`.
|
||||
---
|
||||
---@param min self The minimum `Vec4` components to clamp the components of `self` to.
|
||||
---@param max self The maximum `Vec4` components to clamp the components of `self` to.
|
||||
---@return self
|
||||
function Vec4:clamp(min, max) end
|
||||
|
||||
---Converts `self` to an array `[x, y, z]`
|
||||
---
|
||||
---@return number[]
|
||||
function Vec4:to_array() end
|
|
@ -1,23 +0,0 @@
|
|||
WindowMode = {
|
||||
WNDOWED = "windowed",
|
||||
BORDERLESS_FULLSCREEN = "borderless_fullscreen",
|
||||
SIZED_FULLSCREEN = "sized_fullscreen",
|
||||
FULLSCREEN = "fullscreen",
|
||||
}
|
||||
|
||||
CursorGrabMode = {
|
||||
NONE = "none",
|
||||
CONFINED = "confined",
|
||||
LOCKED = "locked",
|
||||
}
|
||||
|
||||
WindowTheme = {
|
||||
LIGHT = "light",
|
||||
DARK = "dark",
|
||||
}
|
||||
|
||||
WindowLevel = {
|
||||
ALWAYS_ON_BOTTOM = "always_on_bottom",
|
||||
NORMAL = "normal",
|
||||
ALWAYS_ON_TOP = "always_on_top",
|
||||
}
|
|
@ -2,6 +2,7 @@ pub mod dynamic_iter;
|
|||
pub use dynamic_iter::*;
|
||||
|
||||
pub mod world;
|
||||
use lyra_resource::ResourceData;
|
||||
use mlua::ObjectLike;
|
||||
pub use world::*;
|
||||
|
||||
|
@ -19,11 +20,12 @@ pub use proxy::*;
|
|||
|
||||
pub mod system;
|
||||
pub use system::*;
|
||||
use wrappers::{LuaHandleWrapper, LuaResHandleToComponent, LuaWrappedEventProxy};
|
||||
|
||||
use std::{any::TypeId, sync::Mutex};
|
||||
|
||||
use lyra_ecs::World;
|
||||
use lyra_reflect::{Reflect, TypeRegistry};
|
||||
use lyra_reflect::{FromType, Reflect, TypeRegistry};
|
||||
use crate::ScriptBorrow;
|
||||
|
||||
pub type LuaContext = Mutex<mlua::Lua>;
|
||||
|
@ -131,6 +133,36 @@ pub trait RegisterLuaType {
|
|||
fn register_lua_convert<T>(&mut self)
|
||||
where
|
||||
T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static;
|
||||
|
||||
/// Registers a type that can be converted to and from lua and adds a lookup entry.
|
||||
///
|
||||
/// This is a shortcut for `register_lua_convert` and `add_component_lookup_entry`.
|
||||
fn register_lua_convert_component<T>(&mut self, name: &str)
|
||||
where
|
||||
T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static,
|
||||
T::Wrap: lyra_ecs::Component + Reflect;
|
||||
|
||||
/// Register an asset handle wrapper.
|
||||
fn register_asset_handle<T>(&mut self, name: &str)
|
||||
where
|
||||
T: LuaHandleWrapper + Reflect + LuaProxy,
|
||||
T::ResourceType: ResourceData;
|
||||
|
||||
/// Add an entry for a non-component in the [`TypeLookup`] table.
|
||||
fn add_lua_event<T>(&mut self, name: &str)
|
||||
where
|
||||
T: Reflect + LuaWrapper + mlua::FromLua + mlua::IntoLua + Send + Sync,
|
||||
T::Wrap: Clone + lyra_game::Event;
|
||||
|
||||
/// Add an entry for a component in the [`TypeLookup`] table.
|
||||
fn add_component_lookup_entry<T>(&mut self, name: &str)
|
||||
where
|
||||
T: lyra_ecs::Component;
|
||||
|
||||
/// Add an entry for a non-component in the [`TypeLookup`] table.
|
||||
fn add_lookup_entry<T>(&mut self, name: &str)
|
||||
where
|
||||
T: 'static;
|
||||
}
|
||||
|
||||
impl RegisterLuaType for World {
|
||||
|
@ -165,6 +197,63 @@ impl RegisterLuaType for World {
|
|||
let reg_type = registry.get_type_or_default(T::wrapped_type_id());
|
||||
reg_type.add_data(ReflectLuaProxy::from_as_and_from_lua::<T>());
|
||||
}
|
||||
|
||||
fn register_lua_convert_component<T>(&mut self, name: &str)
|
||||
where
|
||||
T: Clone + mlua::FromLua + mlua::IntoLua + LuaWrapper + 'static,
|
||||
T::Wrap: lyra_ecs::Component + Reflect
|
||||
{
|
||||
self.register_lua_convert::<T>();
|
||||
self.add_component_lookup_entry::<T::Wrap>(name);
|
||||
}
|
||||
|
||||
fn register_asset_handle<T>(&mut self, name: &str)
|
||||
where
|
||||
T: LuaHandleWrapper + Reflect + LuaProxy,
|
||||
T::ResourceType: ResourceData
|
||||
{
|
||||
{
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
|
||||
let reg_type = registry.get_type_or_default(TypeId::of::<T::ResourceType>());
|
||||
reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<T>());
|
||||
let l: LuaResHandleToComponent = FromType::<T>::from_type();
|
||||
reg_type.add_data(l);
|
||||
}
|
||||
|
||||
self.add_lookup_entry::<T>(name);
|
||||
}
|
||||
|
||||
fn add_lua_event<T>(&mut self, name: &str)
|
||||
where
|
||||
T: Reflect + LuaWrapper + mlua::FromLua + mlua::IntoLua + Send + Sync,
|
||||
T::Wrap: Clone + lyra_game::Event
|
||||
{
|
||||
{
|
||||
let mut registry = self.get_resource_mut::<TypeRegistry>().unwrap();
|
||||
let reg_type = registry.get_type_or_default(TypeId::of::<T::Wrap>());
|
||||
let proxy: LuaWrappedEventProxy = FromType::<T>::from_type();
|
||||
reg_type.add_data(proxy);
|
||||
}
|
||||
|
||||
self.add_lookup_entry::<T::Wrap>(name);
|
||||
}
|
||||
|
||||
fn add_component_lookup_entry<T>(&mut self, name: &str)
|
||||
where
|
||||
T: lyra_ecs::Component
|
||||
{
|
||||
let mut lookup = self.get_resource_or_default::<TypeLookup>();
|
||||
lookup.comp_info_from_name.insert(name.into(), lyra_ecs::ComponentInfo::new::<T>());
|
||||
lookup.typeid_from_name.insert(name.into(), std::any::TypeId::of::<T>());
|
||||
}
|
||||
|
||||
fn add_lookup_entry<T>(&mut self, name: &str)
|
||||
where
|
||||
T: 'static
|
||||
{
|
||||
let mut lookup = self.get_resource_or_default::<TypeLookup>();
|
||||
lookup.typeid_from_name.insert(name.into(), std::any::TypeId::of::<T>());
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::FromLua for ScriptBorrow {
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use std::any::TypeId;
|
||||
|
||||
use lyra_ecs::ResourceObject;
|
||||
use lyra_reflect::{Reflect, TypeRegistry};
|
||||
use lyra_resource::gltf::Gltf;
|
||||
use lyra_reflect::Reflect;
|
||||
|
||||
use crate::{lua::{wrappers::{LuaActionHandler, LuaDeltaTime, LuaGltfHandle, LuaResHandleToComponent, LuaSceneHandle, LuaWindow}, LuaContext, ReflectLuaProxy, RegisterLuaType, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
|
||||
use crate::{lua::{wrappers::*, LuaContext, LuaWrapper, RegisterLuaType, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, ScriptApiProvider, ScriptBorrow, ScriptData, ScriptDynamicBundle, ScriptWorldPtr};
|
||||
|
||||
//fn register_lua_proxy::<T:
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LyraEcsApiProvider;
|
||||
|
@ -17,40 +16,38 @@ impl ScriptApiProvider for LyraEcsApiProvider {
|
|||
world.register_lua_wrapper::<LuaSceneHandle>();
|
||||
world.register_lua_wrapper::<LuaActionHandler>();
|
||||
world.register_lua_wrapper::<LuaWindow>();
|
||||
|
||||
world.register_lua_convert_component::<LuaCamera>("Camera");
|
||||
world.register_lua_convert_component::<LuaFreeFlyCamera>("FreeFlyCamera");
|
||||
world.register_lua_convert_component::<LuaWorldTransform>("WorldTransform");
|
||||
|
||||
let mut registry = world.get_resource_mut::<TypeRegistry>().unwrap();
|
||||
world.register_lua_wrapper::<LuaDeviceId>();
|
||||
world.register_lua_convert::<LuaDeviceEventRaw>();
|
||||
world.register_lua_convert::<LuaDeviceEvent>();
|
||||
world.add_lua_event::<LuaDeviceEvent>("DeviceEvent");
|
||||
|
||||
let reg_type = registry.get_type_or_default(TypeId::of::<Gltf>());
|
||||
reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<LuaGltfHandle>());
|
||||
|
||||
let l = LuaResHandleToComponent::new(
|
||||
|lua, res| {
|
||||
if let Some(gltf) = res.as_typed::<Gltf>() {
|
||||
Some(lua.create_userdata(LuaGltfHandle(gltf)).unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
);
|
||||
reg_type.add_data(l);
|
||||
|
||||
//reg_type.add_data(ReflectLuaProxy::from_lua_proxy::<LuaGltfHandle>());
|
||||
world.register_asset_handle::<LuaGltfHandle>("Gltf");
|
||||
}
|
||||
|
||||
fn expose_api(&mut self, _: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
|
||||
let ctx = ctx.lock().unwrap();
|
||||
|
||||
// load window util
|
||||
let bytes = include_str!("../../../scripts/lua/window.lua");
|
||||
// load enums
|
||||
let bytes = include_str!("../../../scripts/lua/enums.lua");
|
||||
ctx.load(bytes).exec().unwrap();
|
||||
|
||||
let globals = ctx.globals();
|
||||
globals.set("World", ctx.create_proxy::<ScriptWorldPtr>()?)?;
|
||||
globals.set("DynamicBundle", ctx.create_proxy::<ScriptDynamicBundle>()?)?;
|
||||
globals.set("SceneComponent", ctx.create_proxy::<LuaSceneHandle>()?)?;
|
||||
globals.set("SceneHandler", ctx.create_proxy::<LuaSceneHandle>()?)?;
|
||||
globals.set("ActionHandler", ctx.create_proxy::<LuaActionHandler>()?)?;
|
||||
globals.set("Window", ctx.create_proxy::<LuaWindow>()?)?;
|
||||
|
||||
expose_comp_table_wrapper::<LuaCamera>(&ctx, &globals, "Camera")?;
|
||||
expose_comp_table_wrapper::<LuaFreeFlyCamera>(&ctx, &globals, "FreeFlyCamera")?;
|
||||
expose_comp_table_wrapper::<LuaWorldTransform>(&ctx, &globals, "WorldTransform")?;
|
||||
expose_table_wrapper::<LuaDeviceEvent>(&ctx, &globals, "DeviceEvent")?;
|
||||
|
||||
let dt_table = create_reflect_table::<lyra_game::DeltaTime>(&ctx)?;
|
||||
globals.set("DeltaTime", dt_table)?;
|
||||
|
||||
|
@ -73,4 +70,50 @@ fn create_reflect_table<T: Reflect + ResourceObject + Default + 'static>(lua: &m
|
|||
})?)?;
|
||||
|
||||
Ok(table)
|
||||
}
|
||||
|
||||
fn create_reflect_comp_table<T>(lua: &mlua::Lua, name: &str) -> mlua::Result<mlua::Table>
|
||||
where
|
||||
T: LuaWrapper + mlua::FromLua,
|
||||
T::Wrap: lyra_ecs::Component + Reflect
|
||||
{
|
||||
let table = lua.create_table()?;
|
||||
table.set(FN_NAME_INTERNAL_REFLECT, lua.create_function(|_, this: T| {
|
||||
Ok(ScriptBorrow::from_component::<T::Wrap>(Some(this.into_wrapped())))
|
||||
})?)?;
|
||||
|
||||
table.set(FN_NAME_INTERNAL_REFLECT_TYPE, lua.create_function(|_, ()| {
|
||||
Ok(ScriptBorrow::from_component::<T::Wrap>(None))
|
||||
})?)?;
|
||||
|
||||
table.set(mlua::MetaMethod::Type.name(), name)?;
|
||||
|
||||
Ok(table)
|
||||
}
|
||||
|
||||
/// Expose a wrapper of a component that converts to/from a lua type.
|
||||
///
|
||||
/// This type of wrapper could convert to/from a Lua table, or number, string, etc., any Lua type.
|
||||
///
|
||||
/// This creates the reflection functions on a table specified in globals.
|
||||
/// The table name is set to `name`, which is also how the script will use the table.
|
||||
fn expose_comp_table_wrapper<T>(lua: &mlua::Lua, globals: &mlua::Table, name: &str) -> mlua::Result<()>
|
||||
where
|
||||
T: LuaWrapper + mlua::FromLua,
|
||||
T::Wrap: lyra_ecs::Component + Reflect
|
||||
{
|
||||
let table = create_reflect_comp_table::<T>(&lua, name)?;
|
||||
globals.set(name, table)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn expose_table_wrapper<T>(lua: &mlua::Lua, globals: &mlua::Table, name: &str) -> mlua::Result<()>
|
||||
where
|
||||
T: LuaWrapper,
|
||||
{
|
||||
let table = lua.create_table()?;
|
||||
table.set(mlua::MetaMethod::Type.name(), name.to_string())?;
|
||||
globals.set(name, table)?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
use lyra_ecs::World;
|
||||
use crate::lua::wrappers::{LuaQuat, LuaTransform, LuaVec3, LuaVec2};
|
||||
use crate::lua::wrappers::{LuaAngle, LuaQuat, LuaTransform, LuaVec2, LuaVec3};
|
||||
use crate::ScriptData;
|
||||
use crate::lua::RegisterLuaType;
|
||||
|
||||
|
@ -16,6 +16,7 @@ impl ScriptApiProvider for LyraMathApiProvider {
|
|||
world.register_lua_wrapper::<LuaVec3>();
|
||||
world.register_lua_wrapper::<LuaQuat>();
|
||||
world.register_lua_wrapper::<LuaTransform>();
|
||||
world.register_lua_wrapper::<LuaAngle>();
|
||||
}
|
||||
|
||||
fn expose_api(&mut self, _data: &ScriptData, ctx: &mut Self::ScriptContext) -> Result<(), crate::ScriptError> {
|
||||
|
@ -29,6 +30,7 @@ impl ScriptApiProvider for LyraMathApiProvider {
|
|||
globals.set("Vec3", ctx.create_proxy::<LuaVec3>()?)?;
|
||||
globals.set("Quat", ctx.create_proxy::<LuaQuat>()?)?;
|
||||
globals.set("Transform", ctx.create_proxy::<LuaTransform>()?)?;
|
||||
globals.set("Angle", ctx.create_proxy::<LuaAngle>()?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -8,9 +8,22 @@ use crate::{ScriptBorrow, ScriptDynamicBundle};
|
|||
|
||||
use super::{Error, FN_NAME_INTERNAL_REFLECT};
|
||||
|
||||
pub trait LuaWrapper {
|
||||
pub trait LuaWrapper: Sized {
|
||||
type Wrap: 'static;
|
||||
|
||||
/// The type id of the wrapped type.
|
||||
fn wrapped_type_id() -> TypeId;
|
||||
#[inline(always)]
|
||||
fn wrapped_type_id() -> TypeId {
|
||||
TypeId::of::<Self::Wrap>()
|
||||
}
|
||||
|
||||
fn into_wrapped(self) -> Self::Wrap;
|
||||
|
||||
#[inline(always)]
|
||||
fn from_wrapped(wrap: Self::Wrap) -> Option<Self> {
|
||||
let _ = wrap;
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait that used to convert something into lua, or to set something to a value from lua.
|
||||
|
@ -29,7 +42,7 @@ pub trait LuaProxy {
|
|||
|
||||
impl<'a, T> LuaProxy for T
|
||||
where
|
||||
T: Reflect + Clone + mlua::FromLua + mlua::UserData
|
||||
T: Reflect + Clone + mlua::FromLua + mlua::IntoLua
|
||||
{
|
||||
fn as_lua_value(
|
||||
lua: &mlua::Lua,
|
||||
|
@ -40,24 +53,24 @@ where
|
|||
}
|
||||
|
||||
fn apply(
|
||||
_: &mlua::Lua,
|
||||
lua: &mlua::Lua,
|
||||
this: &mut dyn Reflect,
|
||||
apply: &mlua::Value,
|
||||
) -> mlua::Result<()> {
|
||||
let this = this.as_any_mut().downcast_mut::<T>().unwrap();
|
||||
let apply = apply.as_userdata()
|
||||
.expect("Somehow a non-userdata Lua Value was provided to a LuaProxy")
|
||||
.borrow::<T>()?;
|
||||
let apply = T::from_lua(apply.clone(), lua)?;
|
||||
|
||||
*this = apply.clone();
|
||||
*this = apply;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A struct that is used for retrieving rust type ids of types that implement `TableProxy`.
|
||||
/// ECS resource that can be used to lookup types via name.
|
||||
///
|
||||
/// You can get the [`TypeId`] of the type via name, or the [`ComponentInfo`].
|
||||
#[derive(Default)]
|
||||
pub struct LuaTableProxyLookup {
|
||||
pub struct TypeLookup {
|
||||
pub(crate) typeid_from_name: HashMap<String, TypeId>,
|
||||
pub(crate) comp_info_from_name: HashMap<String, ComponentInfo>,
|
||||
}
|
||||
|
|
|
@ -10,14 +10,20 @@ use lyra_resource::ResourceManager;
|
|||
use mlua::{IntoLua, ObjectLike};
|
||||
|
||||
use super::{
|
||||
reflect_user_data, wrappers::LuaResHandleToComponent, Error, LuaTableProxyLookup, ReflectLuaProxy, ReflectedIterator, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE
|
||||
reflect_user_data,
|
||||
wrappers::{LuaResHandleToComponent, LuaWrappedEventProxy},
|
||||
Error, ReflectLuaProxy, ReflectedIterator, TypeLookup, FN_NAME_INTERNAL_AS_COMPONENT,
|
||||
FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE,
|
||||
};
|
||||
|
||||
impl mlua::FromLua for ScriptEntity {
|
||||
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
match value {
|
||||
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
||||
mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptEntity", "Nil"))),
|
||||
mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch(
|
||||
"ScriptEntity",
|
||||
"Nil",
|
||||
))),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +47,10 @@ impl mlua::FromLua for ScriptWorldPtr {
|
|||
fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
match val {
|
||||
mlua::Value::UserData(ud) => Ok(ud.borrow::<Self>()?.clone()),
|
||||
mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch("ScriptWorldPtr", "Nil"))),
|
||||
mlua::Value::Nil => Err(mlua::Error::external(Error::type_mismatch(
|
||||
"ScriptWorldPtr",
|
||||
"Nil",
|
||||
))),
|
||||
_ => panic!(),
|
||||
}
|
||||
}
|
||||
|
@ -64,8 +73,7 @@ impl mlua::UserData for ScriptWorldPtr {
|
|||
})?;
|
||||
|
||||
let comp_borrow = {
|
||||
if let Ok(as_comp) = ud.get::<mlua::Function>(FN_NAME_INTERNAL_AS_COMPONENT)
|
||||
{
|
||||
if let Ok(as_comp) = ud.get::<mlua::Function>(FN_NAME_INTERNAL_AS_COMPONENT) {
|
||||
let ud = match as_comp.call(ud.clone())? {
|
||||
mlua::Value::UserData(ud) => ud,
|
||||
mlua::Value::Nil => ud.clone(),
|
||||
|
@ -115,11 +123,12 @@ impl mlua::UserData for ScriptWorldPtr {
|
|||
mlua::Value::Table(t) => {
|
||||
let name: String = t.get(mlua::MetaMethod::Type.name())?;
|
||||
|
||||
let lookup = world.get_resource::<LuaTableProxyLookup>().ok_or(
|
||||
mlua::Error::runtime(
|
||||
"Unable to lookup table proxy, none were ever registered!",
|
||||
),
|
||||
)?;
|
||||
let lookup =
|
||||
world
|
||||
.get_resource::<TypeLookup>()
|
||||
.ok_or(mlua::Error::runtime(
|
||||
"Unable to lookup table proxy, none were ever registered!",
|
||||
))?;
|
||||
let info = lookup.comp_info_from_name.get(&name).ok_or_else(|| {
|
||||
mlua::Error::BadArgument {
|
||||
to: Some("World:view".into()),
|
||||
|
@ -139,10 +148,7 @@ impl mlua::UserData for ScriptWorldPtr {
|
|||
}
|
||||
mlua::Value::UserData(ud) => {
|
||||
let reflect = ud
|
||||
.call_function::<ScriptBorrow>(
|
||||
FN_NAME_INTERNAL_REFLECT_TYPE,
|
||||
(),
|
||||
)
|
||||
.call_function::<ScriptBorrow>(FN_NAME_INTERNAL_REFLECT_TYPE, ())
|
||||
.expect("Type does not implement 'reflect_type' properly");
|
||||
let refl_comp = reflect.reflect_branch.as_component_unchecked();
|
||||
|
||||
|
@ -172,7 +178,9 @@ impl mlua::UserData for ScriptWorldPtr {
|
|||
let mut world = this.write();
|
||||
|
||||
while let Some(row) = reflected_iter.next_lua(lua) {
|
||||
let r = row.row.into_iter()
|
||||
let r = row
|
||||
.row
|
||||
.into_iter()
|
||||
.into_iter()
|
||||
.map(|r| (r.comp_val, r.comp_ptr.cast::<()>()))
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -183,7 +191,6 @@ impl mlua::UserData for ScriptWorldPtr {
|
|||
|
||||
// if values were returned, find the type in the type registry, and apply the new values
|
||||
if res.len() <= ptrs.len() {
|
||||
|
||||
for (comp, ptr) in res.into_iter().zip(ptrs) {
|
||||
let lua_typeid = match &comp {
|
||||
mlua::Value::UserData(ud) => {
|
||||
|
@ -195,7 +202,7 @@ impl mlua::UserData for ScriptWorldPtr {
|
|||
mlua::Value::Table(tbl) => {
|
||||
let name: String = tbl.get(mlua::MetaMethod::Type.name())?;
|
||||
|
||||
let lookup = world.get_resource::<LuaTableProxyLookup>().unwrap();
|
||||
let lookup = world.get_resource::<TypeLookup>().unwrap();
|
||||
*lookup.typeid_from_name.get(&name).unwrap()
|
||||
}
|
||||
_ => {
|
||||
|
@ -294,7 +301,7 @@ impl mlua::UserData for ScriptWorldPtr {
|
|||
|
||||
Ok(())
|
||||
});
|
||||
methods.add_method_mut("request_res", |lua, this, path: String| {
|
||||
methods.add_method_mut("request_asset", |lua, this, path: String| {
|
||||
let world = this.write();
|
||||
let man = world.get_resource_mut::<ResourceManager>().unwrap();
|
||||
let handle = man.request_raw(&path).unwrap();
|
||||
|
@ -310,5 +317,41 @@ impl mlua::UserData for ScriptWorldPtr {
|
|||
|
||||
Ok((data.fn_to_lua)(lua, handle).unwrap())
|
||||
});
|
||||
methods.add_method_mut(
|
||||
"read_event",
|
||||
|lua, this, either: mlua::Either<String, mlua::Table>| {
|
||||
let mut world = this.write();
|
||||
|
||||
// get the type name
|
||||
let name = match either {
|
||||
mlua::Either::Left(name) => name,
|
||||
mlua::Either::Right(table) => table.get(mlua::MetaMethod::Type.name())?,
|
||||
};
|
||||
|
||||
// lookup the type id using the name, return an error if not found
|
||||
let lookup = world.get_resource::<TypeLookup>().unwrap();
|
||||
let tyid = lookup.typeid_from_name.get(&name).cloned();
|
||||
if tyid.is_none() {
|
||||
return Err(mlua::Error::runtime(format!(
|
||||
"failed to lookup type id from type name: {}",
|
||||
name
|
||||
)));
|
||||
}
|
||||
let tyid = tyid.unwrap();
|
||||
drop(lookup);
|
||||
|
||||
let registry = world.get_resource::<TypeRegistry>().unwrap();
|
||||
let ty = registry
|
||||
.get_type(tyid)
|
||||
.expect("Could not find asset type in registry");
|
||||
let data = ty
|
||||
.get_data::<LuaWrappedEventProxy>()
|
||||
.expect("Asset type does not have 'LuaWrappedEventProxy' as TypeData")
|
||||
.clone();
|
||||
drop(registry);
|
||||
|
||||
data.reader(&mut world).into_lua(lua)
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,237 @@
|
|||
use std::{any::TypeId, ops::Deref};
|
||||
use lyra_resource::{gltf::{Gltf, Material, Mesh}, FilterMode, ResHandle, Texture, WrappingMode};
|
||||
use lyra_game::scene::SceneGraph;
|
||||
use lyra_reflect::Reflect;
|
||||
use lyra_scripting_derive::{lua_wrap_handle, wrap_lua_struct};
|
||||
|
||||
use crate::{lua::{LuaWrapper, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, lyra_engine, ScriptBorrow};
|
||||
use crate as lyra_scripting;
|
||||
|
||||
use super::LuaHandleWrapper;
|
||||
|
||||
use mlua::IntoLua;
|
||||
|
||||
fn filter_mode_to_str(fm: FilterMode) -> &'static str {
|
||||
match fm {
|
||||
FilterMode::Nearest => "nearest",
|
||||
FilterMode::Linear => "linear",
|
||||
}
|
||||
}
|
||||
|
||||
fn wrapping_mode_to_str(wm: WrappingMode) -> &'static str {
|
||||
match wm {
|
||||
WrappingMode::ClampToEdge => "clamp_to_edge",
|
||||
WrappingMode::MirroredRepeat => "mirrored_repeat",
|
||||
WrappingMode::Repeat => "repeat",
|
||||
}
|
||||
}
|
||||
|
||||
wrap_lua_struct!(lyra_resource::TextureSampler,
|
||||
// this can be safely skipped since it wont be a component or resource.
|
||||
skip(lua_reflect),
|
||||
fields={
|
||||
// don't need to specify field types since setters are skipped
|
||||
(mag_filter, skip_set, get={
|
||||
this.mag_filter.map(|f| filter_mode_to_str(f))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(min_filter, skip_set, get={
|
||||
this.min_filter.map(|f| filter_mode_to_str(f))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(mipmap_filter, skip_set, get={
|
||||
this.mipmap_filter.map(|f| filter_mode_to_str(f))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(wrap_u, skip_set, get={
|
||||
wrapping_mode_to_str(this.wrap_u)
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(wrap_v, skip_set, get={
|
||||
wrapping_mode_to_str(this.wrap_v)
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(wrap_w, skip_set, get={
|
||||
wrapping_mode_to_str(this.wrap_w)
|
||||
.into_lua(lua)
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
wrap_lua_struct!(lyra_resource::gltf::PbrGlossiness,
|
||||
// this can be safely skipped since it wont be a component or resource.
|
||||
skip(lua_reflect),
|
||||
fields={
|
||||
(diffuse_color: wrap(crate::lua::wrappers::LuaVec4), skip_set),
|
||||
(specular: wrap(crate::lua::wrappers::LuaVec3), skip_set),
|
||||
(glossiness, skip_set),
|
||||
}
|
||||
);
|
||||
|
||||
wrap_lua_struct!(lyra_resource::gltf::Specular,
|
||||
// this can be safely skipped since it wont be a component or resource.
|
||||
skip(lua_reflect),
|
||||
fields={
|
||||
(factor, skip_set),
|
||||
(color_factor: wrap(crate::lua::wrappers::LuaVec3), skip_set),
|
||||
(texture, skip_set, get={
|
||||
this.texture.clone()
|
||||
.map(|t| LuaTextureHandle(t))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(color_texture, skip_set, get={
|
||||
this.color_texture.clone()
|
||||
.map(|t| LuaTextureHandle(t))
|
||||
.into_lua(lua)
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: fields
|
||||
lua_wrap_handle!(SceneGraph, name=Scene, {});
|
||||
|
||||
lua_wrap_handle!(Mesh,
|
||||
field_getters={
|
||||
(material, {
|
||||
data.material.clone()
|
||||
.map(|v| LuaMaterialHandle(v.clone()))
|
||||
.into_lua(lua)
|
||||
})
|
||||
},
|
||||
{
|
||||
methods.add_method("indices", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
match &data.indices {
|
||||
Some(lyra_resource::gltf::MeshIndices::U16(v)) => {
|
||||
for (i, ind) in v.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_set(i, *ind)?;
|
||||
}
|
||||
},
|
||||
Some(lyra_resource::gltf::MeshIndices::U32(v)) => {
|
||||
for (i, ind) in v.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_set(i, *ind)?;
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
|
||||
Ok(mlua::Value::Table(table))
|
||||
} else {
|
||||
Ok(mlua::Value::Nil)
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: mesh attributes
|
||||
}
|
||||
);
|
||||
|
||||
lua_wrap_handle!(lyra_resource::Image,
|
||||
field_getters={
|
||||
(width, {
|
||||
data.width().into_lua(lua)
|
||||
}),
|
||||
(height, {
|
||||
data.height().into_lua(lua)
|
||||
})
|
||||
}
|
||||
);
|
||||
|
||||
lua_wrap_handle!(Texture,
|
||||
field_getters={
|
||||
(image, wrapper=LuaImageHandle),
|
||||
(sampler, {
|
||||
data.sampler.clone()
|
||||
.map(|s| LuaTextureSampler(s))
|
||||
.into_lua(lua)
|
||||
})
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
lua_wrap_handle!(Material,
|
||||
field_getters={
|
||||
shader_uuid,
|
||||
name,
|
||||
double_sided,
|
||||
(base_color, wrapper=crate::lua::wrappers::LuaVec4),
|
||||
metallic,
|
||||
roughness,
|
||||
(base_color_texture, {
|
||||
data.base_color_texture.clone()
|
||||
.map(|v| LuaTextureHandle(v.clone()))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(metallic_roughness_texture, {
|
||||
data.metallic_roughness_texture.clone()
|
||||
.map(|v| LuaTextureHandle(v.clone()))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(pbr_glossiness, {
|
||||
data.pbr_glossiness.clone()
|
||||
.map(|v| LuaPbrGlossiness(v.clone()))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
alpha_cutoff,
|
||||
(alpha_mode, {
|
||||
match data.alpha_mode {
|
||||
lyra_resource::gltf::AlphaMode::Opaque => "opaque",
|
||||
lyra_resource::gltf::AlphaMode::Mask => "mask",
|
||||
lyra_resource::gltf::AlphaMode::Blend => "blend",
|
||||
}.into_lua(lua)
|
||||
}),
|
||||
(specular, {
|
||||
data.specular.clone()
|
||||
.map(|v| LuaSpecular(v.clone()))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
lua_wrap_handle!(Gltf, {
|
||||
methods.add_method("scenes", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
for (i, scene) in data.scenes.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_set(i, LuaSceneHandle(scene.clone()))?;
|
||||
}
|
||||
|
||||
Ok(mlua::Value::Table(table))
|
||||
} else {
|
||||
Ok(mlua::Value::Nil)
|
||||
}
|
||||
});
|
||||
methods.add_method("materials", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
for (i, mat) in data.materials.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_set(i, LuaMaterialHandle(mat.clone()))?;
|
||||
}
|
||||
|
||||
Ok(mlua::Value::Table(table))
|
||||
} else {
|
||||
Ok(mlua::Value::Nil)
|
||||
}
|
||||
});
|
||||
methods.add_method("meshes", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
for (i, mesh) in data.meshes.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_set(i, LuaMeshHandle(mesh.clone()))?;
|
||||
}
|
||||
|
||||
Ok(mlua::Value::Table(table))
|
||||
} else {
|
||||
Ok(mlua::Value::Nil)
|
||||
}
|
||||
});
|
||||
});
|
|
@ -1,21 +1,37 @@
|
|||
use std::{any::TypeId, ops::Deref};
|
||||
//use mlua::{AnyUserData, IntoLua, FromLua, Lua, Value};
|
||||
use lyra_resource::{gltf::{Gltf, Material, Mesh}, Texture, ResHandle, UntypedResHandle};
|
||||
use lyra_game::scene::SceneGraph;
|
||||
use lyra_reflect::{Reflect, TypeData};
|
||||
use lyra_scripting_derive::{lua_wrap_handle, wrap_lua_struct};
|
||||
use lyra_resource::{ResHandle, ResourceData, UntypedResHandle};
|
||||
use lyra_reflect::FromType;
|
||||
use crate::lua::LuaWrapper;
|
||||
|
||||
use crate::{lua::{Error, LuaWrapper, FN_NAME_INTERNAL_AS_COMPONENT, FN_NAME_INTERNAL_REFLECT, FN_NAME_INTERNAL_REFLECT_TYPE}, lyra_engine, ScriptBorrow};
|
||||
mod gltf;
|
||||
pub use gltf::*;
|
||||
|
||||
use crate as lyra_scripting;
|
||||
pub trait LuaHandleWrapper: LuaWrapper + mlua::UserData + Send + 'static {
|
||||
type ResourceType: lyra_resource::ResourceData;
|
||||
|
||||
use mlua::IntoLua;
|
||||
fn from_handle(handle: ResHandle<Self::ResourceType>) -> Self;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LuaResHandleToComponent {
|
||||
/// Create the userdata component that
|
||||
pub fn_to_lua: fn(&mlua::Lua, UntypedResHandle) -> Option<mlua::AnyUserData>,
|
||||
}
|
||||
|
||||
impl<T> FromType<T> for LuaResHandleToComponent
|
||||
where
|
||||
T: LuaHandleWrapper,
|
||||
T::ResourceType: ResourceData,
|
||||
{
|
||||
fn from_type() -> Self {
|
||||
Self {
|
||||
fn_to_lua: |lua: &mlua::Lua, handle: UntypedResHandle| {
|
||||
handle.as_typed::<T::ResourceType>()
|
||||
.map(|res| lua.create_userdata(T::from_handle(res)).unwrap())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaResHandleToComponent {
|
||||
pub fn new(f: fn(&mlua::Lua, UntypedResHandle) -> Option<mlua::AnyUserData>) -> Self {
|
||||
Self {
|
||||
|
@ -23,220 +39,3 @@ impl LuaResHandleToComponent {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeData for LuaResHandleToComponent {
|
||||
fn as_any(&self) -> &dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn boxed_clone(&self) -> Box<dyn TypeData> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LuaResHandle(pub UntypedResHandle);
|
||||
|
||||
impl Deref for LuaResHandle {
|
||||
type Target = UntypedResHandle;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UntypedResHandle> for LuaResHandle {
|
||||
fn from(value: UntypedResHandle) -> Self {
|
||||
LuaResHandle(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::UserData for LuaResHandle {
|
||||
fn add_fields<F: mlua::UserDataFields<Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("path", |_, this| Ok(this.path()));
|
||||
fields.add_field_method_get("version", |_, this| Ok(this.version()));
|
||||
fields.add_field_method_get("uuid", |_, this| Ok(this.uuid().to_string()));
|
||||
fields.add_field_method_get("state", |_, this| {
|
||||
let name = if this.is_loaded() {
|
||||
"ready"
|
||||
} else if this.get_error().is_some() {
|
||||
"error"
|
||||
} else { "loading" };
|
||||
|
||||
Ok(name)
|
||||
});
|
||||
}
|
||||
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("is_watched", |_, this, ()| {
|
||||
Ok(this.is_watched())
|
||||
});
|
||||
|
||||
methods.add_method("is_loaded", |_, this, ()| {
|
||||
Ok(this.is_loaded())
|
||||
});
|
||||
|
||||
methods.add_method("wait_until_loaded", |_, this, ()| {
|
||||
this.wait_recurse_dependencies_load();
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
methods.add_method(FN_NAME_INTERNAL_AS_COMPONENT, |lua, this, ()| {
|
||||
let handle = &this.0;
|
||||
|
||||
if let Some(handle) = handle.as_typed::<SceneGraph>() {
|
||||
LuaSceneHandle(handle).into_lua(lua)
|
||||
} else if let Some(handle) = handle.as_typed::<Gltf>() {
|
||||
LuaGltfHandle(handle).into_lua(lua)
|
||||
} else {
|
||||
Ok(mlua::Value::Nil)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::FromLua for LuaResHandle {
|
||||
fn from_lua(val: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let tyname = val.type_name();
|
||||
let ud = val.as_userdata()
|
||||
.ok_or(mlua::Error::external(Error::type_mismatch("Handle", &tyname)))?;
|
||||
let handle = ud.borrow::<LuaResHandle>()?;
|
||||
|
||||
Ok(handle.clone())
|
||||
}
|
||||
}
|
||||
|
||||
wrap_lua_struct!(lyra_resource::gltf::PbrGlossiness, skip(lua_reflect)); // doesn't need internal lua reflection methods
|
||||
wrap_lua_struct!(lyra_resource::gltf::Specular, skip(lua_reflect)); // doesn't need internal lua reflection methods
|
||||
|
||||
lua_wrap_handle!(SceneGraph, name=Scene, {});
|
||||
|
||||
lua_wrap_handle!(Mesh,
|
||||
field_getters={
|
||||
(material, {
|
||||
data.material.clone()
|
||||
.map(|v| LuaMaterialHandle(v.clone()))
|
||||
.into_lua(lua)
|
||||
})
|
||||
},
|
||||
{
|
||||
methods.add_method("indices", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
match &data.indices {
|
||||
Some(lyra_resource::gltf::MeshIndices::U16(v)) => {
|
||||
for (i, ind) in v.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_set(i, *ind)?;
|
||||
}
|
||||
},
|
||||
Some(lyra_resource::gltf::MeshIndices::U32(v)) => {
|
||||
for (i, ind) in v.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_set(i, *ind)?;
|
||||
}
|
||||
},
|
||||
None => {},
|
||||
}
|
||||
|
||||
Ok(mlua::Value::Table(table))
|
||||
} else {
|
||||
Ok(mlua::Value::Nil)
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: attributes
|
||||
}
|
||||
);
|
||||
lua_wrap_handle!(Texture, {});
|
||||
|
||||
lua_wrap_handle!(Material,
|
||||
field_getters={
|
||||
shader_uuid,
|
||||
name,
|
||||
double_sided,
|
||||
(base_color, wrapper=crate::lua::wrappers::LuaVec4),
|
||||
metallic,
|
||||
roughness,
|
||||
(base_color_texture, {
|
||||
data.base_color_texture.clone()
|
||||
.map(|v| LuaTextureHandle(v.clone()))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(metallic_roughness_texture, {
|
||||
data.metallic_roughness_texture.clone()
|
||||
.map(|v| LuaTextureHandle(v.clone()))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
(pbr_glossiness, {
|
||||
data.pbr_glossiness.clone()
|
||||
.map(|v| LuaPbrGlossiness(v.clone()))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
alpha_cutoff,
|
||||
(alpha_mode, {
|
||||
match data.alpha_mode {
|
||||
// TODO: Lua enums
|
||||
lyra_resource::gltf::AlphaMode::Opaque => "opaque",
|
||||
lyra_resource::gltf::AlphaMode::Mask => "mask",
|
||||
lyra_resource::gltf::AlphaMode::Blend => "blend",
|
||||
}.into_lua(lua)
|
||||
}),
|
||||
(specular, {
|
||||
data.specular.clone()
|
||||
.map(|v| LuaSpecular(v.clone()))
|
||||
.into_lua(lua)
|
||||
}),
|
||||
}
|
||||
);
|
||||
|
||||
lua_wrap_handle!(Gltf, {
|
||||
methods.add_method("scenes", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
for (i, scene) in data.scenes.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_set(i, LuaSceneHandle(scene.clone()))?;
|
||||
}
|
||||
|
||||
Ok(mlua::Value::Table(table))
|
||||
} else {
|
||||
Ok(mlua::Value::Nil)
|
||||
}
|
||||
});
|
||||
methods.add_method("materials", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
for (i, mat) in data.materials.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_set(i, LuaMaterialHandle(mat.clone()))?;
|
||||
}
|
||||
|
||||
Ok(mlua::Value::Table(table))
|
||||
} else {
|
||||
Ok(mlua::Value::Nil)
|
||||
}
|
||||
});
|
||||
methods.add_method("meshes", |lua, this, ()| {
|
||||
if let Some(data) = this.0.data_ref() {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
for (i, mesh) in data.meshes.iter().enumerate() {
|
||||
let i = i as i64 + 1; // lua indexes start at 1
|
||||
table.raw_set(i, LuaMeshHandle(mesh.clone()))?;
|
||||
}
|
||||
|
||||
Ok(mlua::Value::Table(table))
|
||||
} else {
|
||||
Ok(mlua::Value::Nil)
|
||||
}
|
||||
});
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
use crate::{
|
||||
lua::{
|
||||
wrappers::{LuaAngle, LuaTransform},
|
||||
LuaWrapper
|
||||
},
|
||||
ScriptBorrow,
|
||||
};
|
||||
use lyra_game::{render::camera::CameraProjectionMode, scene::CameraComponent};
|
||||
use lyra_reflect::Enum;
|
||||
use lyra_scripting_derive::to_lua_convert;
|
||||
|
||||
fn projection_mode_from_str(s: &str) -> Option<CameraProjectionMode> {
|
||||
match s {
|
||||
"perspective" => Some(CameraProjectionMode::Perspective),
|
||||
"orthographic" => Some(CameraProjectionMode::Orthographic),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
to_lua_convert!(
|
||||
// Struct that is being wrapped
|
||||
CameraComponent,
|
||||
// Name of wrapping struct
|
||||
name=LuaCamera,
|
||||
// The name of the type in Lua
|
||||
lua_name="Camera",
|
||||
// Reflection type, can be 'component' or 'resource'
|
||||
reflect=component,
|
||||
fields={
|
||||
transform: wrap(LuaTransform),
|
||||
fov: wrap(LuaAngle),
|
||||
(
|
||||
mode,
|
||||
// Get the table from the value, result must be `CameraProjectionMode`
|
||||
get={
|
||||
let mode: String = table.get("mode")?;
|
||||
projection_mode_from_str(&mode).unwrap()
|
||||
},
|
||||
// Get the value from self, result must be the type in Lua, here its `String`.
|
||||
set={
|
||||
self.mode.variant_name().to_lowercase()
|
||||
}
|
||||
),
|
||||
debug: bool
|
||||
}
|
||||
);
|
|
@ -34,7 +34,15 @@ impl mlua::IntoLua for LuaDeltaTime {
|
|||
}
|
||||
|
||||
impl LuaWrapper for LuaDeltaTime {
|
||||
type Wrap = DeltaTime;
|
||||
|
||||
#[inline(always)]
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
TypeId::of::<DeltaTime>()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,341 @@
|
|||
use crate::lua::LuaWrapper;
|
||||
use lyra_game::{
|
||||
input::{KeyCode, NativeKeyCode},
|
||||
math::Vec2,
|
||||
winit::{
|
||||
dpi::PhysicalPosition, DeviceEvent, DeviceEventPair, DeviceId, MouseScrollDelta,
|
||||
PhysicalKey, RawKeyEvent,
|
||||
},
|
||||
};
|
||||
use lyra_reflect::Reflect;
|
||||
use lyra_scripting_derive::wrap_lua_struct;
|
||||
|
||||
use crate as lyra_scripting;
|
||||
use crate::lyra_engine;
|
||||
|
||||
use super::LuaVec2;
|
||||
|
||||
wrap_lua_struct!(DeviceId, skip(lua_reflect));
|
||||
|
||||
/// Wraps [`DeviceEvent`] and implements [`IntoLua`]
|
||||
#[derive(Clone)]
|
||||
pub struct LuaDeviceEventRaw(pub(crate) DeviceEvent);
|
||||
|
||||
impl std::ops::Deref for LuaDeviceEventRaw {
|
||||
type Target = DeviceEvent;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for LuaDeviceEventRaw {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::FromLua for LuaDeviceEventRaw {
|
||||
fn from_lua(v: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let ty = v.type_name();
|
||||
let table = v.as_table().ok_or(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some("expected Lua Table".into()),
|
||||
})?;
|
||||
|
||||
let type_str: String = table.get("kind")?;
|
||||
let type_str = type_str.as_str();
|
||||
|
||||
let ret = match type_str {
|
||||
"added" => Self(DeviceEvent::Added),
|
||||
"removed" => Self(DeviceEvent::Removed),
|
||||
"mouse_motion" => {
|
||||
let delta: LuaVec2 = table.get("delta")?;
|
||||
Self(DeviceEvent::MouseMotion {
|
||||
delta: (delta.x as _, delta.y as _),
|
||||
})
|
||||
}
|
||||
"mouse_wheel" => {
|
||||
let delta = if table.contains_key("line_delta")? {
|
||||
let ld: LuaVec2 = table.get("line_delta")?;
|
||||
MouseScrollDelta::LineDelta(ld.x, ld.y)
|
||||
} else if table.contains_key("pixel_delta")? {
|
||||
let pd: LuaVec2 = table.get("pixel_delta")?;
|
||||
let pos = PhysicalPosition::new(pd.x as f64, pd.y as f64);
|
||||
MouseScrollDelta::PixelDelta(pos)
|
||||
} else {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(
|
||||
"could not find line_delta or pixel_delta in 'mouse_wheel' \
|
||||
device event"
|
||||
.into(),
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
Self(DeviceEvent::MouseWheel { delta })
|
||||
}
|
||||
"motion" => {
|
||||
let axis: u32 = table.get("axis")?;
|
||||
let value: f64 = table.get("value")?;
|
||||
Self(DeviceEvent::Motion { axis, value })
|
||||
}
|
||||
"button" => {
|
||||
let button: u32 = table.get("button")?;
|
||||
|
||||
let state_str: String = table.get("state")?;
|
||||
let state_str = state_str.as_str();
|
||||
let state = match state_str {
|
||||
"pressed" => lyra_game::winit::ElementState::Pressed,
|
||||
"released" => lyra_game::winit::ElementState::Released,
|
||||
_ => {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(format!("unknown button state: '{}'", state_str)),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Self(DeviceEvent::Button { button, state })
|
||||
}
|
||||
"key" => {
|
||||
let state_str: String = table.get("state")?;
|
||||
let state_str = state_str.as_str();
|
||||
let state = match state_str {
|
||||
"pressed" => lyra_game::winit::ElementState::Pressed,
|
||||
"released" => lyra_game::winit::ElementState::Released,
|
||||
_ => {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(format!("unknown key state: '{}'", state_str)),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if table.contains_key("code")? {
|
||||
let code_str: String = table.get("code")?;
|
||||
let code = KeyCode::from_str(&code_str);
|
||||
if code.is_none() {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(format!("unknown key code: '{}'", code_str)),
|
||||
});
|
||||
}
|
||||
let code = code.unwrap();
|
||||
|
||||
let phy = PhysicalKey::Code(code.as_winit_keycode().unwrap());
|
||||
Self(DeviceEvent::Key(RawKeyEvent {
|
||||
physical_key: phy,
|
||||
state,
|
||||
}))
|
||||
} else if table.contains_key("native")? {
|
||||
let native_str: String = table.get("native")?;
|
||||
let native_str = native_str.as_str();
|
||||
let native_code: u32 = table.get("native_code")?;
|
||||
|
||||
let native_code = match native_str {
|
||||
"unknown" => NativeKeyCode::Unidentified,
|
||||
"android" => NativeKeyCode::Android(native_code),
|
||||
"macos" => NativeKeyCode::MacOS(native_code as u16),
|
||||
"windows" => NativeKeyCode::Windows(native_code as u16),
|
||||
"xkb" => NativeKeyCode::Xkb(native_code),
|
||||
_ => {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(format!("unknown native code type: {}", native_str)),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Self(DeviceEvent::Key(RawKeyEvent {
|
||||
physical_key: PhysicalKey::Unidentified(native_code.into()),
|
||||
state,
|
||||
}))
|
||||
} else {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(
|
||||
"malformed Table for LuaDeviceEventRaw, missing key \
|
||||
code"
|
||||
.into(),
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEventRaw".into(),
|
||||
message: Some(format!("unknown device event type '{}'", type_str)),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::IntoLua for LuaDeviceEventRaw {
|
||||
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
|
||||
let table = lua.create_table()?;
|
||||
|
||||
match self.0 {
|
||||
DeviceEvent::Added => {
|
||||
table.set("kind", "added")?;
|
||||
}
|
||||
DeviceEvent::Removed => {
|
||||
table.set("kind", "removed")?;
|
||||
}
|
||||
DeviceEvent::MouseMotion { delta } => {
|
||||
table.set("kind", "mouse_motion")?;
|
||||
table.set("delta", LuaVec2(Vec2::new(delta.0 as _, delta.1 as _)))?;
|
||||
}
|
||||
DeviceEvent::MouseWheel { delta } => {
|
||||
table.set("kind", "mouse_wheel")?;
|
||||
|
||||
match delta {
|
||||
MouseScrollDelta::LineDelta(x, y) => {
|
||||
let d = LuaVec2(Vec2::new(x, y));
|
||||
table.set("line_delta", d)?;
|
||||
}
|
||||
MouseScrollDelta::PixelDelta(delta) => {
|
||||
let d = delta.cast::<f32>();
|
||||
let d = LuaVec2(Vec2::new(d.x, d.y));
|
||||
table.set("pixel_delta", d)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
DeviceEvent::Motion { axis, value } => {
|
||||
table.set("kind", "motion")?;
|
||||
table.set("axis", axis)?;
|
||||
table.set("value", value)?;
|
||||
}
|
||||
DeviceEvent::Button { button, state } => {
|
||||
table.set("kind", "button")?;
|
||||
table.set("button", button)?;
|
||||
|
||||
let state = match state {
|
||||
lyra_game::winit::ElementState::Pressed => "pressed",
|
||||
lyra_game::winit::ElementState::Released => "released",
|
||||
};
|
||||
table.set("state", state)?;
|
||||
}
|
||||
DeviceEvent::Key(raw_key_event) => {
|
||||
table.set("kind", "key")?;
|
||||
|
||||
let state = match raw_key_event.state {
|
||||
lyra_game::winit::ElementState::Pressed => "pressed",
|
||||
lyra_game::winit::ElementState::Released => "released",
|
||||
};
|
||||
table.set("state", state)?;
|
||||
|
||||
let e = KeyCode::from_raw_event(&raw_key_event);
|
||||
let s = e.as_str();
|
||||
|
||||
if let Some(s) = s {
|
||||
table.set("code", s)?;
|
||||
} else if let Some(un) = e.get_unknown() {
|
||||
match un {
|
||||
lyra_game::input::NativeKeyCode::Unidentified => {
|
||||
table.set("native", "unknown")?;
|
||||
}
|
||||
lyra_game::input::NativeKeyCode::Android(v) => {
|
||||
table.set("native", "android")?;
|
||||
table.set("native_code", *v)?;
|
||||
}
|
||||
lyra_game::input::NativeKeyCode::MacOS(v) => {
|
||||
table.set("native", "macos")?;
|
||||
table.set("native_code", *v)?;
|
||||
}
|
||||
lyra_game::input::NativeKeyCode::Windows(v) => {
|
||||
table.set("native", "windows")?;
|
||||
table.set("native_code", *v)?;
|
||||
}
|
||||
lyra_game::input::NativeKeyCode::Xkb(v) => {
|
||||
table.set("native", "xkb")?;
|
||||
table.set("native_code", *v)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaWrapper for LuaDeviceEventRaw {
|
||||
type Wrap = DeviceEvent;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Reflect)]
|
||||
pub struct LuaDeviceEvent(pub(crate) DeviceEventPair);
|
||||
|
||||
impl std::ops::Deref for LuaDeviceEvent {
|
||||
type Target = DeviceEventPair;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for LuaDeviceEvent {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::FromLua for LuaDeviceEvent {
|
||||
fn from_lua(v: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let ty = v.type_name();
|
||||
let table = v.as_table().ok_or(mlua::Error::FromLuaConversionError {
|
||||
from: ty,
|
||||
to: "LuaDeviceEvent".into(),
|
||||
message: Some("expected Lua Table".into()),
|
||||
})?;
|
||||
|
||||
let id: LuaDeviceId = table.get("device")?;
|
||||
let ev: LuaDeviceEventRaw = table.get("event")?;
|
||||
|
||||
Ok(Self(DeviceEventPair {
|
||||
device_src: id.0,
|
||||
event: ev.0,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::IntoLua for LuaDeviceEvent {
|
||||
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
|
||||
let table = lua.create_table()?;
|
||||
table.set("device", LuaDeviceId(self.device_src))?;
|
||||
table.set("event", LuaDeviceEventRaw(self.event.clone()))?;
|
||||
table.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaWrapper for LuaDeviceEvent {
|
||||
type Wrap = DeviceEventPair;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_wrapped(wrap: Self::Wrap) -> Option<Self> {
|
||||
Some(Self(wrap))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
use std::{marker::PhantomData, sync::Arc};
|
||||
|
||||
use crate::lua::LuaWrapper;
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use lyra_ecs::World;
|
||||
use lyra_game::{
|
||||
Event, EventReader, EventWriter, Events,
|
||||
};
|
||||
use lyra_reflect::{FromType, Reflect};
|
||||
use mlua::{FromLua, IntoLua};
|
||||
|
||||
use super::LuaVec2;
|
||||
|
||||
mod device_event;
|
||||
pub use device_event::*;
|
||||
|
||||
/// Helper trait to type erase EventReader<T>
|
||||
trait UntypedEventReader: Send + Sync {
|
||||
fn next(&self, lua: &mlua::Lua) -> mlua::Result<mlua::Value>;
|
||||
}
|
||||
|
||||
/// Helper trait to type erase EventWriter<T>
|
||||
trait UntypedEventWriter: Send + Sync {
|
||||
fn push(&self, lua: &mlua::Lua, event: mlua::Value) -> mlua::Result<()>;
|
||||
}
|
||||
|
||||
/// Event reader exposed to Lua
|
||||
#[derive(Clone)]
|
||||
pub struct LuaEventReader(Arc<AtomicRefCell<dyn UntypedEventReader>>);
|
||||
|
||||
impl mlua::FromLua for LuaEventReader {
|
||||
fn from_lua(value: mlua::Value, _: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let ty = value.type_name();
|
||||
let ud = value.as_userdata()
|
||||
.ok_or(mlua::Error::FromLuaConversionError { from: ty, to: "EventReader".into(), message: None })?;
|
||||
let reader = ud.borrow::<Self>()?;
|
||||
Ok(reader.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::UserData for LuaEventReader {
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("read", |lua, this, ()| {
|
||||
let reg = lua.create_registry_value(this.clone())?;
|
||||
|
||||
lua.create_function(move |lua: &mlua::Lua, ()| {
|
||||
let reader = lua.registry_value::<Self>(®)?;
|
||||
let reader = reader.0.borrow();
|
||||
reader.next(lua)
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Event writer exposed to Lua
|
||||
#[derive(Clone)]
|
||||
pub struct LuaEventWriter(Arc<AtomicRefCell<dyn UntypedEventWriter>>);
|
||||
|
||||
impl mlua::UserData for LuaEventWriter {
|
||||
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
|
||||
methods.add_method("write", |lua, this, event: mlua::Value| {
|
||||
let writer = this.0.borrow();
|
||||
writer.push(lua, event)?;
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
struct TypedReader<T>
|
||||
where
|
||||
T: Reflect + LuaWrapper + IntoLua + Send + Sync,
|
||||
T::Wrap: Clone + Event,
|
||||
{
|
||||
reader: EventReader<T::Wrap>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> UntypedEventReader for TypedReader<T>
|
||||
where
|
||||
T: Reflect + LuaWrapper + IntoLua + Send + Sync,
|
||||
T::Wrap: Clone + Event,
|
||||
{
|
||||
fn next(&self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
|
||||
self.reader
|
||||
.read()
|
||||
.map(|e| T::from_wrapped(e.clone()).into_lua(lua))
|
||||
.unwrap_or(Ok(mlua::Value::Nil))
|
||||
}
|
||||
}
|
||||
|
||||
struct TypedWriter<T>
|
||||
where
|
||||
T: Reflect + LuaWrapper + FromLua,
|
||||
T::Wrap: Clone + Event,
|
||||
{
|
||||
writer: EventWriter<T::Wrap>,
|
||||
_marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> UntypedEventWriter for TypedWriter<T>
|
||||
where
|
||||
T: Reflect + LuaWrapper + FromLua,
|
||||
T::Wrap: Clone + Event,
|
||||
{
|
||||
fn push(&self, lua: &mlua::Lua, event: mlua::Value) -> mlua::Result<()> {
|
||||
let ev = T::from_lua(event, lua)?.into_wrapped();
|
||||
self.writer.write(ev);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LuaWrappedEventProxy {
|
||||
fn_reader: for<'a> fn(world: &'a mut World) -> Arc<AtomicRefCell<dyn UntypedEventReader>>,
|
||||
fn_writer: for<'a> fn(world: &'a mut World) -> Arc<AtomicRefCell<dyn UntypedEventWriter>>,
|
||||
}
|
||||
|
||||
impl<T> FromType<T> for LuaWrappedEventProxy
|
||||
where
|
||||
T: Reflect + LuaWrapper + FromLua + IntoLua + Send + Sync,
|
||||
T::Wrap: Clone + Event,
|
||||
{
|
||||
fn from_type() -> Self {
|
||||
Self {
|
||||
fn_reader: |world| {
|
||||
let events = world.get_resource_or_default::<Events<T::Wrap>>();
|
||||
Arc::new(AtomicRefCell::new(TypedReader {
|
||||
reader: events.reader(),
|
||||
_marker: PhantomData::<T>,
|
||||
}))
|
||||
},
|
||||
fn_writer: |world| {
|
||||
let events = world.get_resource_or_default::<Events<T::Wrap>>();
|
||||
Arc::new(AtomicRefCell::new(TypedWriter {
|
||||
writer: events.writer(),
|
||||
_marker: PhantomData::<T>,
|
||||
}))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaWrappedEventProxy {
|
||||
pub fn reader(&self, world: &mut World) -> LuaEventReader {
|
||||
LuaEventReader((self.fn_reader)(world))
|
||||
}
|
||||
|
||||
pub fn writer(&self, world: &mut World) -> LuaEventWriter {
|
||||
LuaEventWriter((self.fn_writer)(world))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
use crate::{lua::LuaWrapper, ScriptBorrow};
|
||||
use lyra_game::scene::FreeFlyCamera;
|
||||
use lyra_scripting_derive::to_lua_convert;
|
||||
|
||||
to_lua_convert!(
|
||||
// Struct that is being wrapped
|
||||
FreeFlyCamera,
|
||||
// Reflection type, can be 'component' or 'resource'
|
||||
reflect=component,
|
||||
fields={
|
||||
speed: f32,
|
||||
look_speed: f32,
|
||||
mouse_sensitivity: f32,
|
||||
}
|
||||
);
|
|
@ -1,4 +1,4 @@
|
|||
use lyra_game::input::{keycode_from_str, Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, ActionState, LayoutId, MouseAxis, MouseInput};
|
||||
use lyra_game::input::{Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource, ActionState, KeyCode, LayoutId, MouseAxis, MouseInput};
|
||||
use mlua::IntoLua;
|
||||
use crate::{lua::Error, lyra_engine};
|
||||
|
||||
|
@ -127,13 +127,21 @@ impl mlua::UserData for LuaActionHandler {
|
|||
methods.add_method("get_action_state", |lua, this, action: String| {
|
||||
let state = this.handler.get_action_state(action);
|
||||
|
||||
// if the state wasn't found, return Nil.
|
||||
if state.is_none() {
|
||||
let mut v = mlua::MultiValue::new();
|
||||
v.push_back(mlua::Value::Nil);
|
||||
return Ok(v);
|
||||
}
|
||||
|
||||
let state = state.unwrap();
|
||||
let (name, val) = match state {
|
||||
ActionState::Idle => ("Idle", None),
|
||||
ActionState::Pressed(v) => ("Pressed", Some(v)),
|
||||
ActionState::JustPressed(v) => ("JustPressed", Some(v)),
|
||||
ActionState::JustReleased => ("JustReleased", None),
|
||||
ActionState::Axis(v) => ("Axis", Some(v)),
|
||||
ActionState::Other(v) => ("Other", Some(v)),
|
||||
ActionState::Idle => ("idle", None),
|
||||
ActionState::Pressed(v) => ("pressed", Some(v)),
|
||||
ActionState::JustPressed(v) => ("just_pressed", Some(v)),
|
||||
ActionState::JustReleased => ("just_released", None),
|
||||
ActionState::Axis(v) => ("axis", Some(v)),
|
||||
ActionState::Other(v) => ("other", Some(v)),
|
||||
};
|
||||
|
||||
let mut multi = Vec::new();
|
||||
|
@ -165,13 +173,19 @@ impl mlua::FromLua for LuaActionHandler {
|
|||
}
|
||||
|
||||
impl LuaWrapper for LuaActionHandler {
|
||||
type Wrap = ActionHandler;
|
||||
|
||||
fn wrapped_type_id() -> std::any::TypeId {
|
||||
std::any::TypeId::of::<ActionHandler>()
|
||||
}
|
||||
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.handler
|
||||
}
|
||||
}
|
||||
|
||||
fn process_keyboard_string(key_name: &str) -> Option<ActionSource> {
|
||||
let key = keycode_from_str(key_name)?;
|
||||
let key = KeyCode::from_str(key_name)?;
|
||||
|
||||
Some(ActionSource::Keyboard(key))
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use crate::lyra_engine;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{lua::Error, lyra_engine};
|
||||
use lyra_game::math;
|
||||
use lyra_scripting_derive::{lua_vec_wrap_extension, wrap_lua_struct};
|
||||
use mlua::FromLuaMulti;
|
||||
|
@ -9,7 +11,7 @@ wrap_lua_struct!(
|
|||
math::Vec2,
|
||||
derives(PartialEq, Copy),
|
||||
new,
|
||||
fields(x, y),
|
||||
fields={x: f32, y: f32},
|
||||
metamethods(
|
||||
Add(LuaVec2, f32),
|
||||
Sub(LuaVec2, f32),
|
||||
|
@ -29,7 +31,14 @@ wrap_lua_struct!(
|
|||
} else if let Ok(v) = Self::from_lua_multi(vals_clone, lua) {
|
||||
this.0 += v.0;
|
||||
} else {
|
||||
todo!("handle invalid argument error");
|
||||
return Err(mlua::Error::BadArgument {
|
||||
to: Some("Vec2:move_by".into()),
|
||||
pos: 2,
|
||||
name: None,
|
||||
cause: Arc::new(mlua::Error::runtime(
|
||||
"expected (number, number, number) or (Vec2), received neither"
|
||||
)),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -41,7 +50,7 @@ wrap_lua_struct!(
|
|||
math::Vec3,
|
||||
derives(PartialEq, Copy),
|
||||
new,
|
||||
fields(x, y, z),
|
||||
fields={x: f32, y: f32, z: f32},
|
||||
metamethods(
|
||||
Add(LuaVec3, f32),
|
||||
Sub(LuaVec3, f32),
|
||||
|
@ -62,7 +71,14 @@ wrap_lua_struct!(
|
|||
} else if let Ok(v) = Self::from_lua_multi(vals_clone, lua) {
|
||||
this.0 += v.0;
|
||||
} else {
|
||||
todo!("handle invalid argument error");
|
||||
return Err(mlua::Error::BadArgument {
|
||||
to: Some("Vec3:move_by".into()),
|
||||
pos: 2,
|
||||
name: None,
|
||||
cause: Arc::new(mlua::Error::runtime(
|
||||
"expected (number, number, number) or (Vec3), received neither"
|
||||
)),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -74,7 +90,7 @@ wrap_lua_struct!(
|
|||
math::Vec4,
|
||||
derives(PartialEq, Copy),
|
||||
new,
|
||||
fields(x, y, z, w),
|
||||
fields={x: f32, y: f32, z: f32, w: f32},
|
||||
metamethods(
|
||||
Add(LuaVec4, f32),
|
||||
Sub(LuaVec4, f32),
|
||||
|
@ -92,7 +108,7 @@ wrap_lua_struct!(
|
|||
math::Quat,
|
||||
derives(PartialEq, Copy),
|
||||
//new,
|
||||
fields(x, y, z, w),
|
||||
fields={x: f32, y: f32, z: f32, w: f32},
|
||||
metamethods(
|
||||
Eq,
|
||||
Add,
|
||||
|
@ -122,10 +138,27 @@ wrap_lua_struct!(
|
|||
Ok(Self(q))
|
||||
});
|
||||
|
||||
methods.add_function("from_vec4", |_, v: LuaVec4| {
|
||||
Ok(Self(math::Quat::from_vec4(*v)))
|
||||
});
|
||||
|
||||
methods.add_function("from_axis_angle", |_, (axis, angle): (LuaVec3, f32)| {
|
||||
let q = math::Quat::from_axis_angle(*axis, angle);
|
||||
Ok(Self(q))
|
||||
});
|
||||
|
||||
methods.add_method("dot", |_, this, (rhs,): (Self,)| {
|
||||
Ok(this.dot(rhs.0))
|
||||
});
|
||||
|
||||
methods.add_method("conjugate", |_, this, ()| {
|
||||
Ok(Self(this.conjugate()))
|
||||
});
|
||||
|
||||
methods.add_method("inverse", |_, this, ()| {
|
||||
Ok(Self(this.inverse()))
|
||||
});
|
||||
|
||||
methods.add_method("length", |_, this, ()| {
|
||||
Ok(this.length())
|
||||
});
|
||||
|
@ -134,20 +167,50 @@ wrap_lua_struct!(
|
|||
Ok(this.length_squared())
|
||||
});
|
||||
|
||||
methods.add_method_mut("normalize", |_, this, ()| {
|
||||
this.0 = this.normalize();
|
||||
Ok(())
|
||||
methods.add_method("length_recip", |_, this, ()| {
|
||||
Ok(this.length_recip())
|
||||
});
|
||||
|
||||
methods.add_method_mut("mult_quat", |_, this, (rhs,): (Self,)| {
|
||||
this.0 *= rhs.0;
|
||||
Ok(())
|
||||
methods.add_method("normalize", |_, this, ()| {
|
||||
Ok(Self(this.normalize()))
|
||||
});
|
||||
|
||||
methods.add_method("mult_quat", |_, this, (rhs,): (Self,)| {
|
||||
Ok(Self(this.0 * rhs.0))
|
||||
});
|
||||
|
||||
methods.add_method("mult_vec3", |_, this, (rhs,): (LuaVec3,)| {
|
||||
Ok(LuaVec3(this.0 * rhs.0))
|
||||
});
|
||||
|
||||
methods.add_method("is_finite", |_, this, ()| {
|
||||
Ok(this.is_finite())
|
||||
});
|
||||
|
||||
methods.add_method("is_nan", |_, this, ()| {
|
||||
Ok(this.is_nan())
|
||||
});
|
||||
|
||||
methods.add_method("is_normalized", |_, this, ()| {
|
||||
Ok(this.is_normalized())
|
||||
});
|
||||
|
||||
methods.add_method("is_near_identity", |_, this, ()| {
|
||||
Ok(this.is_near_identity())
|
||||
});
|
||||
|
||||
methods.add_method("angle_between", |_, this, rhs: LuaQuat| {
|
||||
Ok(this.angle_between(*rhs))
|
||||
});
|
||||
|
||||
methods.add_method("rotate_towards", |_, this, (rhs, max_angle): (LuaQuat, f32)| {
|
||||
Ok(Self(this.rotate_towards(*rhs, max_angle)))
|
||||
});
|
||||
|
||||
methods.add_method("abs_diff_eq", |_, this, (rhs, max_abs_diff): (LuaQuat, f32)| {
|
||||
Ok(this.abs_diff_eq(*rhs, max_abs_diff))
|
||||
});
|
||||
|
||||
// manually implemented here since multiplying may not return `Self`.
|
||||
methods.add_meta_method(mlua::MetaMethod::Mul, |lua, this, (val,): (mlua::Value,)| {
|
||||
use mlua::IntoLua;
|
||||
|
@ -169,7 +232,15 @@ wrap_lua_struct!(
|
|||
.into_lua(lua)
|
||||
},
|
||||
_ => {
|
||||
todo!()
|
||||
let t = val.type_name();
|
||||
Err(mlua::Error::BadArgument {
|
||||
to: Some("Quat:__mul".into()),
|
||||
pos: 2,
|
||||
name: None,
|
||||
cause: Arc::new(mlua::Error::external(
|
||||
Error::type_mismatch("Vec3, Quat, or Number", t)
|
||||
)),
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -177,6 +248,10 @@ wrap_lua_struct!(
|
|||
methods.add_method("lerp", |_, this, (rhs, alpha): (Self, f32)| {
|
||||
Ok(Self(this.lerp(*rhs, alpha)))
|
||||
});
|
||||
|
||||
methods.add_method("slerp", |_, this, (rhs, alpha): (Self, f32)| {
|
||||
Ok(Self(this.slerp(*rhs, alpha)))
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -193,12 +268,22 @@ wrap_lua_struct!(
|
|||
Ok(Self(math::Transform::new(*pos, *rot, *scale)))
|
||||
});
|
||||
|
||||
methods.add_function("from_translation", |_, (pos,): (LuaVec3,)| {
|
||||
Ok(Self(math::Transform::from_translation(*pos)))
|
||||
});
|
||||
|
||||
methods.add_function("from_xyz", |_, (x, y, z)| {
|
||||
Ok(Self(math::Transform::from_xyz(x, y, z)))
|
||||
methods.add_function("from_translation", |lua, vals: mlua::MultiValue| {
|
||||
let vals_clone = vals.clone();
|
||||
if let Ok((x, y, z)) = <(f32, f32, f32) as FromLuaMulti>::from_lua_multi(vals, lua) {
|
||||
Ok(Self(math::Transform::from_xyz(x, y, z)))
|
||||
} else if let Ok(v) = LuaVec3::from_lua_multi(vals_clone, lua) {
|
||||
Ok(Self(math::Transform::from_translation(*v)))
|
||||
} else {
|
||||
Err(mlua::Error::BadArgument {
|
||||
to: Some("Transform:from_translation".into()),
|
||||
pos: 2,
|
||||
name: None,
|
||||
cause: Arc::new(mlua::Error::runtime(
|
||||
"expected (number, number, number) or (Vec3), received neither"
|
||||
)),
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
methods.add_method("clone", |_, this, ()| {
|
||||
|
@ -275,4 +360,27 @@ wrap_lua_struct!(
|
|||
Ok(t)
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
wrap_lua_struct!(
|
||||
math::Angle,
|
||||
derives(Copy),
|
||||
skip(lua_reflect),
|
||||
extra_methods = {
|
||||
methods.add_function("new_degrees", |_, deg: f32| {
|
||||
Ok(Self(math::Angle::Degrees(deg)))
|
||||
});
|
||||
|
||||
methods.add_function("new_radians", |_, rad: f32| {
|
||||
Ok(Self(math::Angle::Radians(rad)))
|
||||
});
|
||||
|
||||
methods.add_method("radians", |_, this, ()| {
|
||||
Ok(this.to_radians())
|
||||
});
|
||||
|
||||
methods.add_method("degrees", |_, this, ()| {
|
||||
Ok(this.to_degrees())
|
||||
});
|
||||
},
|
||||
);
|
|
@ -11,4 +11,16 @@ mod delta_time;
|
|||
pub use delta_time::*;
|
||||
|
||||
mod window;
|
||||
pub use window::*;
|
||||
pub use window::*;
|
||||
|
||||
mod camera;
|
||||
pub use camera::*;
|
||||
|
||||
mod free_fly_camera;
|
||||
pub use free_fly_camera::*;
|
||||
|
||||
mod events;
|
||||
pub use events::*;
|
||||
|
||||
mod world_transform;
|
||||
pub use world_transform::*;
|
|
@ -1,7 +1,6 @@
|
|||
use lyra_scripting_derive::wrap_lua_struct;
|
||||
|
||||
use lyra_game::winit::{WindowOptions, FullscreenMode, Theme, CursorGrabMode, WindowLevel};
|
||||
use mlua::IntoLua;
|
||||
|
||||
use crate::lyra_engine;
|
||||
use crate as lyra_scripting;
|
||||
|
@ -15,7 +14,9 @@ wrap_lua_struct!(
|
|||
WindowOptions,
|
||||
//derives(Clone),
|
||||
name=LuaWindow,
|
||||
fields(focused),
|
||||
fields={
|
||||
(focused, skip_set)
|
||||
},
|
||||
extra_fields={
|
||||
fields.add_field_method_get("window_mode", |lua, this| {
|
||||
let s = match &this.fullscreen_mode {
|
||||
|
@ -135,10 +136,6 @@ wrap_lua_struct!(
|
|||
fields.add_field_method_get("scale_factor", |lua, this| {
|
||||
this.scale_factor.into_lua(lua)
|
||||
});
|
||||
fields.add_field_method_set("scale_factor", |_, this, val: f64| {
|
||||
this.scale_factor = val;
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fields.add_field_method_get("blur", |lua, this| {
|
||||
this.blur.into_lua(lua)
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
use crate::lua::{wrappers::LuaTransform, LuaWrapper};
|
||||
use crate::lyra_engine;
|
||||
use lyra_game::scene::WorldTransform;
|
||||
use lyra_reflect::Reflect;
|
||||
|
||||
#[derive(Clone, Default, Reflect)]
|
||||
pub struct LuaWorldTransform(pub(crate) WorldTransform);
|
||||
|
||||
impl std::ops::Deref for LuaWorldTransform {
|
||||
type Target = WorldTransform;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for LuaWorldTransform {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::FromLua for LuaWorldTransform {
|
||||
fn from_lua(v: mlua::Value, lua: &mlua::Lua) -> mlua::Result<Self> {
|
||||
let t = LuaTransform::from_lua(v, lua)?;
|
||||
Ok(Self(WorldTransform::from(*t)))
|
||||
}
|
||||
}
|
||||
|
||||
impl mlua::IntoLua for LuaWorldTransform {
|
||||
fn into_lua(self, lua: &mlua::Lua) -> mlua::Result<mlua::Value> {
|
||||
let t = LuaTransform(*self.0);
|
||||
t.into_lua(lua)
|
||||
}
|
||||
}
|
||||
|
||||
impl LuaWrapper for LuaWorldTransform {
|
||||
type Wrap = WorldTransform;
|
||||
|
||||
#[inline(always)]
|
||||
fn into_wrapped(self) -> Self::Wrap {
|
||||
self.0
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn from_wrapped(wrap: Self::Wrap) -> Option<Self> {
|
||||
Some(Self(wrap))
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
pub use lyra_game::*;
|
||||
|
||||
#[cfg(feature = "scripting")]
|
||||
pub use lyra_scripting::*;
|
||||
pub use lyra_scripting as script;
|
Loading…
Reference in New Issue