Expand description
bevy_flora: parametric procedural trees that grow over time, for Bevy.
Pipeline: TreeParams → generate_skeleton → Skeleton::aged →
wood mesh (mesh::branch::skeleton_mesh) + leaves
(foliage::collect_leaf_points / foliage::foliage_mesh) → MeshData
→ Bevy Mesh. build_tree runs the whole thing; FloraPlugin drives it
from the ProceduralTree / TreeAge / Growing components.
§Growing a tree in a Bevy app
use bevy::prelude::*;
use bevy_flora::{FloraPlugin, Growing, ProceduralTree, TreeAge, TreePreset};
fn main() {
App::new()
.add_plugins((DefaultPlugins, FloraPlugin))
.add_systems(Startup, setup)
.run();
}
fn setup(mut commands: Commands) {
commands.spawn((
ProceduralTree(TreePreset::Oak.params()),
TreeAge(0.0),
Growing::default(),
));
}§Growing a forest: GpuGrowth
Rebuilding a growing tree’s meshes costs the CPU 0.1-0.4 ms per tree per
frame. Add GpuGrowth and the tree is built once, at maturity, and grown in
the vertex shader instead (a few microseconds of CPU per tree per frame, 0.004
ms in the 0.4.0 measurements):
use bevy::prelude::*;
use bevy_flora::{FloraPlugin, GpuGrowth, Growing, ProceduralTree, TreeAge, TreePreset};
fn setup(mut commands: Commands) {
for i in 0..200 {
commands.spawn((
ProceduralTree(TreePreset::Oak.params()),
TreeAge(0.0),
Growing::default(),
GpuGrowth,
Transform::from_xyz((i % 20) as f32 * 8.0, 0.0, (i / 20) as f32 * 8.0),
));
}
}It needs a device with storage buffers in the vertex stage (everything but
WebGL2; see GpuGrowthSupport); elsewhere the trees grow on the CPU.
§Planting without a hitch: BuildInBackground
A tree is built in the frame it is spawned, and that frame waits for it (200 oaks: about
23 ms on an Apple M4 Pro). With BuildInBackground it is built on Bevy’s async compute
pool instead and appears a few frames later, the frame its build is done; the frame it was
spawned in costs well under a millisecond.
use bevy::prelude::*;
use bevy_flora::prelude::*;
fn plant(mut commands: Commands) {
for i in 0..200 {
let at = Vec3::new((i % 20) as f32 * 8.0, 0.0, (i / 20) as f32 * 8.0);
commands.spawn((ProceduralTree(TreePreset::Oak.params()), BuildInBackground, Transform::from_translation(at)));
}
}A forest’s pools build off the main thread too: BackgroundPools hands back a pool’s handle
at once and adds the pool a few frames later (an eight-variant pool takes 0.5 to 14 ms on all
of an Apple M4 Pro’s cores, longer on fewer cores and about 0.3 s for every preset on wasm).
§Distant trees for less: TreeLod
A ProceduralTree is drawn in full at every distance. With TreeLod it is drawn as a
pool’s trees are: in full up close, then at the two coarser levels of build_lods (from 30
and 80 m by default), cross-fading. 1 000 individual oaks draw at 6.5 ms a frame with it and
25 ms without (1080p, Apple M4 Pro). They stay individual trees, which grow, change and fall;
a growing one rebuilds its coarse levels with every new age, which costs the CPU about 40%
more.
use bevy::prelude::*;
use bevy_flora::prelude::*;
fn plant(mut commands: Commands) {
commands.spawn((ProceduralTree(TreePreset::Oak.params()), TreeLod::default(), Transform::default()));
}§Drawing a forest: TreePool
Every ProceduralTree has meshes of its own, which a GPU draws a few thousand
of. For a forest build a TreePool (a few seed variants of one tree, each at
three levels of detail from build_lods) and place PooledTrees from it:
trees that share a mesh are drawn in one instanced call, the distance to the
camera picks the level, and beyond the last one a tree is a baked camera-facing
quad (ImpostorAtlas), shaded by the scene’s own lights. 100 000 oaks draw at
7.6 ms a frame (1080p, Apple M4 Pro).
Pooled trees are static; add FloraPlugin after DefaultPlugins to get the
impostors. See pool for an example.
Scatter says where the trees of a forest stand: trunks at least a spacing apart and
otherwise irregular (Poisson-disc sampling), species in groves with bending borders, every
tree its own yaw, size and variant, and a density per spot for clearings and roads. It is
pure CPU and deterministic per seed; see scatter.
On terrain, FloraGround is the height of your ground: ScatterPoint::transform_on (or
FloraGround::footing with TreeParams::base_radius) stands a tree where its trunk meets
a slope all round instead of floating on its downhill side, and as a resource it is what
felled trees are cut above and land on; see ground.
§A world that is not in metres: FloraUnits
Trees are built in metres and a game scales them to its world with a Transform. If the world’s
unit is not the metre (a foot, say), insert FloraUnits with the number of world units to a
metre, and everything the crate measures in the world follows it: how fast a tree falls and a
leaf drifts, how wide a gust is, how far a fire reaches, the pusher’s size, the see-through
radius and the distances the levels of detail change at. Fields documented as metres are
scaled by it; positions and sizes that address the world are in world units; see units.
use bevy::prelude::*;
use bevy_flora::FloraUnits;
fn setup(mut commands: Commands) {
commands.insert_resource(FloraUnits { units_per_metre: 3.2808 });
}§Leaf shading and wind
The plugin draws trees with FloraMaterial: a darker, glossier upper side of
every leaf, a paler lower side, veins and light through a leaf lit from behind
(LeafShading), and wind. The FloraWind resource sets the weather (a
GustFront sweeps a band of stronger wind across the world,
FloraWind::launch_gust) and TreeParams::wind (WindResponse) how each
species answers it.
use bevy::prelude::*;
use bevy_flora::{FloraWind, GustFront};
// Call this once, when the gust begins (a system run on an event or a timer), not
// every frame: launching restarts the front at its origin.
fn start_storm(mut wind: ResMut<FloraWind>, time: Res<Time>) {
wind.strength = 0.6;
wind.launch_gust(GustFront { speed: 20.0, ..default() }, &time);
}§Bark
Wood wears a bundled CC0 bark scan (BarkScan, chosen per species by the presets in
TreeParams::bark). The scan is laid out in metres on the mature tree, so a growing tree’s
bark never slides, and BarkParams fades it in as a branch thickens: smooth wood below
smooth_below metres of radius, the whole scan above fissured_above. The scan’s average
colour is divided out of the base colour, so a textured tree keeps its preset colour. The
images are compiled in by the default textures feature (about 3.6 MB with the leaf scans);
without it every scan is absent and wood is plain colour.
§Leaf scans and grown veins
A FoliageParams::scan (LeafScan) cuts leaf cards to the real outline of a bundled leaf
scan (one of four leaves of an atlas, by a hash of the leaf’s own random draws) and the leaf
shader wears the atlas’s colour and normal map on them. Leaves without a scan keep the
procedural outline and get veins grown by space colonisation (venation), drawn from one
small image every such leaf shares. Both need the textures feature only for the scans: the
veins are grown at run time.
§Low-poly conifers: hull crowns and prism trunks
TreeParams::crown (CrownStyle) can draw the crown as one faceted hull instead of leaf
cards: CrownStyle::Hull { tiers, sides, lift, jitter, trunk_only } wraps a stack of cones round the leaf
cloud (crown_hull), tiers * sides * 2 flat triangles that read as a few big planes.
TreeParams::trunk_style (TrunkStyle) meshes the wood as flat-shaded prisms. Both work
through every way a tree is drawn (pools, levels of detail, impostors, felling, fire, snow) but
GpuGrowth, which builds such a tree on the CPU (TreeParams::gpu_growable).
§Looks: natural, painterly, toon
TreeParams::style (RenderStyle) changes how a tree is lit, not its shape. Painterly
gives every leaf the normal that points out of the centre of its own clump (the leaves of a
primary branch, the leader or a split) rather than out of the crown’s, so a crown reads as
rounded masses with a lit and a shaded side each. Toon keeps those normals and cuts the
light of every directional light into three bands, with an ink rim toward the silhouette. The
pools, the impostors and GpuGrowth draw them all.
§Seasons
The FloraSeason resource turns deciduous trees (FoliageParams::autumn, set on the
oak, willow, birch, bonsai, maple, poplar, baobab, Japanese maple, cherry and apple presets) to
their autumn colour, sheds their leaves one leaf at a time (evergreens keep theirs), lays snow
on every tree, on what faces up, and brings flowering trees (FoliageParams::blossom, the
cherry and the apple) into blossom, one flower at a time. A fruiting tree’s fruit
(FoliageParams::fruit, the apple’s) hangs below some of its leaves all summer and drops in
the first part of the leaf fall. It shares the wind’s buffer, so a season that changes every
frame costs no material change and nothing per tree. As the leaf fall rises, the leaves it
sheds from trees near a camera fall to the ground (FallingLeaves).
use bevy::prelude::*;
use bevy_flora::FloraSeason;
// A year every two minutes.
fn seasons(time: Res<Time>, mut season: ResMut<FloraSeason>) {
*season = FloraSeason::of_year(time.elapsed_secs() / 120.0);
}FloraWetness is how wet the rain has made the trees, 0 (dry) to 1 (soaked): wet bark
darkens and turns glossy and the leaves’ green grows richer, on every way a tree is drawn, for
one value written to the same buffer.
§Trees that react
TreeShakes swings the trees near a point (an axe on a trunk, a character pushing through,
a blast): TreeShakes::shake a TreeShake, and every tree within its radius swings away
and back, dying away, while its leaves rustle, on every way a tree is drawn. Like the wind it
lives in the shared buffer: no material changes, and nothing per tree while nothing shakes.
use bevy::prelude::*;
use bevy_flora::{TreeShake, TreeShakes};
// An axe on the tree standing at the origin.
fn chop(mut shakes: ResMut<TreeShakes>, time: Res<Time>) {
shakes.shake(TreeShake { at: Vec2::ZERO, radius: 1.0, strength: 2.0, ..default() }, &time);
}A shake knocks leaves off the trees it reaches, and a felled tree sheds some as it lands: they
fall, tumbling and swinging, drift with the wind, lie flat on the ground for a while and shrink
away, all on the GPU, in the colours the tree’s leaves wear this season. FallingLeaves sets
how many; DropLeaves drops some from any tree; see falling.
use bevy::prelude::*;
use bevy_flora::DropLeaves;
// A bird lands in the tree.
fn bird_lands(mut drops: MessageWriter<DropLeaves>, tree: Entity) {
drops.write(DropLeaves { tree, count: 6 });
}Insert FellTree on a tree and it is cut: a TreeStump stays, and the tree falls about
the hinge of the cut, lands on its limbs (TreeLanded) and lies still (Felled). Pooled
and GPU-grown trees can be felled too, and BuckTree then cuts a felled tree’s trunk into
TreeLogs lying on the ground; the felling module has the details.
use bevy::prelude::*;
use bevy_flora::FellTree;
// Fell the tree toward +x.
fn fell(mut commands: Commands, tree: Entity) {
commands.entity(tree).insert(FellTree { direction: Vec2::X, ..default() });
}Before the saw, the axe: a TreeNotch cuts a wedge out of a standing trunk, deeper with each
change of its depth, and a notched tree is felled at the notch’s floor.
use bevy::prelude::*;
use bevy_flora::{FellTree, TreeNotch};
// One blow of the axe on the tree's +x side; the fifth fells it that way.
fn chop(mut commands: Commands, mut notches: Query<&mut TreeNotch>, tree: Entity) {
let Ok(mut notch) = notches.get_mut(tree) else {
commands.entity(tree).insert(TreeNotch { direction: Vec2::X, depth: 0.06, ..default() });
return;
};
notch.depth += 0.06;
if notch.depth >= 0.29 {
commands.entity(tree).insert(FellTree { direction: notch.direction, ..default() });
}
}§Picking fruit
A fruiting tree (FoliageParams::fruit, the apple) drawn as a ProceduralTree keeps a
TreeFruit: TreeFruit::nearest finds the fruit nearest a hand and TreeFruit::pick
takes it off the tree until the next blossom. A shake knocks some of a tree’s fruit loose to
fall and lie on the ground as FallenFruit entities, for the game to pick up
(FallingFruit, FruitFell). See harvest.
§Fire
A FloraFire is a grid of cells over the game’s world, each with how charred it is and how
hot it burns, that the plugin spreads a fire through from where the game lights it
(FloraFire::ignite): through the trees, faster downwind, stopped where nothing burns. The
trees it reaches scorch and lose their leaves, and their wood chars from the ground up with
glowing seams, on every way a tree is drawn; FloraFire::char_at and
FloraFire::heat_at read it back for the game, and each tree it burns through is reported
once (TreeBurnt) and marked Burnt. While no FloraFire exists no shader carries the
fire; see fire.
use bevy::prelude::*;
use bevy_flora::FloraFire;
// A fire over a 256 m square, lit by a lightning strike.
fn setup(mut commands: Commands) {
let mut fire = FloraFire::new(Vec2::ZERO, 256.0);
fire.ignite(Vec3::new(20.0, 0.0, 5.0), 2.0);
commands.insert_resource(fire);
}§Trees from assets and files
TreeParams is an asset: a ProceduralTreeAsset draws a tree from a handle and follows
every change to the asset, so trees that share one are edited together. With the ron
feature the plugin loads .tree.ron files (TreeParams::to_ron writes one), and with the
file_watcher feature (Bevy’s) a saved file rebuilds its trees while the game runs. See
tree_asset.
§Gameplay shapes
A TreeShape on a tree (opt-in: insert TreeShape::default()) is kept up to date by the
plugin: capsules up the trunk and along every limb thicker than its limb_radius, and a box
around the crown, in the tree’s local space, for colliders, picking or line of sight.
TreeMature is sent once when a Growing tree reaches maturity. The prelude has the
types a game usually needs.
§Finding trees
The FloraIndex resource holds every standing tree, pooled ones included, as its trunk,
by where it stands, kept up to date by the plugin: FloraIndex::nearest a point,
FloraIndex::within a radius, and FloraIndex::cast_ray for the trunk a ray meets (an
axe’s swing, an arrow, the player’s look). A query costs the trees near it, not all of them.
use bevy::prelude::*;
use bevy_flora::{FloraIndex, TreeShake, TreeShakes};
// A blast at `at` swings the trees around it; the nearest one is the woodcutter's next.
fn blast(index: Res<FloraIndex>, mut shakes: ResMut<TreeShakes>, time: Res<Time>) {
let at = Vec2::new(10.0, -4.0);
if index.within(at, 8.0).next().is_some() {
shakes.shake(TreeShake { at, radius: 8.0, ..default() }, &time);
}
let next = index.nearest(at, 30.0).map(|tree| tree.entity);
info!("the nearest tree: {next:?}");
}§Sick and dying trees
A game marks a tree with TreeCondition, a component on the tree’s entity (ProceduralTree
or PooledTree), and reads it back through the index: every IndexedTree says how well it
is. A tree without the component is healthy. The condition is the tree’s own, so a pooled tree
keeps it through a change of variant or a reload of its pool; a felled tree leaves the index and
keeps the component. The tree is drawn in its condition: a sick crown has lost about a third of
its leaves and what is left is half way to dry, a dying one four fifths of them
(TreeCondition::defoliation, TreeCondition::withering); wood, fruit and flowers are as
they were.
use bevy::prelude::*;
use bevy_flora::{FloraIndex, TreeCondition};
// A blight takes the trees near `centre`; later the sick ones near it are counted.
fn blight(mut commands: Commands, index: Res<FloraIndex>) {
let centre = Vec2::new(10.0, -4.0);
for tree in index.within(centre, 12.0).filter(|tree| !tree.undergrowth) {
commands.entity(tree.entity).insert(TreeCondition::Sick);
}
let sick = index.within(centre, 12.0).filter(|tree| tree.condition >= TreeCondition::Sick).count();
info!("{sick} trees near the blight are sick or dying");
}§Saving and loading
A tree is its parameters and seed; a save holds what the game did to it. On load,
FellTree::lying lays a felled tree down at once where it came to rest (no fall, no
landing), TreeFruit::with_picked keeps its picked fruit off it (TreeFruit::picked gave
the keys), and FloraFire::restore puts a fire’s grid back as FloraFire::cells gave it,
the cells still burning burning on.
§Seeing the player through the trees
SeeThroughTrees on a third-person or top-down camera dithers out the parts of trees between
it and its target (the player), in the camera’s main pass and its prepasses alike, while
shadow maps and other cameras keep the whole trees. Nothing carries the fade while no camera
has it.
§Deadfall on the forest floor
A pool built with build_pool_with_deadfall also holds each variant cut as FellTree and
BuckTree would cut it, and a PooledDeadfall (a stump, a tree lying where it fell or
bucked logs) is placed from it for the cost of a pooled tree; see deadfall.
§Walking through undergrowth
A FoliagePusher on the player, an NPC or a cart pushes the leaves and thin stems it walks
through out of its way, shadows included, and they close up behind it; trunks stay put. Nothing
carries the push while no entity has one; see push.
§Building meshes without Bevy’s ECS
use bevy_flora::{TreeMeshes, TreePreset, build_tree};
let params = TreePreset::Oak.params();
let meshes: TreeMeshes = build_tree(¶ms, f32::INFINITY);
assert!(!meshes.trunk.is_empty());Re-exports§
pub use background::BuildInBackground;pub use crown::CrownShape;pub use deadfall::Deadfall;pub use deadfall::DeadfallKind;pub use deadfall::DeadfallSettings;pub use deadfall::IntoDeadfall;pub use deadfall::PoolLog;pub use deadfall::PooledDeadfall;pub use falling::DropLeaves;pub use falling::FallingLeaves;pub use falling::LeafBurst;pub use felling::BuckTree;pub use felling::FellTree;pub use felling::Felled;pub use felling::TreeBucked;pub use felling::TreeLanded;pub use felling::TreeLog;pub use felling::TreeNotch;pub use felling::TreeStump;pub use fire::BURNT;pub use fire::Burnt;pub use fire::FireCell;pub use fire::FireSnapshot;pub use fire::FireSpot;pub use fire::FireSpread;pub use fire::FloraFire;pub use fire::TreeBurnt;pub use foliage::LeafPoint;pub use ground::FloraGround;pub use growth::GrowthParams;pub use harvest::FallenFruit;pub use harvest::FallingFruit;pub use harvest::Fruit;pub use harvest::FruitFell;pub use harvest::TreeFruit;pub use condition::TreeCondition;pub use index::FloraIndex;pub use index::IndexedTree;pub use index::Obstacle;pub use index::ObstacleKind;pub use index::TreeHit;pub use impostor::IMPOSTOR_VIEWS;pub use impostor::ImpostorAtlas;pub use impostor::ImpostorMaterial;pub use impostor::ImpostorTint;pub use lod::LodDistances;pub use lod::LodSettings;pub use lod::TreeLod;pub use lod::build_lods;pub use mesh::MeshData;pub use params::BarkParams;pub use params::BarkScan;pub use params::BranchParams;pub use params::CrownStyle;pub use params::FoliageParams;pub use params::FoliagePlacement;pub use params::FruitParams;pub use params::LeafOrientation;pub use params::LeafScan;pub use params::LeafShading;pub use params::LeafStyle;pub use params::MeshParams;pub use params::RenderStyle;pub use params::SplitParams;pub use params::StrandParams;pub use params::TreeParams;pub use params::TrunkParams;pub use params::TrunkStyle;pub use params::TrunkTermination;pub use params::WindResponse;pub use pipeline::TreeMeshes;pub use pipeline::build_tree;pub use plugin::FloraPlugin;pub use plugin::Growing;pub use plugin::ProceduralTree;pub use plugin::TreeAge;pub use plugin::TreeGenerated;pub use plugin::TreeMature;pub use plugin::TreeRender;pub use pool::BackgroundPools;pub use pool::LodScale;pub use pool::PoolColors;pub use pool::PoolMaterials;pub use pool::PoolVariant;pub use pool::PooledLook;pub use pool::PooledTree;pub use pool::RecolorPool;pub use pool::TreePool;pub use pool::build_pool;pub use pool::build_pool_with_deadfall;pub use recolor::FromPool;pub use recolor::RecolorTree;pub use presets::TreePreset;pub use rng::SeededRng;pub use scatter::Scatter;pub use scatter::ScatterPoint;pub use scatter::poisson_disc;pub use see_through::SeeThroughTrees;pub use shadows::FloraShadows;pub use push::FoliagePusher;pub use push::MAX_PUSHERS;pub use shading::FloraExtension;pub use shading::FloraKey;pub use shading::FloraMaterial;pub use shading::FloraMaterialSupport;pub use shading::FloraSeason;pub use shading::FloraUniform;pub use shading::FloraWetness;pub use shading::FloraWind;pub use shading::FloraWindBuffer;pub use shading::GustFront;pub use shading::TreeShake;pub use shading::TreeShakes;pub use shape::TreeShape;pub use shape::WoodCapsule;pub use tree_asset::ProceduralTreeAsset;pub use tree_asset::TreeFileError;pub use tree_asset::TreeParamsLoader;pub use units::FloraUnits;pub use skeleton::Node;pub use skeleton::NodeKind;pub use skeleton::Skeleton;pub use skeleton::generate_skeleton;
Modules§
- background
- Building trees off the main thread:
BuildInBackground. - condition
- How well a tree is:
TreeCondition, a fact about one tree that a game sets, reads back and sees. - crown
- Crown envelope: modulates primary branch length by height within the crown.
- crown_
hull - The faceted crown of a stylised conifer (
CrownStyle::Hull): a stack of cones wrapped round a tree’s leaf cloud. Pure CPU, depends only onbevy::math. - deadfall
- Pooled deadfall: the stumps, fallen trees and bucked logs of a forest floor, drawn from a
TreePoolas its trees are, so thousands of them cost a handful of draws. - falling
- Leaves that fall from the trees: knocked off by a shake (
TreeShakes), shed by a felled tree as it lands (TreeLanded), let go by the season (FloraSeason::leaf_fall), or dropped on demand (DropLeaves). - felling
- Felling: a cut tree falls, lands with a bounce and leaves a stump.
- fire
- Fire: trees that scorch, burn and stand charred (
FloraFire). - foliage
- Leaf placement on the (aged) skeleton and leaf geometry.
- ground
- The ground trees stand on, for a world that is not flat.
- growth
- Continuous age model.
- harvest
- Picking a tree’s fruit, and fruit a shake knocks loose.
- impostor
- Impostors: a far tree drawn as one camera-facing quad.
- index
- Finding trees:
FloraIndexholds every standing tree by where it stands, for a game to ask which trees are near a point (the player’s axe, a blast, a fire) or which trunk a ray from the camera or a weapon hits. - lod
- Levels of detail built from the skeleton, not by simplifying a mesh.
- mesh
- CPU-side mesh buffers and the meshers that fill them.
- params
- All tunable parameters of a tree, grouped by subsystem.
- pipeline
- One call from parameters + age to CPU meshes.
- plugin
- Bevy integration: components, the
FloraPlugin, and the systems that grow trees and rebuild their meshes. - pool
- Forests: many trees drawn from a few built ones.
- prelude
- What a game usually needs, in one
use bevy_flora::prelude::*;: the plugin, the tree, pool and growth components, trees from assets, the presets and parameters, the wind, the season, the shakes, felling, the ground, the gameplay shape and the forest scatter. - presets
- Built-in tree presets.
- push
- Foliage that parts around a body moving through it:
FoliagePusher. - recolor
- Recolouring trees that are not pooled.
- rng
- Deterministic xorshift64 RNG with splitmix64 seeding.
- scatter
- Where the trees of a forest stand: trunks never closer than a spacing and otherwise
irregular (Poisson-disc sampling, Bridson 2007), species growing in groves instead of salt and
pepper, and every tree its own yaw, size and pool variant. Pure CPU and deterministic: the
same
Scattergives the same forest on every run. - see_
through - Seeing what the camera follows through the trees in front of it:
SeeThroughTrees. - shading
- Leaf shading and wind: the material trees are drawn with.
- shadows
- Shadows near the camera only:
FloraShadows. - shape
- A tree’s simple gameplay shape: capsules for its trunk and thick limbs, and a box around its leaves, in the tree’s local space, made from the same skeleton its meshes are.
- skeleton
- Tree skeleton: an explicit node tree (trunk, primaries, splits, sub-branches)
generated deterministically from
TreeParams. - tree_
asset - Trees from assets:
TreeParamsis anAsset, and aProceduralTreeAssetdraws a tree from a handle to one. - units
- The world’s unit: how many world units make a metre (
FloraUnits). - venation
- Leaf veins by open space colonisation (Runions, Fuhrer, Lane, Prusinkiewicz, Hammel and Mech, “Modeling and visualization of leaf venation patterns”, SIGGRAPH 2005, section 4).
Structs§
- GpuGrowth
- Grow this tree in the vertex shader instead of rebuilding its meshes every
frame. Add it to an entity that has
ProceduralTreeandTreeAge(and, to make the tree grow,Growing). - GpuGrowth
Support - Whether this device can grow trees in the vertex shader.
FloraPluginsets it once the renderer is ready, from the device’s limits:truewhere the vertex stage can read a storage buffer,falseon WebGL2. While it isfalse, trees withGpuGrowthgrow on the CPU. Insert it yourself only where there is no renderer (a headless test). - Growth
Extension - The part of
GrowthMaterialthat moves vertices: it binds the tree’s per-node state, which the vertex shader reads (and writes nothing back), and the uniform of the flora shaders.
Type Aliases§
- Growth
Material StandardMaterialwith the vertex stage of a tree that grows on the GPU, and the leaf shading and wind ofFloraMaterial. The wood and leaves of a tree withGpuGrowthuse it.