pub struct Shape(/* private fields */);Expand description
A collision shape that bodies are created from.
Owns one Jolt reference. Every body created from it holds its own reference, so the shape
may be dropped while bodies use it. Jolt shapes cannot change after construction, so one
shape may serve any number of bodies in any number of worlds. To change a compound at run
time, edit a MutableCompound and install the shapes it publishes.
Implementations§
Source§impl Shape
impl Shape
Sourcepub fn new_box_with_material(
half_extent: Vec3,
convex_radius: f32,
material: &PhysicsMaterial,
) -> Result<Self, ShapeError>
pub fn new_box_with_material( half_extent: Vec3, convex_radius: f32, material: &PhysicsMaterial, ) -> Result<Self, ShapeError>
new_box_with_convex_radius made of material;
the same rules apply.
Sourcepub fn new_sphere_with_material(
radius: f32,
material: &PhysicsMaterial,
) -> Result<Self, ShapeError>
pub fn new_sphere_with_material( radius: f32, material: &PhysicsMaterial, ) -> Result<Self, ShapeError>
new_sphere made of material; the same rules apply.
Sourcepub fn new_capsule_with_material(
half_height_of_cylinder: f32,
radius: f32,
material: &PhysicsMaterial,
) -> Result<Self, ShapeError>
pub fn new_capsule_with_material( half_height_of_cylinder: f32, radius: f32, material: &PhysicsMaterial, ) -> Result<Self, ShapeError>
new_capsule made of material; the same rules apply.
Sourcepub fn new_cylinder_with_material(
half_height: f32,
radius: f32,
convex_radius: f32,
material: &PhysicsMaterial,
) -> Result<Self, ShapeError>
pub fn new_cylinder_with_material( half_height: f32, radius: f32, convex_radius: f32, material: &PhysicsMaterial, ) -> Result<Self, ShapeError>
new_cylinder_with_convex_radius made of
material; the same rules apply.
Source§impl Shape
impl Shape
Sourcepub fn collide_point(&self, point: Vec3) -> Result<Vec<SubShapeId>, QueryError>
pub fn collide_point(&self, point: Vec3) -> Result<Vec<SubShapeId>, QueryError>
The sub-shape ids of every leaf of this shape that contains point, given in the shape’s
own frame (the frame a body puts at its position), sorted; empty when none does.
What “contains” means is Jolt’s per shape (Shape::CollidePoint):
- box, sphere: the solid shape, boundary included (a box’s convex radius is ignored);
- capsule, cylinder, tapered cylinder, convex hull: the solid shape (sharp edges for cylinders and hulls); points within about 1e-4 m of the surface may go either way;
- tapered capsule: within about 1e-4 m of the rounded shape;
- mesh: the point lies in the shape’s bounds and a ray from it along +Y crosses an odd number of triangles (the id is the last triangle crossed). Jolt does not check that the mesh is closed: an open mesh reports points whose ray happens to cross an odd number of triangles. A ray through an edge or a vertex counts every triangle that meets there, so such a point can come out on the wrong side even for a closed mesh: the centre of a cube mesh whose top face is split along a diagonal is outside. Every other point of a closed mesh comes out as the mesh encloses it;
- heightfield: never;
- plane: strictly behind the plane (
normal · p + constant < 0), anywhere, also beyond the half extent, unlike a ray, which counts the plane itself as solid; - compound: each child whose bounds contain the point is asked (so a plane child is only found within its bounds); scaled, rotated-translated and offset shapes ask their inner shape.
Each component of point must be finite and at most limits::MAX_SHAPE_EXTENT in
absolute value; otherwise QueryError::InvalidValue is returned.
Source§impl Shape
impl Shape
Sourcepub fn save_binary_state(&self) -> Result<Vec<u8>, ShapeError>
pub fn save_binary_state(&self) -> Result<Vec<u8>, ShapeError>
Saves the shape with its children and materials to bytes that
restore_binary_state turns back into an equal shape (Jolt’s
cooked shape data, Shape::SaveBinaryState per shape). Build large meshes once, at
asset-build time, save them, and restore them when a level loads: restoring copies Jolt’s
built data instead of building it again.
Every shape kind this crate builds is supported. A child or material shared by several
parents is saved once and shared again after restoring; a mesh keeps its
MeshSettings::max_convex_extent, compound
children their user data, and PhysicsMaterials their user data.
Two saves of equal shapes give equal bytes. The bytes start with a header that names this
build (format version, Jolt and joltc commits, precision, determinism mode, byte order)
and a checksum; the layout is in docs/shape-cooking.md.
§Errors
ShapeError::BinaryState with BinaryStateError::Rejected when joltc refuses a shape
or material kind it cannot save; shapes and materials made by this crate are all
supported.
§Example
use oxijolt::prelude::math::Vec3;
use oxijolt::Shape;
let crate_box = Shape::new_box(Vec3::new(0.5, 0.5, 0.5))?;
let bytes = crate_box.save_binary_state()?;
// SAFETY: the bytes were saved just above by this build and not changed.
let restored = unsafe { Shape::restore_binary_state(&bytes) }?;
assert_eq!(restored.save_binary_state()?, bytes);Sourcepub unsafe fn restore_binary_state(bytes: &[u8]) -> Result<Shape, ShapeError>
pub unsafe fn restore_binary_state(bytes: &[u8]) -> Result<Shape, ShapeError>
Restores a shape from bytes written by save_binary_state.
The header must name this build, and the checksum must match, before anything reaches
Jolt. joltc then checks each record’s envelope (type, lengths, child and material
indices) before Jolt reads the record, and the record’s child and material counts after
Jolt read it, before they are attached. After restoring, the shape’s local bounds must
lie within limits::MAX_SHAPE_EXTENT and a compound
must fit Jolt’s sub-shape ids and
limits::MAX_EXPANDED_SUB_SHAPES, the rules
Shape::new_compound applies.
§Safety
bytes are the unchanged output of save_binary_state of a
build with the same build id, in any process; they may have been stored or sent on the
way. The header, the checksum and joltc’s record checks refuse bytes of another build,
truncated bytes and most damage with an error, but passing them does not make other bytes
valid: Jolt does not validate the inside of its own records (array lengths, mesh tree
offsets, hull indices), so changed bytes that pass, whether damaged or crafted, can make
Jolt read and write out of bounds. Do not restore bytes from a source you do not trust,
such as another player.
§Errors
ShapeError::BinaryState with:
BinaryStateError::NotBinaryStatewhen the magic does not match;BinaryStateError::OtherBuildwhen the format version, flags or build id differ;BinaryStateError::Truncatedwhen the bytes end before the header or the payload;BinaryStateError::Corruptwhen the checksum does not match;BinaryStateError::Malformedfor bytes after the payload or a restored shape that breaks a rule above;BinaryStateError::Rejectedwhen joltc refuses the record structure.
ShapeError::InitFailed when Jolt could not be initialised.
Source§impl Shape
impl Shape
Sourcepub fn new_convex_hull(points: &[Vec3]) -> Result<Self, ShapeError>
pub fn new_convex_hull(points: &[Vec3]) -> Result<Self, ShapeError>
The convex hull of points (shape space, metres) with Jolt’s default convex radius,
0.05 m; otherwise as
new_convex_hull_with_convex_radius.
Sourcepub fn new_convex_hull_with_convex_radius(
points: &[Vec3],
convex_radius: f32,
) -> Result<Self, ShapeError>
pub fn new_convex_hull_with_convex_radius( points: &[Vec3], convex_radius: f32, ) -> Result<Self, ShapeError>
The convex hull of points (shape space, metres) with a convex radius in metres.
Needs at least 4 points (ConvexHullError::TooFewPoints), each finite with every
component at most limits::MAX_SHAPE_EXTENT in absolute value, and a convex radius that
is finite and not negative (ShapeError::InvalidValue). Points on or close to a line or
in one spot are refused as ConvexHullError::Degenerate, points on or close to a plane as
ConvexHullError::Coplanar: a flat hull has no volume, so Jolt would give a dynamic body
made of it zero mass and a meaningless inertia, and Jolt’s single-precision hull builder
cannot build very thin needles and slabs reliably. “Close” grows with the cloud’s length and
its distance from the shape origin; docs/limits.md#convex-hulls gives the rules. Use a
mesh, a capsule or a thin box instead. Whatever else Jolt’s hull builder refuses comes back
as ShapeError::Rejected.
With the asserts feature Jolt’s hull builder can abort the process on some clouds with
many nearly coplanar faces: densely sampled faces a few coplanar distances off their
planes, dense flat cones and domes. Without it Jolt refuses most of them
(ShapeError::Rejected) and builds a hull from the rest; see
docs/limits.md#clouds-the-hull-builder-asserts-on.
Jolt keeps at most 256 vertices of the hull and drops the points inside it. It shrinks the
hull by the convex radius and inflates it again, reducing the radius where the hull is too
small for it; contacts and shape casts use at most 0.05 m of it, ray casts see the hull
without it. The hull’s centre of mass becomes its shape-space reference for bodies, and
its local bounds, relative to that centre, must lie within
limits::MAX_SHAPE_EXTENT. See docs/limits.md#convex-hulls.
Sourcepub fn new_convex_hull_with_material(
points: &[Vec3],
convex_radius: f32,
material: &PhysicsMaterial,
) -> Result<Self, ShapeError>
pub fn new_convex_hull_with_material( points: &[Vec3], convex_radius: f32, material: &PhysicsMaterial, ) -> Result<Self, ShapeError>
new_convex_hull_with_convex_radius made of
material; the same rules apply.
Source§impl Shape
impl Shape
Sourcepub fn new_mesh(
vertices: &[Vec3],
triangles: &[[u32; 3]],
) -> Result<(Self, DroppedTriangles), ShapeError>
pub fn new_mesh( vertices: &[Vec3], triangles: &[[u32; 3]], ) -> Result<(Self, DroppedTriangles), ShapeError>
A triangle mesh of vertices (shape space, metres) and triangles (three indices into
vertices each) with MeshSettings::default, and the triangles it dropped; see
new_mesh_with_settings.
Sourcepub fn new_mesh_with_settings(
vertices: &[Vec3],
triangles: &[[u32; 3]],
settings: &MeshSettings<'_>,
) -> Result<(Self, DroppedTriangles), ShapeError>
pub fn new_mesh_with_settings( vertices: &[Vec3], triangles: &[[u32; 3]], settings: &MeshSettings<'_>, ) -> Result<(Self, DroppedTriangles), ShapeError>
A triangle mesh of vertices (shape space, metres) and triangles (three indices into
vertices each), and the triangles it dropped.
A triangle’s front face is the side from which its vertices run counter-clockwise.
Triangles too small or too thin for Jolt to collide with reliably are dropped and
reported in DroppedTriangles: twice a triangle’s area must be above 1e-6 m² plus
twice the largest change Jolt’s 21-bit vertex quantization and f32 rounding can make
to it. That margin follows the triangle’s own shape and distance from the shape origin,
the quantization step of the mesh’s bounds on each axis and the size of the convex shapes
it collides with (MeshSettings::max_convex_extent); with the defaults a strip 1 m long
near the origin is kept from about 0.15 mm wide along the axes and from 0.26 mm in any
orientation (docs/limits.md#triangle-meshes). Jolt itself
keeps one copy of duplicate triangles and reorders the rest, so sub-shape ids do not
follow the input order. Closest-hit rays hit back faces too.
let vertices = [Vec3::ZERO, Vec3::new(0.0, 0.0, 1.0), Vec3::new(1.0, 0.0, 0.0)];
// The second triangle repeats a vertex.
let (mesh, dropped) = Shape::new_mesh(&vertices, &[[0, 1, 2], [0, 0, 1]])?;
assert_eq!(dropped.indices(), [1]);Meshes have no volume. They suit static bodies, and kinematic bodies with an explicit
BodySettings::mass;
PhysicsWorld::create_body refuses them for
dynamic bodies.
§Errors
ShapeError::InvalidValue: no vertices or no triangles, more thani32::MAXof either, an index beyondvertices, or a setting out of range (seeMeshSettings);ShapeError::InvalidValue: a vertex (referenced or not) that is not finite or has a component beyondlimits::MAX_SHAPE_EXTENTin absolute value;ShapeError::Mesh: no triangle is left after dropping small, thin and degenerate ones;ShapeError::Rejected: anything else Jolt refuses.
Building cost grows with the triangle count; see docs/benchmarks.md and docs/limits.md#triangle-meshes.
Source§impl Shape
impl Shape
Sourcepub fn new_plane(
normal: Vec3,
constant: f32,
half_extent: f32,
) -> Result<Self, ShapeError>
pub fn new_plane( normal: Vec3, constant: f32, half_extent: f32, ) -> Result<Self, ShapeError>
A plane normal · p + constant = 0 in the shape’s frame (Jolt PlaneShape); everything
on the side away from the normal, normal · p + constant < 0, is solid.
The plane is only infinite in name: it ends at a square of 2 * half_extent metres
around the point -constant * normal, and its bounds reach half_extent metres behind
it. Jolt does not collide anything outside these bounds and gives inconsistent contacts
at their edge, so half_extent should leave room around where bodies will be (and stay
small, for the broad phase).
Only static bodies, and compounds or decorators on static bodies, may use a plane: Jolt
marks it MustBeStatic, it has no volume or mass, and Jolt cannot collide it with meshes,
heightfields or other planes. It collides with convex shapes (also as compound or decorated
children), soft bodies and characters, and ray and shape casts hit it. It cannot be scaled
(new_scaled refuses it), and query, character and ragdoll shapes
refuse it. A ray that starts behind the plane hits it at fraction 0 (solid, <= 0), while a
point query reports only points strictly behind it (< 0).
normal must be a finite unit vector, constant finite and at most
limits::MAX_SHAPE_EXTENT in absolute value, half_extent positive and at most
limits::MAX_SHAPE_EXTENT, and the bounds within limits::MAX_SHAPE_EXTENT on
every axis; otherwise ShapeError::InvalidValue or, for the normal,
ShapeError::InvalidValue. For a normal along an axis the bounds reach
max(|constant|, |constant + half_extent|) along it: with normal +Y a half extent of
2000 m fits for constant = -1 (plane at y = 1) but not for constant = 1.
let mut world = PhysicsWorld::new(WorldSettings::default())?;
// Ground at y = 0, solid below, 500 m in every direction.
let ground = Shape::new_plane(Vec3::new(0.0, 1.0, 0.0), 0.0, 500.0)?;
world.create_body(&ground, &BodySettings::new_static())?;Sourcepub fn new_plane_with_material(
normal: Vec3,
constant: f32,
half_extent: f32,
material: &PhysicsMaterial,
) -> Result<Self, ShapeError>
pub fn new_plane_with_material( normal: Vec3, constant: f32, half_extent: f32, material: &PhysicsMaterial, ) -> Result<Self, ShapeError>
new_plane made of material; the same rules apply.
Source§impl Shape
impl Shape
Sourcepub fn new_scaled(shape: &Shape, scale: Vec3) -> Result<Self, ShapeError>
pub fn new_scaled(shape: &Shape, scale: Vec3) -> Result<Self, ShapeError>
shape scaled by scale along its local axes, a Jolt ScaledShape. The new shape holds
its own reference to shape, which may be dropped afterwards.
Each component must be finite (ShapeError::InvalidValue) and the scale valid for
the shape under Jolt’s rules (ShapeError::InvalidValue): every component at least
1e-6 in absolute value; uniform for spheres, capsules and tapered capsules; uniform in X
and Z for cylinders and tapered cylinders; for a compound, uniform unless every rotated
child is turned so the scale maps onto its own axes. Negative components mirror the
shape; a mirrored mesh’s front faces follow the mirrored winding.
Meshes and heightfields inside the shape must stay collidable: each stored triangle,
scaled, must pass the rule Shape::new_mesh applies, for the
MeshSettings::max_convex_extent the mesh was
built with (the default for heightfields) and without its quantization term (the stored
triangles are quantized already). Shrinking a mesh far below the size it was built at, or
flattening it, is refused with ShapeError::ThinTriangles, which names the scale and
the extent (docs/limits.md#scaled-shapes). The check reads every stored triangle back
from Jolt, so its cost grows with the triangle count.
The scaled shape’s local bounds must lie within limits::MAX_SHAPE_EXTENT on every
axis, and so must its centre of mass, which moves with the scale
(ShapeError::InvalidValue). Mass and inertia scale with the shape; a body made
of it goes through the usual mass and inertia checks of
PhysicsWorld::create_body. A shape that only
static bodies may use stays static-only, and a scaled mesh stays usable by kinematic
bodies. Jolt scales the convex radius by the smallest absolute component, and contacts
use at most 0.05 m of it. compound_sub_shape does not look
through the decorator: it returns None for a scaled compound.
Source§impl Shape
impl Shape
Sourcepub fn new_tapered_capsule(
half_height: f32,
top_radius: f32,
bottom_radius: f32,
) -> Result<Self, ShapeError>
pub fn new_tapered_capsule( half_height: f32, top_radius: f32, bottom_radius: f32, ) -> Result<Self, ShapeError>
A capsule along the local Y axis whose end spheres differ: a cone section
2 * half_height metres high between the centres of a sphere of top_radius at
y = half_height and one of bottom_radius at y = -half_height, a Jolt
TaperedCapsuleShape. Its centre of mass is halfway between the outer ends of the two
spheres (Jolt’s approximation).
All values must be finite and positive, half_height + max(top_radius, bottom_radius)
at most limits::MAX_SHAPE_EXTENT, and the radii may differ by at most
2 * half_height * (1 - 2^-21); otherwise one sphere contains the other and the shape
is a sphere, which new_sphere builds
(ShapeError::InvalidValue; docs/limits.md#tapered-shapes). Only a uniform
new_scaled applies.
Sourcepub fn new_tapered_cylinder(
half_height: f32,
top_radius: f32,
bottom_radius: f32,
) -> Result<Self, ShapeError>
pub fn new_tapered_cylinder( half_height: f32, top_radius: f32, bottom_radius: f32, ) -> Result<Self, ShapeError>
A tapered cylinder with Jolt’s default convex radius, 0.05 m; otherwise as
new_tapered_cylinder_with_convex_radius.
Sourcepub fn new_tapered_cylinder_with_convex_radius(
half_height: f32,
top_radius: f32,
bottom_radius: f32,
convex_radius: f32,
) -> Result<Self, ShapeError>
pub fn new_tapered_cylinder_with_convex_radius( half_height: f32, top_radius: f32, bottom_radius: f32, convex_radius: f32, ) -> Result<Self, ShapeError>
A cylinder along the local Y axis whose ends differ: 2 * half_height metres high with
a disc of top_radius at the top and one of bottom_radius at the bottom, a Jolt
TaperedCylinderShape. A radius of 0 makes a cone. The centre of mass lies on the axis
at the centroid of the volume.
The half height must be finite and positive, the radii finite, not negative and
different (equal radii make a cylinder: use
new_cylinder_with_convex_radius), the larger
radius at least 2^-63 m, and all of them at most limits::MAX_SHAPE_EXTENT
(ShapeError::InvalidValue; docs/limits.md#tapered-shapes). The convex radius
must be finite and not negative; Jolt clamps it to the smaller radius, and contacts use
at most 0.05 m of it. Only a new_scaled that is uniform in X and Z
applies.
Source§impl Shape
impl Shape
Sourcepub fn new_box(half_extent: Vec3) -> Result<Self, ShapeError>
pub fn new_box(half_extent: Vec3) -> Result<Self, ShapeError>
A box with the given half extents in metres (each finite, positive and at most
limits::MAX_SHAPE_EXTENT) and Jolt’s default convex radius of 0.05 m; see
new_box_with_convex_radius.
Sourcepub fn new_box_with_convex_radius(
half_extent: Vec3,
convex_radius: f32,
) -> Result<Self, ShapeError>
pub fn new_box_with_convex_radius( half_extent: Vec3, convex_radius: f32, ) -> Result<Self, ShapeError>
A box with the given half extents in metres (each finite, positive and at most
limits::MAX_SHAPE_EXTENT) and convex radius in metres (finite and not negative).
Jolt shrinks the box by the convex radius and inflates it again, so the faces stay where
they are while edges and corners are rounded for contacts and shape casts. Jolt clamps
the radius to the smallest half extent (BoxShape.h), and contacts and shape casts use
at most 0.05 m of it (ScaleHelpers::ScaleConvexRadius caps it at Jolt’s default), so a
larger radius collides like 0.05. A radius of 0 gives sharp edges; collision detection
is then somewhat slower, because Jolt falls back to EPA more often. Ray casts always see
the sharp box, whatever the radius (BoxShape::CastRay tests the half extents only).
Sourcepub fn new_sphere(radius: f32) -> Result<Self, ShapeError>
pub fn new_sphere(radius: f32) -> Result<Self, ShapeError>
A sphere with the given radius in metres (finite, positive and at most
limits::MAX_SHAPE_EXTENT).
Sourcepub fn new_cylinder(half_height: f32, radius: f32) -> Result<Self, ShapeError>
pub fn new_cylinder(half_height: f32, radius: f32) -> Result<Self, ShapeError>
A cylinder along the local Y axis, centred on the origin, 2 * half_height metres high,
with Jolt’s default convex radius of 0.05 m; see
new_cylinder_with_convex_radius.
Sourcepub fn new_cylinder_with_convex_radius(
half_height: f32,
radius: f32,
convex_radius: f32,
) -> Result<Self, ShapeError>
pub fn new_cylinder_with_convex_radius( half_height: f32, radius: f32, convex_radius: f32, ) -> Result<Self, ShapeError>
A cylinder along the local Y axis, centred on the origin, 2 * half_height metres high.
Half height and radius must be finite, positive and at most limits::MAX_SHAPE_EXTENT,
the convex radius finite and not negative. Like a box’s, the convex radius rounds the edges
for contacts and shape casts; Jolt clamps it to min(half_height, radius)
(CylinderShape.cpp) and uses at most 0.05 m of it there
(ScaleHelpers::ScaleConvexRadius).
Sourcepub fn new_capsule(
half_height_of_cylinder: f32,
radius: f32,
) -> Result<Self, ShapeError>
pub fn new_capsule( half_height_of_cylinder: f32, radius: f32, ) -> Result<Self, ShapeError>
A capsule along the local Y axis, centred on the origin: a cylinder
2 * half_height_of_cylinder metres high with a hemisphere of radius at each end, so
2 * (half_height_of_cylinder + radius) metres high in total. Both values must be finite
and positive, and half_height_of_cylinder + radius at most
limits::MAX_SHAPE_EXTENT.
Sourcepub fn new_height_field(
sample_count: u32,
samples: &[f32],
settings: &HeightFieldSettings<'_>,
) -> Result<Self, ShapeError>
pub fn new_height_field( sample_count: u32, samples: &[f32], settings: &HeightFieldSettings<'_>, ) -> Result<Self, ShapeError>
A heightfield of sample_count x sample_count height samples, in metres before
scaling.
§Layout
The surface passes through offset + scale * (x, samples[y * n + x], y) for x, y in
0..n, n = sample_count. y runs along +Z, so row y of samples holds the heights
at z = offset.z + y * scale.z (Jolt’s row-major order). A column-major source
heights[x * n + z] must be transposed first:
samples[z * n + x] = heights[x * n + z]Each cell is split into two triangles along the diagonal from sample (x, y) to sample
(x + 1, y + 1). A sample of f32::MAX is a hole: the cells touching it have no
collision. Every other sample must be finite.
§Block size
Jolt rounds n up to a multiple of the block size and fills the extra rows and columns
with holes, so the cells touching them have no collision. With the default block size 2,
n = 33 is stored as 34 x 34 and the surface covers exactly the 32 x 32 cells given.
n / block_size, rounded up, must be at least 2.
§Precision
Jolt first quantises the heights to 16 bits over the height range of the whole field,
then to HeightFieldSettings::bits_per_sample bits within the height range of each
block. height_field_position reads back the stored
heights.
§Static only
PhysicsWorld::create_body refuses heightfields for
dynamic and kinematic bodies.
§Extent
The shape’s local bounds must lie within limits::MAX_SHAPE_EXTENT on every axis;
otherwise ShapeError::InvalidValue is returned. A field of holes only has empty
bounds and passes.
Sourcepub fn height_field_position(&self, x: u32, y: u32) -> Option<Vec3>
pub fn height_field_position(&self, x: u32, y: u32) -> Option<Vec3>
The stored surface point of heightfield sample (x, y) in shape-local space, after
Jolt’s quantisation. None when the shape is not a heightfield, when (x, y) lies
outside the stored (padded) grid, or when the sample is a hole, including the padding
Jolt adds.
Sourcepub fn new_compound(children: &[CompoundChild<'_>]) -> Result<Self, ShapeError>
pub fn new_compound(children: &[CompoundChild<'_>]) -> Result<Self, ShapeError>
A compound of children, each with its own pose and user data.
The children may be dropped afterwards: the compound holds its own references. Child order is part of the shape and so of a deterministic state. Jolt moves the compound’s centre of mass to the children’s mass-weighted centre, so body positions of compounds read back through arithmetic.
Two or more children make a Jolt StaticCompoundShape. A single child makes a
MutableCompoundShape, because Jolt’s static compound replaces a lone child by the
child itself (or a RotatedTranslatedShape) and drops its user data
(StaticCompoundShape.cpp); oxijolt never changes it after construction.
Every child position must be finite with each component at most
limits::MAX_SHAPE_EXTENT in absolute value (ShapeError::InvalidValue), and the
compound’s local bounds must lie within limits::MAX_SHAPE_EXTENT on every axis
(ShapeError::InvalidValue). A hierarchy whose sub-shape ids Jolt cannot form
gives ShapeError::Rejected when it needs more than 32 bits and
ShapeError::InvalidValue when a one-child compound would start at bit 32. A
compound of more than limits::MAX_EXPANDED_SUB_SHAPES shapes, counting a child shared
by several parents at every use, gives ShapeError::TooManySubShapes.
Sourcepub fn new_offset_center_of_mass(
shape: &Shape,
offset: Vec3,
) -> Result<Self, ShapeError>
pub fn new_offset_center_of_mass( shape: &Shape, offset: Vec3, ) -> Result<Self, ShapeError>
shape with its centre of mass moved by offset (shape space, metres, each component at
most limits::MAX_SHAPE_EXTENT in absolute value), a Jolt OffsetCenterOfMassShape.
The new shape’s local bounds, which are relative to the new centre of mass, must lie
within limits::MAX_SHAPE_EXTENT on every axis.
Only the centre of mass moves: the body origin and the collision surface stay where
shape puts them, and mass and inertia are computed about the new centre. A vehicle
chassis gets a low centre of mass this way. The new shape holds its own reference to
shape, which may be dropped afterwards. A decorated shape that only static bodies may
use (a heightfield, mesh or plane) stays static-only.
compound_sub_shape does not look through the decorator: it
returns None for an offset compound.
Sourcepub fn compound_sub_shape(&self, id: SubShapeId) -> Option<CompoundSubShape>
pub fn compound_sub_shape(&self, id: SubShapeId) -> Option<CompoundSubShape>
The child of this compound that id leads to.
None for shapes that are not compounds. Only the root level is decoded, so id may be
a hit’s full path or a partial path below this shape. An id that came from another
shape gives None or an arbitrary valid child: the bits cannot prove where they came
from.