Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
bevy_symbios
Bevy integration for the Symbios L-System ecosystem.
Converts L-System skeletons into Bevy meshes and physics colliders for procedural plant generation, organic structures, and generative art.
Features
- Mesh Generation: Smooth tube meshes from skeleton strands using parallel transport
- Multi-Material Support: Separate meshes per
u16material ID for palette-driven PBR (bark, leaves, etc.) - Vertex Colors: Per-vertex RGBA colors from skeleton data
- UV Mapping: Arc-length parameterized UVs with aspect-ratio preservation
- Mesh Caching: Optional fingerprint-keyed
MeshCacheto avoid re-meshing identical L-systems - Procedural Materials: 57 procedural texture generators (Leaf, Twig, Bark, Brick, Sand, Ice, Lava, Flower, Flame, …) plus Grid/Noise/Checker previews
- OBJ + GLB Export: Pure data conversion for tooling / asset pipelines
- Egui Editor (optional): Drop-in
material_palette_editorwidget for live PBR + per-texture-config editing - Physics Colliders (optional): Compound capsule colliders for Avian3D physics
- Robot Spawning (optional): Spawn articulated rigid-body robots from
symbios-robotblueprints - Asset Loaders (optional):
.lsysgrammars and.matpalette.jsonpalettes loaded throughAssetServer(hot-reload friendly)
Feature flags:
| Feature | Implies | Enables |
|---|---|---|
physics |
ColliderGenerator, PositionedCollider (Avian3D) |
|
egui |
ui::material_palette_editor (via bevy_egui) |
|
robot |
physics |
spawn_robot, SpawnedRobot, ImuSensor, TouchSensor |
asset-loader |
LSystemAssetPlugin, .lsys + .matpalette.json loaders |
Usage
Basic Mesh Generation
use *;
use ;
Multi-Material Workflow
The material system separates PBR surface properties from local color variation:
- Material ID (
SkeletonPoint::material_id) — Selects a palette entry that defines surface properties like roughness, metallic, and emissive. Each unique ID produces a separate mesh, so different BevyStandardMaterials can be applied per group. - Vertex Colors (
SkeletonPoint::color) — Baked into mesh vertices asATTRIBUTE_COLOR. These provide per-vertex tinting (e.g. darker bark at branch bases, lighter tips on leaves) without needing additional materials or textures.
Set base_color: Color::WHITE on your palette materials so vertex colors pass through
unmodified. Any non-white base color will multiply with the vertex color.
Physics Colliders
Generate a compound capsule collider for physics simulation (requires physics feature):
use *;
use ;
Working with Symbios
This crate works with skeletons from the symbios-turtle-3d interpreter:
use System;
use ;
use LSystemMeshBuilder;
// Parse and derive an L-System
let mut sys = new;
sys.set_axiom.unwrap;
sys.add_rule.unwrap;
sys.derive.unwrap;
// Interpret derived state as a 3D skeleton
let mut interpreter = new;
interpreter.populate_standard_symbols;
let skeleton = interpreter.build_skeleton;
// Now use LSystemMeshBuilder to create meshes
let meshes = new
.with_resolution
.build;
Material Palette System
The materials module provides a palette-first PBR workflow with live editing support
driven by an explicit-trigger observer:
use *;
use ;
// In your app setup:
app.
.add_systems
.add_observer
.add_systems;
// After editing MaterialSettingsMap, ask the observer to re-apply the palette:
MaterialSettingsMap holds editable settings per material ID (base color, emission,
roughness, metallic, texture type, UV scale). The default seeds three entries (IDs 0/1/2)
but the map accepts any u16 key. on_material_settings_changed is an observer —
nothing happens until you commands.trigger(MaterialSettingsChanged) after mutating the
map, which keeps re-application off the hot path and lets the UI decide when to commit
in-progress edits. For procedural foliage configs the observer spawns an async
generation task; apply_foliage_textures drains those tasks each frame and applies the
resulting handles to the MaterialPalette.
Material Palette Editor (requires egui feature)
use material_palette_editor;
// Inside an egui panel — returns true when a property changed in a way that
// warrants texture regeneration. Pair with `commands.trigger(MaterialSettingsChanged)`.
let regen = material_palette_editor;
The editor exposes PBR sliders plus a Texture parameters subsection that dispatches
to the per-variant editor for every bevy_symbios_texture generator — 57 as of 0.8,
spanning architectural surfaces (Brick, Plank, Ashlar, Wainscoting, Parquet, Truchet,
SolarPanel, …), natural surfaces (Bark, Ground, Sand, Snow, Ice, Lava, Moss, Lichen,
CactusSkin, CrackedEarth, Gravel, ForestFloor, Obsidian, …), and alpha-masked
foliage/particle cards (Leaf, Twig, Flower, GrassTuft, Frond, Reed, Needle, Broadleaf,
Flame, Spark, Snowflake, …). New upstream generators appear in the dropdown
automatically.
Mesh Caching
For scenes that re-spawn the same L-system many times (props, tile-based foliage,
deterministic regrowth) use MeshCache to avoid re-meshing identical skeletons:
use *;
use ;
;
build_cached fingerprints the skeleton (positions, rotations, radii, colors, material
IDs, UV scales) plus the builder's resolution. A matching fingerprint returns the cached
Handle<Mesh> map; otherwise it builds, inserts, and bumps the miss counter. Inspect
cache.hits() / cache.misses() for instrumentation, or use the lower-level
MeshCache::get_or_insert_with with compute_skeleton_fingerprint if you need to drive
caching outside build_cached. The cache does not LRU-evict — call clear()
periodically in long-running scenes that generate many unique skeletons.
Robot Spawning (requires robot feature)
use *;
use ;
use RobotBlueprint;
# ;
Module shapes (Box, Cylinder, Sphere, Capsule) are converted to both a Bevy
mesh and an Avian3D collider. JointType::Fixed, Hinge, Ball, and Prismatic map
to the corresponding Avian joints; Screw is approximated as Fixed with a warning
since Avian3D has no helical constraint. Hinge and prismatic joints pick the
AxisLimit whose axis aligns with the drive axis and install a motor with that
entry's effort/velocity. IMU and Touch sensors attach ImuSensor / TouchSensor
marker components.
Asset Loaders (requires asset-loader feature)
Load .lsys grammars and .matpalette.json files through Bevy's AssetServer:
use *;
use ;
new
.add_plugins
.add_plugins
.add_systems
.run;
#
The plugin registers LSystemSource (wrapping symbios::System) and
MaterialSettingsSource (wrapping HashMap<u16, MaterialSettings>) plus their loaders.
For runtime hot-reload, enable Bevy's file_watcher feature in your consuming crate
and listen for AssetEvent::Modified — see examples/hot_reload.rs
and the assets/ sample files.
For one-shot parsing without the asset pipeline, the pure parsers are also exported:
loader::parse_lsys_source(&str) and loader::parse_material_settings(&[u8]).
Export
The export module converts meshes to OBJ and GLB (binary glTF) formats:
use ;
// OBJ string from a single mesh (vertex_offset=0 for standalone)
let obj_string = mesh_to_obj;
// Combined OBJ from all material buckets
let obj_combined = meshes_to_obj;
// GLB binary with embedded PBR materials
let glb_bytes = meshes_to_glb;
ExportFormat::{Obj, Glb} (with name() / extension() helpers) is provided for
UI / CLI selection. The GLB writer emits POSITION, NORMAL, COLOR_0, and indexed
primitives with one PBR material per bucket; vertex tangents and UVs are not yet
included in the GLB output.
API Reference
LSystemMeshBuilder
| Method | Description |
|---|---|
new() |
Create builder with default resolution (8) |
with_resolution(n) |
Set vertices per ring (clamped to 3..=128) |
build(&skeleton) |
Convert to HashMap<u16, Mesh> (one mesh per material ID) |
build_cached(&skeleton, &mut cache, meshes) |
Cache-aware variant returning HashMap<u16, Handle<Mesh>>; see [MeshCache] |
MeshCache
| Method / Free fn | Description |
|---|---|
new() / default() |
Empty cache |
len() / is_empty() |
Entry count |
contains(&skeleton, resolution) |
Probe a fingerprint without bumping counters |
hits() / misses() / reset_stats() |
Cumulative counters since construction or last reset |
clear() |
Drop all cached entries (counters preserved) |
get_or_insert_with(fingerprint, build) |
Lookup-or-build by explicit fingerprint |
compute_skeleton_fingerprint(&skel, res) |
Free function — derive the same fingerprint build_cached uses |
ColliderGenerator (requires physics feature)
| Method | Description |
|---|---|
new() |
Create generator with no filtering |
with_min_radius(r) |
Skip segments thinner than r |
build(&skeleton) |
Generate Option<Collider> (single compound collider) |
build_parts(&skeleton) |
Generate Vec<PositionedCollider> (individual segments) |
PositionedCollider
| Field | Type | Description |
|---|---|---|
transform |
Transform |
World-space position and rotation |
collider |
Collider |
Avian3D capsule (or sphere for short segments) |
radius |
f32 |
Average segment radius |
length |
f32 |
Segment length |
Mesh Attributes
Generated meshes include:
| Attribute | Description |
|---|---|
POSITION |
Vertex positions |
NORMAL |
Smooth normals |
COLOR |
RGBA vertex colors for local tinting (SkeletonPoint::color) |
UV_0 |
Texture coordinates (U: around tube, V: along strand, scaled by uv_scale) |
TANGENT |
Tangent vectors (auto-generated for normal mapping) |
License
MIT