tecs.physics.contract

The batched crossing between Tecs entities and a native rigid-body solver.

One fixed step is one crossing. A frame's systems queue commands through the queue* functions, step applies every queued command and advances the solver once, and the results are read back from buffers the simulation already holds. Nothing here calls a solver once per body, and nothing a solver owns ever calls back into Nupp: a Nupp function reached through a foreign function pointer cannot be entered from a compiled trace or from a thread the Nupp VM never created, so contacts, poses and assigned handles leave through buffers instead.

tecs.physics.rapier implements this over the tecs_physics Rust library. A test supplies its own implementation, which is what lets the entity-facing half of tecs.physics be exercised with no native library present.

Every length here is in solver meters and every angle is in radians. tecs.physics owns the pixel conversion and does not push it across this boundary.

A handle is a one-based index1 plus a generation. Zero index1 names nothing, which is what a snapshot load leaves behind and what a failed creation reports.

Module contents

Types

TypeKindDescription
BodyCreaterecordOne body creation, together with the primary collider it always carries.
ColliderCreaterecordOne secondary collider added to an existing or same-batch body.
ColliderDefrecordOne collider's geometry, material and collision filtering.
ColliderUpdaterecordOne in-place collider replacement, addressed by handle or by entity.
EntityHandlesrecordThe body and collider handle owned by each entity a lookup asked about.
JointCreaterecordOne joint creation between two existing or same-batch bodies.
OptionstypeOptions accepted when a simulation is created.
SimulationrecordOne live simulation, driven entirely through batched commands.

Values

ValueKindDescription
ACTION_FORCEvariableAdds a center-of-mass force from a and b.
ACTION_FORCE_ATvariableAdds the force a, b at the world point c, d.
ACTION_IMPULSEvariableAdds a center-of-mass impulse from a and b.
ACTION_IMPULSE_ATvariableAdds the impulse a, b at the world point c, d.
ACTION_SET_ANGULAR_VELOCITYvariableReplaces angular velocity with a.
ACTION_SET_AWAKEvariableWakes the body when a is nonzero and sleeps it otherwise.
ACTION_SET_BODY_TYPEvariableReplaces the body kind with a, one of the body-kind constants.
ACTION_SET_ENABLEDvariableEnables the body when a is nonzero and disables it otherwise.
ACTION_SET_TRANSFORMvariableTeleports the body to a, b at angle c, leaving velocity alone.
ACTION_SET_VELOCITYvariableReplaces linear velocity with a, b.
ACTION_TORQUEvariableAdds the torque a.
BODY_DYNAMICvariableA body the solver moves.
BODY_KINEMATICvariableA body the game moves through velocity, which pushes dynamic bodies.
BODY_STATICvariableA body the solver never moves.
JOINT_FIXEDvariableA joint holding one relative pose between two bodies.
JOINT_PRISMATICvariableA joint sharing one sliding axis, leaving translation along it free.
JOINT_REVOLUTEvariableA joint sharing one anchor point, leaving rotation about it free.
SHAPE_BOXvariableAn axis-aligned box in the body frame.
SHAPE_CAPSULEvariableA vertical capsule.
SHAPE_CIRCLEvariableA circle.
SHAPE_SEGMENTvariableA centered segment from negative to positive half extents.

Types#

BodyCreaterecord#

record BodyCreate
    entity: integer
    collider: ColliderDef
    kind: integer
    fixedRotation: boolean
    isBullet: boolean
    sleepEnabled: boolean
    gravityScale: number
    linearDamping: number
    angularDamping: number
    x: number
    y: number
    angle: number
    vx: number
    vy: number
    omega: number
end

One body creation, together with the primary collider it always carries.

Fields

entity#
entity: integer

Caller-writable. Names the entity the body belongs to.

collider#
collider: ColliderDef

Caller-writable. Describes the shape created with the body.

kind#
kind: integer

Caller-writable. Selects one of the body-kind constants.

fixedRotation#
fixedRotation: boolean

Caller-writable. Locks the body's angle.

isBullet#
isBullet: boolean

Caller-writable. Sweeps the body's motion for continuous collision.

sleepEnabled#
sleepEnabled: boolean

Caller-writable. Permits the body to sleep when it comes to rest.

gravityScale#
gravityScale: number

Caller-writable. Scales gravity for this body alone.

linearDamping#
linearDamping: number

Caller-writable. Damps linear velocity.

angularDamping#
angularDamping: number

Caller-writable. Damps angular velocity.

x#
x: number

Caller-writable. Places the body's origin along x, in meters.

y#
y: number

Caller-writable. Places the body's origin along y with x.

angle#
angle: number

Caller-writable. Sets the body's initial angle, in radians.

vx#
vx: number

Caller-writable. Sets initial linear velocity along x, in meters per second.

vy#
vy: number

Caller-writable. Sets initial linear velocity along y with vx.

omega#
omega: number

Caller-writable. Sets initial angular velocity, in radians per second.

ColliderCreaterecord#

record ColliderCreate
    def: ColliderDef
    bodyIndex1: integer
    bodyGeneration: integer
    bodyOrder: integer
end

One secondary collider added to an existing or same-batch body.

Fields

def#

Caller-writable. Describes the shape to add.

bodyIndex1#
bodyIndex1: integer

Caller-writable. Names the owning body slot, one-based, when bodyOrder is zero.

bodyGeneration#
bodyGeneration: integer

Caller-writable. Rejects a stale owning body with bodyIndex1.

bodyOrder#
bodyOrder: integer

Caller-writable. Names the one-based order of the queueCreateBody call in this same batch that owns the shape, or zero when bodyIndex1 is authoritative. Queueing a shape onto a body the same batch creates is what keeps a compound body one crossing.

ColliderDefrecord#

record ColliderDef
    entity: integer
    shape: integer
    sensor: boolean
    halfWidth: number
    halfHeight: number
    radius: number
    length: number
    offsetX: number
    offsetY: number
    density: number
    friction: number
    restitution: number
    categoryBits: integer
    maskBits: integer
end

One collider's geometry, material and collision filtering.

Fields

entity#
entity: integer

Caller-writable. Names the entity that owns this shape, which is what a contact event and an entity-addressed replacement both report.

shape#
shape: integer

Caller-writable. Selects one of the shape constants.

sensor#
sensor: boolean

Caller-writable. Reports overlap without a collision response.

halfWidth#
halfWidth: number

Caller-writable. Sets the box half extent along x, in meters.

halfHeight#
halfHeight: number

Caller-writable. Sets the box half extent along y, in meters.

radius#
radius: number

Caller-writable. Sets the circle or capsule radius, in meters.

length#
length: number

Caller-writable. Sets the distance between capsule end centers, in meters, excluding the caps.

offsetX#
offsetX: number

Caller-writable. Offsets the shape along x in the body frame, in meters.

offsetY#
offsetY: number

Caller-writable. Offsets the shape along y with offsetX.

density#
density: number

Caller-writable. Sets mass per unit area.

friction#
friction: number

Caller-writable. Sets Coulomb friction.

restitution#
restitution: number

Caller-writable. Sets the bounce coefficient.

categoryBits#
categoryBits: integer

Caller-writable. Sets the low 32 collision-group category bits.

maskBits#
maskBits: integer

Caller-writable. Sets the categories this shape may contact.

ColliderUpdaterecord#

record ColliderUpdate
    def: ColliderDef
    index1: integer
    generation: integer
    bodyIndex1: integer
    bodyGeneration: integer
end

One in-place collider replacement, addressed by handle or by entity.

Fields

def#

Caller-writable. Describes the replacement shape.

index1#
index1: integer

Caller-writable. Names the collider slot to replace, one-based, or zero to find the shape under bodyIndex1 whose entity is def.entity.

generation#
generation: integer

Caller-writable. Rejects a stale collider slot with index1.

bodyIndex1#
bodyIndex1: integer

Caller-writable. Names the owning body slot, one-based, used when index1 is zero.

bodyGeneration#
bodyGeneration: integer

Caller-writable. Rejects a stale owning body with bodyIndex1.

EntityHandlesrecord#

record EntityHandles
    bodyIndex1: {integer}
    bodyGeneration: {integer}
    colliderIndex1: {integer}
    colliderGeneration: {integer}
    jointIndex1: {integer}
    jointGeneration: {integer}
end

The body and collider handle owned by each entity a lookup asked about.

Each array holds one entry per requested entity, in the requested order, and a zero index1 means the simulation has nothing for that entity.

Fields

bodyIndex1#
bodyIndex1: {integer}

Read-only. Holds each entity's body slot, one-based.

bodyGeneration#
bodyGeneration: {integer}

Read-only. Holds each entity's body generation.

colliderIndex1#
colliderIndex1: {integer}

Read-only. Holds each entity's collider slot, one-based.

colliderGeneration#
colliderGeneration: {integer}

Read-only. Holds each entity's collider generation.

jointIndex1#
jointIndex1: {integer}

Read-only. Holds each entity's joint slot, one-based.

jointGeneration#
jointGeneration: {integer}

Read-only. Holds each entity's joint generation.

JointCreaterecord#

record JointCreate
    entity: integer
    kind: integer
    bodyAIndex1: integer
    bodyAGeneration: integer
    bodyAOrder: integer
    bodyBIndex1: integer
    bodyBGeneration: integer
    bodyBOrder: integer
    anchorAX: number
    anchorAY: number
    anchorBX: number
    anchorBY: number
    axisX: number
    axisY: number
    limitsEnabled: boolean
    limitMin: number
    limitMax: number
    motorEnabled: boolean
    motorTargetVelocity: number
    motorMaxForce: number
    contactsEnabled: boolean
end

One joint creation between two existing or same-batch bodies.

Fields

entity#
entity: integer

Caller-writable. Names the entity that owns the joint.

kind#
kind: integer

Caller-writable. Selects one of the joint-kind constants.

bodyAIndex1#
bodyAIndex1: integer

Caller-writable. Names the first body slot, one-based, when bodyAOrder is zero.

bodyAGeneration#
bodyAGeneration: integer

Caller-writable. Rejects a stale first body with bodyAIndex1.

bodyAOrder#
bodyAOrder: integer

Caller-writable. Names the one-based queueCreateBody order in this batch that supplies the first body, or zero.

bodyBIndex1#
bodyBIndex1: integer

Caller-writable. Names the second body slot, one-based, when bodyBOrder is zero.

bodyBGeneration#
bodyBGeneration: integer

Caller-writable. Rejects a stale second body with bodyBIndex1.

bodyBOrder#
bodyBOrder: integer

Caller-writable. Names the one-based queueCreateBody order in this batch that supplies the second body, or zero.

anchorAX#
anchorAX: number

Caller-writable. Anchors the joint in the first body's frame along x, in meters.

anchorAY#
anchorAY: number

Caller-writable. Anchors the joint in the first body's frame along y.

anchorBX#
anchorBX: number

Caller-writable. Anchors the joint in the second body's frame along x.

anchorBY#
anchorBY: number

Caller-writable. Anchors the joint in the second body's frame along y.

axisX#
axisX: number

Caller-writable. Sets the prismatic sliding axis along x. The solver normalizes it, and the other joint kinds ignore it.

axisY#
axisY: number

Caller-writable. Sets the prismatic sliding axis along y.

limitsEnabled#
limitsEnabled: boolean

Caller-writable. Enforces limitMin and limitMax.

limitMin#
limitMin: number

Caller-writable. Sets the lower limit, in radians for a revolute joint and meters for a prismatic one.

limitMax#
limitMax: number

Caller-writable. Sets the upper limit with limitMin.

motorEnabled#
motorEnabled: boolean

Caller-writable. Drives the joint's free degree of freedom.

motorTargetVelocity#
motorTargetVelocity: number

Caller-writable. Sets the motor's target velocity, in radians or meters per second.

motorMaxForce#
motorMaxForce: number

Caller-writable. Bounds the force or torque the motor may apply.

contactsEnabled#
contactsEnabled: boolean

Caller-writable. Lets the two joined bodies still collide.

Optionstype#

type Options = {
    gravityX: number?,
    gravityY: number?,
    subStepCount: integer?,
    workerCount: integer?
}

Options accepted when a simulation is created.

Simulationrecord#

record Simulation
    id: integer
    subStepCount: integer
    workerCount: integer
    queueCreateBody: function(command: BodyCreate): integer
    queueCreateCollider: function(command: ColliderCreate): integer
    queueCreateJoint: function(command: JointCreate): integer
    queueUpdateBody: function(
        index1: integer,
        generation: integer,
        kind: integer,
        fixedRotation: boolean,
        isBullet: boolean,
        sleepEnabled: boolean,
        gravityScale: number,
        linearDamping: number,
        angularDamping: number
    ): nil
    queueUpdateCollider: function(command: ColliderUpdate): integer
    queueAction: function(
        index1: integer,
        generation: integer,
        action: integer,
        a: number,
        b: number,
        c: number,
        d: number
    ): nil
    queueDestroyBody: function(index1: integer, generation: integer): nil
    queueDestroyCollider: function(index1: integer, generation: integer): nil
    queueDestroyJoint: function(index1: integer, generation: integer): nil
    step: function(dt: number): nil
    createdBody: function(order: integer): (integer, integer, integer, integer)
    createdCollider: function(order: integer): (integer, integer)
    createdJoint: function(order: integer): (integer, integer)
    updatedCollider: function(order: integer): (integer, integer)
    isLive: function(index1: integer, generation: integer): boolean
    isAwake: function(index1: integer, generation: integer): boolean
    poseOf: function(index1: integer, generation: integer): (number?, number, number, number)
    velocityOf: function(index1: integer, generation: integer): (number, number, number)
    eventCount: function(): integer
    eventAt: function(index: integer): (integer, integer, boolean, boolean)
    raycast: function(
        x1: number,
        y1: number,
        x2: number,
        y2: number,
        categoryBits: integer,
        maskBits: integer
    ): (integer?, number, number, number, number, number)
    resolveEntities: function(entities: {integer}): EntityHandles
    save: function(): (string?, string?)
    load: function(bytes: string): (boolean, string?)
    destroy: function(): nil
end

One live simulation, driven entirely through batched commands.

Every field is a closure over one simulation's private state, so an implementation is a value rather than a subclass and a test may supply its own without a native library.

Methods

queueCreateBody#
queueCreateBody: function(command: BodyCreate): integer

Queues one body creation and returns its one-based order in this batch, which createdBody and a same-batch bodyOrder both use.

Arguments
NameTypeDescription
commandBodyCreate
Returns
TypeDescription
integer
queueCreateCollider#
queueCreateCollider: function(command: ColliderCreate): integer

Queues one secondary collider and returns its one-based order.

Arguments
NameTypeDescription
commandColliderCreate
Returns
TypeDescription
integer
queueCreateJoint#
queueCreateJoint: function(command: JointCreate): integer

Queues one joint and returns its one-based order.

Arguments
NameTypeDescription
commandJointCreate
Returns
TypeDescription
integer
queueUpdateBody#
queueUpdateBody: function(
    index1: integer,
    generation: integer,
    kind: integer,
    fixedRotation: boolean,
    isBullet: boolean,
    sleepEnabled: boolean,
    gravityScale: number,
    linearDamping: number,
    angularDamping: number
): nil

Queues one body redeclaration, which never moves the body and never changes its owning entity.

The arguments are positional because a redeclaration runs once per dirty body row, and a record per row is an allocation in a loop whose length is the game's business.

Arguments
NameTypeDescription
index1integer

the one-based target body slot

generationinteger

the generation that must match

kindinteger

one of the body-kind constants

fixedRotationboolean

whether the body's angle is locked

isBulletboolean

whether the body sweeps its motion

sleepEnabledboolean

whether the body may sleep at rest

gravityScalenumber

the body's own gravity multiplier

linearDampingnumber

the linear velocity damping term

angularDampingnumber

the angular velocity damping term

Returns
TypeDescription
nil
queueUpdateCollider#
queueUpdateCollider: function(command: ColliderUpdate): integer

Queues one collider replacement and returns its one-based order, which updatedCollider uses to read the replacement's handle.

Arguments
NameTypeDescription
commandColliderUpdate
Returns
TypeDescription
integer
queueAction#
queueAction: function(
    index1: integer,
    generation: integer,
    action: integer,
    a: number,
    b: number,
    c: number,
    d: number
): nil

Queues one mutation of a live body.

Actions apply at the start of the step that follows them, in the order they were queued, and reads report the state after the last completed step. A queued impulse is therefore not visible until that step runs. The arguments are positional because a game applies actions in its own loops.

Arguments
NameTypeDescription
index1integer

the one-based target body slot

generationinteger

the generation that must match

actioninteger

one of the action constants

anumber

the action's first operand

bnumber

the action's second operand

cnumber

the action's third operand

dnumber

the action's fourth operand

Returns
TypeDescription
nil
queueDestroyBody#
queueDestroyBody: function(index1: integer, generation: integer): nil

Queues one body destruction, which also removes its colliders and joints. A slot the simulation no longer holds is ignored.

Arguments
NameTypeDescription
index1integer
generationinteger
Returns
TypeDescription
nil
queueDestroyCollider#
queueDestroyCollider: function(index1: integer, generation: integer): nil

Queues one collider destruction, ignoring a slot already gone.

Arguments
NameTypeDescription
index1integer
generationinteger
Returns
TypeDescription
nil
queueDestroyJoint#
queueDestroyJoint: function(index1: integer, generation: integer): nil

Queues one joint destruction, ignoring a slot already gone.

Arguments
NameTypeDescription
index1integer
generationinteger
Returns
TypeDescription
nil
step#
step: function(dt: number): nil

Applies every queued command and advances the simulation once.

Commands apply in a fixed order that does not depend on the order systems discovered them: joint, collider and body destruction, then body, collider and joint creation, then body redeclaration, collider replacement, and queued actions. The queues are empty afterwards.

Arguments
NameTypeDescription
dtnumber

the interval to advance, in seconds

Returns
TypeDescription
nil
Raises
  • when the simulation cannot apply the batch

createdBody#
createdBody: function(order: integer): (integer, integer, integer, integer)

Returns the body and collider assigned to one queued creation.

Arguments
NameTypeDescription
orderinteger

the one-based order queueCreateBody returned

Returns
TypeDescription
integer

the body slot, one-based and zero when creation failed, the body generation, the primary collider slot, and its generation

integer
integer
integer
createdCollider#
createdCollider: function(order: integer): (integer, integer)

Returns the collider assigned to one queued secondary creation.

Arguments
NameTypeDescription
orderinteger

the one-based order queueCreateCollider returned

Returns
TypeDescription
integer

the collider slot, one-based and zero on failure, and its generation

integer
createdJoint#
createdJoint: function(order: integer): (integer, integer)

Returns the joint assigned to one queued creation.

Arguments
NameTypeDescription
orderinteger

the one-based order queueCreateJoint returned

Returns
TypeDescription
integer

the joint slot, one-based and zero on failure, and its generation

integer
updatedCollider#
updatedCollider: function(order: integer): (integer, integer)

Returns the collider a queued replacement produced.

Arguments
NameTypeDescription
orderinteger

the one-based order queueUpdateCollider returned

Returns
TypeDescription
integer

the collider slot, one-based and zero on failure, and its generation

integer
isLive#
isLive: function(index1: integer, generation: integer): boolean

Reports whether the simulation still holds this body.

Arguments
NameTypeDescription
index1integer
generationinteger
Returns
TypeDescription
boolean
isAwake#
isAwake: function(index1: integer, generation: integer): boolean

Reports whether the solver is still integrating this body.

Arguments
NameTypeDescription
index1integer
generationinteger
Returns
TypeDescription
boolean
poseOf#
poseOf: function(index1: integer, generation: integer): (number?, number, number, number)

Returns a body's pose only when the last step moved it.

Arguments
NameTypeDescription
index1integer

the one-based body slot

generationinteger

the generation that must match

Returns
TypeDescription
number?

nil when the body is gone or did not move, otherwise x in meters, y in meters, the cosine of its angle, and the sine

number
number
number
velocityOf#
velocityOf: function(index1: integer, generation: integer): (number, number, number)

Returns a body's velocity after the last completed step.

Arguments
NameTypeDescription
index1integer

the one-based body slot

generationinteger

the generation that must match

Returns
TypeDescription
number

linear velocity along x and y in meters per second, and angular velocity in radians per second, all zero when the body is gone

number
number
eventCount#
eventCount: function(): integer

Reports how many contact and sensor transitions the last step queued.

Returns
TypeDescription
integer
eventAt#
eventAt: function(index: integer): (integer, integer, boolean, boolean)

Returns one queued transition.

Arguments
NameTypeDescription
indexinteger

the one-based position within the last step's events

Returns
TypeDescription
integer

the first entity, the second entity, whether the pair began touching rather than stopped, and whether the pair involved a sensor. A sensor transition names the sensor first.

integer
boolean
boolean
raycast#
raycast: function(
    x1: number,
    y1: number,
    x2: number,
    y2: number,
    categoryBits: integer,
    maskBits: integer
): (integer?, number, number, number, number, number)

Casts a finite segment and returns its nearest hit.

Arguments
NameTypeDescription
x1number

the segment start along x, in meters

y1number

the segment start along y, in meters

x2number

the segment end along x, in meters

y2number

the segment end along y, in meters

categoryBitsinteger

the ray's collision categories

maskBitsinteger

the categories the ray may hit

Returns
TypeDescription
integer?

nil on a miss, otherwise the owning entity, the contact point x and y in meters, the outward unit normal x and y, and the fraction along the segment

number
number
number
number
number
resolveEntities#
resolveEntities: function(entities: {integer}): EntityHandles

Answers the handles the simulation holds for a list of entities.

A snapshot load asks about every restored entity at once, so the cost stays linear in the simulation rather than quadratic.

Arguments
NameTypeDescription
entities{integer}

the entity ids to look up

Returns
TypeDescription
EntityHandles

one caller-owned record of parallel result arrays

save#
save: function(): (string?, string?)

Serializes the complete simulation state.

Returns
TypeDescription
string?

the bytes, or nil and a reason when the state cannot be saved

string?
load#
load: function(bytes: string): (boolean, string?)

Replaces the simulation state with previously saved bytes.

Slots and generations survive, so a handle a save recorded still addresses the same body afterwards.

Arguments
NameTypeDescription
bytesstring

the value a previous save returned

Returns
TypeDescription
boolean

whether the state was restored, and a reason when it was not

string?
destroy#
destroy: function(): nil

Releases the simulation and everything it owns.

Calling it twice is harmless. Every other function is undefined afterwards.

Returns
TypeDescription
nil

Fields

id#
id: integer

Read-only. Identifies this simulation among the process's live ones, which is what a stored handle carries so a row left over from another simulation is rejected rather than resolved against whatever took its place.

subStepCount#
subStepCount: integer

Read-only. Reports how many substeps each step divides its interval into.

workerCount#
workerCount: integer

Read-only. Reports the solver width the process settled on.

Values#

ACTION_FORCEvariable#

const ACTION_FORCE: integer

Adds a center-of-mass force from a and b.

ACTION_FORCE_ATvariable#

const ACTION_FORCE_AT: integer

Adds the force a, b at the world point c, d.

ACTION_IMPULSEvariable#

const ACTION_IMPULSE: integer

Adds a center-of-mass impulse from a and b.

ACTION_IMPULSE_ATvariable#

const ACTION_IMPULSE_AT: integer

Adds the impulse a, b at the world point c, d.

ACTION_SET_ANGULAR_VELOCITYvariable#

Replaces angular velocity with a.

ACTION_SET_AWAKEvariable#

const ACTION_SET_AWAKE: integer

Wakes the body when a is nonzero and sleeps it otherwise.

ACTION_SET_BODY_TYPEvariable#

const ACTION_SET_BODY_TYPE: integer

Replaces the body kind with a, one of the body-kind constants.

ACTION_SET_ENABLEDvariable#

const ACTION_SET_ENABLED: integer

Enables the body when a is nonzero and disables it otherwise.

ACTION_SET_TRANSFORMvariable#

const ACTION_SET_TRANSFORM: integer

Teleports the body to a, b at angle c, leaving velocity alone.

ACTION_SET_VELOCITYvariable#

const ACTION_SET_VELOCITY: integer

Replaces linear velocity with a, b.

ACTION_TORQUEvariable#

const ACTION_TORQUE: integer

Adds the torque a.

BODY_DYNAMICvariable#

const BODY_DYNAMIC: integer

A body the solver moves.

BODY_KINEMATICvariable#

const BODY_KINEMATIC: integer

A body the game moves through velocity, which pushes dynamic bodies.

BODY_STATICvariable#

const BODY_STATIC: integer

A body the solver never moves.

JOINT_FIXEDvariable#

const JOINT_FIXED: integer

A joint holding one relative pose between two bodies.

JOINT_PRISMATICvariable#

const JOINT_PRISMATIC: integer

A joint sharing one sliding axis, leaving translation along it free.

JOINT_REVOLUTEvariable#

const JOINT_REVOLUTE: integer

A joint sharing one anchor point, leaving rotation about it free.

SHAPE_BOXvariable#

const SHAPE_BOX: integer

An axis-aligned box in the body frame.

SHAPE_CAPSULEvariable#

const SHAPE_CAPSULE: integer

A vertical capsule.

SHAPE_CIRCLEvariable#

const SHAPE_CIRCLE: integer

A circle.

SHAPE_SEGMENTvariable#

const SHAPE_SEGMENT: integer

A centered segment from negative to positive half extents.