bevy_symbios_shape 0.3.0

Bevy integration for Symbios Shape.
Documentation
# bevy_symbios_shape

Bevy integration for [symbios-shape](https://crates.io/crates/symbios-shape) — a CGA Shape Grammar engine for procedural architecture.

Converts grammar outputs (`ShapeModel` / `Terminal` nodes) into Bevy entity hierarchies with procedural or asset-based meshes.

## Features

- **Plugin**: `BevySymbiosShapePlugin` registers `ShapeRegistry`, `ShapeMeshCache`, `LastDerivedModel`, and `SnapPlanes` resources, plus an observer for `SpawnShapeRequest` events.
- **Asset routing**: Register GLTF scenes and materials by string ID; unregistered terminals fall back to procedural meshes.
- **Procedural meshes**: `build_profiled_mesh` generates geometry for tapered prisms, triangles, trapezoids, and arbitrary polygons based on the terminal's `FaceProfile`.
- **Coordinate translation**: `scope_to_transform` handles the f64→f32 downcast and corner-to-centroid offset between `symbios-shape` scopes and Bevy transforms.
- **Spawn extension**: `commands.spawn_shape(...)` derives a grammar and spawns the full terminal hierarchy in one call.
- **Event-driven spawning**: trigger `SpawnShapeRequest` and observe `SpawnShapeSpawned` / `SpawnShapeFailed` for decoupled call sites.
- **Cross-spawn mesh cache**: `ShapeMeshCache` deduplicates procedural meshes across `spawn_shape` calls (with hit/miss counters).
- **Mass properties**: terminals whose grammar carries `Mat(id, density)` get a `TerminalMass` component (mass / centroid / inertia) for downstream physics, IK, and LOD.
- **Snap planes**: `RegSnap("label")` recordings are surfaced via the `SnapPlanes` resource.
- **Spatial queries**: `LastDerivedModel` plus the re-exported `TerminalQuery` / `obb_overlap` give downstream systems OBB collision tests against spawned terminals.

## Quick start

```rust
use bevy::prelude::*;
use bevy_symbios_shape::prelude::*;
use symbios_shape::{Interpreter, Scope, grammar::parse_ops};

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, BevySymbiosShapePlugin))
        .add_systems(Startup, setup)
        .run();
}

fn setup(
    mut commands: Commands,
    registry: Res<ShapeRegistry>,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    mut cache: ResMut<ShapeMeshCache>,
) {
    let mut interp = Interpreter::new();
    interp.add_rule("Lot",    parse_ops("Extrude(12) Split(Y) { 3: Ground | ~1: Upper | 2: Roof }").unwrap());
    interp.add_rule("Ground", parse_ops(r#"I("GroundFloor")"#).unwrap());
    interp.add_rule("Upper",  parse_ops(r#"I("Floor")"#).unwrap());
    interp.add_rule("Roof",   parse_ops(r#"Taper(0.8) I("Roof")"#).unwrap());

    let footprint = Scope::new(
        symbios_shape::Vec3::ZERO,
        symbios_shape::Quat::IDENTITY,
        symbios_shape::Vec3::new(10.0, 0.0, 10.0),
    );

    commands
        .spawn_shape(&interp, footprint, "Lot", &registry, &mut meshes, &mut materials, &mut cache)
        .unwrap();
}
```

## Modules

| Module      | Purpose                                                                                 |
|-------------|-----------------------------------------------------------------------------------------|
| `cache`     | `ShapeMeshCache` — persistent procedural mesh cache shared across spawns                |
| `events`    | `SpawnShapeRequest` / `SpawnShapeSpawned` / `SpawnShapeFailed` events + observer        |
| `mass`      | `TerminalMass` Component — mass / centroid / inertia from upstream `MassProperties`     |
| `mesh`      | `build_profiled_mesh` / `build_tapered_cuboid` — procedural mesh generation             |
| `query`     | `LastDerivedModel` resource holding the most recently derived `ShapeModel`              |
| `registry`  | `ShapeRegistry` — asset routing table (mesh IDs → scenes, material IDs → materials)     |
| `snap`      | `SnapPlane` / `SnapPlanes` resource exposing `RegSnap` recordings                       |
| `spawner`   | `SpawnShapeExt``Commands` extension trait for one-call grammar derivation + spawning |
| `transform` | `scope_to_transform` — f64 scope to f32 Bevy `Transform` with corner-to-centroid offset |

## Asset registration

Register GLTF scenes and materials before spawning so the grammar can resolve them:

```rust
fn setup(
    mut registry: ResMut<ShapeRegistry>,
    mut materials: ResMut<Assets<StandardMaterial>>,
    asset_server: Res<AssetServer>,
) {
    registry.register_mesh("Window", asset_server.load("window.glb#Scene0"));
    registry.register_material("Brick", asset_server.load("brick_material.glb#Material0"));
    registry.default_material = Some(materials.add(StandardMaterial::default()));

    // Stretch UVs across the face for materials whose texture should fit exactly
    // once per face (signage, stained glass) instead of tiling in world space.
    registry.register_stretch_material("Window");
}
```

Terminals whose `mesh_id` is not registered get a procedural mesh colored by a stable hash of the ID. Material fallback order is: registered handle → `default_material` → generated grey.

## Event-driven spawning

For decoupled call sites — e.g. a UI button that requests a building without holding the mesh/material asset registries — trigger [`SpawnShapeRequest`]:

```rust
use std::sync::Arc;

fn on_button_click(mut commands: Commands, interp: Res<MyInterpreter>) {
    commands.trigger(SpawnShapeRequest {
        interpreter: Arc::new(interp.0.clone()),
        root_scope: footprint(),
        root_rule: "Lot".into(),
    });
}

// Observe the result anywhere:
fn track_spawns(mut spawned: On<SpawnShapeSpawned>) { /* spawned.event().0 = root Entity */ }
fn track_errors(mut failed: On<SpawnShapeFailed>)   { /* failed.event().0 = error string */ }
```

The plugin's built-in observer performs the derivation and spawn, then triggers `SpawnShapeSpawned(root_entity)` on success or `SpawnShapeFailed(message)` on derivation failure.

## Cargo features

| Feature    | Default | Effect                                                                                              |
|------------|---------|-----------------------------------------------------------------------------------------------------|
| `egui`     | off     | Pulls in [`bevy_egui`]https://crates.io/crates/bevy_egui for downstream inspector/editor UIs.     |
| `mutation` | off     | Enables the `mutation` module: `MaterialMutationPlugin`, `TextureConfigStore`, and the `MutateMaterialsRequest` / `MaterialTextureMutated` event pair. Drives procedural-texture evolution via [`bevy_symbios_texture`]https://crates.io/crates/bevy_symbios_texture and [`symbios-genetics`]https://crates.io/crates/symbios-genetics. |

Enable from `Cargo.toml`:

```toml
[dependencies]
bevy_symbios_shape = { version = "0.3", features = ["mutation"] }
```

## Examples

```bash
cargo run --example detailed_villa
cargo run --example medieval_castle
```

## License

MIT