bevy_prototype_sdf 0.1.0

Experimental APIs for using Signed Distance Functions in bevy
Documentation
#define_import_path bevy_prototype_sdf

@group(2) @binding(0) var<storage, read> sdf_order: array<u32>;
@group(2) @binding(1) var<storage, read> sdf_data: array<f32>;

// Universal operations
const EXEC_PUSH_STACK = 0u;
const EXEC_POP_POSITION = 1u;
const EXEC_UNION = 2u;
const EXEC_SUBTRACT = 3u;
const EXEC_INTERSECT = 4u;
const EXEC_INVERT = 5u;
const EXEC_SHELL = 6u;

// 2D operations
const EXEC_TRANSLATE_2D = 7u;
const EXEC_ROTATE_2D = 8u;

// 3D operations
const EXEC_TRANSLATE_3D = 9u;
const EXEC_ROTATE_3D = 10u;
const EXEC_PRE_EXTRUDE = 11u;
const EXEC_EXTRUDE = 12u;
const EXEC_REVOLVE = 13u;
const EXEC_POST_REVOLVE = 14u;

// 2D primitives
const EXEC_CIRCLE = 15u;
const EXEC_RECTANGLE = 16u;
const EXEC_ARC = 17u;

// 3D primitives
const EXEC_SPHERE = 18u;
const EXEC_CAPSULE_3D = 19u;
const EXEC_CYLINDER = 20u;
const EXEC_CUBOID = 21u;
const EXEC_INFINITE_PLANE_3D = 22u;

fn sdf(p: vec3<f32>, order_start: u32, data_start: u32) -> f32 {
    let order_len = sdf_order[order_start];

    var dist = 1e9;
    var dist_stack: array<f32, 16>;
    var dist_stack_location = 0u;

    var pos = p;
    var pos_stack: array<vec3<f32>, 16>;
    var pos_stack_location = 0u;

    for (var i = 0u; i < order_len; i++) {
        let node_start = order_start + 1 + i * 2;
        let node_exec = sdf_order[node_start];
        let node_value = sdf_order[node_start + 1];

        // data offset if the node value is an index into data
        let offset = data_start + node_value;

        switch node_exec {
            default: {
                return sd_sphere(pos, 0.1);
            }

            // Universal operations
            case EXEC_PUSH_STACK: {
                dist_stack[dist_stack_location] = dist;
                dist_stack_location += 1u;
            }
            case EXEC_UNION: {
                dist_stack_location -= 1u;
                let a = dist_stack[dist_stack_location];
                dist = min(a, dist);
            }
            case EXEC_SUBTRACT: {
                dist_stack_location -= 1u;
                let a = dist_stack[dist_stack_location];
                dist = max(a, -dist);
            }
            case EXEC_INTERSECT: {
                dist_stack_location -= 1u;
                let a = dist_stack[dist_stack_location];
                dist = max(a, dist);
            }
            case EXEC_INVERT: {
                dist = -dist;
            }
            case EXEC_SHELL: {
                dist = abs(dist) - sdf_data[offset];
            }
            case EXEC_POP_POSITION: {
                pos_stack_location -= 1u;
                pos = pos_stack[pos_stack_location];
            }

            // 2D operations
            case EXEC_TRANSLATE_2D: {
                pos_stack[pos_stack_location] = pos;
                pos_stack_location += 1u;

                let x = sdf_data[offset];
                let y = sdf_data[offset + 1];
                pos -= vec3<f32>(x, 0., y);
            }
            case EXEC_ROTATE_2D: {
                pos_stack[pos_stack_location] = pos;
                pos_stack_location += 1u;

                let angle = sdf_data[offset];
                let s = sin(angle);
                let c = cos(angle);

                let rot = mat2x2(
                    c, -s,
                    s, c,
                );
                let pos2d = rot * pos.xz;
                pos = vec3<f32>(pos2d.x, pos.y, pos2d.y);
            }

            // 3D operations
            case EXEC_TRANSLATE_3D: {
                pos_stack[pos_stack_location] = pos;
                pos_stack_location += 1u;

                let x = sdf_data[offset];
                let y = sdf_data[offset + 1];
                let z = sdf_data[offset + 2];
                pos -= vec3<f32>(x, y, z);
            }
            case EXEC_ROTATE_3D: {
                pos_stack[pos_stack_location] = pos;
                pos_stack_location += 1u;

                let x = sdf_data[offset];
                let y = sdf_data[offset + 1];
                let z = sdf_data[offset + 2];
                let w = sdf_data[offset + 3];

                pos = qtransform(vec4<f32>(-x, -y, -z, w), pos);
            }
            case EXEC_PRE_EXTRUDE: {
                pos_stack[pos_stack_location] = pos;
                pos_stack_location += 1u;
            }
            case EXEC_EXTRUDE: {
                pos_stack_location -= 1u;
                pos = pos_stack[pos_stack_location];
                dist = sd_extrude(dist, pos, sdf_data[offset]);
            }
            case EXEC_REVOLVE: {
                pos_stack[pos_stack_location] = pos;
                pos_stack_location += 1u;

                let x_offset = sdf_data[offset];
                pos = vec3<f32>(length(pos.xz) - x_offset, 0., pos.y);
            }
            case EXEC_POST_REVOLVE: {
                pos_stack_location -= 1u;
                pos = pos_stack[pos_stack_location];
            }

            // 2D primitives
            case EXEC_CIRCLE: {
                dist = sd_circle(pos.xz, sdf_data[offset]);
            }
            case EXEC_RECTANGLE: {
                let x = sdf_data[offset];
                let y = sdf_data[offset+1];
                dist = sd_rectangle(pos.xz, vec2<f32>(x, y));
            }
            case EXEC_ARC: {
                let radius = sdf_data[offset];
                let thickness = sdf_data[offset+1];
                let segment = sdf_data[offset+2];
                dist = sd_arc(pos.xz, radius, thickness, segment);
            }

            // 3D primitives
            case EXEC_SPHERE: {
                 dist = sd_sphere(pos, sdf_data[offset]);
            }
            case EXEC_CAPSULE_3D: {
                let radius = sdf_data[offset];
                let half_length = sdf_data[offset+1];
                dist = sd_capsule3d(pos, radius, half_length);
            }
            case EXEC_CYLINDER: {
                let radius = sdf_data[offset];
                let half_height = sdf_data[offset+1];
                dist = sd_cylinder(pos, radius, half_height);
            }
            case EXEC_CUBOID: {
                let x = sdf_data[offset];
                let y = sdf_data[offset + 1];
                let z = sdf_data[offset + 2];
                dist = sd_cuboid(pos, vec3<f32>(x, y, z));
            }
            case EXEC_INFINITE_PLANE_3D: {
                let x = sdf_data[offset];
                let y = sdf_data[offset + 1];
                let z = sdf_data[offset + 2];
                dist = dot(pos, vec3<f32>(x, y, z));
            }
        }
    }

    return dist;
}

fn sd_sphere(pos: vec3<f32>, radius: f32) -> f32 {
    return length(pos) - radius;
}

fn sd_capsule3d(pos: vec3<f32>, radius: f32, half_length: f32) -> f32 {
    var p = pos;
    p.y -= clamp(p.y, -half_length, half_length);
    return length(p) - radius;
}

fn sd_cylinder(pos: vec3<f32>, radius: f32, half_height: f32) -> f32 {
    let d = abs(vec2<f32>(length(pos.xz), pos.y)) - vec2<f32>(radius, half_height);
    return min(max(d.x, d.y), 0.0) + length(max(d, vec2<f32>(0.)));
}

fn sd_cuboid(pos: vec3<f32>, bounds: vec3<f32>) -> f32 {
    let q = abs(pos) - bounds;
    return length(max(q, vec3<f32>(0.))) + min(max(q.x, max(q.y, q.z)), 0.);
}

fn sd_extrude(dist: f32, pos: vec3<f32>, half_height: f32) -> f32 {
    let w = vec2<f32>(dist, abs(pos.y) - half_height);

    return min(max(w.x, w.y), 0.) + length(max(w, vec2<f32>(0.)));
}

fn sdf2d(pos: vec2<f32>, shape_kind: u32, offset: u32) -> f32 {
    if shape_kind == 3 {
        let radius = bitcast<f32>(sdf_data[offset]);
        let thickness = bitcast<f32>(sdf_data[offset+1]);
        let segment = bitcast<f32>(sdf_data[offset+2]);
        return sd_arc(pos, radius, thickness, segment);
    }

    return 1e9;
}

fn sd_circle(pos: vec2<f32>, radius: f32) -> f32 {
    return length(pos) - radius;
}

fn sd_rectangle(pos: vec2<f32>, bounds: vec2<f32>) -> f32 {
    let d = abs(pos) - bounds;
    return length(max(d, vec2<f32>(0.))) + min(max(d.x, d.y), 0.);
}

fn sd_arc(pos: vec2<f32>, radius: f32, thickness: f32, segment: f32) -> f32 {
    let sc = vec2<f32>(sin(segment), cos(segment));
    let p = vec2<f32>(abs(pos.x), pos.y);
    if sc.y * p.x > sc.x * p.y {
        let w = p - radius * sc;
        let l = length(w);
        return l - thickness;
    } else {
        let l = length(pos);
        let w = l - radius;
        return abs(w) - thickness;
    }
}

fn qtransform(q:vec4<f32>, v: vec3<f32>) -> vec3<f32> {
    return v + 2.0*cross(cross(v, q.xyz) + q.w*v, q.xyz);
}