1use crate::math::Vec3;
2use gizmo_physics_core::{Collider, ColliderShape, Transform, components::GpuPhysicsLink};
3use gizmo_physics_rigid::components::RigidBody;
4#[cfg(feature = "render")]
5use crate::renderer::Renderer;
6
7#[cfg(feature = "render")]
8pub fn physics_debug_system(world: &crate::core::World) {
9 if let Some(mut gizmos) = world.get_resource_mut::<crate::renderer::Gizmos>() {
10 fn draw_collider(
11 trans: &Transform,
12 col: &Collider,
13 color: [f32; 4],
14 gizmos: &mut crate::renderer::Gizmos,
15 ) {
16 match &col.shape {
17 gizmo_physics_core::ColliderShape::Box(b) => {
18 let h = b.half_extents;
19 let r = trans.rotation;
20 let p = trans.position;
21 let p0 = p + r.mul_vec3(Vec3::new(-h.x, -h.y, -h.z));
22 let p1 = p + r.mul_vec3(Vec3::new(h.x, -h.y, -h.z));
23 let p2 = p + r.mul_vec3(Vec3::new(h.x, h.y, -h.z));
24 let p3 = p + r.mul_vec3(Vec3::new(-h.x, h.y, -h.z));
25 let p4 = p + r.mul_vec3(Vec3::new(-h.x, -h.y, h.z));
26 let p5 = p + r.mul_vec3(Vec3::new(h.x, -h.y, h.z));
27 let p6 = p + r.mul_vec3(Vec3::new(h.x, h.y, h.z));
28 let p7 = p + r.mul_vec3(Vec3::new(-h.x, h.y, h.z));
29
30 gizmos.draw_line(p0, p1, color);
31 gizmos.draw_line(p1, p2, color);
32 gizmos.draw_line(p2, p3, color);
33 gizmos.draw_line(p3, p0, color);
34 gizmos.draw_line(p4, p5, color);
35 gizmos.draw_line(p5, p6, color);
36 gizmos.draw_line(p6, p7, color);
37 gizmos.draw_line(p7, p4, color);
38 gizmos.draw_line(p0, p4, color);
39 gizmos.draw_line(p1, p5, color);
40 gizmos.draw_line(p2, p6, color);
41 gizmos.draw_line(p3, p7, color);
42 }
43 gizmo_physics_core::ColliderShape::Sphere(s) => {
44 let r = s.radius;
45 let min = trans.position - Vec3::new(r, r, r);
46 let max = trans.position + Vec3::new(r, r, r);
47 gizmos.draw_box(min, max, color);
48 }
49 gizmo_physics_core::ColliderShape::ConvexHull(ch) => {
50 let r = trans.rotation;
51 let p = trans.position;
52 for face in ch.faces.iter() {
53 let p0 = p + r.mul_vec3(ch.vertices[face[0] as usize]);
54 let p1 = p + r.mul_vec3(ch.vertices[face[1] as usize]);
55 let p2 = p + r.mul_vec3(ch.vertices[face[2] as usize]);
56 gizmos.draw_line(p0, p1, color);
57 gizmos.draw_line(p1, p2, color);
58 gizmos.draw_line(p2, p0, color);
59 }
60 }
61 gizmo_physics_core::ColliderShape::Compound(shapes) => {
62 for (local_t, sub_shape) in shapes {
63 let world_pos = trans.position + trans.rotation.mul_vec3(local_t.position);
64 let world_rot = trans.rotation * local_t.rotation;
65 let sub_trans = gizmo_physics_core::Transform {
66 position: world_pos,
67 rotation: world_rot,
68 scale: trans.scale,
69 ..*trans
70 };
71 let temp_col = gizmo_physics_core::Collider {
72 shape: *sub_shape.clone(),
73 ..Default::default()
74 };
75 draw_collider(&sub_trans, &temp_col, color, gizmos);
76 }
77 }
78 _ => {
79 let min = trans.position - Vec3::new(1.0, 1.0, 1.0);
80 let max = trans.position + Vec3::new(1.0, 1.0, 1.0);
81 gizmos.draw_box(min, max, color);
82 }
83 }
84 }
85
86 if let Some(q) = world.query::<(
87 &gizmo_physics_core::Transform,
88 &gizmo_physics_core::Collider,
89 &gizmo_physics_rigid::components::RigidBody,
90 )>() {
91 for (_, (trans, col, rb)) in q.iter() {
92 let color = if rb.is_static() {
93 [0.5, 0.5, 0.5, 1.0]
94 } else if rb.is_sleeping {
95 [0.9, 0.1, 0.1, 1.0]
96 } else {
97 [0.1, 0.9, 0.1, 1.0]
98 };
99 draw_collider(trans, col, color, &mut gizmos);
100 }
101 }
102
103 if let Some(q) = world.query::<(
104 &gizmo_physics_core::Transform,
105 &gizmo_physics_core::Collider,
106 crate::core::query::Without<gizmo_physics_rigid::components::RigidBody>,
107 )>() {
108 for (_, (trans, col, _)) in q.iter() {
109 draw_collider(trans, col, [0.5, 0.5, 0.5, 1.0], &mut gizmos);
110 }
111 }
112
113 let draw_oriented_box = |trans: &gizmo_physics_core::Transform,
115 offset: gizmo_math::Vec3,
116 h: gizmo_math::Vec3,
117 color: [f32; 4],
118 gizmos: &mut crate::renderer::Gizmos| {
119 let p = trans.position + trans.rotation.mul_vec3(offset);
120 let r = trans.rotation;
121 let p0 = p + r.mul_vec3(Vec3::new(-h.x, -h.y, -h.z));
122 let p1 = p + r.mul_vec3(Vec3::new(h.x, -h.y, -h.z));
123 let p2 = p + r.mul_vec3(Vec3::new(h.x, h.y, -h.z));
124 let p3 = p + r.mul_vec3(Vec3::new(-h.x, h.y, -h.z));
125 let p4 = p + r.mul_vec3(Vec3::new(-h.x, -h.y, h.z));
126 let p5 = p + r.mul_vec3(Vec3::new(h.x, -h.y, h.z));
127 let p6 = p + r.mul_vec3(Vec3::new(h.x, h.y, h.z));
128 let p7 = p + r.mul_vec3(Vec3::new(-h.x, h.y, h.z));
129
130 gizmos.draw_line(p0, p1, color);
131 gizmos.draw_line(p1, p2, color);
132 gizmos.draw_line(p2, p3, color);
133 gizmos.draw_line(p3, p0, color);
134 gizmos.draw_line(p4, p5, color);
135 gizmos.draw_line(p5, p6, color);
136 gizmos.draw_line(p6, p7, color);
137 gizmos.draw_line(p7, p4, color);
138 gizmos.draw_line(p0, p4, color);
139 gizmos.draw_line(p1, p5, color);
140 gizmos.draw_line(p2, p6, color);
141 gizmos.draw_line(p3, p7, color);
142 };
143
144 if let Some(q) = world.query::<(&gizmo_physics_core::Transform, &gizmo_physics_core::components::Hitbox)>() {
145 for (_, (trans, hitbox)) in q.iter() {
146 if hitbox.active {
147 draw_oriented_box(trans, hitbox.offset, hitbox.half_extents, [1.0, 0.0, 0.0, 1.0], &mut gizmos); }
149 }
150 }
151
152 if let Some(q) = world.query::<(&gizmo_physics_core::Transform, &gizmo_physics_core::components::Hurtbox)>() {
153 for (_, (trans, hurtbox)) in q.iter() {
154 draw_oriented_box(trans, hurtbox.offset, hurtbox.half_extents, [0.0, 1.0, 0.0, 1.0], &mut gizmos); }
156 }
157
158 let soft_color = [1.0, 0.4, 0.8, 1.0]; #[cfg(feature = "physics-soft")]
160 if let Some(q) = world.query::<&gizmo_physics_soft::SoftBodyMesh>() {
161 for (_, sm) in q.iter() {
162 for elem in &sm.elements {
163 let p0 = sm.nodes[elem.node_indices[0] as usize].position;
164 let p1 = sm.nodes[elem.node_indices[1] as usize].position;
165 let p2 = sm.nodes[elem.node_indices[2] as usize].position;
166 let p3 = sm.nodes[elem.node_indices[3] as usize].position;
167
168 gizmos.draw_line(p0, p1, soft_color);
170 gizmos.draw_line(p0, p2, soft_color);
171 gizmos.draw_line(p0, p3, soft_color);
172 gizmos.draw_line(p1, p2, soft_color);
173 gizmos.draw_line(p1, p3, soft_color);
174 gizmos.draw_line(p2, p3, soft_color);
175 }
176 }
177 }
178
179 #[cfg(feature = "physics-dynamics")]
181 if let Some(q) = world.query::<(
182 &gizmo_physics_core::Transform,
183 &gizmo_physics_dynamics::VehicleController,
184 )>() {
185 for (_, (trans, vehicle)) in q.iter() {
186 for wheel in &vehicle.wheels {
187 let attach_world =
188 trans.position + trans.rotation.mul_vec3(wheel.attachment_local_pos);
189 let ray_dir = trans.rotation.mul_vec3(wheel.direction_local).normalize();
190 let ray_end = attach_world
191 + ray_dir
192 * (wheel.suspension_rest_length
193 + wheel.suspension_max_travel
194 + wheel.radius);
195
196 gizmos.draw_line(attach_world, ray_end, [1.0, 1.0, 0.0, 1.0]);
198
199 if wheel.is_grounded {
200 if let Some(hit) = &wheel.ground_hit {
201 let force_dir = -ray_dir;
203 let force_len = (wheel.suspension_force / 10000.0).clamp(0.1, 2.0);
204 let arrow_end = hit.point + force_dir * force_len;
205 gizmos.draw_line(hit.point, arrow_end, [0.0, 0.0, 1.0, 1.0]);
206
207 let wheel_center = attach_world + ray_dir * wheel.suspension_length;
209 gizmos.draw_line(wheel_center, hit.point, [1.0, 0.5, 0.0, 1.0]);
210 }
211 }
212 }
213 }
214 }
215
216 if let Some(phys_world) = world.get_resource::<gizmo_physics_rigid::world::PhysicsWorld>() {
218 for event in phys_world.collision_events() {
219 for contact in &event.contact_points {
220 let p1 = contact.point;
221 let p2 = contact.point + contact.normal * 0.5; gizmos.draw_line(p1, p2, [1.0, 0.0, 0.0, 1.0]); let p_pen = contact.point - contact.normal * contact.penetration;
225 gizmos.draw_line(p1, p_pen, [1.0, 0.0, 1.0, 1.0]); }
227 }
228 }
229 tracing::info!(
230 "physics_debug_system: gizmos lines count = {}",
231 gizmos.lines.len()
232 );
233 }
234}
235
236static NEXT_STATIC_COLLIDER_SLOT: std::sync::atomic::AtomicU32 =
240 std::sync::atomic::AtomicU32::new(3);
241
242#[cfg(feature = "render")]
243pub fn gpu_physics_submit_system(world: &mut crate::core::World, renderer: &Renderer) {
244 use gizmo_physics_rigid::components::Velocity;
245
246 if let Some(physics) = &renderer.gpu_physics {
247 let mut unlinked_entities = Vec::new();
248 if let Some(q) = world.query::<(&RigidBody, &Transform, &Collider)>() {
249 let links = world.borrow::<GpuPhysicsLink>();
250 let velocities = world.borrow::<Velocity>();
251 for (e, (rb, trans, col)) in q.iter() {
252 if links.get(e).is_none() {
253 let vel = velocities.get(e).copied().unwrap_or_default();
254 unlinked_entities.push((e, *rb, *trans, col.clone(), vel));
255 }
256 }
257 }
258
259 let mut next_dynamic_id = world
260 .query::<&GpuPhysicsLink>()
261 .map(|q| q.iter().count() as u32)
262 .unwrap_or(0);
263
264 for (e, rb, trans, col, vel) in unlinked_entities {
265 if matches!(col.shape, ColliderShape::Plane(_)) {
266 let slot =
268 NEXT_STATIC_COLLIDER_SLOT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
269 if slot >= 100 {
270 tracing::error!("[GpuPhysics] Statik collider slot limiti (100) aşıldı, collider atlanıyor.");
271 NEXT_STATIC_COLLIDER_SLOT.fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
272 continue;
273 }
274
275 let gpu_col = gizmo_renderer::gpu_physics::GpuCollider {
276 shape_type: match col.shape {
277 ColliderShape::Plane(_) => 1,
278 _ => 0, },
280 _pad1: [0; 3],
281 data1: match &col.shape {
282 ColliderShape::Plane(p) => [p.normal.x, p.normal.y, p.normal.z, 0.0],
283 ColliderShape::Box(b) => {
284 let min = trans.position - b.half_extents;
285 [min.x, min.y, min.z, 0.0]
286 }
287 _ => [0.0; 4],
288 },
289 data2: match &col.shape {
290 ColliderShape::Plane(p) => [p.distance, 0.0, 0.0, 0.0],
291 ColliderShape::Box(b) => {
292 let max = trans.position + b.half_extents;
293 [max.x, max.y, max.z, 0.0]
294 }
295 _ => [0.0; 4],
296 },
297 };
298 physics.update_collider(&renderer.queue, slot, &gpu_col);
299 } else {
300 let id = next_dynamic_id;
302 next_dynamic_id += 1;
303
304 let (extents, offset) = match &col.shape {
305 ColliderShape::Box(b) => ([b.half_extents.x, b.half_extents.y, b.half_extents.z], Vec3::ZERO),
306 ColliderShape::Compound(shapes) => {
307 if let Some((local_t, box_shape)) = shapes.first() {
308 if let ColliderShape::Box(b) = &**box_shape {
309 ([b.half_extents.x, b.half_extents.y, b.half_extents.z], local_t.position)
310 } else {
311 ([0.5, 0.5, 0.5], Vec3::ZERO)
312 }
313 } else {
314 ([0.5, 0.5, 0.5], Vec3::ZERO)
315 }
316 }
317 _ => ([0.5, 0.5, 0.5], Vec3::ZERO),
318 };
319
320 let world_pos = trans.position + trans.rotation.mul_vec3(offset);
321
322 let gpu_box = gizmo_renderer::gpu_physics::GpuBox {
323 position: [world_pos.x, world_pos.y, world_pos.z],
324 mass: rb.mass,
325 velocity: [vel.linear.x, vel.linear.y, vel.linear.z],
326 state: 0,
327 rotation: [
328 trans.rotation.x,
329 trans.rotation.y,
330 trans.rotation.z,
331 trans.rotation.w,
332 ],
333 angular_velocity: [vel.angular.x, vel.angular.y, vel.angular.z],
334 sleep_counter: if rb.is_sleeping { 60 } else { 0 },
335 color: [0.3, 0.8, 1.0, 1.0],
336 half_extents: extents,
337 _pad: 0,
338 };
339 physics.update_box(&renderer.queue, id, &gpu_box);
340
341 world.add_component(world.get_entity(e).unwrap(), GpuPhysicsLink { id });
342 }
343 }
344 }
345}
346
347pub fn gpu_physics_readback_system(world: &mut crate::core::World, renderer: &Renderer) {
350 if let Some(physics) = &renderer.gpu_physics {
351 if let Some(gpu_data) = physics.poll_readback_data(&renderer.device) {
352 if let Some(mut q) =
353 world.query::<(gizmo_core::prelude::Mut<Transform>, &GpuPhysicsLink, &gizmo_physics_core::Collider)>()
354 {
355 for (_, (mut trans, link, col)) in q.iter_mut() {
356 let idx = link.id as usize;
357 if idx < gpu_data.len() {
358 let box_data = &gpu_data[idx];
359
360 let offset = match &col.shape {
361 gizmo_physics_core::ColliderShape::Compound(shapes) => {
362 if let Some((local_t, _)) = shapes.first() {
363 local_t.position
364 } else {
365 gizmo_math::Vec3::ZERO
366 }
367 }
368 _ => gizmo_math::Vec3::ZERO,
369 };
370
371 let new_rot = gizmo_math::Quat::from_xyzw(
372 box_data.rotation[0],
373 box_data.rotation[1],
374 box_data.rotation[2],
375 box_data.rotation[3],
376 );
377
378 trans.position = gizmo_math::Vec3::new(
379 box_data.position[0],
380 box_data.position[1],
381 box_data.position[2],
382 ) - new_rot.mul_vec3(offset);
383
384 trans.rotation = new_rot;
385 trans.update_local_matrix();
386 }
387 }
388 }
389 }
390 }
391}
392
393pub fn cpu_physics_step_system(world: &crate::core::World, dt: f32) {
397 gizmo_physics_rigid::system::physics_step_system(world, dt);
398
399 #[cfg(feature = "physics-soft")]
400 {
401 let gravity = world.get_resource::<gizmo_physics_rigid::world::PhysicsWorld>()
403 .map(|w| w.integrator.gravity)
404 .unwrap_or(gizmo_math::Vec3::new(0.0, -9.81, 0.0));
405
406 gizmo_physics_soft::system::soft_body_step_system(world, dt, gravity);
407 gizmo_physics_soft::system::cloth_step_system(world, dt, gravity);
408 gizmo_physics_soft::system::rope_step_system(world, dt, gravity);
409 }
410}