载入中...
搜索中...
未找到
Shape3D.cpp
浏览该文件的文档.
1#include "physics/Shape3D.h"
2#include "physics/Body3D.h"
3#include "physics/World3D.h"
4
5#include "common/Exception.h"
6
7#include <box3d/box3d.h>
8
9#include <cstdint>
10#include <cmath>
11#include <utility>
12
13namespace eve::physics {
14namespace {
15
16constexpr uint64_t materialIdMask = 0xFFFFFFFFull;
17constexpr int frictionModeShift = 32;
18constexpr int restitutionModeShift = 35;
19constexpr uint64_t combineModeMask = 0x7ull;
20
21uint64_t parseCombineMode(const std::string &mode, const char *operation) {
22 if (mode == "default") return 0;
23 if (mode == "average") return 1;
24 if (mode == "minimum" || mode == "min") return 2;
25 if (mode == "multiply") return 3;
26 if (mode == "maximum" || mode == "max") return 4;
27 throw eve::Exception("%s: mode must be default|average|minimum|multiply|maximum",
28 operation);
29}
30
31std::string combineModeName(uint64_t mode) {
32 switch (mode) {
33 case 0: return "default";
34 case 1: return "average";
35 case 2: return "minimum";
36 case 3: return "multiply";
37 case 4: return "maximum";
38 default: return "default";
39 }
40}
41
42b3ShapeDef makeShapeDef(float density, float friction, float restitution, bool sensor,
43 bool hitEvents, bool preSolveEvents) {
44 b3ShapeDef def = b3DefaultShapeDef();
45 def.density = density;
46 def.baseMaterial.friction = friction;
47 def.baseMaterial.restitution = restitution;
48 def.isSensor = sensor;
49 def.enableContactEvents = !sensor;
50 def.enableSensorEvents = true;
51 def.enableHitEvents = hitEvents && !sensor;
52 def.enablePreSolveEvents = preSolveEvents && !sensor;
53 return def;
54}
55
56b3HullData *createCheckedHull(const std::vector<float> &vertices, int maxVertices,
57 const char *operation) {
58 if (vertices.size() < 12 || vertices.size() % 3 != 0)
59 throw eve::Exception("%s: vertices must contain at least four packed XYZ points",
60 operation);
61 if (vertices.size() / 3 > 100000)
62 throw eve::Exception("%s: source point count must be <= 100000", operation);
63 if (maxVertices < 4 || maxVertices > 254)
64 throw eve::Exception("%s: maxVertices must be in [4, 254]", operation);
65 std::vector<b3Vec3> points;
66 points.reserve(vertices.size() / 3);
67 for (size_t i = 0; i < vertices.size(); i += 3) {
68 if (!std::isfinite(vertices[i]) || !std::isfinite(vertices[i + 1]) ||
69 !std::isfinite(vertices[i + 2]))
70 throw eve::Exception("%s: all vertex components must be finite", operation);
71 points.push_back({vertices[i], vertices[i + 1], vertices[i + 2]});
72 }
73 b3HullData *hull = b3CreateHull(points.data(), static_cast<int>(points.size()), maxVertices);
74 if (!hull)
75 throw eve::Exception("%s: points do not form a valid three-dimensional convex hull",
76 operation);
77 return hull;
78}
79
80b3MeshData *createCheckedMesh(const std::vector<float> &vertices,
81 const std::vector<int32_t> &indices, bool weldVertices,
82 float weldTolerance, bool identifyEdges, bool useMedianSplit,
83 b3Transform transform,
84 const std::vector<uint8_t> *materialIndices,
85 const char *operation) {
86 if (vertices.size() < 9 || vertices.size() % 3 != 0)
87 throw eve::Exception("%s: vertices must contain at least three packed XYZ points",
88 operation);
89 if (indices.size() < 3 || indices.size() % 3 != 0)
90 throw eve::Exception("%s: indices must contain complete triangles", operation);
91 if (vertices.size() / 3 > 1000000 || indices.size() / 3 > 2000000)
92 throw eve::Exception("%s: mesh exceeds 1000000 vertices or 2000000 triangles",
93 operation);
94 if (!std::isfinite(weldTolerance) || weldTolerance < 0.f)
95 throw eve::Exception("%s: weldTolerance must be finite and >= 0", operation);
96 std::vector<b3Vec3> points;
97 points.reserve(vertices.size() / 3);
98 for (size_t i = 0; i < vertices.size(); i += 3) {
99 if (!std::isfinite(vertices[i]) || !std::isfinite(vertices[i + 1]) ||
100 !std::isfinite(vertices[i + 2]))
101 throw eve::Exception("%s: all vertex components must be finite", operation);
102 const b3Vec3 point{vertices[i], vertices[i + 1], vertices[i + 2]};
103 points.push_back(transform.p + b3RotateVector(transform.q, point));
104 }
105 for (int32_t index : indices) {
106 if (index < 0 || static_cast<size_t>(index) >= points.size())
107 throw eve::Exception("%s: triangle index is outside the vertex array", operation);
108 }
109 std::vector<int32_t> mutableIndices = indices;
110 b3MeshDef def{};
111 def.vertices = points.data();
112 def.indices = mutableIndices.data();
113 def.materialIndices = materialIndices && !materialIndices->empty()
114 ? const_cast<uint8_t *>(materialIndices->data())
115 : nullptr;
116 def.vertexCount = static_cast<int>(points.size());
117 def.triangleCount = static_cast<int>(indices.size() / 3);
118 def.weldVertices = weldVertices;
119 def.weldTolerance = weldTolerance;
120 def.identifyEdges = identifyEdges;
121 def.useMedianSplit = useMedianSplit;
122 b3MeshData *mesh = b3CreateMesh(&def, nullptr, 0);
123 if (!mesh || mesh->triangleCount != def.triangleCount) {
124 if (mesh) b3DestroyMesh(mesh);
125 throw eve::Exception("%s: mesh contains degenerate or zero-area triangles", operation);
126 }
127 return mesh;
128}
129
130b3HeightFieldData *createCheckedHeightField(int countX, int countZ, float cellSizeX,
131 float cellSizeZ,
132 const std::vector<float> &heights,
133 float globalMin, float globalMax,
134 bool clockwiseWinding, const char *operation) {
135 if (countX < 2 || countZ < 2)
136 throw eve::Exception("%s: countX and countZ must be >= 2", operation);
137 const size_t sampleCount = static_cast<size_t>(countX) * static_cast<size_t>(countZ);
138 if (sampleCount > 16000000)
139 throw eve::Exception("%s: sample count must be <= 16000000", operation);
140 if (heights.size() != sampleCount)
141 throw eve::Exception("%s: heights size must equal countX * countZ", operation);
142 if (!(cellSizeX > 0.f) || !(cellSizeZ > 0.f) || !std::isfinite(cellSizeX) ||
143 !std::isfinite(cellSizeZ))
144 throw eve::Exception("%s: cell sizes must be finite and > 0", operation);
145 if (!std::isfinite(globalMin) || !std::isfinite(globalMax) || globalMin > globalMax)
146 throw eve::Exception("%s: global height range must be finite and ordered", operation);
147 for (float height : heights) {
148 if (!std::isfinite(height) || height < globalMin || height > globalMax)
149 throw eve::Exception("%s: every height must be finite and inside global range",
150 operation);
151 }
152 std::vector<float> mutableHeights = heights;
153 b3HeightFieldDef def{};
154 def.heights = mutableHeights.data();
155 def.scale = {cellSizeX, 1.f, cellSizeZ};
156 def.countX = countX;
157 def.countZ = countZ;
158 def.globalMinimumHeight = globalMin;
159 def.globalMaximumHeight = globalMax;
160 def.clockwiseWinding = clockwiseWinding;
161 return b3CreateHeightField(&def);
162}
163
164} // namespace
165
166Shape3D::Shape3D(World3D *world, Body3D *body, b3ShapeId shapeId, PhysicsShapeHandle runtimeHandle, Kind kind, float a,
167 float b, float c, std::vector<float> hullVertices, int hullMaxVertices,
168 std::vector<float> meshVertices, std::vector<int32_t> meshIndices, b3MeshData *meshData,
169 bool meshWeldVertices, float meshWeldTolerance, bool meshIdentifyEdges, bool meshUseMedianSplit,
170 std::vector<float> heightValues, int heightCountX, int heightCountZ, float heightCellSizeX,
171 float heightCellSizeZ, float heightGlobalMin, float heightGlobalMax, bool heightClockwise,
172 b3HeightFieldData *heightData)
173 : world_(world),
174 body_(body),
175 shapeId_(shapeId),
176 runtimeHandle_(runtimeHandle),
177 kind_(kind),
178 a_(a),
179 b_(b),
180 c_(c),
181 hullVertices_(std::move(hullVertices)),
182 hullMaxVertices_(hullMaxVertices),
183 meshVertices_(std::move(meshVertices)),
184 meshIndices_(std::move(meshIndices)),
185 meshData_(meshData),
186 meshWeldVertices_(meshWeldVertices),
187 meshWeldTolerance_(meshWeldTolerance),
188 meshIdentifyEdges_(meshIdentifyEdges),
189 meshUseMedianSplit_(meshUseMedianSplit),
190 heightValues_(std::move(heightValues)),
191 heightCountX_(heightCountX),
192 heightCountZ_(heightCountZ),
193 heightCellSizeX_(heightCellSizeX),
194 heightCellSizeZ_(heightCellSizeZ),
195 heightGlobalMin_(heightGlobalMin),
196 heightGlobalMax_(heightGlobalMax),
197 heightClockwise_(heightClockwise),
198 heightData_(heightData),
199 id_(world ? world->nextShapeId() : 0) {
200 if (kind_ == Kind::TriangleMesh && isValid() && !meshMaterialsDirty_) {
201 const int materialCount = b3Shape_GetMeshMaterialCount(shapeId_);
202 meshMaterials_.reserve(static_cast<size_t>(materialCount));
203 for (int i = 0; i < materialCount; ++i)
204 meshMaterials_.push_back(b3Shape_GetMeshSurfaceMaterial(shapeId_, i));
205 }
206 if (world_) world_->registerShapeHandle(this);
207}
208
210 if (isValid() && body_ && body_->isValid()) {
211 b3Shape_SetUserData(shapeId_, nullptr);
212 b3DestroyShape(shapeId_, true);
213 }
214 if (world_) world_->forgetShape(this);
215 if (meshData_) {
216 b3DestroyMesh(meshData_);
217 meshData_ = nullptr;
218 }
219 if (heightData_) {
220 b3DestroyHeightField(heightData_);
221 heightData_ = nullptr;
222 }
223 shapeId_ = {};
224 body_ = nullptr;
225 world_ = nullptr;
226 runtimeHandle_ = PhysicsShapeHandle::invalid();
227}
228
229bool Shape3D::isValid() const { return b3Shape_IsValid(shapeId_); }
230
232 if (isValid()) b3Shape_SetUserData(shapeId_, nullptr);
233 if (world_) world_->forgetShape(this);
234 shapeId_ = {};
235 body_ = nullptr;
236 world_ = nullptr;
237 runtimeHandle_ = PhysicsShapeHandle::invalid();
238}
239
241 if (!isValid() || !body_ || !body_->isValid()) {
242 invalidate();
243 if (meshData_) {
244 b3DestroyMesh(meshData_);
245 meshData_ = nullptr;
246 }
247 if (heightData_) {
248 b3DestroyHeightField(heightData_);
249 heightData_ = nullptr;
250 }
251 return;
252 }
253 b3Shape_SetUserData(shapeId_, nullptr);
254 b3DestroyShape(shapeId_, true);
255 if (meshData_) {
256 b3DestroyMesh(meshData_);
257 meshData_ = nullptr;
258 }
259 if (heightData_) {
260 b3DestroyHeightField(heightData_);
261 heightData_ = nullptr;
262 }
263 if (world_) world_->forgetShape(this);
264 shapeId_ = {};
265 body_ = nullptr;
266 world_ = nullptr;
267 runtimeHandle_ = PhysicsShapeHandle::invalid();
268}
269
270void Shape3D::recreate(bool sensor) {
271 if (!body_ || !body_->isValid())
272 throw eve::Exception("Shape3D: cannot rebuild geometry after body destruction");
273
274 float density = isValid() ? b3Shape_GetDensity(shapeId_) : 1.f;
275 b3SurfaceMaterial material =
276 isValid() ? b3Shape_GetSurfaceMaterial(shapeId_) : b3DefaultSurfaceMaterial();
277 if (kind_ == Kind::TriangleMesh && isValid() && !meshMaterialsDirty_) {
278 const int materialCount = b3Shape_GetMeshMaterialCount(shapeId_);
279 meshMaterials_.clear();
280 meshMaterials_.reserve(static_cast<size_t>(materialCount));
281 for (int i = 0; i < materialCount; ++i)
282 meshMaterials_.push_back(b3Shape_GetMeshSurfaceMaterial(shapeId_, i));
283 }
284 b3Filter filter = isValid() ? b3Shape_GetFilter(shapeId_) : b3DefaultShapeDef().filter;
285
286 if (isValid()) {
287 b3Shape_SetUserData(shapeId_, nullptr);
288 b3DestroyShape(shapeId_, false);
289 shapeId_ = {};
290 }
291 if (meshData_) {
292 b3DestroyMesh(meshData_);
293 meshData_ = nullptr;
294 }
295 if (heightData_) {
296 b3DestroyHeightField(heightData_);
297 heightData_ = nullptr;
298 }
299
300 b3ShapeDef def = makeShapeDef(density, material.friction, material.restitution, sensor,
301 hitEventsEnabled_, oneWayEnabled_);
302 def.enableCustomFiltering = contactOverrideFiltering_;
303 def.explosionScale = explosionScale_;
304 def.baseMaterial = material;
305 def.filter = filter;
306 const b3Quat localRotation{{localQx_, localQy_, localQz_}, localQw_};
307 const b3Vec3 localPosition{localX_, localY_, localZ_};
308 switch (kind_) {
309 case Kind::Box: {
310 b3BoxHull box = b3MakeBoxHull(a_, b_, c_);
311 b3Transform transform{localPosition, localRotation};
312 shapeId_ = b3CreateTransformedHullShape(body_->raw(), &def, &box.base, transform,
313 b3Vec3{1.f, 1.f, 1.f});
314 break;
315 }
316 case Kind::Sphere: {
317 b3Sphere sphere;
318 sphere.center = localPosition;
319 sphere.radius = a_;
320 shapeId_ = b3CreateSphereShape(body_->raw(), &def, &sphere);
321 break;
322 }
323 case Kind::Capsule: {
324 b3Capsule capsule;
325 const b3Vec3 axis = b3RotateVector(localRotation, b3Vec3{0.f, a_, 0.f});
326 capsule.center1 = localPosition - axis;
327 capsule.center2 = localPosition + axis;
328 capsule.radius = b_;
329 shapeId_ = b3CreateCapsuleShape(body_->raw(), &def, &capsule);
330 break;
331 }
332 case Kind::ConvexHull: {
333 b3HullData *hull =
334 createCheckedHull(hullVertices_, hullMaxVertices_, "Shape3D.recreate");
335 b3Transform transform{localPosition, localRotation};
336 shapeId_ = b3CreateTransformedHullShape(body_->raw(), &def, hull, transform,
337 b3Vec3{1.f, 1.f, 1.f});
338 b3DestroyHull(hull);
339 break;
340 }
341 case Kind::TriangleMesh: {
342 if (b3Body_GetType(body_->raw()) != b3_staticBody)
343 throw eve::Exception("Shape3D.recreate: triangle meshes require a static body");
344 b3Transform transform{localPosition, localRotation};
345 meshData_ = createCheckedMesh(meshVertices_, meshIndices_, meshWeldVertices_,
346 meshWeldTolerance_, meshIdentifyEdges_,
347 meshUseMedianSplit_, transform,
348 &meshMaterialIndices_, "Shape3D.recreate");
349 if (!meshMaterials_.empty()) {
350 def.materials = meshMaterials_.data();
351 def.materialCount = static_cast<int>(meshMaterials_.size());
352 }
353 shapeId_ = b3CreateMeshShape(body_->raw(), &def, meshData_, b3Vec3_one);
354 break;
355 }
356 case Kind::HeightField: {
357 if (b3Body_GetType(body_->raw()) != b3_staticBody)
358 throw eve::Exception("Shape3D.recreate: height fields require a static body");
359 heightData_ = createCheckedHeightField(
360 heightCountX_, heightCountZ_, heightCellSizeX_, heightCellSizeZ_, heightValues_,
361 heightGlobalMin_, heightGlobalMax_, heightClockwise_, "Shape3D.recreate");
362 shapeId_ = b3CreateHeightFieldShape(body_->raw(), &def, heightData_);
363 break;
364 }
365 }
366 b3Shape_SetUserData(shapeId_, this);
367 meshMaterialsDirty_ = false;
368 if (world_) world_->registerShapeHandle(this);
369}
370
371void Shape3D::setSensor(bool sensor) {
372 if (!body_ || !body_->isValid()) return;
373 if (isValid() && b3Shape_IsSensor(shapeId_) == sensor) return;
374 recreate(sensor);
375}
376
377void Shape3D::enableContactOverrideFiltering() {
378 if (contactOverrideFiltering_) return;
379 contactOverrideFiltering_ = true;
380 recreate(isSensor());
381}
382
384 tag_ = tag;
385 if (world_) world_->updateShapeTag(this);
386}
387
389 hitEventsEnabled_ = enabled;
390 if (!isValid()) return;
391 b3Shape_EnableHitEvents(shapeId_, enabled && !isSensor());
392}
393
395 return hitEventsEnabled_;
396}
397
398void Shape3D::setOneWay(float nx, float ny, float nz, float planeOffset, float margin,
399 float minNormalDot) {
400 if (!std::isfinite(nx) || !std::isfinite(ny) || !std::isfinite(nz) ||
401 !std::isfinite(planeOffset) || !std::isfinite(margin) ||
402 !std::isfinite(minNormalDot))
403 throw eve::Exception("Shape3D.setOneWay: all values must be finite");
404 const float lengthSquared = nx * nx + ny * ny + nz * nz;
405 if (!(lengthSquared > 1e-12f))
406 throw eve::Exception("Shape3D.setOneWay: normal must be non-zero");
407 if (margin < 0.f)
408 throw eve::Exception("Shape3D.setOneWay: margin must be >= 0");
409 if (minNormalDot < -1.f || minNormalDot > 1.f)
410 throw eve::Exception("Shape3D.setOneWay: minNormalDot must be in [-1,1]");
411 const float inverseLength = 1.f / std::sqrt(lengthSquared);
412 oneWayLocalNormalX_ = nx * inverseLength;
413 oneWayLocalNormalY_ = ny * inverseLength;
414 oneWayLocalNormalZ_ = nz * inverseLength;
415 oneWayPlaneOffset_ = planeOffset;
416 oneWayMargin_ = margin;
417 oneWayMinNormalDot_ = minNormalDot;
418 oneWayEnabled_ = true;
419 if (isValid()) b3Shape_EnablePreSolveEvents(shapeId_, !isSensor());
420}
421
423 oneWayEnabled_ = false;
424 if (isValid()) b3Shape_EnablePreSolveEvents(shapeId_, false);
425}
426
427void Shape3D::refreshOneWayWorldData() {
428 if (!oneWayEnabled_ || !body_ || !body_->isValid()) return;
429 const b3WorldTransform bodyTransform = b3Body_GetTransform(body_->raw());
430 const b3Quat localRotation{{localQx_, localQy_, localQz_}, localQw_};
431 const b3Quat worldRotation = b3MulQuat(bodyTransform.q, localRotation);
432 const b3Vec3 localNormal{oneWayLocalNormalX_, oneWayLocalNormalY_, oneWayLocalNormalZ_};
433 const b3Vec3 worldNormal = b3RotateVector(worldRotation, localNormal);
434 const b3Vec3 localPosition{localX_, localY_, localZ_};
435 const b3Vec3 bodyOffset = b3RotateVector(bodyTransform.q, localPosition);
436 const b3Pos shapeOrigin = b3OffsetPos(bodyTransform.p, bodyOffset);
437 const b3Pos planePoint = b3OffsetPos(shapeOrigin, oneWayPlaneOffset_ * worldNormal);
438 oneWayWorldPointX_ = planePoint.x;
439 oneWayWorldPointY_ = planePoint.y;
440 oneWayWorldPointZ_ = planePoint.z;
441 oneWayWorldNormalX_ = worldNormal.x;
442 oneWayWorldNormalY_ = worldNormal.y;
443 oneWayWorldNormalZ_ = worldNormal.z;
444}
445
446bool Shape3D::allowsOneWayContact(double pointX, double pointY, double pointZ, float normalX,
447 float normalY, float normalZ) const {
448 if (!oneWayEnabled_) return true;
449 const double side = (pointX - oneWayWorldPointX_) * oneWayWorldNormalX_ +
450 (pointY - oneWayWorldPointY_) * oneWayWorldNormalY_ +
451 (pointZ - oneWayWorldPointZ_) * oneWayWorldNormalZ_;
452 const float alignment = normalX * oneWayWorldNormalX_ + normalY * oneWayWorldNormalY_ +
453 normalZ * oneWayWorldNormalZ_;
454 return side >= -static_cast<double>(oneWayMargin_) && alignment >= oneWayMinNormalDot_;
455}
456
457std::string Shape3D::getKind() const {
458 switch (kind_) {
459 case Kind::Box: return "box";
460 case Kind::Sphere: return "sphere";
461 case Kind::Capsule: return "capsule";
462 case Kind::ConvexHull: return "convexHull";
463 case Kind::TriangleMesh: return "triangleMesh";
464 case Kind::HeightField: return "heightField";
465 }
466 return "box";
467}
468
469void Shape3D::setBoxSize(float width, float height, float depth) {
470 if (kind_ != Kind::Box)
471 throw eve::Exception("Shape3D.setBoxSize: shape is not a box");
472 if (!(width > 0.f) || !(height > 0.f) || !(depth > 0.f) || !std::isfinite(width) ||
473 !std::isfinite(height) || !std::isfinite(depth))
474 throw eve::Exception("Shape3D.setBoxSize: dimensions must be finite and > 0");
475 if (!body_ || !body_->isValid())
476 throw eve::Exception("Shape3D.setBoxSize: body destroyed");
477 const float halfWidth = 0.5f * width;
478 const float halfHeight = 0.5f * height;
479 const float halfDepth = 0.5f * depth;
480 if (a_ == halfWidth && b_ == halfHeight && c_ == halfDepth) return;
481 const bool sensor = isSensor();
482 a_ = halfWidth;
483 b_ = halfHeight;
484 c_ = halfDepth;
485 recreate(sensor);
486}
487
489 if (kind_ != Kind::Sphere)
490 throw eve::Exception("Shape3D.setSphereRadius: shape is not a sphere");
491 if (!(radius > 0.f) || !std::isfinite(radius))
492 throw eve::Exception("Shape3D.setSphereRadius: radius must be finite and > 0");
493 if (!body_ || !body_->isValid())
494 throw eve::Exception("Shape3D.setSphereRadius: body destroyed");
495 if (a_ == radius) return;
496 const bool sensor = isSensor();
497 a_ = radius;
498 recreate(sensor);
499}
500
502 if (kind_ != Kind::Capsule)
503 throw eve::Exception("Shape3D.setCapsuleSize: shape is not a capsule");
504 if (!(height >= 0.f) || !(radius > 0.f) || !std::isfinite(height) ||
505 !std::isfinite(radius))
506 throw eve::Exception(
507 "Shape3D.setCapsuleSize: height must be finite and >= 0; radius finite and > 0");
508 if (!body_ || !body_->isValid())
509 throw eve::Exception("Shape3D.setCapsuleSize: body destroyed");
510 const float halfHeight = 0.5f * height;
511 if (a_ == halfHeight && b_ == radius) return;
512 const bool sensor = isSensor();
513 a_ = halfHeight;
514 b_ = radius;
515 recreate(sensor);
516}
517
518void Shape3D::setConvexHullVertices(const std::vector<float> &vertices, int maxVertices) {
519 if (kind_ != Kind::ConvexHull)
520 throw eve::Exception("Shape3D.setConvexHullVertices: shape is not a convex hull");
521 if (!body_ || !body_->isValid())
522 throw eve::Exception("Shape3D.setConvexHullVertices: body destroyed");
523 b3HullData *validated =
524 createCheckedHull(vertices, maxVertices, "Shape3D.setConvexHullVertices");
525 b3DestroyHull(validated);
526 if (vertices == hullVertices_ && maxVertices == hullMaxVertices_) return;
527 const bool sensor = isSensor();
528 hullVertices_ = vertices;
529 hullMaxVertices_ = maxVertices;
530 recreate(sensor);
531}
532
533void Shape3D::setTriangleMeshData(const std::vector<float> &vertices,
534 const std::vector<int32_t> &indices, bool weldVertices,
535 float weldTolerance, bool identifyEdges,
536 bool useMedianSplit) {
537 if (kind_ != Kind::TriangleMesh)
538 throw eve::Exception("Shape3D.setTriangleMeshData: shape is not a triangle mesh");
539 if (!body_ || !body_->isValid())
540 throw eve::Exception("Shape3D.setTriangleMeshData: body destroyed");
541 if (b3Body_GetType(body_->raw()) != b3_staticBody)
542 throw eve::Exception("Shape3D.setTriangleMeshData: triangle meshes require a static body");
543 const b3Transform transform{{localX_, localY_, localZ_},
544 b3Quat{{localQx_, localQy_, localQz_}, localQw_}};
545 b3MeshData *validated = createCheckedMesh(vertices, indices, weldVertices, weldTolerance,
546 identifyEdges, useMedianSplit, transform,
547 nullptr,
548 "Shape3D.setTriangleMeshData");
549 b3DestroyMesh(validated);
550 if (vertices == meshVertices_ && indices == meshIndices_ &&
551 weldVertices == meshWeldVertices_ && weldTolerance == meshWeldTolerance_ &&
552 identifyEdges == meshIdentifyEdges_ && useMedianSplit == meshUseMedianSplit_)
553 return;
554 const bool sensor = isSensor();
555 meshVertices_ = vertices;
556 meshIndices_ = indices;
557 meshWeldVertices_ = weldVertices;
558 meshWeldTolerance_ = weldTolerance;
559 meshIdentifyEdges_ = identifyEdges;
560 meshUseMedianSplit_ = useMedianSplit;
561 if (meshMaterialIndices_.size() != indices.size() / 3) {
562 meshMaterialIndices_.clear();
563 if (meshMaterials_.size() > 1) meshMaterials_.resize(1);
564 }
565 recreate(sensor);
566}
567
569 const std::vector<int32_t> &materialIndices) {
570 if (kind_ != Kind::TriangleMesh)
571 throw eve::Exception(
572 "Shape3D.setTriangleMeshMaterialIndices: shape is not a triangle mesh");
573 const size_t triangleCount = meshIndices_.size() / 3;
574 if (materialIndices.size() != triangleCount)
575 throw eve::Exception(
576 "Shape3D.setTriangleMeshMaterialIndices: one index is required per triangle");
577 std::vector<uint8_t> encoded;
578 encoded.reserve(materialIndices.size());
579 int requiredCount = 1;
580 for (int32_t index : materialIndices) {
581 if (index < 0 || index > 254)
582 throw eve::Exception(
583 "Shape3D.setTriangleMeshMaterialIndices: indices must be in [0, 254]");
584 encoded.push_back(static_cast<uint8_t>(index));
585 requiredCount = std::max(requiredCount, static_cast<int>(index) + 1);
586 }
587 if (encoded == meshMaterialIndices_ &&
588 requiredCount == static_cast<int>(meshMaterials_.size()))
589 return;
590 const bool sensor = isSensor();
591 const auto oldIndices = meshMaterialIndices_;
592 const auto oldMaterials = meshMaterials_;
593 meshMaterialIndices_ = std::move(encoded);
594 if (meshMaterials_.empty()) meshMaterials_.push_back(b3DefaultSurfaceMaterial());
595 meshMaterials_.resize(static_cast<size_t>(requiredCount), meshMaterials_.front());
596 meshMaterialsDirty_ = true;
597 try {
598 recreate(sensor);
599 } catch (...) {
600 meshMaterialIndices_ = oldIndices;
601 meshMaterials_ = oldMaterials;
602 meshMaterialsDirty_ = true;
603 recreate(sensor);
604 throw;
605 }
606}
607
608int Shape3D::getTriangleMeshMaterialIndex(int triangleIndex) const {
609 if (kind_ != Kind::TriangleMesh)
610 throw eve::Exception(
611 "Shape3D.getTriangleMeshMaterialIndex: shape is not a triangle mesh");
612 const int triangleCount = static_cast<int>(meshIndices_.size() / 3);
613 if (triangleIndex < 0 || triangleIndex >= triangleCount)
614 throw eve::Exception("Shape3D.getTriangleMeshMaterialIndex: triangle out of range");
615 return meshMaterialIndices_.empty() ? 0 : meshMaterialIndices_[static_cast<size_t>(triangleIndex)];
616}
617
619 return kind_ == Kind::TriangleMesh ? std::max(1, static_cast<int>(meshMaterials_.size())) : 0;
620}
621
622void Shape3D::setTriangleMeshMaterial(int slot, float friction, float restitution,
623 float rollingResistance, float tangentX,
624 float tangentY, float tangentZ, int materialId,
625 const std::string &frictionMode,
626 const std::string &restitutionMode) {
627 if (kind_ != Kind::TriangleMesh)
628 throw eve::Exception("Shape3D.setTriangleMeshMaterial: shape is not a triangle mesh");
629 if (slot < 0 || slot >= getTriangleMeshMaterialCount())
630 throw eve::Exception("Shape3D.setTriangleMeshMaterial: slot out of range");
631 if (!std::isfinite(friction) || friction < 0.f || !std::isfinite(restitution) ||
632 restitution < 0.f || !std::isfinite(rollingResistance) || rollingResistance < 0.f ||
633 !std::isfinite(tangentX) || !std::isfinite(tangentY) || !std::isfinite(tangentZ))
634 throw eve::Exception(
635 "Shape3D.setTriangleMeshMaterial: numeric values must be finite and non-negative where required");
636 const uint64_t frictionCode =
637 parseCombineMode(frictionMode, "Shape3D.setTriangleMeshMaterial");
638 const uint64_t restitutionCode =
639 parseCombineMode(restitutionMode, "Shape3D.setTriangleMeshMaterial");
640 b3SurfaceMaterial material = b3DefaultSurfaceMaterial();
641 material.friction = friction;
642 material.restitution = restitution;
643 material.rollingResistance = rollingResistance;
644 material.tangentVelocity = {tangentX, tangentY, tangentZ};
645 material.userMaterialId = static_cast<uint32_t>(materialId) |
646 (frictionCode << frictionModeShift) |
647 (restitutionCode << restitutionModeShift);
648 if (meshMaterials_.empty()) meshMaterials_.push_back(b3DefaultSurfaceMaterial());
649 meshMaterials_[static_cast<size_t>(slot)] = material;
650 if (isValid()) b3Shape_SetMeshMaterial(shapeId_, material, slot);
651}
652
654 if (kind_ != Kind::TriangleMesh || slot < 0 || slot >= getTriangleMeshMaterialCount())
655 throw eve::Exception("Shape3D.getTriangleMeshMaterialFriction: slot out of range");
656 return meshMaterials_.empty() ? getFriction() : meshMaterials_[static_cast<size_t>(slot)].friction;
657}
658
660 if (kind_ != Kind::TriangleMesh || slot < 0 || slot >= getTriangleMeshMaterialCount())
661 throw eve::Exception("Shape3D.getTriangleMeshMaterialRestitution: slot out of range");
662 return meshMaterials_.empty() ? getRestitution() : meshMaterials_[static_cast<size_t>(slot)].restitution;
663}
664
666 if (kind_ != Kind::TriangleMesh || slot < 0 || slot >= getTriangleMeshMaterialCount())
667 throw eve::Exception(
668 "Shape3D.getTriangleMeshMaterialRollingResistance: slot out of range");
669 return meshMaterials_.empty() ? getRollingResistance()
670 : meshMaterials_[static_cast<size_t>(slot)].rollingResistance;
671}
672
674 if (kind_ != Kind::TriangleMesh || slot < 0 || slot >= getTriangleMeshMaterialCount())
675 throw eve::Exception("Shape3D.getTriangleMeshMaterialId: slot out of range");
676 const uint64_t id = meshMaterials_.empty()
677 ? b3Shape_GetSurfaceMaterial(shapeId_).userMaterialId
678 : meshMaterials_[static_cast<size_t>(slot)].userMaterialId;
679 return static_cast<int>(static_cast<uint32_t>(id));
680}
681
682void Shape3D::setHeightFieldHeights(const std::vector<float> &heights) {
683 if (kind_ != Kind::HeightField)
684 throw eve::Exception("Shape3D.setHeightFieldHeights: shape is not a height field");
685 if (!body_ || !body_->isValid())
686 throw eve::Exception("Shape3D.setHeightFieldHeights: body destroyed");
687 b3HeightFieldData *validated = createCheckedHeightField(
688 heightCountX_, heightCountZ_, heightCellSizeX_, heightCellSizeZ_, heights,
689 heightGlobalMin_, heightGlobalMax_, heightClockwise_,
690 "Shape3D.setHeightFieldHeights");
691 b3DestroyHeightField(validated);
692 if (heights == heightValues_) return;
693 const bool sensor = isSensor();
694 heightValues_ = heights;
695 recreate(sensor);
696}
697
699 const std::vector<float> &heights) {
700 if (kind_ != Kind::HeightField)
701 throw eve::Exception("Shape3D.setHeightFieldRegion: shape is not a height field");
702 if (x < 0 || z < 0 || width < 1 || depth < 1 || x > heightCountX_ - width ||
703 z > heightCountZ_ - depth)
704 throw eve::Exception("Shape3D.setHeightFieldRegion: region is outside the sample grid");
705 if (heights.size() != static_cast<size_t>(width) * static_cast<size_t>(depth))
706 throw eve::Exception("Shape3D.setHeightFieldRegion: data size must equal width * depth");
707 std::vector<float> updated = heightValues_;
708 for (int row = 0; row < depth; ++row) {
709 for (int column = 0; column < width; ++column) {
710 updated[static_cast<size_t>(z + row) * static_cast<size_t>(heightCountX_) +
711 static_cast<size_t>(x + column)] =
712 heights[static_cast<size_t>(row) * static_cast<size_t>(width) +
713 static_cast<size_t>(column)];
714 }
715 }
717}
718
719float Shape3D::getHeightFieldHeight(int x, int z) const {
720 if (kind_ != Kind::HeightField)
721 throw eve::Exception("Shape3D.getHeightFieldHeight: shape is not a height field");
722 if (x < 0 || x >= heightCountX_ || z < 0 || z >= heightCountZ_)
723 throw eve::Exception("Shape3D.getHeightFieldHeight: sample index out of range");
724 return heightValues_[static_cast<size_t>(z) * static_cast<size_t>(heightCountX_) +
725 static_cast<size_t>(x)];
726}
727
728void Shape3D::setLocalTransform(float px, float py, float pz, float qx, float qy, float qz,
729 float qw) {
730 if (!body_ || !body_->isValid())
731 throw eve::Exception("Shape3D.setLocalTransform: body destroyed");
732 if (!std::isfinite(px) || !std::isfinite(py) || !std::isfinite(pz) ||
733 !std::isfinite(qx) || !std::isfinite(qy) || !std::isfinite(qz) ||
734 !std::isfinite(qw))
735 throw eve::Exception("Shape3D.setLocalTransform: values must be finite");
736 const float lengthSquared = qx * qx + qy * qy + qz * qz + qw * qw;
737 if (!(lengthSquared > 1e-12f))
738 throw eve::Exception("Shape3D.setLocalTransform: quaternion must be non-zero");
739 const float inverseLength = 1.f / std::sqrt(lengthSquared);
740 const float normalizedQx = qx * inverseLength;
741 const float normalizedQy = qy * inverseLength;
742 const float normalizedQz = qz * inverseLength;
743 const float normalizedQw = qw * inverseLength;
744 if (kind_ == Kind::HeightField &&
745 (px != 0.f || py != 0.f || pz != 0.f || normalizedQx != 0.f ||
746 normalizedQy != 0.f || normalizedQz != 0.f || normalizedQw != 1.f))
747 throw eve::Exception(
748 "Shape3D.setLocalTransform: height fields use their Body transform and require an identity local transform");
749 if (localX_ == px && localY_ == py && localZ_ == pz && localQx_ == normalizedQx &&
750 localQy_ == normalizedQy && localQz_ == normalizedQz && localQw_ == normalizedQw)
751 return;
752
753 const bool sensor = isSensor();
754 localX_ = px;
755 localY_ = py;
756 localZ_ = pz;
757 localQx_ = normalizedQx;
758 localQy_ = normalizedQy;
759 localQz_ = normalizedQz;
760 localQw_ = normalizedQw;
761 recreate(sensor);
762}
763
764void Shape3D::setLocalPosition(float x, float y, float z) {
765 setLocalTransform(x, y, z, localQx_, localQy_, localQz_, localQw_);
766}
767
768void Shape3D::setLocalRotation(float qx, float qy, float qz, float qw) {
769 setLocalTransform(localX_, localY_, localZ_, qx, qy, qz, qw);
770}
771
772bool Shape3D::isSensor() const { return isValid() ? b3Shape_IsSensor(shapeId_) : false; }
773
774void Shape3D::setFriction(float friction) {
775 if (!std::isfinite(friction) || friction < 0.f)
776 throw eve::Exception("Shape3D.setFriction: friction must be finite and >= 0");
777 if (!isValid()) return;
778 b3Shape_SetFriction(shapeId_, friction);
779}
780
781float Shape3D::getFriction() const { return isValid() ? b3Shape_GetFriction(shapeId_) : 0.f; }
782
784 if (!std::isfinite(restitution) || restitution < 0.f)
785 throw eve::Exception("Shape3D.setRestitution: restitution must be finite and >= 0");
786 if (!isValid()) return;
787 b3Shape_SetRestitution(shapeId_, restitution);
788}
789
791 return isValid() ? b3Shape_GetRestitution(shapeId_) : 0.f;
792}
793
794void Shape3D::setRollingResistance(float resistance) {
795 if (!std::isfinite(resistance) || resistance < 0.f)
796 throw eve::Exception(
797 "Shape3D.setRollingResistance: resistance must be finite and >= 0");
798 if (!isValid()) return;
799 b3SurfaceMaterial material = b3Shape_GetSurfaceMaterial(shapeId_);
800 material.rollingResistance = resistance;
801 b3Shape_SetSurfaceMaterial(shapeId_, material);
802}
803
805 return isValid() ? b3Shape_GetSurfaceMaterial(shapeId_).rollingResistance : 0.f;
806}
807
808void Shape3D::setTangentVelocity(float x, float y, float z) {
809 if (!std::isfinite(x) || !std::isfinite(y) || !std::isfinite(z))
810 throw eve::Exception("Shape3D.setTangentVelocity: components must be finite");
811 if (!isValid()) return;
812 b3SurfaceMaterial material = b3Shape_GetSurfaceMaterial(shapeId_);
813 material.tangentVelocity = b3Vec3{x, y, z};
814 b3Shape_SetSurfaceMaterial(shapeId_, material);
815}
816
818 return isValid() ? b3Shape_GetSurfaceMaterial(shapeId_).tangentVelocity.x : 0.f;
819}
820
822 return isValid() ? b3Shape_GetSurfaceMaterial(shapeId_).tangentVelocity.y : 0.f;
823}
824
826 return isValid() ? b3Shape_GetSurfaceMaterial(shapeId_).tangentVelocity.z : 0.f;
827}
828
830 if (!isValid()) return;
831 b3SurfaceMaterial material = b3Shape_GetSurfaceMaterial(shapeId_);
832 material.userMaterialId =
833 (material.userMaterialId & ~materialIdMask) | static_cast<uint32_t>(id);
834 b3Shape_SetSurfaceMaterial(shapeId_, material);
835}
836
837void Shape3D::setFrictionCombineMode(const std::string &mode) {
838 const uint64_t encoded = parseCombineMode(mode, "Shape3D.setFrictionCombineMode");
839 if (!isValid()) return;
840 b3SurfaceMaterial material = b3Shape_GetSurfaceMaterial(shapeId_);
841 material.userMaterialId &= ~(combineModeMask << frictionModeShift);
842 material.userMaterialId |= encoded << frictionModeShift;
843 b3Shape_SetSurfaceMaterial(shapeId_, material);
844}
845
847 if (!isValid()) return "default";
848 return combineModeName((b3Shape_GetSurfaceMaterial(shapeId_).userMaterialId >>
849 frictionModeShift) & combineModeMask);
850}
851
852void Shape3D::setRestitutionCombineMode(const std::string &mode) {
853 const uint64_t encoded = parseCombineMode(mode, "Shape3D.setRestitutionCombineMode");
854 if (!isValid()) return;
855 b3SurfaceMaterial material = b3Shape_GetSurfaceMaterial(shapeId_);
856 material.userMaterialId &= ~(combineModeMask << restitutionModeShift);
857 material.userMaterialId |= encoded << restitutionModeShift;
858 b3Shape_SetSurfaceMaterial(shapeId_, material);
859}
860
862 if (!isValid()) return "default";
863 return combineModeName((b3Shape_GetSurfaceMaterial(shapeId_).userMaterialId >>
864 restitutionModeShift) & combineModeMask);
865}
866
868 return isValid()
869 ? static_cast<int>(static_cast<uint32_t>(
870 b3Shape_GetSurfaceMaterial(shapeId_).userMaterialId))
871 : 0;
872}
873
875 if (!std::isfinite(scale) || scale < 0.f)
876 throw eve::Exception("Shape3D.setExplosionScale: scale must be finite and >= 0");
877 if (scale == explosionScale_) return;
878 explosionScale_ = scale;
879 if (body_ && body_->isValid()) recreate(isSensor());
880}
881
882void Shape3D::setDensity(float density) {
883 if (!std::isfinite(density) || density < 0.f)
884 throw eve::Exception("Shape3D.setDensity: density must be finite and >= 0");
885 if (!isValid()) return;
886 b3Shape_SetDensity(shapeId_, density, true);
887}
888
889float Shape3D::getDensity() const { return isValid() ? b3Shape_GetDensity(shapeId_) : 0.f; }
890
891void Shape3D::setFilterBits(uint64_t categoryBits, uint64_t maskBits) {
892 if (!isValid()) return;
893 b3Filter filter = b3Shape_GetFilter(shapeId_);
894 filter.categoryBits = categoryBits;
895 filter.maskBits = maskBits;
896 b3Shape_SetFilter(shapeId_, filter, false);
897}
898
899uint64_t Shape3D::getCategoryBits() const {
900 return isValid() ? b3Shape_GetFilter(shapeId_).categoryBits : 0;
901}
902
903void Shape3D::setCategoryBits(uint64_t bits) {
904 if (!isValid()) return;
905 b3Filter filter = b3Shape_GetFilter(shapeId_);
906 filter.categoryBits = bits;
907 b3Shape_SetFilter(shapeId_, filter, true);
908}
909
910void Shape3D::setMaskBits(uint64_t bits) {
911 if (!isValid()) return;
912 b3Filter filter = b3Shape_GetFilter(shapeId_);
913 filter.maskBits = bits;
914 b3Shape_SetFilter(shapeId_, filter, true);
915}
916
917uint64_t Shape3D::getMaskBits() const {
918 return isValid() ? b3Shape_GetFilter(shapeId_).maskBits : 0;
919}
920
922 if (!isValid()) return;
923 b3Filter filter = b3Shape_GetFilter(shapeId_);
924 filter.groupIndex = index;
925 b3Shape_SetFilter(shapeId_, filter, true);
926}
927
929 return isValid() ? b3Shape_GetFilter(shapeId_).groupIndex : 0;
930}
931
932bool Shape3D::testPoint(float x, float y, float z) const {
933 if (!isValid() || !body_ || !body_->isValid()) return false;
934
935 b3Vec3 point{x, y, z};
936 b3ShapeProxy proxy;
937 proxy.points = &point;
938 proxy.count = 1;
939 proxy.radius = 0.f;
940
941 b3WorldTransform wt = b3Body_GetTransform(body_->raw());
942 b3Transform xf;
943 xf.p = b3Vec3{static_cast<float>(wt.p.x), static_cast<float>(wt.p.y),
944 static_cast<float>(wt.p.z)};
945 xf.q = wt.q;
946
947 switch (kind_) {
948 case Kind::Box:
949 case Kind::ConvexHull: {
950 const b3HullData *hull = b3Shape_GetHull(shapeId_);
951 if (!hull) return false;
952 return b3OverlapHull(hull, xf, &proxy);
953 }
954 case Kind::Sphere: {
955 b3Sphere sphere = b3Shape_GetSphere(shapeId_);
956 return b3OverlapSphere(&sphere, xf, &proxy);
957 }
958 case Kind::Capsule: {
959 b3Capsule capsule = b3Shape_GetCapsule(shapeId_);
960 return b3OverlapCapsule(&capsule, xf, &proxy);
961 }
962 case Kind::TriangleMesh: {
963 const b3Mesh mesh = b3Shape_GetMesh(shapeId_);
964 return mesh.data ? b3OverlapMesh(&mesh, xf, &proxy) : false;
965 }
966 case Kind::HeightField: {
967 const b3HeightFieldData *heightField = b3Shape_GetHeightField(shapeId_);
968 return heightField ? b3OverlapHeightField(heightField, xf, &proxy) : false;
969 }
970 }
971 return false;
972}
973
974} // namespace eve::physics
ActionParameterOperation operation
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
float py
float nx
float nz
float ny
float pz
int column
std::vector< std::uint32_t > indices
std::int32_t c
std::vector< Colorf > px
std::uint32_t height
std::uint32_t width
TokenKind kind
std::array< float, 3 > scale
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
const std::string * tag
World3D * world
std::array< std::uint64_t, kPixelChunkSize *kPixelChunkSize > updated
std::vector< Point > vertices
float radius
float halfWidth
float halfHeight
std::string id
Definition PlayHost.cpp:108
std::shared_ptr< const std::vector< glm::vec2 > > points
Mesh * mesh
Material * material
std::string filter
double restitution
ecs::EntityHandle side
int margin
std::string body
uint32_t index
std::uint32_t depth
glm::vec3 point
float qy
float qx
float qw
float qz
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
static constexpr RuntimeHandle invalid() noexcept
Returns the canonical invalid handle.
3D rigid body (Box3D) in meter-space coordinates (+Y up by convention). Owned by a World3D; create pr...
Definition Body3D.h:24
b3BodyId raw() const
Raw.
Definition Body3D.h:349
bool isValid() const
True while the underlying Box3D body is still alive.
Definition Body3D.cpp:216
void setLocalPosition(float x, float y, float z)
Sets local translation while preserving local rotation.
Definition Shape3D.cpp:764
void setDensity(float density)
Definition Shape3D.cpp:882
void invalidate()
Internal: marks the wrapper invalid after destruction.
Definition Shape3D.cpp:231
void setTag(int tag)
User-defined integer tag preserved for the wrapper lifetime.
Definition Shape3D.cpp:383
void setBoxSize(float width, float height, float depth)
Atomically changes full box dimensions in meters.
Definition Shape3D.cpp:469
float getTriangleMeshMaterialRestitution(int slot) const
Restitution of a triangle-mesh material slot.
Definition Shape3D.cpp:659
int getTriangleMeshMaterialId(int slot) const
Application material ID of a triangle-mesh material slot.
Definition Shape3D.cpp:673
void setOneWay(float nx, float ny, float nz, float planeOffset, float margin, float minNormalDot)
Enables a one-way collision plane in shape-local space.
Definition Shape3D.cpp:398
void setTriangleMeshData(const std::vector< float > &vertices, const std::vector< int32_t > &indices, bool weldVertices=true, float weldTolerance=0.001f, bool identifyEdges=true, bool useMedianSplit=false)
Atomically replaces static triangle-mesh source data and rebuilds its BVH.
Definition Shape3D.cpp:533
std::string getKind() const
Kind name including primitives, "convexHull", "triangleMesh", or "heightField".
Definition Shape3D.cpp:457
bool testPoint(float x, float y, float z) const
Point-in-shape test in world meters.
Definition Shape3D.cpp:932
void setHitEventsEnabled(bool enabled)
Enables significant collision hit events for this shape.
Definition Shape3D.cpp:388
void setMaterialId(int id)
Sets a low-32-bit application material identifier exposed by native queries.
Definition Shape3D.cpp:829
int getTriangleMeshMaterialIndex(int triangleIndex) const
Material-slot index assigned to a source triangle.
Definition Shape3D.cpp:608
void setTriangleMeshMaterialIndices(const std::vector< int32_t > &materialIndices)
Atomically assigns one material-slot index per source mesh triangle.
Definition Shape3D.cpp:568
void setMaskBits(uint64_t bits)
Sets collision mask bits without changing the category.
Definition Shape3D.cpp:910
void setTriangleMeshMaterial(int slot, float friction, float restitution, float rollingResistance, float tangentX, float tangentY, float tangentZ, int materialId, const std::string &frictionMode, const std::string &restitutionMode)
Replaces one triangle-mesh material slot without rebuilding geometry.
Definition Shape3D.cpp:622
float getHeightFieldHeight(int x, int z) const
Source height at a grid sample.
Definition Shape3D.cpp:719
void setCategoryBits(uint64_t bits)
Sets collision category bits without changing the mask.
Definition Shape3D.cpp:903
int getTriangleMeshMaterialCount() const
Number of material slots on a triangle mesh, otherwise zero.
Definition Shape3D.cpp:618
Kind
Shape geometry kind.
Definition Shape3D.h:28
float getTangentVelocityY() const
Local-space conveyor velocity Y component.
Definition Shape3D.cpp:821
float getTriangleMeshMaterialRollingResistance(int slot) const
Rolling resistance of a triangle-mesh material slot.
Definition Shape3D.cpp:665
Shape3D(World3D *world, Body3D *body, b3ShapeId shapeId, PhysicsShapeHandle runtimeHandle, Kind kind, float a, float b, float c, std::vector< float > hullVertices={}, int hullMaxVertices=64, std::vector< float > meshVertices={}, std::vector< int32_t > meshIndices={}, b3MeshData *meshData=nullptr, bool meshWeldVertices=true, float meshWeldTolerance=0.001f, bool meshIdentifyEdges=true, bool meshUseMedianSplit=false, std::vector< float > heightValues={}, int heightCountX=0, int heightCountZ=0, float heightCellSizeX=1.f, float heightCellSizeZ=1.f, float heightGlobalMin=0.f, float heightGlobalMax=0.f, bool heightClockwise=false, b3HeightFieldData *heightData=nullptr)
Internal: wraps a Box3D shape (use Body3D::new*Shape).
Definition Shape3D.cpp:166
bool isSensor() const
Definition Shape3D.cpp:772
void setRestitution(float restitution)
Definition Shape3D.cpp:783
void setSphereRadius(float radius)
Atomically changes sphere radius in meters.
Definition Shape3D.cpp:488
void setLocalTransform(float px, float py, float pz, float qx, float qy, float qz, float qw)
Atomically sets the shape transform relative to its Body3D.
Definition Shape3D.cpp:728
void setGroupIndex(int index)
Collision group; negative never collides, positive always collides.
Definition Shape3D.cpp:921
float getTriangleMeshMaterialFriction(int slot) const
Friction of a triangle-mesh material slot.
Definition Shape3D.cpp:653
void setHeightFieldHeights(const std::vector< float > &heights)
Atomically replaces every source height and rebuilds compressed terrain data.
Definition Shape3D.cpp:682
uint64_t getMaskBits() const
Current 64-bit collision mask.
Definition Shape3D.cpp:917
bool isValid() const
Definition Shape3D.cpp:229
float getFriction() const
Definition Shape3D.cpp:781
float getRestitution() const
Definition Shape3D.cpp:790
uint64_t getCategoryBits() const
Definition Shape3D.cpp:899
void setFrictionCombineMode(const std::string &mode)
Sets friction combine mode: default, average, minimum, multiply, or maximum.
Definition Shape3D.cpp:837
void destroy()
Destroys the shape inside its world.
Definition Shape3D.cpp:240
void setLocalRotation(float qx, float qy, float qz, float qw)
Sets local rotation while preserving local translation.
Definition Shape3D.cpp:768
void setTangentVelocity(float x, float y, float z)
Sets local-space conveyor velocity projected onto contact surfaces.
Definition Shape3D.cpp:808
void setFilterBits(uint64_t categoryBits, uint64_t maskBits)
Collision filter bits used by world ray/query filters (Box3D b3Filter). categoryBits 声明本形状属于哪些类别;mask...
Definition Shape3D.cpp:891
int getMaterialId() const
Low-32-bit application material identifier.
Definition Shape3D.cpp:867
void setCapsuleSize(float height, float radius)
Atomically changes capsule segment height and radius in meters.
Definition Shape3D.cpp:501
void setConvexHullVertices(const std::vector< float > &vertices, int maxVertices=64)
Atomically rebuilds a convex hull from packed local XYZ vertices.
Definition Shape3D.cpp:518
float getDensity() const
Definition Shape3D.cpp:889
int getGroupIndex() const
Definition Shape3D.cpp:928
void setRestitutionCombineMode(const std::string &mode)
Sets restitution combine mode: default, average, minimum, multiply, or maximum.
Definition Shape3D.cpp:852
float getTangentVelocityZ() const
Local-space conveyor velocity Z component.
Definition Shape3D.cpp:825
std::string getRestitutionCombineMode() const
Current restitution combine mode.
Definition Shape3D.cpp:861
void setHeightFieldRegion(int x, int z, int width, int depth, const std::vector< float > &heights)
Atomically updates a rectangular sample region in row-major order.
Definition Shape3D.cpp:698
void setRollingResistance(float resistance)
Rolling resistance coefficient, finite and non-negative.
Definition Shape3D.cpp:794
void disableOneWay()
Disables one-way filtering and restores ordinary two-sided collision.
Definition Shape3D.cpp:422
void setSensor(bool sensor)
Sensor shapes report contacts but never collide.
Definition Shape3D.cpp:371
std::string getFrictionCombineMode() const
Current friction combine mode.
Definition Shape3D.cpp:846
bool areHitEventsEnabled() const
Whether significant collision hit events are enabled.
Definition Shape3D.cpp:394
void setFriction(float friction)
Material properties.
Definition Shape3D.cpp:774
void setExplosionScale(float scale)
Sets non-negative multiplier applied to geometry-aware explosion impulses.
Definition Shape3D.cpp:874
float getTangentVelocityX() const
Local-space conveyor velocity X component.
Definition Shape3D.cpp:817
float getRollingResistance() const
Current rolling resistance; physically affects spheres and capsules.
Definition Shape3D.cpp:804
Box3D rigid-body world. Script coordinates are meters (Box3D native), unlike 2D World which uses pixe...
Definition World3D.h:63
void updateShapeTag(Shape3D *shape)
Internal: refreshes the tag snapshot for a live shape handle.
Definition World3D.cpp:1069
void registerShapeHandle(Shape3D *shape)
Internal: snapshots a backend handle for destruction-safe end events.
Definition World3D.cpp:1026
void forgetShape(Shape3D *shape)
Definition World3D.cpp:1006
Optional physics backend for vehicle mobility and body attach.
Definition Climbing.h:36
int axis(int64_t a, size_t rank)
Axis.
bool enabled