bevy_flora 0.4.0

Parametric, stylized procedural trees for Bevy that grow over time: 15 presets, bent and forking branches, leaf styles, deterministic per seed.
Documentation
// Where a growing vertex goes. The Rust mirror is `gpu/frame.rs`; the buffer
// layout is written by `GrowthState::encode` in `gpu/state.rs`.
//
// The buffer is an array of vec4: a header [node count, ring count, trunk flags,
// cone lift], then 5 vec4 per node, then 2 vec4 per trunk ring.

struct GrowthAttrs {
    // Node id; placement class in the low 8 bits and a leaf's ordinal above them.
    @location(8) id: vec2<u32>,
    // Anchor on the node's axis (xyz, in node lengths); fraction along the piece or trunk ring (w).
    @location(9) axis: vec4<f32>,
    // Offset from the anchor (xyz, in units of the piece's scale); trunk root bulge unit (w).
    @location(10) offset: vec4<f32>,
};

@group(constants::MATERIAL_BIND_GROUP) @binding(100) var<storage, read> growth: array<vec4<f32>>;

const KIND_TUBE: u32 = 0u;
const KIND_LEAF: u32 = 1u;
const KIND_COLLAR: u32 = 2u;
const KIND_BASE: u32 = 3u;
const KIND_BASE_COLLAR: u32 = 4u;
const KIND_ROOT_FAR: u32 = 5u;
const KIND_ROOT_FAR_COLLAR: u32 = 6u;
const KIND_TRUNK: u32 = 7u;
const KIND_TRUNK_TOP_CAP: u32 = 8u;
const KIND_TRUNK_CONE: u32 = 9u;

const WOOD: u32 = 1u;
const COLLAR: u32 = 2u;
const BASE: u32 = 4u;
const BASE_COLLAR: u32 = 8u;

const TOP_CAP: u32 = 1u;
const CONE: u32 = 2u;

const NODE_VEC4S: u32 = 5u;
const RING_VEC4S: u32 = 2u;

// `v` turned about the Y axis by `twist` radians, the way a trunk ring's twist
// turns its vertices.
fn rotate_y(v: vec3<f32>, twist: f32) -> vec3<f32> {
    let s = sin(twist);
    let c = cos(twist);
    return vec3<f32>(v.x * c + v.z * s, v.y, -v.x * s + v.z * c);
}

fn trunk_position(g: GrowthAttrs, kind: u32, node_count: u32, trunk_flags: u32, cone_lift: f32) -> vec3<f32> {
    let ring = u32(g.axis.w + 0.5);
    let at = 1u + node_count * NODE_VEC4S + ring * RING_VEC4S;
    let ring_a = growth[at];
    let ring_b = growth[at + 1u];
    let center = ring_a.xyz;
    let scale = ring_a.w + g.offset.w * ring_b.x;
    let twist = ring_b.y;
    if kind == KIND_TRUNK_CONE {
        if (trunk_flags & CONE) != 0u {
            return center + vec3<f32>(0.0, cone_lift, 0.0) + g.offset.xyz;
        }
        return center + scale * rotate_y(g.axis.xyz, twist);
    }
    if kind == KIND_TRUNK_TOP_CAP && (trunk_flags & TOP_CAP) == 0u {
        return center;
    }
    return center + scale * rotate_y(g.offset.xyz, twist);
}

// The vertex's position in the tree's local space at the current growth state.
fn grown_position(g: GrowthAttrs) -> vec3<f32> {
    let kind = g.id.y & 255u;
    let header = growth[0];
    let node_count = u32(header.x);
    if kind >= KIND_TRUNK {
        return trunk_position(g, kind, node_count, u32(header.z), header.w);
    }

    let at = 1u + g.id.x * NODE_VEC4S;
    let a = growth[at];       // start, length
    let b = growth[at + 1u];  // base radius, tip radius, leaf scale, leaf count
    let c = growth[at + 2u];  // collar start, radius
    let d = growth[at + 3u];  // collar end, radius
    let e = growth[at + 4u];  // root, flags
    let flags = u32(e.w);
    let start = a.xyz;
    let on_axis = start + a.w * g.axis.xyz;

    var anchor = on_axis;
    var scale = 0.0;
    var on = true;
    switch kind {
        case KIND_TUBE: {
            scale = mix(b.x, b.y, g.axis.w);
            on = (flags & WOOD) != 0u;
        }
        case KIND_LEAF: {
            let ordinal = g.id.y >> 8u;
            let shown = b.z > 0.0 && (b.w < 0.0 || f32(ordinal) < b.w);
            scale = select(0.0, b.z, shown);
        }
        case KIND_COLLAR: {
            anchor = mix(c.xyz, d.xyz, g.axis.w);
            scale = mix(c.w, d.w, smoothstep(0.0, 1.0, g.axis.w));
            on = (flags & COLLAR) != 0u;
        }
        case KIND_BASE: {
            anchor = start;
            scale = b.x;
            on = (flags & BASE) != 0u;
        }
        case KIND_BASE_COLLAR: {
            anchor = c.xyz;
            scale = c.w;
            on = (flags & BASE_COLLAR) != 0u;
        }
        case KIND_ROOT_FAR: {
            anchor = e.xyz;
            scale = b.x;
            on = (flags & BASE) != 0u;
        }
        default: {
            // KIND_ROOT_FAR_COLLAR
            anchor = e.xyz;
            scale = c.w;
            on = (flags & BASE_COLLAR) != 0u;
        }
    }
    // A piece that does not exist yet collapses onto the node's start (a leaf
    // with no size already sits on its anchor).
    return select(start, anchor + scale * g.offset.xyz, on);
}