8#include <box3d/box3d.h>
20 if (!std::isfinite(
x) || !std::isfinite(
y) || !std::isfinite(
z))
26b3BodyType parseBodyType(
const std::string &
type) {
27 if (
type ==
"static")
return b3_staticBody;
28 if (
type ==
"kinematic")
return b3_kinematicBody;
29 if (
type ==
"dynamic")
return b3_dynamicBody;
33const char *bodyTypeName(b3BodyType
t) {
35 case b3_staticBody:
return "static";
36 case b3_kinematicBody:
return "kinematic";
37 case b3_dynamicBody:
return "dynamic";
38 default:
return "static";
42void requireFinite(
float value,
const char *
operation,
const char *parameter) {
43 if (!std::isfinite(
value))
47void requireNonNegative(
float value,
const char *
operation,
const char *parameter) {
53b3Vec3 checkedVector(
float x,
float y,
float z,
const char *
operation,
54 const char *parameter) {
55 if (!std::isfinite(
x) || !std::isfinite(
y) || !std::isfinite(
z))
60b3Quat normalizedQuaternion(
float qx,
float qy,
float qz,
float qw,
const char *
operation) {
66 if (lengthSquared <= 1e-16f)
68 const float inverseLength = 1.f / std::sqrt(lengthSquared);
69 return b3Quat{{
qx * inverseLength,
qy * inverseLength,
qz * inverseLength},
73b3ShapeDef makeShapeDef(
float density,
float friction,
float restitution) {
74 b3ShapeDef def = b3DefaultShapeDef();
75 def.density = density;
76 def.baseMaterial.friction = friction;
78 def.enableContactEvents =
true;
79 def.enableSensorEvents =
true;
83b3HullData *createCheckedHull(
const std::vector<float> &
vertices,
int maxVertices,
86 throw eve::Exception(
"%s: vertices must contain at least four packed XYZ points",
90 if (maxVertices < 4 || maxVertices > 254)
92 std::vector<b3Vec3>
points;
94 for (
size_t i = 0; i <
vertices.size(); i += 3) {
100 b3HullData *hull = b3CreateHull(
points.data(),
static_cast<int>(
points.size()), maxVertices);
102 throw eve::Exception(
"%s: points do not form a valid three-dimensional convex hull",
107b3MeshData *createCheckedMesh(
const std::vector<float> &
vertices,
108 const std::vector<int32_t> &
indices,
bool weldVertices,
109 float weldTolerance,
bool identifyEdges,
bool useMedianSplit,
112 throw eve::Exception(
"%s: vertices must contain at least three packed XYZ points",
117 throw eve::Exception(
"%s: mesh exceeds 1000000 vertices or 2000000 triangles",
119 if (!std::isfinite(weldTolerance) || weldTolerance < 0.f)
121 std::vector<b3Vec3>
points;
123 for (
size_t i = 0; i <
vertices.size(); i += 3) {
133 std::vector<int32_t> mutableIndices =
indices;
135 def.vertices =
points.data();
136 def.indices = mutableIndices.data();
137 def.vertexCount =
static_cast<int>(
points.size());
138 def.triangleCount =
static_cast<int>(
indices.size() / 3);
139 def.weldVertices = weldVertices;
140 def.weldTolerance = weldTolerance;
141 def.identifyEdges = identifyEdges;
142 def.useMedianSplit = useMedianSplit;
143 b3MeshData *
mesh = b3CreateMesh(&def,
nullptr, 0);
144 if (!
mesh ||
mesh->triangleCount != def.triangleCount) {
151b3HeightFieldData *createCheckedHeightField(
int countX,
int countZ,
float cellSizeX,
153 const std::vector<float> &heights,
154 float globalMin,
float globalMax,
155 bool clockwiseWinding,
const char *
operation) {
156 if (countX < 2 || countZ < 2)
158 const size_t sampleCount =
static_cast<size_t>(countX) *
static_cast<size_t>(countZ);
159 if (sampleCount > 16000000)
161 if (heights.size() != sampleCount)
163 if (!(cellSizeX > 0.f) || !(cellSizeZ > 0.f) || !std::isfinite(cellSizeX) ||
164 !std::isfinite(cellSizeZ))
166 if (!std::isfinite(globalMin) || !std::isfinite(globalMax) || globalMin > globalMax)
168 for (
float height : heights) {
169 if (!std::isfinite(
height) || height < globalMin || height > globalMax)
170 throw eve::Exception(
"%s: every height must be finite and inside global range",
173 std::vector<float> mutableHeights = heights;
174 b3HeightFieldDef def{};
175 def.heights = mutableHeights.data();
176 def.scale = {cellSizeX, 1.f, cellSizeZ};
179 def.globalMinimumHeight = globalMin;
180 def.globalMaximumHeight = globalMax;
181 def.clockwiseWinding = clockwiseWinding;
182 return b3CreateHeightField(&def);
188 : world_(
world), bodyId_(bodyId), id_(
id), runtimeHandle_(runtimeHandle) {}
192 std::vector<Joint3D *> joints(world_->joints_.begin(), world_->joints_.end());
193 for (
Joint3D *joint : joints) {
194 if (joint && (joint->bodyA_ ==
this || joint->bodyB_ ==
this)) {
200 std::vector<Shape3D *> shapes(world_->shapes_.begin(), world_->shapes_.end());
202 if (
s &&
s->getBody() ==
this) {
207 b3Body_SetUserData(bodyId_,
nullptr);
208 b3DestroyBody(bodyId_);
219 if (
isValid()) b3Body_SetUserData(bodyId_,
nullptr);
230 std::vector<Joint3D *> joints(world_->joints_.begin(), world_->joints_.end());
231 for (
Joint3D *joint : joints) {
232 if (joint && (joint->bodyA_ ==
this || joint->bodyB_ ==
this)) {
237 std::vector<Shape3D *> shapes(world_->shapes_.begin(), world_->shapes_.end());
239 if (
s &&
s->getBody() ==
this) {
242 if (
s->isValid()) b3DestroyShape(
s->raw(),
false);
246 b3Body_SetUserData(bodyId_,
nullptr);
247 b3DestroyBody(bodyId_);
256 b3Body_SetTransform(bodyId_, b3Pos{
x,
y,
z}, b3Body_GetRotation(bodyId_));
261 return static_cast<float>(b3Body_GetPosition(bodyId_).x);
266 return static_cast<float>(b3Body_GetPosition(bodyId_).y);
271 return static_cast<float>(b3Body_GetPosition(bodyId_).z);
288 b3Body_SetTransform(bodyId_, b3Body_GetPosition(bodyId_),
q);
293 return b3Body_GetRotation(bodyId_).v.x;
298 return b3Body_GetRotation(bodyId_).v.y;
303 return b3Body_GetRotation(bodyId_).v.z;
308 return b3Body_GetRotation(bodyId_).s;
313 b3Body_SetLinearVelocity(bodyId_, b3Vec3{
vx,
vy,
vz});
318 return b3Body_GetLinearVelocity(bodyId_).x;
323 return b3Body_GetLinearVelocity(bodyId_).y;
328 return b3Body_GetLinearVelocity(bodyId_).z;
333 return b3Body_GetMass(bodyId_);
338 b3Body_SetAngularVelocity(bodyId_, b3Vec3{
wx,
wy,
wz});
343 return b3Body_GetAngularVelocity(bodyId_).x;
348 return b3Body_GetAngularVelocity(bodyId_).y;
353 return b3Body_GetAngularVelocity(bodyId_).z;
357 if (!
isValid())
return {0.f, 0.f, 0.f};
358 const b3Pos
value = b3Body_GetWorldPoint(
359 bodyId_, checkedVector(
x,
y,
z,
"Body3D.localToWorldPoint",
"point"));
360 return {
static_cast<float>(
value.x),
static_cast<float>(
value.y),
361 static_cast<float>(
value.z)};
365 auto valid = validateOwnedTransformInput(*
this,
x,
y,
z);
367 const b3Pos
value = b3Body_GetWorldPoint(bodyId_, b3Vec3{
x,
y,
z});
369 {
static_cast<float>(
value.x),
static_cast<float>(
value.y),
static_cast<float>(
value.z)});
373 if (!
isValid())
return {0.f, 0.f, 0.f};
374 const b3Vec3
value = b3Body_GetLocalPoint(
375 bodyId_, checkedVector(
x,
y,
z,
"Body3D.worldToLocalPoint",
"point"));
380 auto valid = validateOwnedTransformInput(*
this,
x,
y,
z);
382 const b3Vec3
value = b3Body_GetLocalPoint(bodyId_, b3Vec3{
x,
y,
z});
387 if (!
isValid())
return {0.f, 0.f, 0.f};
388 const b3Vec3
value = b3Body_GetWorldVector(
389 bodyId_, checkedVector(
x,
y,
z,
"Body3D.localToWorldVector",
"vector"));
394 auto valid = validateOwnedTransformInput(*
this,
x,
y,
z);
396 const b3Vec3
value = b3Body_GetWorldVector(bodyId_, b3Vec3{
x,
y,
z});
401 if (!
isValid())
return {0.f, 0.f, 0.f};
402 const b3Vec3
value = b3Body_GetLocalVector(
403 bodyId_, checkedVector(
x,
y,
z,
"Body3D.worldToLocalVector",
"vector"));
408 if (!
isValid())
return {0.f, 0.f, 0.f};
409 const b3Vec3
value = b3Body_GetLocalPointVelocity(
410 bodyId_, checkedVector(
x,
y,
z,
"Body3D.getLocalPointVelocity",
"point"));
415 auto valid = validateOwnedTransformInput(*
this,
x,
y,
z);
417 const b3Vec3
value = b3Body_GetLocalPointVelocity(bodyId_, b3Vec3{
x,
y,
z});
422 if (!
isValid())
return {0.f, 0.f, 0.f};
423 const b3Vec3
value = b3Body_GetWorldPointVelocity(
424 bodyId_, checkedVector(
x,
y,
z,
"Body3D.getWorldPointVelocity",
"point"));
430 b3Body_ApplyForceToCenter(bodyId_, b3Vec3{fx, fy, fz},
true);
435 b3Body_ApplyForce(bodyId_, b3Vec3{fx, fy, fz}, b3Pos{
x,
y,
z},
true);
440 b3Body_ApplyTorque(bodyId_, b3Vec3{tx, ty, tz},
true);
445 b3Body_ApplyLinearImpulseToCenter(bodyId_, b3Vec3{ix, iy, iz},
true);
450 b3Body_ApplyLinearImpulse(bodyId_, b3Vec3{ix, iy, iz}, b3Pos{
x,
y,
z},
true);
455 b3Body_ApplyAngularImpulse(bodyId_, b3Vec3{ix, iy, iz},
true);
460 const b3Vec3 force = b3Body_GetWorldVector(
461 bodyId_, checkedVector(fx, fy, fz,
"Body3D.applyLocalForce",
"force"));
462 const b3Pos
point = b3Body_GetWorldPoint(
463 bodyId_, checkedVector(
x,
y,
z,
"Body3D.applyLocalForce",
"point"));
464 b3Body_ApplyForce(bodyId_, force,
point,
true);
469 const b3Vec3 force = checkedVector(fx, fy, fz,
"Body3D.applyLocalForceToCenter",
471 b3Body_ApplyForceToCenter(bodyId_, b3Body_GetWorldVector(bodyId_, force),
true);
476 const b3Vec3 torque = checkedVector(tx, ty, tz,
"Body3D.applyLocalTorque",
"torque");
477 b3Body_ApplyTorque(bodyId_, b3Body_GetWorldVector(bodyId_, torque),
true);
483 const b3Vec3 impulse = b3Body_GetWorldVector(
484 bodyId_, checkedVector(ix, iy, iz,
"Body3D.applyLocalLinearImpulse",
"impulse"));
485 const b3Pos
point = b3Body_GetWorldPoint(
486 bodyId_, checkedVector(
x,
y,
z,
"Body3D.applyLocalLinearImpulse",
"point"));
487 b3Body_ApplyLinearImpulse(bodyId_, impulse,
point,
true);
492 const b3Vec3 impulse = checkedVector(ix, iy, iz,
493 "Body3D.applyLocalLinearImpulseToCenter",
"impulse");
494 b3Body_ApplyLinearImpulseToCenter(bodyId_, b3Body_GetWorldVector(bodyId_, impulse),
true);
499 const b3Vec3 impulse =
500 checkedVector(ix, iy, iz,
"Body3D.applyLocalAngularImpulse",
"impulse");
501 b3Body_ApplyAngularImpulse(bodyId_, b3Body_GetWorldVector(bodyId_, impulse),
true);
505 float qw,
float timeStep) {
507 requireFinite(
x,
"Body3D.setTargetTransform",
"x");
508 requireFinite(
y,
"Body3D.setTargetTransform",
"y");
509 requireFinite(
z,
"Body3D.setTargetTransform",
"z");
510 requireFinite(timeStep,
"Body3D.setTargetTransform",
"timeStep");
512 throw eve::Exception(
"Body3D.setTargetTransform: timeStep must be > 0");
514 normalizedQuaternion(
qx,
qy,
qz,
qw,
"Body3D.setTargetTransform");
515 b3Body_SetTargetTransform(bodyId_, b3WorldTransform{b3Pos{
x,
y,
z},
rotation}, timeStep,
521 requireNonNegative(damping,
"Body3D.setLinearDamping",
"damping");
522 b3Body_SetLinearDamping(bodyId_, damping);
526 return isValid() ? b3Body_GetLinearDamping(bodyId_) : 0.f;
531 requireNonNegative(damping,
"Body3D.setAngularDamping",
"damping");
532 b3Body_SetAngularDamping(bodyId_, damping);
536 return isValid() ? b3Body_GetAngularDamping(bodyId_) : 0.f;
541 requireFinite(
scale,
"Body3D.setGravityScale",
"scale");
542 b3Body_SetGravityScale(bodyId_,
scale);
546 return isValid() ? b3Body_GetGravityScale(bodyId_) : 0.f;
551 b3Body_EnableSleep(bodyId_,
enabled);
555 return isValid() ? b3Body_IsSleepEnabled(bodyId_) :
false;
560 requireNonNegative(threshold,
"Body3D.setSleepThreshold",
"threshold");
561 b3Body_SetSleepThreshold(bodyId_, threshold);
565 return isValid() ? b3Body_GetSleepThreshold(bodyId_) : 0.f;
569 bool angularY,
bool angularZ) {
571 b3Body_SetMotionLocks(bodyId_,
572 b3MotionLocks{linearX, linearY, linearZ, angularX, angularY,
576#define EV_BODY_LOCK_GETTER(name, member) \
577 bool Body3D::name() const { \
578 return isValid() ? b3Body_GetMotionLocks(bodyId_).member : false; \
586#undef EV_BODY_LOCK_GETTER
589 float inertiaXX,
float inertiaYY,
float inertiaZZ,
590 float inertiaXY,
float inertiaXZ,
float inertiaYZ) {
592 constexpr const char *
operation =
"Body3D.setMassProperties";
593 if (b3Body_GetType(bodyId_) != b3_dynamicBody)
596 requireFinite(centerX,
operation,
"centerX");
597 requireFinite(centerY,
operation,
"centerY");
598 requireFinite(centerZ,
operation,
"centerZ");
599 requireFinite(inertiaXX,
operation,
"inertiaXX");
600 requireFinite(inertiaYY,
operation,
"inertiaYY");
601 requireFinite(inertiaZZ,
operation,
"inertiaZZ");
602 requireFinite(inertiaXY,
operation,
"inertiaXY");
603 requireFinite(inertiaXZ,
operation,
"inertiaXZ");
604 requireFinite(inertiaYZ,
operation,
"inertiaYZ");
608 const double xx = inertiaXX, yy = inertiaYY, zz = inertiaZZ;
609 const double xy = inertiaXY, xz = inertiaXZ, yz = inertiaYZ;
610 const double minor2 = xx * yy - xy * xy;
611 const double determinant =
612 xx * (yy * zz - yz * yz) - xy * (xy * zz - yz * xz) +
613 xz * (xy * yz - yy * xz);
614 if (!(xx > 0.0 && minor2 > 0.0 && determinant > 0.0) ||
615 !std::isfinite(minor2) || !std::isfinite(determinant))
620 data.center = {centerX, centerY, centerZ};
621 data.inertia = {{inertiaXX, inertiaXY, inertiaXZ},
622 {inertiaXY, inertiaYY, inertiaYZ},
623 {inertiaXZ, inertiaYZ, inertiaZZ}};
624 b3Body_SetMassData(bodyId_, data);
629 b3Body_ApplyMassFromShapes(bodyId_);
632#define EV_BODY_INERTIA_GETTER(name, column, member) \
633 float Body3D::name() const { \
634 return isValid() ? b3Body_GetMassData(bodyId_).inertia.column.member : 0.f; \
642#undef EV_BODY_INERTIA_GETTER
644#define EV_BODY_CENTER_GETTER(name, functionName, member) \
645 float Body3D::name() const { \
646 return isValid() ? static_cast<float>(functionName(bodyId_).member) : 0.f; \
654#undef EV_BODY_CENTER_GETTER
658 const b3BodyType
type = parseBodyType(bodyType);
659 if (
type != b3_staticBody && world_) {
665 "Body3D.setType: triangle-mesh and height-field colliders require a static body");
668 b3Body_SetType(bodyId_,
type);
672 if (!
isValid())
return "static";
673 return bodyTypeName(b3Body_GetType(bodyId_));
678 b3MotionLocks locks = b3Body_GetMotionLocks(bodyId_);
679 locks.angularX = fixed;
680 locks.angularY = fixed;
681 locks.angularZ = fixed;
682 b3Body_SetMotionLocks(bodyId_, locks);
687 b3MotionLocks locks = b3Body_GetMotionLocks(bodyId_);
688 return locks.angularX && locks.angularY && locks.angularZ;
694 b3Body_Enable(bodyId_);
696 b3Body_Disable(bodyId_);
703 b3Body_SetBullet(bodyId_,
bullet);
710 b3Body_SetAwake(bodyId_,
awake);
719 throw eve::Exception(
"Body3D.newBoxShape: width/height/depth must be > 0");
721 float hx =
width * 0.5f;
723 float hz =
depth * 0.5f;
725 b3BoxHull box = b3MakeBoxHull(hx, hy, hz);
726 b3ShapeDef def = makeShapeDef(density, friction,
restitution);
727 b3ShapeId
id = b3CreateHullShape(bodyId_, &def, &box.base);
730 b3Shape_SetUserData(
id,
shape);
731 world_->shapes_.insert(
shape);
740 sphere.center = b3Vec3_zero;
743 b3ShapeDef def = makeShapeDef(density, friction,
restitution);
744 b3ShapeId
id = b3CreateSphereShape(bodyId_, &def, &sphere);
748 b3Shape_SetUserData(
id,
shape);
749 world_->shapes_.insert(
shape);
757 throw eve::Exception(
"Body3D.newCapsuleShape: height >= 0 and radius > 0 required");
759 float half =
height * 0.5f;
761 capsule.center1 = b3Vec3{0.f, -half, 0.f};
762 capsule.center2 = b3Vec3{0.f, half, 0.f};
765 b3ShapeDef def = makeShapeDef(density, friction,
restitution);
766 b3ShapeId
id = b3CreateCapsuleShape(bodyId_, &def, &capsule);
770 b3Shape_SetUserData(
id,
shape);
771 world_->shapes_.insert(
shape);
776 float density,
float friction,
float restitution) {
778 throw eve::Exception(
"Body3D.newConvexHullShape: body destroyed");
779 b3HullData *hull = createCheckedHull(
vertices, maxVertices,
"Body3D.newConvexHullShape");
780 b3ShapeDef def = makeShapeDef(density, friction,
restitution);
781 b3ShapeId
id = b3CreateHullShape(bodyId_, &def, hull);
786 b3Shape_SetUserData(
id,
shape);
787 world_->shapes_.insert(
shape);
792 const std::vector<int32_t> &
indices,
bool weldVertices,
793 float weldTolerance,
bool identifyEdges,
794 bool useMedianSplit) {
796 throw eve::Exception(
"Body3D.newTriangleMeshShape: body destroyed");
797 if (b3Body_GetType(bodyId_) != b3_staticBody)
798 throw eve::Exception(
"Body3D.newTriangleMeshShape: triangle meshes require a static body");
800 identifyEdges, useMedianSplit,
801 "Body3D.newTriangleMeshShape");
802 b3ShapeDef def = makeShapeDef(0.f, 0.2f, 0.f);
803 b3ShapeId
id = b3CreateMeshShape(bodyId_, &def,
mesh, b3Vec3_one);
804 if (B3_IS_NULL(
id)) {
806 throw eve::Exception(
"Body3D.newTriangleMeshShape: Box3D rejected the mesh shape");
811 b3Shape_SetUserData(
id,
shape);
812 world_->shapes_.insert(
shape);
817 float cellSizeZ,
const std::vector<float> &heights,
818 float globalMin,
float globalMax,
819 bool clockwiseWinding) {
821 throw eve::Exception(
"Body3D.newHeightFieldShape: body destroyed");
822 if (b3Body_GetType(bodyId_) != b3_staticBody)
823 throw eve::Exception(
"Body3D.newHeightFieldShape: height fields require a static body");
824 b3HeightFieldData *heightData = createCheckedHeightField(
825 countX, countZ, cellSizeX, cellSizeZ, heights, globalMin, globalMax,
826 clockwiseWinding,
"Body3D.newHeightFieldShape");
827 b3ShapeDef def = makeShapeDef(0.f, 0.2f, 0.f);
828 b3ShapeId
id = b3CreateHeightFieldShape(bodyId_, &def, heightData);
829 if (B3_IS_NULL(
id)) {
830 b3DestroyHeightField(heightData);
831 throw eve::Exception(
"Body3D.newHeightFieldShape: Box3D rejected the height field");
834 0.f, {}, 64, {}, {},
nullptr,
true, 0.001f,
true,
false, heights, countX, countZ,
835 cellSizeX, cellSizeZ, globalMin, globalMax, clockwiseWinding, heightData);
836 b3Shape_SetUserData(
id,
shape);
837 world_->shapes_.insert(
shape);
ActionParameterOperation operation
#define EV_BODY_LOCK_GETTER(name, member)
#define EV_BODY_INERTIA_GETTER(name, column, member)
#define EV_BODY_CENTER_GETTER(name, functionName, member)
std::array< double, 10 > q
std::vector< std::uint32_t > indices
std::array< float, 4 > rotation
std::array< float, 3 > scale
std::vector< Point > vertices
std::shared_ptr< const std::vector< glm::vec2 > > points
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
EVENGINE_API_FOUNDATION public API.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
static constexpr RuntimeHandle invalid() noexcept
Returns the canonical invalid handle.
void setActive(bool active)
Disables/enables the body and its shapes.
void setRotation(float qx, float qy, float qz, float qw)
Orientation as quaternion (x, y, z, w).
std::vector< float > localToWorldVector(float x, float y, float z) const
Rotates a body-local direction/vector into world space; returns {x,y,z}.
std::vector< float > worldToLocalVector(float x, float y, float z) const
Rotates a world direction/vector into body-local space; returns {x,y,z}.
bool isBullet() const
True when bullet.
void setAngularDamping(float damping)
Angular damping coefficient, finite and non-negative.
bool isActive() const
True when active.
void applyForce(float fx, float fy, float fz)
Force applied at the center of mass.
float getLinearVelocityY() const
Returns the linear velocity y.
std::vector< float > localToWorldPoint(float x, float y, float z) const
Converts a body-local point to world coordinates; returns {x,y,z}.
void applyLinearImpulse(float ix, float iy, float iz)
Instantaneous linear impulse.
void setFixedRotation(bool fixed)
Lock all angular axes (Box3D motion locks).
void applyAngularImpulse(float ix, float iy, float iz)
Instantaneous angular impulse.
float getAngularDamping() const
Current angular damping coefficient.
float getX() const
Returns the x.
void setLinearDamping(float damping)
Linear damping coefficient, finite and non-negative.
std::vector< float > worldToLocalPoint(float x, float y, float z) const
Converts a world point to body-local coordinates; returns {x,y,z}.
void applyLocalForceToCenter(float fx, float fy, float fz)
Body-local force applied at the center of mass.
float getMass() const
Body mass in kg.
bool isFixedRotation() const
True when fixed rotation.
eve::Result< PhysicsVector3D > getLocalPointVelocityOwned(float x, float y, float z) const
Returns allocation-free world velocity at a body-local point.
eve::Result< PhysicsVector3D > worldToLocalPointOwned(float x, float y, float z) const
Converts a world point to an allocation-free owning value with structured stale/input failure.
void setBullet(bool bullet)
CCD bullet mode.
Shape3D * newBoxShape(float width, float height, float depth, float density=1.f, float friction=0.2f, float restitution=0.f)
Creates a box shape in meter-space units.
bool isValid() const
True while the underlying Box3D body is still alive.
void applyLocalTorque(float tx, float ty, float tz)
Body-local torque.
float getY() const
Returns the y.
Shape3D * newTriangleMeshShape(const std::vector< float > &vertices, const std::vector< int32_t > &indices, bool weldVertices=true, float weldTolerance=0.001f, bool identifyEdges=true, bool useMedianSplit=false)
Creates a static concave triangle-mesh collider from packed arrays.
void setAwake(bool awake)
Wakes / sleeps the body manually.
void applyLinearImpulseAt(float ix, float iy, float iz, float x, float y, float z)
Instantaneous linear impulse applied at a world position.
eve::Result< PhysicsVector3D > localToWorldPointOwned(float x, float y, float z) const
Converts a local point to an allocation-free owning value with structured stale/input failure.
float getLinearDamping() const
Current linear damping coefficient.
Shape3D * newConvexHullShape(const std::vector< float > &vertices, int maxVertices=64, float density=1.f, float friction=0.2f, float restitution=0.f)
Creates a convex hull from packed local XYZ vertices.
void destroy()
Destroys the body inside its world.
float getRotZ() const
Returns the rot z.
float getSleepThreshold() const
Current sleep velocity threshold.
void setMassProperties(float mass, float centerX, float centerY, float centerZ, float inertiaXX, float inertiaYY, float inertiaZZ, float inertiaXY=0.f, float inertiaXZ=0.f, float inertiaYZ=0.f)
Overrides mass, local center of mass, and the symmetric local inertia tensor.
void setTargetTransform(float x, float y, float z, float qx, float qy, float qz, float qw, float timeStep)
Drives a kinematic body to a target pose over a positive time step. The target quaternion is normaliz...
float getRotY() const
Returns the rot y.
void applyLocalForce(float fx, float fy, float fz, float x, float y, float z)
Body-local force applied at a body-local point.
float getGravityScale() const
Current world-gravity multiplier.
float getRotX() const
Returns the rot x.
float getAngularVelocityZ() const
Returns the angular velocity z.
void setSleepEnabled(bool enabled)
Enables or disables automatic sleeping for this body.
void invalidate()
Internal: marks the wrapper invalid after world destruction.
void applyLocalLinearImpulseToCenter(float ix, float iy, float iz)
Body-local linear impulse applied at the center of mass.
float getAngularVelocityX() const
Returns the angular velocity x.
std::string getType() const
Returns the type.
float getRotW() const
Returns the rot w.
void applyTorque(float tx, float ty, float tz)
Torque applied in world space.
bool isSleepEnabled() const
Whether automatic sleeping is enabled.
void setMotionLocks(bool linearX, bool linearY, bool linearZ, bool angularX, bool angularY, bool angularZ)
Atomically locks translation and rotation on individual local solver axes.
void applyForceAt(float fx, float fy, float fz, float x, float y, float z)
Force applied at a world position.
void resetMassProperties()
Restores automatic mass, center, and inertia calculation from attached shapes.
eve::Result< PhysicsVector3D > localToWorldVectorOwned(float x, float y, float z) const
Rotates a local vector into an allocation-free owning world vector.
Shape3D * newCapsuleShape(float height, float radius, float density=1.f, float friction=0.2f, float restitution=0.f)
Creates a capsule along local Y.
void setLinearVelocity(float vx, float vy, float vz)
Linear velocity in m/s.
void applyLocalLinearImpulse(float ix, float iy, float iz, float x, float y, float z)
Body-local linear impulse applied at a body-local point.
void setGravityScale(float scale)
Multiplier applied to world gravity; may be negative.
bool isAwake() const
True when awake.
Shape3D * newHeightFieldShape(int countX, int countZ, float cellSizeX, float cellSizeZ, const std::vector< float > &heights, float globalMin, float globalMax, bool clockwiseWinding=false)
Creates a compressed static height-field collider extending along +X/+Z.
void setPosition(float x, float y, float z)
Position in meters.
void setType(const std::string &bodyType)
"static" | "kinematic" | "dynamic".
float getLinearVelocityZ() const
Returns the linear velocity z.
std::vector< float > getWorldPointVelocity(float x, float y, float z) const
World-space velocity at the supplied world point.
void setAngularVelocity(float wx, float wy, float wz)
Angular velocity in rad/s.
void setSleepThreshold(float threshold)
Sleep velocity threshold, finite and non-negative.
float getAngularVelocityY() const
Returns the angular velocity y.
Body3D(World3D *world, b3BodyId bodyId, int id, PhysicsBodyHandle runtimeHandle)
Internal: wraps a Box3D body (use World3D::newBody).
std::vector< float > getLocalPointVelocity(float x, float y, float z) const
World-space velocity at a point expressed in body-local coordinates.
float getZ() const
Returns the z.
Shape3D * newSphereShape(float radius, float density=1.f, float friction=0.2f, float restitution=0.f)
Creates a sphere shape in meter-space units.
float getLinearVelocityX() const
Returns the linear velocity x.
void applyLocalAngularImpulse(float ix, float iy, float iz)
Body-local angular impulse.
Script-facing Box3D joint owned by a World3D.
3D shape (box/sphere/capsule) attached to a Body3D with material settings. Created via Body3D::new*Sh...
Box3D rigid-body world. Script coordinates are meters (Box3D native), unlike 2D World which uses pixe...
void forgetBody(Body3D *body)
Internal: wrapper teardown bookkeeping.
void forgetJoint(Joint3D *joint)
Internal: removes a joint wrapper from ownership bookkeeping.
bool isValid() const
True while the underlying Box3D world is alive.
void forgetShape(Shape3D *shape)
PhysicsShapeHandle nextShapeRuntimeHandle()
Internal: next generation-qualified shape handle.
float lengthSquared(Vec3 value)
Length squared.
Optional physics backend for vehicle mobility and body attach.