1use crate::commands::{CommandQueue, ScriptCommand};
6use gizmo_math::Vec3;
7use mlua::prelude::*;
8use std::sync::Arc;
9
10pub fn register_physics_api(lua: &Lua, command_queue: Arc<CommandQueue>) -> Result<(), LuaError> {
12 let physics_table = lua.create_table()?;
13
14 {
16 let cq = command_queue.clone();
17 physics_table.set(
18 "apply_force",
19 lua.create_function(move |_, (id, fx, fy, fz): (u32, f32, f32, f32)| {
20 cq.push(ScriptCommand::ApplyForce(id, Vec3::new(fx, fy, fz)));
21 Ok(())
22 })?,
23 )?;
24 }
25
26 {
28 let cq = command_queue.clone();
29 physics_table.set(
30 "apply_impulse",
31 lua.create_function(move |_, (id, ix, iy, iz): (u32, f32, f32, f32)| {
32 cq.push(ScriptCommand::ApplyImpulse(id, Vec3::new(ix, iy, iz)));
33 Ok(())
34 })?,
35 )?;
36 }
37
38 {
40 let cq = command_queue.clone();
41 physics_table.set(
42 "add_rigidbody",
43 lua.create_function(
44 move |_,
45 (id, mass, restitution, friction, use_gravity): (
46 u32,
47 f32,
48 f32,
49 f32,
50 bool,
51 )| {
52 cq.push(ScriptCommand::AddRigidBody {
53 id,
54 mass,
55 restitution,
56 friction,
57 use_gravity,
58 });
59 Ok(())
60 },
61 )?,
62 )?;
63 }
64
65 {
67 let cq = command_queue.clone();
68 physics_table.set(
69 "add_box_collider",
70 lua.create_function(move |_, (id, hx, hy, hz): (u32, f32, f32, f32)| {
71 cq.push(ScriptCommand::AddBoxCollider { id, hx, hy, hz });
72 Ok(())
73 })?,
74 )?;
75 }
76
77 {
78 let cq = command_queue.clone();
79 physics_table.set(
80 "add_sphere_collider",
81 lua.create_function(move |_, (id, radius): (u32, f32)| {
82 cq.push(ScriptCommand::AddSphereCollider { id, radius });
83 Ok(())
84 })?,
85 )?;
86 }
87
88 lua.globals().set("physics", physics_table)?;
89
90 Ok(())
91}
92
93pub fn update_physics_api(
95 lua: &Lua,
96 world: &gizmo_core::World,
97) -> Result<(), LuaError> {
98 let physics_table: LuaTable = lua.globals().get("physics")?;
99
100 let triggers = lua.create_table()?;
101 let collisions = lua.create_table()?;
102
103 if let Ok(physics_world) = world.try_get_resource::<gizmo_physics_rigid::world::PhysicsWorld>() {
104 for (i, t_event) in physics_world.trigger_events().iter().enumerate() {
106 let ev = lua.create_table()?;
107 ev.set("trigger_id", t_event.trigger_entity.id())?;
108 ev.set("other_id", t_event.other_entity.id())?;
109 let status = match t_event.event_type {
110 gizmo_physics_core::collision::CollisionEventType::Started => "enter",
111 gizmo_physics_core::collision::CollisionEventType::Persisting => "stay",
112 gizmo_physics_core::collision::CollisionEventType::Ended => "exit",
113 };
114 ev.set("status", status)?;
115 triggers.set(i + 1, ev)?;
116 }
117
118 for (i, c_event) in physics_world.collision_events().iter().enumerate() {
120 let ev = lua.create_table()?;
121 ev.set("entity_a", c_event.entity_a.id())?;
122 ev.set("entity_b", c_event.entity_b.id())?;
123 let status = match c_event.event_type {
124 gizmo_physics_core::collision::CollisionEventType::Started => "enter",
125 gizmo_physics_core::collision::CollisionEventType::Persisting => "stay",
126 gizmo_physics_core::collision::CollisionEventType::Ended => "exit",
127 };
128 ev.set("status", status)?;
129 collisions.set(i + 1, ev)?;
130 }
131 }
132
133 physics_table.set("triggers", triggers)?;
135 physics_table.set("collisions", collisions)?;
136
137 Ok(())
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use gizmo_core::World;
144 use gizmo_physics_rigid::world::PhysicsWorld;
145 use gizmo_physics_core::collision::{TriggerEvent, CollisionEvent, CollisionEventType};
146 use mlua::Lua;
147
148 #[test]
149 fn test_update_physics_api() {
150 let lua = Lua::new();
151 let globals = lua.globals();
152
153 let physics_table = lua.create_table().unwrap();
155 globals.set("physics", physics_table).unwrap();
156
157 let mut world = World::new();
158 let ent1 = world.spawn();
159 let ent2 = world.spawn();
160
161 let mut physics_world = PhysicsWorld::new();
162 physics_world.trigger_events.push(TriggerEvent {
163 trigger_entity: ent1,
164 other_entity: ent2,
165 event_type: CollisionEventType::Started,
166 });
167
168 physics_world.collision_events.push(CollisionEvent {
169 entity_a: ent1,
170 entity_b: ent2,
171 event_type: CollisionEventType::Started,
172 contact_points: Default::default(),
173 });
174
175 world.insert_resource(physics_world);
176
177 update_physics_api(&lua, &world).unwrap();
178
179 let script = r#"
180 local triggers = physics.triggers
181 local collisions = physics.collisions
182 assert(#triggers == 1)
183 assert(triggers[1].status == "enter")
184 assert(#collisions == 1)
185 assert(collisions[1].status == "enter")
186 "#;
187
188 lua.load(script).exec().unwrap();
189 }
190}