载入中...
搜索中...
未找到
Body3D.cpp
浏览该文件的文档.
1#include "physics/Body3D.h"
2#include "physics/Shape3D.h"
3#include "physics/World3D.h"
4#include "physics/Joint3D.h"
5
6#include "common/Exception.h"
7
8#include <box3d/box3d.h>
9
10#include <cmath>
11#include <vector>
12
13namespace eve::physics {
14namespace {
15
16eve::Result<void> validateOwnedTransformInput(const Body3D& body, float x, float y, float z) {
17 if (!body.isValid())
19 eve::DiagnosticCode::StaleHandle, "physics body is no longer valid", "body", {}, "physics.body3d"));
20 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(z))
22 eve::DiagnosticCode::InvalidArgument, "transform input must be finite", "value", {}, "physics.body3d"));
24}
25
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;
30 throw eve::Exception("Body3D.setType: unknown body type '%s'", type.c_str());
31}
32
33const char *bodyTypeName(b3BodyType t) {
34 switch (t) {
35 case b3_staticBody: return "static";
36 case b3_kinematicBody: return "kinematic";
37 case b3_dynamicBody: return "dynamic";
38 default: return "static";
39 }
40}
41
42void requireFinite(float value, const char *operation, const char *parameter) {
43 if (!std::isfinite(value))
44 throw eve::Exception("%s: %s must be finite", operation, parameter);
45}
46
47void requireNonNegative(float value, const char *operation, const char *parameter) {
48 requireFinite(value, operation, parameter);
49 if (value < 0.f)
50 throw eve::Exception("%s: %s must be >= 0", operation, parameter);
51}
52
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))
56 throw eve::Exception("%s: %s components must be finite", operation, parameter);
57 return {x, y, z};
58}
59
60b3Quat normalizedQuaternion(float qx, float qy, float qz, float qw, const char *operation) {
61 requireFinite(qx, operation, "qx");
62 requireFinite(qy, operation, "qy");
63 requireFinite(qz, operation, "qz");
64 requireFinite(qw, operation, "qw");
65 const float lengthSquared = qx * qx + qy * qy + qz * qz + qw * qw;
66 if (lengthSquared <= 1e-16f)
67 throw eve::Exception("%s: quaternion length must be > 0", operation);
68 const float inverseLength = 1.f / std::sqrt(lengthSquared);
69 return b3Quat{{qx * inverseLength, qy * inverseLength, qz * inverseLength},
70 qw * inverseLength};
71}
72
73b3ShapeDef makeShapeDef(float density, float friction, float restitution) {
74 b3ShapeDef def = b3DefaultShapeDef();
75 def.density = density;
76 def.baseMaterial.friction = friction;
77 def.baseMaterial.restitution = restitution;
78 def.enableContactEvents = true;
79 def.enableSensorEvents = true;
80 return def;
81}
82
83b3HullData *createCheckedHull(const std::vector<float> &vertices, int maxVertices,
84 const char *operation) {
85 if (vertices.size() < 12 || vertices.size() % 3 != 0)
86 throw eve::Exception("%s: vertices must contain at least four packed XYZ points",
87 operation);
88 if (vertices.size() / 3 > 100000)
89 throw eve::Exception("%s: source point count must be <= 100000", operation);
90 if (maxVertices < 4 || maxVertices > 254)
91 throw eve::Exception("%s: maxVertices must be in [4, 254]", operation);
92 std::vector<b3Vec3> points;
93 points.reserve(vertices.size() / 3);
94 for (size_t i = 0; i < vertices.size(); i += 3) {
95 if (!std::isfinite(vertices[i]) || !std::isfinite(vertices[i + 1]) ||
96 !std::isfinite(vertices[i + 2]))
97 throw eve::Exception("%s: all vertex components must be finite", operation);
98 points.push_back({vertices[i], vertices[i + 1], vertices[i + 2]});
99 }
100 b3HullData *hull = b3CreateHull(points.data(), static_cast<int>(points.size()), maxVertices);
101 if (!hull)
102 throw eve::Exception("%s: points do not form a valid three-dimensional convex hull",
103 operation);
104 return hull;
105}
106
107b3MeshData *createCheckedMesh(const std::vector<float> &vertices,
108 const std::vector<int32_t> &indices, bool weldVertices,
109 float weldTolerance, bool identifyEdges, bool useMedianSplit,
110 const char *operation) {
111 if (vertices.size() < 9 || vertices.size() % 3 != 0)
112 throw eve::Exception("%s: vertices must contain at least three packed XYZ points",
113 operation);
114 if (indices.size() < 3 || indices.size() % 3 != 0)
115 throw eve::Exception("%s: indices must contain complete triangles", operation);
116 if (vertices.size() / 3 > 1000000 || indices.size() / 3 > 2000000)
117 throw eve::Exception("%s: mesh exceeds 1000000 vertices or 2000000 triangles",
118 operation);
119 if (!std::isfinite(weldTolerance) || weldTolerance < 0.f)
120 throw eve::Exception("%s: weldTolerance must be finite and >= 0", operation);
121 std::vector<b3Vec3> points;
122 points.reserve(vertices.size() / 3);
123 for (size_t i = 0; i < vertices.size(); i += 3) {
124 if (!std::isfinite(vertices[i]) || !std::isfinite(vertices[i + 1]) ||
125 !std::isfinite(vertices[i + 2]))
126 throw eve::Exception("%s: all vertex components must be finite", operation);
127 points.push_back({vertices[i], vertices[i + 1], vertices[i + 2]});
128 }
129 for (int32_t index : indices) {
130 if (index < 0 || static_cast<size_t>(index) >= points.size())
131 throw eve::Exception("%s: triangle index is outside the vertex array", operation);
132 }
133 std::vector<int32_t> mutableIndices = indices;
134 b3MeshDef def{};
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) {
145 if (mesh) b3DestroyMesh(mesh);
146 throw eve::Exception("%s: mesh contains degenerate or zero-area triangles", operation);
147 }
148 return mesh;
149}
150
151b3HeightFieldData *createCheckedHeightField(int countX, int countZ, float cellSizeX,
152 float cellSizeZ,
153 const std::vector<float> &heights,
154 float globalMin, float globalMax,
155 bool clockwiseWinding, const char *operation) {
156 if (countX < 2 || countZ < 2)
157 throw eve::Exception("%s: countX and countZ must be >= 2", operation);
158 const size_t sampleCount = static_cast<size_t>(countX) * static_cast<size_t>(countZ);
159 if (sampleCount > 16000000)
160 throw eve::Exception("%s: sample count must be <= 16000000", operation);
161 if (heights.size() != sampleCount)
162 throw eve::Exception("%s: heights size must equal countX * countZ", operation);
163 if (!(cellSizeX > 0.f) || !(cellSizeZ > 0.f) || !std::isfinite(cellSizeX) ||
164 !std::isfinite(cellSizeZ))
165 throw eve::Exception("%s: cell sizes must be finite and > 0", operation);
166 if (!std::isfinite(globalMin) || !std::isfinite(globalMax) || globalMin > globalMax)
167 throw eve::Exception("%s: global height range must be finite and ordered", operation);
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",
171 operation);
172 }
173 std::vector<float> mutableHeights = heights;
174 b3HeightFieldDef def{};
175 def.heights = mutableHeights.data();
176 def.scale = {cellSizeX, 1.f, cellSizeZ};
177 def.countX = countX;
178 def.countZ = countZ;
179 def.globalMinimumHeight = globalMin;
180 def.globalMaximumHeight = globalMax;
181 def.clockwiseWinding = clockwiseWinding;
182 return b3CreateHeightField(&def);
183}
184
185} // namespace
186
187Body3D::Body3D(World3D *world, b3BodyId bodyId, int id, PhysicsBodyHandle runtimeHandle)
188 : world_(world), bodyId_(bodyId), id_(id), runtimeHandle_(runtimeHandle) {}
189
191 if (isValid() && world_ && world_->isValid()) {
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)) {
195 world_->forgetJoint(joint);
196 joint->invalidate();
197 }
198 }
199 // Invalidate shapes that still reference this body.
200 std::vector<Shape3D *> shapes(world_->shapes_.begin(), world_->shapes_.end());
201 for (Shape3D *s : shapes) {
202 if (s && s->getBody() == this) {
203 world_->forgetShape(s);
204 s->invalidate();
205 }
206 }
207 b3Body_SetUserData(bodyId_, nullptr);
208 b3DestroyBody(bodyId_);
209 world_->forgetBody(this);
210 }
211 bodyId_ = {};
212 world_ = nullptr;
213 runtimeHandle_ = PhysicsBodyHandle::invalid();
214}
215
216bool Body3D::isValid() const { return b3Body_IsValid(bodyId_); }
217
219 if (isValid()) b3Body_SetUserData(bodyId_, nullptr);
220 bodyId_ = {};
221 world_ = nullptr;
222 runtimeHandle_ = PhysicsBodyHandle::invalid();
223}
224
226 if (!isValid() || !world_ || !world_->isValid()) {
227 invalidate();
228 return;
229 }
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)) {
233 world_->forgetJoint(joint);
234 joint->invalidate();
235 }
236 }
237 std::vector<Shape3D *> shapes(world_->shapes_.begin(), world_->shapes_.end());
238 for (Shape3D *s : shapes) {
239 if (s && s->getBody() == this) {
240 world_->forgetShape(s);
241 // Destroy the underlying shape before invalidating the wrapper.
242 if (s->isValid()) b3DestroyShape(s->raw(), false);
243 s->invalidate();
244 }
245 }
246 b3Body_SetUserData(bodyId_, nullptr);
247 b3DestroyBody(bodyId_);
248 world_->forgetBody(this);
249 bodyId_ = {};
250 world_ = nullptr;
251 runtimeHandle_ = PhysicsBodyHandle::invalid();
252}
253
254void Body3D::setPosition(float x, float y, float z) {
255 if (!isValid()) return;
256 b3Body_SetTransform(bodyId_, b3Pos{x, y, z}, b3Body_GetRotation(bodyId_));
257}
258
259float Body3D::getX() const {
260 if (!isValid()) return 0.f;
261 return static_cast<float>(b3Body_GetPosition(bodyId_).x);
262}
263
264float Body3D::getY() const {
265 if (!isValid()) return 0.f;
266 return static_cast<float>(b3Body_GetPosition(bodyId_).y);
267}
268
269float Body3D::getZ() const {
270 if (!isValid()) return 0.f;
271 return static_cast<float>(b3Body_GetPosition(bodyId_).z);
272}
273
274void Body3D::setRotation(float qx, float qy, float qz, float qw) {
275 if (!isValid()) return;
276 b3Quat q;
277 q.v = b3Vec3{qx, qy, qz};
278 q.s = qw;
279 float len = std::sqrt(qx * qx + qy * qy + qz * qz + qw * qw);
280 if (len > 1e-8f) {
281 q.v.x /= len;
282 q.v.y /= len;
283 q.v.z /= len;
284 q.s /= len;
285 } else {
286 q = b3Quat_identity;
287 }
288 b3Body_SetTransform(bodyId_, b3Body_GetPosition(bodyId_), q);
289}
290
291float Body3D::getRotX() const {
292 if (!isValid()) return 0.f;
293 return b3Body_GetRotation(bodyId_).v.x;
294}
295
296float Body3D::getRotY() const {
297 if (!isValid()) return 0.f;
298 return b3Body_GetRotation(bodyId_).v.y;
299}
300
301float Body3D::getRotZ() const {
302 if (!isValid()) return 0.f;
303 return b3Body_GetRotation(bodyId_).v.z;
304}
305
306float Body3D::getRotW() const {
307 if (!isValid()) return 1.f;
308 return b3Body_GetRotation(bodyId_).s;
309}
310
311void Body3D::setLinearVelocity(float vx, float vy, float vz) {
312 if (!isValid()) return;
313 b3Body_SetLinearVelocity(bodyId_, b3Vec3{vx, vy, vz});
314}
315
317 if (!isValid()) return 0.f;
318 return b3Body_GetLinearVelocity(bodyId_).x;
319}
320
322 if (!isValid()) return 0.f;
323 return b3Body_GetLinearVelocity(bodyId_).y;
324}
325
327 if (!isValid()) return 0.f;
328 return b3Body_GetLinearVelocity(bodyId_).z;
329}
330
331float Body3D::getMass() const {
332 if (!isValid()) return 0.f;
333 return b3Body_GetMass(bodyId_);
334}
335
336void Body3D::setAngularVelocity(float wx, float wy, float wz) {
337 if (!isValid()) return;
338 b3Body_SetAngularVelocity(bodyId_, b3Vec3{wx, wy, wz});
339}
340
342 if (!isValid()) return 0.f;
343 return b3Body_GetAngularVelocity(bodyId_).x;
344}
345
347 if (!isValid()) return 0.f;
348 return b3Body_GetAngularVelocity(bodyId_).y;
349}
350
352 if (!isValid()) return 0.f;
353 return b3Body_GetAngularVelocity(bodyId_).z;
354}
355
356std::vector<float> Body3D::localToWorldPoint(float x, float y, float z) const {
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)};
362}
363
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)});
370}
371
372std::vector<float> Body3D::worldToLocalPoint(float x, float y, float z) const {
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"));
376 return {value.x, value.y, value.z};
377}
378
380 auto valid = validateOwnedTransformInput(*this, x, y, z);
382 const b3Vec3 value = b3Body_GetLocalPoint(bodyId_, b3Vec3{x, y, z});
384}
385
386std::vector<float> Body3D::localToWorldVector(float x, float y, float z) const {
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"));
390 return {value.x, value.y, value.z};
391}
392
394 auto valid = validateOwnedTransformInput(*this, x, y, z);
396 const b3Vec3 value = b3Body_GetWorldVector(bodyId_, b3Vec3{x, y, z});
398}
399
400std::vector<float> Body3D::worldToLocalVector(float x, float y, float z) const {
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"));
404 return {value.x, value.y, value.z};
405}
406
407std::vector<float> Body3D::getLocalPointVelocity(float x, float y, float z) const {
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"));
411 return {value.x, value.y, value.z};
412}
413
415 auto valid = validateOwnedTransformInput(*this, x, y, z);
417 const b3Vec3 value = b3Body_GetLocalPointVelocity(bodyId_, b3Vec3{x, y, z});
419}
420
421std::vector<float> Body3D::getWorldPointVelocity(float x, float y, float z) const {
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"));
425 return {value.x, value.y, value.z};
426}
427
428void Body3D::applyForce(float fx, float fy, float fz) {
429 if (!isValid()) return;
430 b3Body_ApplyForceToCenter(bodyId_, b3Vec3{fx, fy, fz}, true);
431}
432
433void Body3D::applyForceAt(float fx, float fy, float fz, float x, float y, float z) {
434 if (!isValid()) return;
435 b3Body_ApplyForce(bodyId_, b3Vec3{fx, fy, fz}, b3Pos{x, y, z}, true);
436}
437
438void Body3D::applyTorque(float tx, float ty, float tz) {
439 if (!isValid()) return;
440 b3Body_ApplyTorque(bodyId_, b3Vec3{tx, ty, tz}, true);
441}
442
443void Body3D::applyLinearImpulse(float ix, float iy, float iz) {
444 if (!isValid()) return;
445 b3Body_ApplyLinearImpulseToCenter(bodyId_, b3Vec3{ix, iy, iz}, true);
446}
447
448void Body3D::applyLinearImpulseAt(float ix, float iy, float iz, float x, float y, float z) {
449 if (!isValid()) return;
450 b3Body_ApplyLinearImpulse(bodyId_, b3Vec3{ix, iy, iz}, b3Pos{x, y, z}, true);
451}
452
453void Body3D::applyAngularImpulse(float ix, float iy, float iz) {
454 if (!isValid()) return;
455 b3Body_ApplyAngularImpulse(bodyId_, b3Vec3{ix, iy, iz}, true);
456}
457
458void Body3D::applyLocalForce(float fx, float fy, float fz, float x, float y, float z) {
459 if (!isValid()) return;
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);
465}
466
467void Body3D::applyLocalForceToCenter(float fx, float fy, float fz) {
468 if (!isValid()) return;
469 const b3Vec3 force = checkedVector(fx, fy, fz, "Body3D.applyLocalForceToCenter",
470 "force");
471 b3Body_ApplyForceToCenter(bodyId_, b3Body_GetWorldVector(bodyId_, force), true);
472}
473
474void Body3D::applyLocalTorque(float tx, float ty, float tz) {
475 if (!isValid()) return;
476 const b3Vec3 torque = checkedVector(tx, ty, tz, "Body3D.applyLocalTorque", "torque");
477 b3Body_ApplyTorque(bodyId_, b3Body_GetWorldVector(bodyId_, torque), true);
478}
479
480void Body3D::applyLocalLinearImpulse(float ix, float iy, float iz, float x, float y,
481 float z) {
482 if (!isValid()) return;
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);
488}
489
490void Body3D::applyLocalLinearImpulseToCenter(float ix, float iy, float iz) {
491 if (!isValid()) return;
492 const b3Vec3 impulse = checkedVector(ix, iy, iz,
493 "Body3D.applyLocalLinearImpulseToCenter", "impulse");
494 b3Body_ApplyLinearImpulseToCenter(bodyId_, b3Body_GetWorldVector(bodyId_, impulse), true);
495}
496
497void Body3D::applyLocalAngularImpulse(float ix, float iy, float iz) {
498 if (!isValid()) return;
499 const b3Vec3 impulse =
500 checkedVector(ix, iy, iz, "Body3D.applyLocalAngularImpulse", "impulse");
501 b3Body_ApplyAngularImpulse(bodyId_, b3Body_GetWorldVector(bodyId_, impulse), true);
502}
503
504void Body3D::setTargetTransform(float x, float y, float z, float qx, float qy, float qz,
505 float qw, float timeStep) {
506 if (!isValid()) return;
507 requireFinite(x, "Body3D.setTargetTransform", "x");
508 requireFinite(y, "Body3D.setTargetTransform", "y");
509 requireFinite(z, "Body3D.setTargetTransform", "z");
510 requireFinite(timeStep, "Body3D.setTargetTransform", "timeStep");
511 if (timeStep <= 0.f)
512 throw eve::Exception("Body3D.setTargetTransform: timeStep must be > 0");
513 const b3Quat rotation =
514 normalizedQuaternion(qx, qy, qz, qw, "Body3D.setTargetTransform");
515 b3Body_SetTargetTransform(bodyId_, b3WorldTransform{b3Pos{x, y, z}, rotation}, timeStep,
516 true);
517}
518
519void Body3D::setLinearDamping(float damping) {
520 if (!isValid()) return;
521 requireNonNegative(damping, "Body3D.setLinearDamping", "damping");
522 b3Body_SetLinearDamping(bodyId_, damping);
523}
524
526 return isValid() ? b3Body_GetLinearDamping(bodyId_) : 0.f;
527}
528
529void Body3D::setAngularDamping(float damping) {
530 if (!isValid()) return;
531 requireNonNegative(damping, "Body3D.setAngularDamping", "damping");
532 b3Body_SetAngularDamping(bodyId_, damping);
533}
534
536 return isValid() ? b3Body_GetAngularDamping(bodyId_) : 0.f;
537}
538
540 if (!isValid()) return;
541 requireFinite(scale, "Body3D.setGravityScale", "scale");
542 b3Body_SetGravityScale(bodyId_, scale);
543}
544
546 return isValid() ? b3Body_GetGravityScale(bodyId_) : 0.f;
547}
548
550 if (!isValid()) return;
551 b3Body_EnableSleep(bodyId_, enabled);
552}
553
555 return isValid() ? b3Body_IsSleepEnabled(bodyId_) : false;
556}
557
558void Body3D::setSleepThreshold(float threshold) {
559 if (!isValid()) return;
560 requireNonNegative(threshold, "Body3D.setSleepThreshold", "threshold");
561 b3Body_SetSleepThreshold(bodyId_, threshold);
562}
563
565 return isValid() ? b3Body_GetSleepThreshold(bodyId_) : 0.f;
566}
567
568void Body3D::setMotionLocks(bool linearX, bool linearY, bool linearZ, bool angularX,
569 bool angularY, bool angularZ) {
570 if (!isValid()) return;
571 b3Body_SetMotionLocks(bodyId_,
572 b3MotionLocks{linearX, linearY, linearZ, angularX, angularY,
573 angularZ});
574}
575
576#define EV_BODY_LOCK_GETTER(name, member) \
577 bool Body3D::name() const { \
578 return isValid() ? b3Body_GetMotionLocks(bodyId_).member : false; \
579 }
580EV_BODY_LOCK_GETTER(isLinearXLocked, linearX)
581EV_BODY_LOCK_GETTER(isLinearYLocked, linearY)
582EV_BODY_LOCK_GETTER(isLinearZLocked, linearZ)
583EV_BODY_LOCK_GETTER(isAngularXLocked, angularX)
584EV_BODY_LOCK_GETTER(isAngularYLocked, angularY)
585EV_BODY_LOCK_GETTER(isAngularZLocked, angularZ)
586#undef EV_BODY_LOCK_GETTER
587
588void Body3D::setMassProperties(float mass, float centerX, float centerY, float centerZ,
589 float inertiaXX, float inertiaYY, float inertiaZZ,
590 float inertiaXY, float inertiaXZ, float inertiaYZ) {
591 if (!isValid()) return;
592 constexpr const char *operation = "Body3D.setMassProperties";
593 if (b3Body_GetType(bodyId_) != b3_dynamicBody)
594 throw eve::Exception("%s: body must be dynamic", operation);
595 requireFinite(mass, operation, "mass");
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");
605 if (!(mass > 0.f)) throw eve::Exception("%s: mass must be > 0", operation);
606
607 // Sylvester's criterion for a symmetric positive-definite 3x3 tensor.
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))
616 throw eve::Exception("%s: inertia tensor must be positive definite", operation);
617
618 b3MassData data{};
619 data.mass = mass;
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);
625}
626
628 if (!isValid()) return;
629 b3Body_ApplyMassFromShapes(bodyId_);
630}
631
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; \
635 }
636EV_BODY_INERTIA_GETTER(getInertiaXX, cx, x)
637EV_BODY_INERTIA_GETTER(getInertiaYY, cy, y)
638EV_BODY_INERTIA_GETTER(getInertiaZZ, cz, z)
639EV_BODY_INERTIA_GETTER(getInertiaXY, cx, y)
640EV_BODY_INERTIA_GETTER(getInertiaXZ, cx, z)
641EV_BODY_INERTIA_GETTER(getInertiaYZ, cy, z)
642#undef EV_BODY_INERTIA_GETTER
643
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; \
647 }
648EV_BODY_CENTER_GETTER(getLocalCenterX, b3Body_GetLocalCenterOfMass, x)
649EV_BODY_CENTER_GETTER(getLocalCenterY, b3Body_GetLocalCenterOfMass, y)
650EV_BODY_CENTER_GETTER(getLocalCenterZ, b3Body_GetLocalCenterOfMass, z)
651EV_BODY_CENTER_GETTER(getWorldCenterX, b3Body_GetWorldCenterOfMass, x)
652EV_BODY_CENTER_GETTER(getWorldCenterY, b3Body_GetWorldCenterOfMass, y)
653EV_BODY_CENTER_GETTER(getWorldCenterZ, b3Body_GetWorldCenterOfMass, z)
654#undef EV_BODY_CENTER_GETTER
655
656void Body3D::setType(const std::string &bodyType) {
657 if (!isValid()) return;
658 const b3BodyType type = parseBodyType(bodyType);
659 if (type != b3_staticBody && world_) {
660 for (Shape3D *shape : world_->shapes_) {
661 if (shape && shape->getBody() == this &&
664 throw eve::Exception(
665 "Body3D.setType: triangle-mesh and height-field colliders require a static body");
666 }
667 }
668 b3Body_SetType(bodyId_, type);
669}
670
671std::string Body3D::getType() const {
672 if (!isValid()) return "static";
673 return bodyTypeName(b3Body_GetType(bodyId_));
674}
675
676void Body3D::setFixedRotation(bool fixed) {
677 if (!isValid()) return;
678 b3MotionLocks locks = b3Body_GetMotionLocks(bodyId_);
679 locks.angularX = fixed;
680 locks.angularY = fixed;
681 locks.angularZ = fixed;
682 b3Body_SetMotionLocks(bodyId_, locks);
683}
684
686 if (!isValid()) return false;
687 b3MotionLocks locks = b3Body_GetMotionLocks(bodyId_);
688 return locks.angularX && locks.angularY && locks.angularZ;
689}
690
692 if (!isValid()) return;
693 if (active)
694 b3Body_Enable(bodyId_);
695 else
696 b3Body_Disable(bodyId_);
697}
698
699bool Body3D::isActive() const { return isValid() ? b3Body_IsEnabled(bodyId_) : false; }
700
702 if (!isValid()) return;
703 b3Body_SetBullet(bodyId_, bullet);
704}
705
706bool Body3D::isBullet() const { return isValid() ? b3Body_IsBullet(bodyId_) : false; }
707
709 if (!isValid()) return;
710 b3Body_SetAwake(bodyId_, awake);
711}
712
713bool Body3D::isAwake() const { return isValid() ? b3Body_IsAwake(bodyId_) : false; }
714
715Shape3D *Body3D::newBoxShape(float width, float height, float depth, float density,
716 float friction, float restitution) {
717 if (!isValid() || !world_) throw eve::Exception("Body3D.newBoxShape: body destroyed");
718 if (width <= 0.f || height <= 0.f || depth <= 0.f)
719 throw eve::Exception("Body3D.newBoxShape: width/height/depth must be > 0");
720
721 float hx = width * 0.5f;
722 float hy = height * 0.5f;
723 float hz = depth * 0.5f;
724
725 b3BoxHull box = b3MakeBoxHull(hx, hy, hz);
726 b3ShapeDef def = makeShapeDef(density, friction, restitution);
727 b3ShapeId id = b3CreateHullShape(bodyId_, &def, &box.base);
728
729 auto *shape = new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::Box, hx, hy, hz);
730 b3Shape_SetUserData(id, shape);
731 world_->shapes_.insert(shape);
732 return shape;
733}
734
735Shape3D *Body3D::newSphereShape(float radius, float density, float friction, float restitution) {
736 if (!isValid() || !world_) throw eve::Exception("Body3D.newSphereShape: body destroyed");
737 if (radius <= 0.f) throw eve::Exception("Body3D.newSphereShape: radius must be > 0");
738
739 b3Sphere sphere;
740 sphere.center = b3Vec3_zero;
741 sphere.radius = radius;
742
743 b3ShapeDef def = makeShapeDef(density, friction, restitution);
744 b3ShapeId id = b3CreateSphereShape(bodyId_, &def, &sphere);
745
746 auto *shape =
747 new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::Sphere, radius, 0.f, 0.f);
748 b3Shape_SetUserData(id, shape);
749 world_->shapes_.insert(shape);
750 return shape;
751}
752
753Shape3D *Body3D::newCapsuleShape(float height, float radius, float density, float friction,
754 float restitution) {
755 if (!isValid() || !world_) throw eve::Exception("Body3D.newCapsuleShape: body destroyed");
756 if (height < 0.f || radius <= 0.f)
757 throw eve::Exception("Body3D.newCapsuleShape: height >= 0 and radius > 0 required");
758
759 float half = height * 0.5f;
760 b3Capsule capsule;
761 capsule.center1 = b3Vec3{0.f, -half, 0.f};
762 capsule.center2 = b3Vec3{0.f, half, 0.f};
763 capsule.radius = radius;
764
765 b3ShapeDef def = makeShapeDef(density, friction, restitution);
766 b3ShapeId id = b3CreateCapsuleShape(bodyId_, &def, &capsule);
767
768 auto *shape =
769 new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::Capsule, half, radius, 0.f);
770 b3Shape_SetUserData(id, shape);
771 world_->shapes_.insert(shape);
772 return shape;
773}
774
775Shape3D *Body3D::newConvexHullShape(const std::vector<float> &vertices, int maxVertices,
776 float density, float friction, float restitution) {
777 if (!isValid() || !world_)
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);
782 b3DestroyHull(hull);
783
784 auto *shape = new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::ConvexHull, 0.f, 0.f,
785 0.f, vertices, maxVertices);
786 b3Shape_SetUserData(id, shape);
787 world_->shapes_.insert(shape);
788 return shape;
789}
790
792 const std::vector<int32_t> &indices, bool weldVertices,
793 float weldTolerance, bool identifyEdges,
794 bool useMedianSplit) {
795 if (!isValid() || !world_)
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");
799 b3MeshData *mesh = createCheckedMesh(vertices, indices, weldVertices, weldTolerance,
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)) {
805 b3DestroyMesh(mesh);
806 throw eve::Exception("Body3D.newTriangleMeshShape: Box3D rejected the mesh shape");
807 }
808 auto *shape =
809 new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::TriangleMesh, 0.f, 0.f, 0.f, {},
810 64, vertices, indices, mesh, weldVertices, weldTolerance, identifyEdges, useMedianSplit);
811 b3Shape_SetUserData(id, shape);
812 world_->shapes_.insert(shape);
813 return shape;
814}
815
816Shape3D *Body3D::newHeightFieldShape(int countX, int countZ, float cellSizeX,
817 float cellSizeZ, const std::vector<float> &heights,
818 float globalMin, float globalMax,
819 bool clockwiseWinding) {
820 if (!isValid() || !world_)
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");
832 }
833 auto *shape = new Shape3D(world_, this, id, world_->nextShapeRuntimeHandle(), Shape3D::Kind::HeightField, 0.f, 0.f,
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);
838 return shape;
839}
840
841} // namespace eve::physics
ActionParameterOperation operation
double value
bool & active
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
const std::string & s
#define EV_BODY_LOCK_GETTER(name, member)
Definition Body3D.cpp:576
#define EV_BODY_INERTIA_GETTER(name, column, member)
Definition Body3D.cpp:632
#define EV_BODY_CENTER_GETTER(name, functionName, member)
Definition Body3D.cpp:644
float cx
Definition CardTypes.cpp:33
float cy
Definition CardTypes.cpp:34
ShaderImageInput shape
std::array< double, 10 > q
std::vector< std::uint32_t > indices
std::uint32_t height
std::uint32_t width
std::array< float, 4 > rotation
std::array< float, 3 > scale
bool valid
World3D * world
std::vector< Point > vertices
float radius
std::string id
Definition PlayHost.cpp:108
std::shared_ptr< const std::vector< glm::vec2 > > points
float t
Mesh * mesh
double restitution
std::string body
uint32_t index
std::uint32_t depth
glm::vec3 point
float vz
float wz
float wx
float vy
float qy
bool awake
float vx
float qx
float qw
float qz
float wy
bool bullet
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.
Definition Diagnostic.h:125
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static constexpr RuntimeHandle invalid() noexcept
Returns the canonical invalid handle.
void setActive(bool active)
Disables/enables the body and its shapes.
Definition Body3D.cpp:691
void setRotation(float qx, float qy, float qz, float qw)
Orientation as quaternion (x, y, z, w).
Definition Body3D.cpp:274
std::vector< float > localToWorldVector(float x, float y, float z) const
Rotates a body-local direction/vector into world space; returns {x,y,z}.
Definition Body3D.cpp:386
std::vector< float > worldToLocalVector(float x, float y, float z) const
Rotates a world direction/vector into body-local space; returns {x,y,z}.
Definition Body3D.cpp:400
~Body3D()
Body 3 d.
Definition Body3D.cpp:190
bool isBullet() const
True when bullet.
Definition Body3D.cpp:706
void setAngularDamping(float damping)
Angular damping coefficient, finite and non-negative.
Definition Body3D.cpp:529
bool isActive() const
True when active.
Definition Body3D.cpp:699
void applyForce(float fx, float fy, float fz)
Force applied at the center of mass.
Definition Body3D.cpp:428
float getLinearVelocityY() const
Returns the linear velocity y.
Definition Body3D.cpp:321
std::vector< float > localToWorldPoint(float x, float y, float z) const
Converts a body-local point to world coordinates; returns {x,y,z}.
Definition Body3D.cpp:356
void applyLinearImpulse(float ix, float iy, float iz)
Instantaneous linear impulse.
Definition Body3D.cpp:443
void setFixedRotation(bool fixed)
Lock all angular axes (Box3D motion locks).
Definition Body3D.cpp:676
void applyAngularImpulse(float ix, float iy, float iz)
Instantaneous angular impulse.
Definition Body3D.cpp:453
float getAngularDamping() const
Current angular damping coefficient.
Definition Body3D.cpp:535
float getX() const
Returns the x.
Definition Body3D.cpp:259
void setLinearDamping(float damping)
Linear damping coefficient, finite and non-negative.
Definition Body3D.cpp:519
std::vector< float > worldToLocalPoint(float x, float y, float z) const
Converts a world point to body-local coordinates; returns {x,y,z}.
Definition Body3D.cpp:372
void applyLocalForceToCenter(float fx, float fy, float fz)
Body-local force applied at the center of mass.
Definition Body3D.cpp:467
float getMass() const
Body mass in kg.
Definition Body3D.cpp:331
bool isFixedRotation() const
True when fixed rotation.
Definition Body3D.cpp:685
eve::Result< PhysicsVector3D > getLocalPointVelocityOwned(float x, float y, float z) const
Returns allocation-free world velocity at a body-local point.
Definition Body3D.cpp:414
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.
Definition Body3D.cpp:379
void setBullet(bool bullet)
CCD bullet mode.
Definition Body3D.cpp:701
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.
Definition Body3D.cpp:715
bool isValid() const
True while the underlying Box3D body is still alive.
Definition Body3D.cpp:216
void applyLocalTorque(float tx, float ty, float tz)
Body-local torque.
Definition Body3D.cpp:474
float getY() const
Returns the y.
Definition Body3D.cpp:264
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.
Definition Body3D.cpp:791
void setAwake(bool awake)
Wakes / sleeps the body manually.
Definition Body3D.cpp:708
void applyLinearImpulseAt(float ix, float iy, float iz, float x, float y, float z)
Instantaneous linear impulse applied at a world position.
Definition Body3D.cpp:448
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.
Definition Body3D.cpp:364
float getLinearDamping() const
Current linear damping coefficient.
Definition Body3D.cpp:525
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.
Definition Body3D.cpp:775
void destroy()
Destroys the body inside its world.
Definition Body3D.cpp:225
float getRotZ() const
Returns the rot z.
Definition Body3D.cpp:301
float getSleepThreshold() const
Current sleep velocity threshold.
Definition Body3D.cpp:564
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.
Definition Body3D.cpp:588
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...
Definition Body3D.cpp:504
float getRotY() const
Returns the rot y.
Definition Body3D.cpp:296
void applyLocalForce(float fx, float fy, float fz, float x, float y, float z)
Body-local force applied at a body-local point.
Definition Body3D.cpp:458
float getGravityScale() const
Current world-gravity multiplier.
Definition Body3D.cpp:545
float getRotX() const
Returns the rot x.
Definition Body3D.cpp:291
float getAngularVelocityZ() const
Returns the angular velocity z.
Definition Body3D.cpp:351
void setSleepEnabled(bool enabled)
Enables or disables automatic sleeping for this body.
Definition Body3D.cpp:549
void invalidate()
Internal: marks the wrapper invalid after world destruction.
Definition Body3D.cpp:218
void applyLocalLinearImpulseToCenter(float ix, float iy, float iz)
Body-local linear impulse applied at the center of mass.
Definition Body3D.cpp:490
float getAngularVelocityX() const
Returns the angular velocity x.
Definition Body3D.cpp:341
std::string getType() const
Returns the type.
Definition Body3D.cpp:671
float getRotW() const
Returns the rot w.
Definition Body3D.cpp:306
void applyTorque(float tx, float ty, float tz)
Torque applied in world space.
Definition Body3D.cpp:438
bool isSleepEnabled() const
Whether automatic sleeping is enabled.
Definition Body3D.cpp:554
void setMotionLocks(bool linearX, bool linearY, bool linearZ, bool angularX, bool angularY, bool angularZ)
Atomically locks translation and rotation on individual local solver axes.
Definition Body3D.cpp:568
void applyForceAt(float fx, float fy, float fz, float x, float y, float z)
Force applied at a world position.
Definition Body3D.cpp:433
void resetMassProperties()
Restores automatic mass, center, and inertia calculation from attached shapes.
Definition Body3D.cpp:627
eve::Result< PhysicsVector3D > localToWorldVectorOwned(float x, float y, float z) const
Rotates a local vector into an allocation-free owning world vector.
Definition Body3D.cpp:393
Shape3D * newCapsuleShape(float height, float radius, float density=1.f, float friction=0.2f, float restitution=0.f)
Creates a capsule along local Y.
Definition Body3D.cpp:753
void setLinearVelocity(float vx, float vy, float vz)
Linear velocity in m/s.
Definition Body3D.cpp:311
void applyLocalLinearImpulse(float ix, float iy, float iz, float x, float y, float z)
Body-local linear impulse applied at a body-local point.
Definition Body3D.cpp:480
void setGravityScale(float scale)
Multiplier applied to world gravity; may be negative.
Definition Body3D.cpp:539
bool isAwake() const
True when awake.
Definition Body3D.cpp:713
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.
Definition Body3D.cpp:816
void setPosition(float x, float y, float z)
Position in meters.
Definition Body3D.cpp:254
void setType(const std::string &bodyType)
"static" | "kinematic" | "dynamic".
Definition Body3D.cpp:656
float getLinearVelocityZ() const
Returns the linear velocity z.
Definition Body3D.cpp:326
std::vector< float > getWorldPointVelocity(float x, float y, float z) const
World-space velocity at the supplied world point.
Definition Body3D.cpp:421
void setAngularVelocity(float wx, float wy, float wz)
Angular velocity in rad/s.
Definition Body3D.cpp:336
void setSleepThreshold(float threshold)
Sleep velocity threshold, finite and non-negative.
Definition Body3D.cpp:558
float getAngularVelocityY() const
Returns the angular velocity y.
Definition Body3D.cpp:346
Body3D(World3D *world, b3BodyId bodyId, int id, PhysicsBodyHandle runtimeHandle)
Internal: wraps a Box3D body (use World3D::newBody).
Definition Body3D.cpp:187
friend class Shape3D
Definition Body3D.h:365
std::vector< float > getLocalPointVelocity(float x, float y, float z) const
World-space velocity at a point expressed in body-local coordinates.
Definition Body3D.cpp:407
float getZ() const
Returns the z.
Definition Body3D.cpp:269
Shape3D * newSphereShape(float radius, float density=1.f, float friction=0.2f, float restitution=0.f)
Creates a sphere shape in meter-space units.
Definition Body3D.cpp:735
float getLinearVelocityX() const
Returns the linear velocity x.
Definition Body3D.cpp:316
void applyLocalAngularImpulse(float ix, float iy, float iz)
Body-local angular impulse.
Definition Body3D.cpp:497
Script-facing Box3D joint owned by a World3D.
Definition Joint3D.h:17
3D shape (box/sphere/capsule) attached to a Body3D with material settings. Created via Body3D::new*Sh...
Definition Shape3D.h:25
Box3D rigid-body world. Script coordinates are meters (Box3D native), unlike 2D World which uses pixe...
Definition World3D.h:63
void forgetBody(Body3D *body)
Internal: wrapper teardown bookkeeping.
Definition World3D.cpp:1002
void forgetJoint(Joint3D *joint)
Internal: removes a joint wrapper from ownership bookkeeping.
Definition World3D.cpp:714
bool isValid() const
True while the underlying Box3D world is alive.
Definition World3D.cpp:430
void forgetShape(Shape3D *shape)
Definition World3D.cpp:1006
PhysicsShapeHandle nextShapeRuntimeHandle()
Internal: next generation-qualified shape handle.
Definition World3D.cpp:890
float lengthSquared(Vec3 value)
Length squared.
Optional physics backend for vehicle mobility and body attach.
Definition Climbing.h:36
bool enabled