Skip to main content

gizmo_engine/systems/
physics.rs

1use crate::math::Vec3;
2use crate::physics::{Collider, ColliderShape, GpuPhysicsLink, RigidBody, Transform};
3use crate::renderer::Renderer;
4
5pub fn physics_debug_system(world: &crate::core::World) {
6    if let Some(mut gizmos) = world.get_resource_mut::<crate::renderer::Gizmos>() {
7        let draw_collider = |trans: &Transform,
8                             col: &Collider,
9                             color: [f32; 4],
10                             gizmos: &mut crate::renderer::Gizmos| {
11            match &col.shape {
12                gizmo_physics::ColliderShape::Box(b) => {
13                    let h = b.half_extents;
14                    let r = trans.rotation;
15                    let p = trans.position;
16                    let p0 = p + r.mul_vec3(Vec3::new(-h.x, -h.y, -h.z));
17                    let p1 = p + r.mul_vec3(Vec3::new(h.x, -h.y, -h.z));
18                    let p2 = p + r.mul_vec3(Vec3::new(h.x, h.y, -h.z));
19                    let p3 = p + r.mul_vec3(Vec3::new(-h.x, h.y, -h.z));
20                    let p4 = p + r.mul_vec3(Vec3::new(-h.x, -h.y, h.z));
21                    let p5 = p + r.mul_vec3(Vec3::new(h.x, -h.y, h.z));
22                    let p6 = p + r.mul_vec3(Vec3::new(h.x, h.y, h.z));
23                    let p7 = p + r.mul_vec3(Vec3::new(-h.x, h.y, h.z));
24
25                    gizmos.draw_line(p0, p1, color);
26                    gizmos.draw_line(p1, p2, color);
27                    gizmos.draw_line(p2, p3, color);
28                    gizmos.draw_line(p3, p0, color);
29                    gizmos.draw_line(p4, p5, color);
30                    gizmos.draw_line(p5, p6, color);
31                    gizmos.draw_line(p6, p7, color);
32                    gizmos.draw_line(p7, p4, color);
33                    gizmos.draw_line(p0, p4, color);
34                    gizmos.draw_line(p1, p5, color);
35                    gizmos.draw_line(p2, p6, color);
36                    gizmos.draw_line(p3, p7, color);
37                }
38                gizmo_physics::ColliderShape::Sphere(s) => {
39                    let r = s.radius;
40                    let min = trans.position - Vec3::new(r, r, r);
41                    let max = trans.position + Vec3::new(r, r, r);
42                    gizmos.draw_box(min, max, color);
43                }
44                gizmo_physics::ColliderShape::ConvexHull(ch) => {
45                    let r = trans.rotation;
46                    let p = trans.position;
47                    for face in ch.faces.iter() {
48                        let p0 = p + r.mul_vec3(ch.vertices[face[0] as usize]);
49                        let p1 = p + r.mul_vec3(ch.vertices[face[1] as usize]);
50                        let p2 = p + r.mul_vec3(ch.vertices[face[2] as usize]);
51                        gizmos.draw_line(p0, p1, color);
52                        gizmos.draw_line(p1, p2, color);
53                        gizmos.draw_line(p2, p0, color);
54                    }
55                }
56                _ => {
57                    let min = trans.position - Vec3::new(1.0, 1.0, 1.0);
58                    let max = trans.position + Vec3::new(1.0, 1.0, 1.0);
59                    gizmos.draw_box(min, max, color);
60                }
61            }
62        };
63
64        if let Some(q) = world.query::<(
65            &crate::physics::Transform,
66            &gizmo_physics::Collider,
67            &gizmo_physics::RigidBody,
68        )>() {
69            for (_, (trans, col, rb)) in q.iter() {
70                let color = if rb.is_static() {
71                    [0.5, 0.5, 0.5, 1.0]
72                } else if rb.is_sleeping {
73                    [0.9, 0.1, 0.1, 1.0]
74                } else {
75                    [0.1, 0.9, 0.1, 1.0]
76                };
77                draw_collider(trans, col, color, &mut gizmos);
78            }
79        }
80
81        if let Some(q) = world.query::<(
82            &crate::physics::Transform,
83            &gizmo_physics::Collider,
84            crate::core::query::Without<gizmo_physics::RigidBody>,
85        )>() {
86            for (_, (trans, col, _)) in q.iter() {
87                draw_collider(trans, col, [0.5, 0.5, 0.5, 1.0], &mut gizmos);
88            }
89        }
90
91        let soft_color = [1.0, 0.4, 0.8, 1.0]; // Pinkish for soft body
92        if let Some(q) = world.query::<&gizmo_physics::soft_body::SoftBodyMesh>() {
93            for (_, sm) in q.iter() {
94                for elem in &sm.elements {
95                    let p0 = sm.nodes[elem.node_indices[0] as usize].position;
96                    let p1 = sm.nodes[elem.node_indices[1] as usize].position;
97                    let p2 = sm.nodes[elem.node_indices[2] as usize].position;
98                    let p3 = sm.nodes[elem.node_indices[3] as usize].position;
99
100                    // 6 edges of a tetrahedron
101                    gizmos.draw_line(p0, p1, soft_color);
102                    gizmos.draw_line(p0, p2, soft_color);
103                    gizmos.draw_line(p0, p3, soft_color);
104                    gizmos.draw_line(p1, p2, soft_color);
105                    gizmos.draw_line(p1, p3, soft_color);
106                    gizmos.draw_line(p2, p3, soft_color);
107                }
108            }
109        }
110
111        // --- Phase 6.1: Süspansiyon Raycast Çizgisi + Kuvvet Okları ---
112        if let Some(q) = world.query::<(
113            &crate::physics::Transform,
114            &gizmo_physics::vehicle::VehicleController,
115        )>() {
116            for (_, (trans, vehicle)) in q.iter() {
117                for wheel in &vehicle.wheels {
118                    let attach_world =
119                        trans.position + trans.rotation.mul_vec3(wheel.attachment_local_pos);
120                    let ray_dir = trans.rotation.mul_vec3(wheel.direction_local).normalize();
121                    let ray_end = attach_world
122                        + ray_dir
123                            * (wheel.suspension_rest_length
124                                + wheel.suspension_max_travel
125                                + wheel.radius);
126
127                    // Draw raycast maximum extent (Yellow line)
128                    gizmos.draw_line(attach_world, ray_end, [1.0, 1.0, 0.0, 1.0]);
129
130                    if wheel.is_grounded {
131                        if let Some(hit) = &wheel.ground_hit {
132                            // Kuvvet oku (Mavi) - sadece uzunluğu normalize edip görselleştirmek için / 10000 kullanıyoruz
133                            let force_dir = -ray_dir;
134                            let force_len = (wheel.suspension_force / 10000.0).clamp(0.1, 2.0);
135                            let arrow_end = hit.point + force_dir * force_len;
136                            gizmos.draw_line(hit.point, arrow_end, [0.0, 0.0, 1.0, 1.0]);
137
138                            // Mevcut süspansiyon uzunluğu + tekerlek merkezi çizgisi (Turuncu)
139                            let wheel_center = attach_world + ray_dir * wheel.suspension_length;
140                            gizmos.draw_line(wheel_center, hit.point, [1.0, 0.5, 0.0, 1.0]);
141                        }
142                    }
143                }
144            }
145        }
146
147        // --- Phase 6.2: Temas Normalleri ve Penetrasyon Derinliği ---
148        if let Some(phys_world) = world.get_resource::<gizmo_physics::world::PhysicsWorld>() {
149            for event in phys_world.collision_events() {
150                for contact in &event.contact_points {
151                    let p1 = contact.point;
152                    let p2 = contact.point + contact.normal * 0.5; // Normal arrow
153                    gizmos.draw_line(p1, p2, [1.0, 0.0, 0.0, 1.0]); // Red normal
154
155                    let p_pen = contact.point - contact.normal * contact.penetration;
156                    gizmos.draw_line(p1, p_pen, [1.0, 0.0, 1.0, 1.0]); // Magenta penetration depth
157                }
158            }
159        }
160        tracing::info!(
161            "physics_debug_system: gizmos lines count = {}",
162            gizmos.lines.len()
163        );
164    }
165}
166
167/// ECS'deki yeni yaratılmış Fiziksel Objeleri (RigidBody + Transform + Collider)
168/// GPU Physics çekirdeğinin otoyoluna (GpuPhysicsSystem::spheres_buffer) kaydeder.
169/// Statik collider'lar için ayrı sayaç. İlk 3 slot başlangıç collider'larına ayrılmıştır.
170static NEXT_STATIC_COLLIDER_SLOT: std::sync::atomic::AtomicU32 =
171    std::sync::atomic::AtomicU32::new(3);
172
173pub fn gpu_physics_submit_system(world: &mut crate::core::World, renderer: &Renderer) {
174    use crate::physics::Velocity;
175
176    if let Some(physics) = &renderer.gpu_physics {
177        let mut unlinked_entities = Vec::new();
178        if let Some(q) = world.query::<(&RigidBody, &Transform, &Collider)>() {
179            let links = world.borrow::<GpuPhysicsLink>();
180            let velocities = world.borrow::<Velocity>();
181            for (e, (rb, trans, col)) in q.iter() {
182                if links.get(e).is_none() {
183                    let vel = velocities.get(e).copied().unwrap_or_default();
184                    unlinked_entities.push((e, *rb, *trans, col.clone(), vel));
185                }
186            }
187        }
188
189        let mut next_dynamic_id = world
190            .query::<&GpuPhysicsLink>()
191            .map(|q| q.iter().count() as u32)
192            .unwrap_or(0);
193
194        for (e, rb, trans, col, vel) in unlinked_entities {
195            if matches!(col.shape, ColliderShape::Plane(_)) {
196                // Statik engel — ayrı slot sayacı kullan
197                let slot =
198                    NEXT_STATIC_COLLIDER_SLOT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
199                if slot >= 100 {
200                    eprintln!("[GpuPhysics] Statik collider slot limiti (100) aşıldı, collider atlanıyor.");
201                    NEXT_STATIC_COLLIDER_SLOT.fetch_sub(1, std::sync::atomic::Ordering::Relaxed);
202                    continue;
203                }
204
205                let gpu_col = gizmo_renderer::gpu_physics::GpuCollider {
206                    shape_type: match col.shape {
207                        ColliderShape::Plane(_) => 1,
208                        _ => 0, // Varsayılan Box (AABB)
209                    },
210                    _pad1: [0; 3],
211                    data1: match &col.shape {
212                        ColliderShape::Plane(p) => [p.normal.x, p.normal.y, p.normal.z, 0.0],
213                        ColliderShape::Box(b) => {
214                            let min = trans.position - b.half_extents;
215                            [min.x, min.y, min.z, 0.0]
216                        }
217                        _ => [0.0; 4],
218                    },
219                    data2: match &col.shape {
220                        ColliderShape::Plane(p) => [p.distance, 0.0, 0.0, 0.0],
221                        ColliderShape::Box(b) => {
222                            let max = trans.position + b.half_extents;
223                            [max.x, max.y, max.z, 0.0]
224                        }
225                        _ => [0.0; 4],
226                    },
227                };
228                physics.update_collider(&renderer.queue, slot, &gpu_col);
229            } else {
230                // Dinamik Kutu (AABB)
231                let id = next_dynamic_id;
232                next_dynamic_id += 1;
233
234                let extents = match &col.shape {
235                    ColliderShape::Box(b) => [b.half_extents.x, b.half_extents.y, b.half_extents.z],
236                    _ => [0.5, 0.5, 0.5],
237                };
238
239                let gpu_box = gizmo_renderer::gpu_physics::GpuBox {
240                    position: [trans.position.x, trans.position.y, trans.position.z],
241                    mass: rb.mass,
242                    velocity: [vel.linear.x, vel.linear.y, vel.linear.z],
243                    state: 0,
244                    rotation: [
245                        trans.rotation.x,
246                        trans.rotation.y,
247                        trans.rotation.z,
248                        trans.rotation.w,
249                    ],
250                    angular_velocity: [vel.angular.x, vel.angular.y, vel.angular.z],
251                    sleep_counter: if rb.is_sleeping { 60 } else { 0 },
252                    color: [0.3, 0.8, 1.0, 1.0],
253                    half_extents: extents,
254                    _pad: 0,
255                };
256                physics.update_box(&renderer.queue, id, &gpu_box);
257
258                world.add_component(world.get_entity(e).unwrap(), GpuPhysicsLink { id });
259            }
260        }
261    }
262}
263
264/// GPU'dan Asenkron (0ms) çekilen devasa Fizik lokasyon durumlarını,
265/// Ekrandaki objelerin render edilmesi için ECS'deki Transform'larına kopyalar.
266pub fn gpu_physics_readback_system(world: &mut crate::core::World, renderer: &Renderer) {
267    if let Some(physics) = &renderer.gpu_physics {
268        if let Some(gpu_data) = physics.poll_readback_data(&renderer.device) {
269            if let Some(mut q) =
270                world.query::<(gizmo_core::prelude::Mut<Transform>, &GpuPhysicsLink)>()
271            {
272                for (_, (mut trans, link)) in q.iter_mut() {
273                    let idx = link.id as usize;
274                    if idx < gpu_data.len() {
275                        let box_data = &gpu_data[idx];
276                        trans.position = gizmo_math::Vec3::new(
277                            box_data.position[0],
278                            box_data.position[1],
279                            box_data.position[2],
280                        );
281                        trans.rotation = gizmo_math::Quat::from_xyzw(
282                            box_data.rotation[0],
283                            box_data.rotation[1],
284                            box_data.rotation[2],
285                            box_data.rotation[3],
286                        );
287                        trans.update_local_matrix();
288                    }
289                }
290            }
291        }
292    }
293}
294
295/// Phase 7.1: Fluid-Rigid Coupling
296/// Senkronize eder: GpuPhysicsLink sahibi objeleri FluidCollider buffer'ına yazar.
297
298pub fn cpu_physics_step_system(world: &crate::core::World, dt: f32) {
299    gizmo_physics::system::physics_step_system(world, dt);
300}