gizmo-renderer 0.1.3

A custom ECS and physics engine aimed for realistic simulations.
Documentation
// ═══════════════════════════════════════════════════════════════════════
//  Physics Debug Line Renderer
//
//  Compute pass: Joint + Box verilerinden debug çizgi vertex'leri üretir.
//  Render pass:  Çizgileri ekrana çizer.
//
//  Renk kodlaması:
//    🟢 Yeşil   → Joint bağlantıları
//    🔵 Mavi    → Aktif kutu wireframe
//    ⚫ Koyu    → Uyuyan kutu wireframe
//    🟡 Sarı    → Hız vektörleri
//    🔴 Kırmızı → Kırılmış joint
// ═══════════════════════════════════════════════════════════════════════

struct DebugVertex {
    position: vec3<f32>,
    color: u32,  // packed RGBA: R | (G<<8) | (B<<16) | (A<<24)
}

struct BoxItem {
    position: vec3<f32>,
    mass: f32,
    velocity: vec3<f32>,
    state: u32,
    rotation: vec4<f32>,
    angular_velocity: vec3<f32>,
    sleep_counter: u32,
    color: vec4<f32>,
    half_extents: vec3<f32>,
    _pad: u32,
}

struct Joint {
    body_a: u32,
    body_b: u32,
    joint_type: u32,
    flags: u32,
    anchor_a: vec3<f32>,
    compliance: f32,
    anchor_b: vec3<f32>,
    damping_coeff: f32,
    axis: vec3<f32>,
    max_force: f32,
}

struct DebugParams {
    num_boxes: u32,
    num_joints: u32,
    show_wireframes: u32,  // bit0=boxes, bit1=joints, bit2=velocity
    _pad: u32,
}

@group(0) @binding(0) var<uniform> debug_params: DebugParams;
@group(0) @binding(1) var<storage, read> boxes: array<BoxItem>;
@group(0) @binding(2) var<storage, read> joints: array<Joint>;
@group(0) @binding(3) var<storage, read_write> debug_lines: array<DebugVertex>;
@group(0) @binding(4) var<storage, read_write> line_count: atomic<u32>;

fn pack_color(r: f32, g: f32, b: f32, a: f32) -> u32 {
    let ri = u32(clamp(r * 255.0, 0.0, 255.0));
    let gi = u32(clamp(g * 255.0, 0.0, 255.0));
    let bi = u32(clamp(b * 255.0, 0.0, 255.0));
    let ai = u32(clamp(a * 255.0, 0.0, 255.0));
    return ri | (gi << 8u) | (bi << 16u) | (ai << 24u);
}

fn rotate_vec(v: vec3<f32>, q: vec4<f32>) -> vec3<f32> {
    let u = q.xyz;
    let s = q.w;
    return 2.0 * dot(u, v) * u + (s * s - dot(u, u)) * v + 2.0 * s * cross(u, v);
}

fn emit_line(start: vec3<f32>, end: vec3<f32>, color: u32) {
    let vi = atomicAdd(&line_count, 2u); // 2 vertices per line
    let max_vertices = 65536u;  // 32768 lines * 2
    if (vi >= max_vertices) { return; }
    
    debug_lines[vi] = DebugVertex(start, color);
    debug_lines[vi + 1u] = DebugVertex(end, color);
}

// ─── Box Wireframe ───
fn emit_box_wireframe(box_item: BoxItem, color: u32) {
    let e = box_item.half_extents;
    let p = box_item.position;
    let q = box_item.rotation;
    
    // 8 köşe
    var corners = array<vec3<f32>, 8>(
        p + rotate_vec(vec3<f32>(-e.x, -e.y, -e.z), q),
        p + rotate_vec(vec3<f32>( e.x, -e.y, -e.z), q),
        p + rotate_vec(vec3<f32>( e.x,  e.y, -e.z), q),
        p + rotate_vec(vec3<f32>(-e.x,  e.y, -e.z), q),
        p + rotate_vec(vec3<f32>(-e.x, -e.y,  e.z), q),
        p + rotate_vec(vec3<f32>( e.x, -e.y,  e.z), q),
        p + rotate_vec(vec3<f32>( e.x,  e.y,  e.z), q),
        p + rotate_vec(vec3<f32>(-e.x,  e.y,  e.z), q)
    );
    
    // 12 kenar
    emit_line(corners[0], corners[1], color);
    emit_line(corners[1], corners[2], color);
    emit_line(corners[2], corners[3], color);
    emit_line(corners[3], corners[0], color);
    
    emit_line(corners[4], corners[5], color);
    emit_line(corners[5], corners[6], color);
    emit_line(corners[6], corners[7], color);
    emit_line(corners[7], corners[4], color);
    
    emit_line(corners[0], corners[4], color);
    emit_line(corners[1], corners[5], color);
    emit_line(corners[2], corners[6], color);
    emit_line(corners[3], corners[7], color);
}

// ═══ Compute: Debug Çizgi Üretici ═══
@compute @workgroup_size(256)
fn generate_debug_lines(@builtin(global_invocation_id) global_id: vec3<u32>) {
    let idx = global_id.x;
    
    // Her thread bir kutu işler
    if (idx < debug_params.num_boxes) {
        let box_item = boxes[idx];
        
        // Box wireframe
        if ((debug_params.show_wireframes & 1u) != 0u) {
            var color = pack_color(0.0, 0.8, 1.0, 0.6); // Cyan — aktif
            if (box_item.state == 1u) {
                color = pack_color(0.2, 0.2, 0.3, 0.3);  // Koyu — uyuyan
            }
            emit_box_wireframe(box_item, color);
        }
        
        // Velocity vektörü
        if ((debug_params.show_wireframes & 4u) != 0u) {
            let speed = length(box_item.velocity);
            if (speed > 0.1) {
                let vel_end = box_item.position + box_item.velocity * 0.5;
                let color = pack_color(1.0, 0.9, 0.0, 0.8); // Sarı
                emit_line(box_item.position, vel_end, color);
            }
        }
    }
    
    // Joint çizgileri (sadece ilk thread'ler)
    if (idx < debug_params.num_joints && (debug_params.show_wireframes & 2u) != 0u) {
        let joint = joints[idx];
        if ((joint.flags & 1u) == 0u) { return; } // Inactive
        
        let body_a = boxes[joint.body_a];
        let body_b = boxes[joint.body_b];
        
        let world_a = body_a.position + rotate_vec(joint.anchor_a, body_a.rotation);
        let world_b = body_b.position + rotate_vec(joint.anchor_b, body_b.rotation);
        
        // Joint tipi renkleri
        var color = pack_color(0.0, 1.0, 0.3, 0.9); // Yeşil — Ball
        if (joint.joint_type == 1u) {
            color = pack_color(1.0, 0.5, 0.0, 0.9); // Turuncu — Hinge
        } else if (joint.joint_type == 2u) {
            color = pack_color(0.8, 0.0, 1.0, 0.9); // Mor — Fixed
        } else if (joint.joint_type == 3u) {
            color = pack_color(0.0, 0.5, 1.0, 0.9); // Mavi — Spring
        } else if (joint.joint_type == 4u) {
            color = pack_color(1.0, 1.0, 0.0, 0.9); // Sarı — Slider
        }
        
        // Body merkez → anchor çizgisi
        emit_line(body_a.position, world_a, color);
        // Anchor → anchor bağlantı çizgisi
        emit_line(world_a, world_b, color);
        // Anchor → body merkez çizgisi
        emit_line(world_b, body_b.position, color);
        
        // Hinge: eksen yönü gösterimi
        if (joint.joint_type == 1u) {
            let axis_world = rotate_vec(joint.axis, body_a.rotation);
            let axis_color = pack_color(1.0, 0.8, 0.2, 0.7);
            emit_line(world_a - axis_world * 1.5, world_a + axis_world * 1.5, axis_color);
        }
        
        // Spring: zigzag çizgi
        if (joint.joint_type == 3u) {
            let mid = (world_a + world_b) * 0.5;
            let spring_color = pack_color(0.3, 0.7, 1.0, 0.6);
            let dir = normalize(world_b - world_a);
            let perp = normalize(cross(dir, vec3<f32>(0.0, 1.0, 0.0)));
            emit_line(world_a, mid + perp * 0.3, spring_color);
            emit_line(mid + perp * 0.3, mid - perp * 0.3, spring_color);
            emit_line(mid - perp * 0.3, world_b, spring_color);
        }
    }
}

// ═══ Render: Debug Çizgi Çizici ═══
struct Globals {
    view_proj: mat4x4<f32>,
    camera_pos: vec3<f32>,
    _pad: f32,
}

@group(0) @binding(0) var<uniform> globals: Globals;

struct VertexInput {
    @location(0) position: vec3<f32>,
    @location(1) color: u32,
}

struct VertexOutput {
    @builtin(position) clip_pos: vec4<f32>,
    @location(0) frag_color: vec4<f32>,
}

@vertex
fn vs_debug(@location(0) position: vec3<f32>, @location(1) color: u32) -> VertexOutput {
    var out: VertexOutput;
    out.clip_pos = globals.view_proj * vec4<f32>(position, 1.0);
    
    // Renk unpack
    let r = f32(color & 0xFFu) / 255.0;
    let g = f32((color >> 8u) & 0xFFu) / 255.0;
    let b = f32((color >> 16u) & 0xFFu) / 255.0;
    let a = f32((color >> 24u) & 0xFFu) / 255.0;
    out.frag_color = vec4<f32>(r, g, b, a);
    
    return out;
}

@fragment
fn fs_debug(in: VertexOutput) -> @location(0) vec4<f32> {
    return in.frag_color;
}