Files
EVEngine/src/modules/physics/Shape3D.cpp
T
西风逍遥游andCursor Agent c26a077cb5 feat(physics): integrate Box3D for 3D rigid-body simulation (#31)
Add Erin Catto's Box3D (v0.1.0) as external/box3d and expose World3D,
Body3D, and Shape3D through Physics.newWorld3D, mirroring the existing
Box2D script API with meter-space coordinates, queries, and contact events.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-11 17:01:13 +08:00

167 lines
4.9 KiB
C++

#include "physics/Shape3D.h"
#include "physics/Body3D.h"
#include "physics/World3D.h"
#include "common/Exception.h"
#include <box3d/box3d.h>
namespace eve::physics {
namespace {
b3ShapeDef makeShapeDef(float density, float friction, float restitution, bool sensor) {
b3ShapeDef def = b3DefaultShapeDef();
def.density = density;
def.baseMaterial.friction = friction;
def.baseMaterial.restitution = restitution;
def.isSensor = sensor;
def.enableContactEvents = !sensor;
def.enableSensorEvents = true;
return def;
}
} // namespace
Shape3D::Shape3D(World3D *world, Body3D *body, b3ShapeId shapeId, Kind kind, float a, float b,
float c)
: world_(world), body_(body), shapeId_(shapeId), kind_(kind), a_(a), b_(b), c_(c) {}
Shape3D::~Shape3D() {
if (isValid() && body_ && body_->isValid()) {
b3Shape_SetUserData(shapeId_, nullptr);
b3DestroyShape(shapeId_, true);
if (world_) world_->forgetShape(this);
}
shapeId_ = {};
body_ = nullptr;
world_ = nullptr;
}
bool Shape3D::isValid() const { return b3Shape_IsValid(shapeId_); }
void Shape3D::invalidate() {
if (isValid()) b3Shape_SetUserData(shapeId_, nullptr);
shapeId_ = {};
body_ = nullptr;
world_ = nullptr;
}
void Shape3D::destroy() {
if (!isValid() || !body_ || !body_->isValid()) {
invalidate();
return;
}
b3Shape_SetUserData(shapeId_, nullptr);
b3DestroyShape(shapeId_, true);
if (world_) world_->forgetShape(this);
shapeId_ = {};
body_ = nullptr;
world_ = nullptr;
}
void Shape3D::recreate(bool sensor) {
if (!body_ || !body_->isValid())
throw eve::Exception("Shape3D.setSensor: body destroyed");
float density = isValid() ? b3Shape_GetDensity(shapeId_) : 1.f;
float friction = isValid() ? b3Shape_GetFriction(shapeId_) : 0.2f;
float restitution = isValid() ? b3Shape_GetRestitution(shapeId_) : 0.f;
if (isValid()) {
b3Shape_SetUserData(shapeId_, nullptr);
b3DestroyShape(shapeId_, false);
shapeId_ = {};
}
b3ShapeDef def = makeShapeDef(density, friction, restitution, sensor);
switch (kind_) {
case Kind::Box: {
b3BoxHull box = b3MakeBoxHull(a_, b_, c_);
shapeId_ = b3CreateHullShape(body_->raw(), &def, &box.base);
break;
}
case Kind::Sphere: {
b3Sphere sphere;
sphere.center = b3Vec3_zero;
sphere.radius = a_;
shapeId_ = b3CreateSphereShape(body_->raw(), &def, &sphere);
break;
}
case Kind::Capsule: {
b3Capsule capsule;
capsule.center1 = b3Vec3{0.f, -a_, 0.f};
capsule.center2 = b3Vec3{0.f, a_, 0.f};
capsule.radius = b_;
shapeId_ = b3CreateCapsuleShape(body_->raw(), &def, &capsule);
break;
}
}
b3Shape_SetUserData(shapeId_, this);
}
void Shape3D::setSensor(bool sensor) {
if (!body_ || !body_->isValid()) return;
if (isValid() && b3Shape_IsSensor(shapeId_) == sensor) return;
recreate(sensor);
}
bool Shape3D::isSensor() const { return isValid() ? b3Shape_IsSensor(shapeId_) : false; }
void Shape3D::setFriction(float friction) {
if (!isValid()) return;
b3Shape_SetFriction(shapeId_, friction);
}
float Shape3D::getFriction() const { return isValid() ? b3Shape_GetFriction(shapeId_) : 0.f; }
void Shape3D::setRestitution(float restitution) {
if (!isValid()) return;
b3Shape_SetRestitution(shapeId_, restitution);
}
float Shape3D::getRestitution() const {
return isValid() ? b3Shape_GetRestitution(shapeId_) : 0.f;
}
void Shape3D::setDensity(float density) {
if (!isValid()) return;
b3Shape_SetDensity(shapeId_, density, true);
}
float Shape3D::getDensity() const { return isValid() ? b3Shape_GetDensity(shapeId_) : 0.f; }
bool Shape3D::testPoint(float x, float y, float z) const {
if (!isValid() || !body_ || !body_->isValid()) return false;
b3Vec3 point{x, y, z};
b3ShapeProxy proxy;
proxy.points = &point;
proxy.count = 1;
proxy.radius = 0.f;
b3WorldTransform wt = b3Body_GetTransform(body_->raw());
b3Transform xf;
xf.p = b3Vec3{static_cast<float>(wt.p.x), static_cast<float>(wt.p.y),
static_cast<float>(wt.p.z)};
xf.q = wt.q;
switch (kind_) {
case Kind::Box: {
const b3HullData *hull = b3Shape_GetHull(shapeId_);
if (!hull) return false;
return b3OverlapHull(hull, xf, &proxy);
}
case Kind::Sphere: {
b3Sphere sphere = b3Shape_GetSphere(shapeId_);
return b3OverlapSphere(&sphere, xf, &proxy);
}
case Kind::Capsule: {
b3Capsule capsule = b3Shape_GetCapsule(shapeId_);
return b3OverlapCapsule(&capsule, xf, &proxy);
}
}
return false;
}
} // namespace eve::physics