Skip to main content

Crate bevy_carnage

Crate bevy_carnage 

Source
Expand description

§bevy_carnage

⚠️ Vibe Coded. Written by AI agents working from a human’s direction, covered by tests rather than a line-by-line human review. Read the code before you rely on it for something you care about.

Wounds that peel to the bone, bleed, pool and dry, and bodies that break into chunks, for Bevy characters, deterministic to the bit.

crates.io docs.rs CI Bevy 0.20 licence

A textured blocked-out body takes a blow, a burn, a slash and shots to the chest; the wounds bleed and pool on the floor and the body comes apartA smooth mannequin walks in place with its left arm raised; a shot opens the raised forearm, a slash the thigh, shots the chest, and each wound keeps bleeding from the limb it is on
examples/preset.rs: a blow, a burn, a slash and shots to the boneexamples/skinned.rs: the same on a skinned, animated body, the wounds riding the limbs

Status: usable · Bevy 0.20

Why: a fracture computed at the moment of impact costs a frame and a static pre-fracture cannot know where the shot landed, so this crate bakes each asset once into a hierarchy and a bond graph, and every blow is a cheap query against it, which is the shape PhysX Blast and Unreal’s Chaos arrived at too (why).

§Quick start

[dependencies]
bevy_carnage = "0.8"
use bevy::prelude::*;
use bevy_carnage::preset::{Gore, GoreHit, GorePlugin, GoreScene};

fn wire(app: &mut App) {
    app.add_plugins(GorePlugin);
}

// Dress a loaded character: every mesh under it is dressed by its node's name (`Head`, `Arm_L`).
fn dress(mut commands: Commands, character: Entity, floor: Entity) {
    commands.entity(character).insert(GoreScene::humanoid());
    commands.entity(floor).insert(Gore::floor());
}

// One message per injury, along your hit ray.
fn on_hit(mut hits: MessageWriter<GoreHit>, target: Entity, from: Vec3, dir: Vec3) {
    hits.write(GoreHit::impact(target, from, dir, 0.03, 12.0)); // a crater 3 cm wide, 12 mm deep
    hits.write(GoreHit::slash(target, from, dir, Vec3::X, 0.06, 8.0)); // a cut that gapes
}

Putting it on a real glTF character has a checklist: docs/preset.md. The full quick start, and baking a fracture by hand, are in the guide.

§Try it

cargo run --release --example preset

The left GIF above, with keys to aim it yourself; T cycles the violence setting.

The preset mid-scene: the body has come apart on the floor into chunks with raw red cut faces, lying in a pool of blood beside a standing sheet

A frame of preset after the last shot: the chunks, their cut faces, and the pool.

§What you can do

  • Hurt a character with one message: GoreHit::impact, blunt, burn and slash peel skin, fat and muscle to bone, bruise and burn under the skin, and cut wounds that gape; a wound has a pale lip and, from a hand’s width, a ragged margin round a knobbly floor that glints (0.8.7 and now); a shot sent through() leaves a round entrance and a torn, star-shaped exit (the pair, 0.8.3 and now): quick start.
  • Make wounds bleed where they are: a pulsing bleed runs downhill on the pose on screen, pools on the floor and dries from its edge in where it fell (a dark crust round a red middle, crazed once dry: the front, 0.8.4 and now), a wall’s spatter keeping every drop it landed as, on skinned, animated bodies too: docs/preset.md.
  • Let an artery spurt: GoreHit::arterial() makes the wound throw an arc of large drops on every heartbeat, flying the parabola to the walls and floor, each stain on a wall with its run and a stipple of fine drops beside it, from where the wound is and the way it faces as the body turns, until it clots (the clip, 0.8.9 beside now, the stills): docs/preset.md.
  • Cut meat that reads as meat: the muscle in a cut face and in a laceration’s bed is a cherry red divided by pale sheets between its bellies and by wandering marbling, drawn at the sizes the anatomy has them and tuned against measured beef and photographs of a shank, not a flat burgundy with a hairline net (0.8.10 beside now, the clip): docs/cross_section.md.
  • Cut faces that glisten: the cut faces of a body that has broken are no longer flat plates of one tone but wet, undulating surfaces whose clear coat catches glints along their folds, with the colour unchanged (0.8.12 beside now, the stills): docs/preset.md.
  • Burst a body: GoreBurst throws a body’s blood in every direction at once, a spray of 960 droplets on the one size-and-speed law a wound’s cone obeys, each flying the parabola to the first wall, floor, ceiling, cloth or body it meets, with the body shadowing what is behind it and the stains arriving over three quarters of a second at a bounded cost a tick (0.8.11 beside now, the stills): docs/preset.md.
  • Throw a spray that reads as drops: each droplet a wound throws lands as its own round stain at the size it is, on a floor, a wall or a skin, and holds still while it is wet: the gallery.
  • Drag a hand, a body or a sleeve through blood: GoreSmear lifts the wet blood a contact crosses and lays what it carries along its path in parallel streaks, heavy where it touched down and broken into lines as it runs dry, on a floor or a wall, the blood moved and none made (the clip, the stills): docs/preset.md.
  • Break a body into its own pieces: bake once, then shoot a shoulder and the arm comes off, with watertight cut faces and convex colliders, and the guts it spills lie soaked, lumpy and flattened in its pool, two dark organs among the loops (0.8.6 and now): the API.
  • Bore bullet channels that come out the far side: the hole and the gore it throws are the same subtraction: features.
  • Honour the player’s violence setting: GorePolicy swaps blood for sparks, or keeps blood and refuses the breaking, by substitution and never deletion: docs/preset.md.
  • Replay a fight exactly: every canvas is owned by the CPU and hashed, so two runs of a build print the same digest: determinism.
  • Reach the seven kernels underneath: blood rheology, wetmaps, viscera, cross-sections, flaying, laceration and fracture modes, all modules of this one crate: the family.

§Part of the Coven

bevy_carnage is one of the Bevy Coven crates. The Cauldron, the Coven’s editor, uses it for destructible props and its damage dials: use it in the Cauldron.

§Bevy compatibility

bevy_carnageBevy
0.6 – 0.80.20
0.50.19

§Learn more

§Who and licence

Maintained by @Ladvien. This repository is the source of truth: issues and pull requests belong at github.com/bevy-coven/bevy_carnage/issues, and how to contribute follows the org’s CONTRIBUTING.md.

MIT OR Apache-2.0, at your option.

Re-exports§

pub use crate::bloodstain::Appearance;
pub use crate::bloodstain::BACK_SPATTER_SPEED;
pub use crate::bloodstain::BLOOD_DENSITY;
pub use crate::bloodstain::BLOOD_SURFACE_TENSION;
pub use crate::bloodstain::Bleed;
pub use crate::bloodstain::BloodSettings;
pub use crate::bloodstain::Droplet;
pub use crate::bloodstain::FORWARD_SPATTER_SPEED;
pub use crate::bloodstain::Impact;
pub use crate::bloodstain::PatternClass;
pub use crate::bloodstain::Pool;
pub use crate::bloodstain::Stain;
pub use crate::bloodstain::StainShape;
pub use crate::bloodstain::WELD;
pub use crate::bloodstain::WoundKind;
pub use crate::bloodstain::absorb;
pub use crate::bloodstain::appearance;
pub use crate::bloodstain::area_of_origin;
pub use crate::bloodstain::droplet;
pub use crate::bloodstain::droplet;
pub use crate::bloodstain::droplet_count;
pub use crate::bloodstain::droplets;
pub use crate::bloodstain::flow;
pub use crate::bloodstain::flows;
pub use crate::bloodstain::hash_f32;
pub use crate::bloodstain::landing;
pub use crate::bloodstain::pick;
pub use crate::bloodstain::pulse_period;
pub use crate::bloodstain::pulse_wound;
pub use crate::bloodstain::pulses_on;
pub use crate::bloodstain::spread_pools;
pub use crate::bloodstain::viscosity;
pub use crate::bloodstain::wound_seed;
pub use crate::bloodstain::yield_stress;
pub use crate::bloodstain::stain::impact_at_plane;
pub use crate::bloodstain::stain::rasterise;
pub use crate::bloodstain::stain::stain_radius;
pub use crate::bloodstain::stain::stain_shape;
pub use crate::bloodstain::stain::stains;
pub use bloodstain as blood;

Modules§

bloodstain
The blood model’s own modules, re-exported so a caller can reach the parts that have no single-name entry point — bevy_carnage::blood::patterns::cast_off, blood::rheo::flows, blood::dry::dry_ticks. One path to each function, spelled the way the leaf spells it.
cross_section
The six kernels this crate composes, re-exported whole so a consumer needs one dependency line and cannot end up with two versions of a leaf. Each is reached by its own name — a cap’s bands from bevy_carnage::cross_section, a body’s peel from bevy_carnage::flaymap, a cut that gapes from bevy_carnage::laceration, the modes a blow is projected onto from bevy_carnage::fracture_modes, the texture-space blood a hit leaves from bevy_carnage::wetmap, and the strands a torso spills from bevy_carnage::viscera — and this crate’s own bridges are modal, FractureRegion and FragmentGeometry::annotate_cap. wetmap and viscera were dependencies without a door until 0.3.1; examples/entrails.rs and carnage_web.rs had to name them directly, which is the two-versions hazard this block exists to close.
flaymap
bevy_carnage::flaymap
flesh
Flesh as a material — the pixel half. Behind flesh (default on) because it embeds a shader and asks bevy for bevy_image; the deterministic half never needs either. Flesh, as a material. The pixel half of the stack: everything the CPU kernels compute is a byte in a canvas or a coordinate on a cap, and until 0.4.0 all of it was drawn by a plain StandardMaterial — flat red caps, matte stains, fat that reads as painted plastic. This module is one ExtendedMaterial over StandardMaterial that reads those same bytes and renders them the way tissue and blood actually scatter and reflect light, on the forward path, on WebGL2.
fracture_modes
bevy_carnage::fracture_modes
laceration
bevy_carnage::laceration
modal
Fracture modes over a bond graph — fracture_modes composed onto this crate’s own decomposition.
preset
The two-line inclusion: GorePlugin, Gore, GoreHit. Behind flesh, because it dresses every surface it touches in the flesh material. The two-line inclusion. GorePlugin, a Gore component on the meshes that can be hurt, and a GoreHit message when one is — everything else in this family is wired behind it.
skin
A skinned mesh, posed on the CPU — the shader’s blend of joint matrices, so a ray can be cast at the body as it is drawn rather than at its bind pose. The preset casts every hit through it. A skinned mesh, posed on the CPU.
viscera
bevy_carnage::viscera
wetmap
bevy_carnage::wetmap

Structs§

Bleeding
A bleeding thing, as an entity. The ECS half of Bleed.
Bond
The shared face between two neighbouring fragments.
BondGraph
Which fragments touch which, and over how much surface.
BondId
Index of one bond in a BondGraph.
BondSet
The caller’s accumulated damage state: which bonds have been severed so far.
Bore
A channel to subtract from the proxy — a bullet hole, a drill, a spear thrust.
CapFace
One cut face of a convex cell — the wound surface that travels with a fragment or a plug.
CarnagePlugin
Registers the fracture cache, its settings, the one-shot bake, and the Wounded message.
CarnageSettings
The carnage dials — how a wound bleeds, sprays, stains and hits, with nothing in it that decides when any of that happens.
CarnageSystems
The set bake_fractures runs in. Gate and order against this, not against the system.
CutSettings
The geometry dials for one bake, without the ECS sizing policy that chooses target.
DecalBudget
The live-decal ceiling, with canonical eviction.
DetachedChunk
The pruned DetachedPart, flung intact as a single tumbling chunk (it kept its own material). Baked in the same subject-local space as body fragments.
DetachedPart
Marks a subtree to be pruned out of the body and baked as one intact chunk, keeping its own material — a carried weapon, a hat, a backpack. The walk does not descend past it.
Ejecta
What a bore pushed out the far side — the channel’s own material, as a spawnable chunk.
EjectaChunk
A plug a bore pushed out, baked into mesh handles alongside the fragments.
FlashGate
WCAG’s safe harbour, as a hard ceiling. Three flashes in any one-second period.
Fracture
One bake: every node the cut loop produced as a solid, plus the hierarchy that says how they nest — and the drawn meshes for the nodes somebody actually asked for.
FractureBores
Channels bored through this subject’s proxy before it is cut — bullet holes, baked in.
FractureCache
Baked fracture data, keyed by the subject’s source scene asset id so multiple distinct subjects each get their own bake and swapping the asset needs zero code change.
FractureProxy
The caller’s convex decomposition of the subject, in subject-local space.
FractureRegion
Which body region this subject’s cut faces belong to — a limb, a torso or a head.
FractureSettings
How hard to break things. Twelve dials, all bake-time — nothing here decides how a chunk moves after it exists, because that is the caller’s physics, not this crate’s business.
FractureSubject
Marks an entity whose descendants should be pre-fractured, and names the asset to key that bake by. Put it on the root the scene hangs under; the bake walks Children from there.
Fragment
One baked body fragment, in subject-local units (render scale is applied at spawn). Both meshes are recentered to center_local (their shared bounding-box center), so a physics body placed at origin + center_local*scale with a half_extents*scale box collider lines up exactly with the rendered chunk. Either mesh may be None (a fragment with no cut faces has no cap; a pure-cap sliver has no outer skin).
FragmentGeometry
One fractured piece: a convex proxy cell, and the render surface that belongs to it.
FragmentId
Index of one fragment — a node in the FragmentTree and, equivalently, a position in the parallel fragment payload array (Vec<Fragment> or Vec<FragmentGeometry>).
FragmentSolid
Tier A for one node: the solid, with no drawn mesh. Cheap to produce for every node of the tree, which is why it is separate from the geometry — a bake keeps one of these per node and builds the FragmentGeometry only for the ids a caller actually asks to draw.
FragmentTree
The binary forest a bake produced: one tree per proxy cell, plus the frontier queries that read it at a chosen granularity.
GorePolicy
The framework’s one policy resource. What is drawn, how much of it, and what is refused.
JointGroup
The joints that make one piece, and the one that carries it.
JointLink
Two pieces that meet at a joint: indices into the groups slice, and the joint whose bind position is where they are bonded.
ProxyCell
A convex cell of the caller-supplied proxy: the volume Tier A actually cuts.
Reach
How strongly a region reached each bond it touched.
SkeletalFracture
What fracture_skeleton makes: the pieces, and the bonds between them as a graph over FragmentId(0)..FragmentId(pieces.len()).
SkeletalPiece
One piece of the skeleton: group i is piece i, id is FragmentId(i).
SolidAudit
What one finished fragment turned out to be.
SurfaceReport
What a fragment’s drawn surface turned out to be. Every field is a count to be recorded.
TreeNode
One node of the hierarchy. Roots are the caller’s proxy cells, unsplit.
Wound
A wound surface in subject-local space. Deterministic: derived only from baked geometry.
Wounded
A wound, in world space, announced. The crate’s one message.

Enums§

FaultPolicy
How the cut plane is chosen. One policy with a parameter, not a switch between two engines.
GoreTier
How much gore is drawn. Four stops, and they map onto rating descriptors rather than taste.
LoadingMode
How a subject was loaded when it broke.
SkeletalRefusal
Why a body could not be cut along its skeleton. Nothing is repaired or guessed.
TissueClass
What is breaking. Cortical bone, trabecular bone, or soft tissue.

Constants§

MAX_SAG
The flat-face sag a channel is allowed, subject-local, where a character is about 1.0 tall.
MAX_SIDES
Barrel faces beyond which the shard count is the cost and the roundness is not the benefit: 24 planes already read as smooth, and each one is a shard with its own two meshes.
MIN_ROUND_SIDES
The fewest faces sides_for will derive, as opposed to the fewest [prism] will accept.
TRABECULAR_MAX_PIECES
Ceiling on the fragment count for trabecular bone.
WCAG_FLASHES_PER_SECOND
The absolute ceiling on flashes per one-second window, whatever a policy says.

Functions§

audit_cell
Audit one convex cell as the closed solid it is.
audit_proxies
Audit every fragment of a fracture. Fragments that cannot be measured are warn!-skipped and omitted, so the returned length may be shorter than frags — deliberately, because padding the result with a fabricated clean audit is exactly the lie this module exists to stop telling.
audit_proxy
Audit a fragment as the solid it is — its proxy cell, every face, closed.
audit_render
Measure a fragment’s drawn surface — the subject’s own skin plus the cut face, welded.
bake_fractures
Once a subject’s whole scene has streamed in, bake its fracture set (and its detached chunk) exactly once per source. Walk the subject’s descendants, prune the DetachedPart subtree into a separate chunk, merge the rest into one soup in subject-local space, and fracture. Self-gates on all sub-meshes being present in Assets<Mesh>.
cap_faces
Every raw-interior face of a convex cell: fracture cut faces and bore channel walls.
capsule
A swung edge. Falloff by distance from the segment a → b to each shared face.
coalesce_hitstop
One stop per frame, not a sum.
directional
A pull. Radial falloff, weighted by how squarely each shared face meets direction.
fracture_mesh
The whole pipeline, with no assets and no ECS. Cut the caller’s convex proxy into at most target cells, carry the parts triangles along as a payload, and return each piece.
fracture_skeleton
Cut a skinned body along its skeleton, once.
grady_mott_target
How many fragments an impact should produce, from the energy — not from an artist constant.
hitstop_ticks
How many fixed ticks of hit-stop this wound deserves. Zero for a graze.
largest_cap
The widest raw-interior face of a cell — “the wound on this chunk”.
materialise_fragments
Build the Tier B meshes somebody asked for. Chained straight after bake_fractures, so a bake and a request that happen on the same frame are drawn on that frame — otherwise the first frame after a death would spawn nothing.
occludes_aim
Would this land on the reticle? true means refuse it.
radial
A blast. Falloff by straight-line distance from center to each shared face.
shake_offset
Camera offset for a given trauma at a given tick: trauma² magnitude, eased, along dir.
sides_for
The fewest barrel faces that keep a channel of this radius under MAX_SAG, in MIN_ROUND_SIDES..=MAX_SIDES.
spread
A projectile. Find the fragment nearest point, then spread outward along the bonds, with falloff on distance travelled through the object rather than through space.
swept_triangle
A swept blade. Every bond whose two fragments sit on opposite sides of the triangle a, b, c, and whose joining segment crosses inside it, gives way completely.
trauma_for
Trauma to add for a wound, in [0, 1] — the caller adds it to its own accumulator.
wound_from_ejecta
The wound a baked plug leaves behind — wound_of_channel for an EjectaChunk.
wound_of_channel
The wound a channel is, from the plug’s own geometry.
wounds_from_bonds
The wounds a set of already-broken bonds represents, at full severity.
wounds_from_reach
The wounds a blow opened: every bond it reached at or above threshold.