载入中...
搜索中...
未找到
Body3D.cpp
浏览该文件的文档.
1#include "physics/Body3D.h"
2#include "physics/Shape3D.h"
3#include "physics/World3D.h"
4
5#include "common/Exception.h"
6
7#include <box3d/box3d.h>
8
9#include <cmath>
10#include <vector>
11
12namespace eve::physics {
13namespace {
14
15b3BodyType parseBodyType(const std::string &type) {
16 if (type == "static") return b3_staticBody;
17 if (type == "kinematic") return b3_kinematicBody;
18 if (type == "dynamic") return b3_dynamicBody;
19 throw eve::Exception("Body3D.setType: unknown body type '%s'", type.c_str());
20}
21
22const char *bodyTypeName(b3BodyType t) {
23 switch (t) {
24 case b3_staticBody: return "static";
25 case b3_kinematicBody: return "kinematic";
26 case b3_dynamicBody: return "dynamic";
27 default: return "static";
28 }
29}
30
31b3ShapeDef makeShapeDef(float density, float friction, float restitution) {
32 b3ShapeDef def = b3DefaultShapeDef();
33 def.density = density;
34 def.baseMaterial.friction = friction;
35 def.baseMaterial.restitution = restitution;
36 def.enableContactEvents = true;
37 def.enableSensorEvents = true;
38 return def;
39}
40
41} // namespace
42
43Body3D::Body3D(World3D *world, b3BodyId bodyId, int id)
44 : world_(world), bodyId_(bodyId), id_(id) {}
45
47 if (isValid() && world_ && world_->isValid()) {
48 // Invalidate shapes that still reference this body.
49 std::vector<Shape3D *> shapes(world_->shapes_.begin(), world_->shapes_.end());
50 for (Shape3D *s : shapes) {
51 if (s && s->getBody() == this) {
52 world_->forgetShape(s);
53 s->invalidate();
54 }
55 }
56 b3Body_SetUserData(bodyId_, nullptr);
57 b3DestroyBody(bodyId_);
58 world_->forgetBody(this);
59 }
60 bodyId_ = {};
61 world_ = nullptr;
62}
63
64bool Body3D::isValid() const { return b3Body_IsValid(bodyId_); }
65
67 if (isValid()) b3Body_SetUserData(bodyId_, nullptr);
68 bodyId_ = {};
69 world_ = nullptr;
70}
71
73 if (!isValid() || !world_ || !world_->isValid()) {
74 invalidate();
75 return;
76 }
77 std::vector<Shape3D *> shapes(world_->shapes_.begin(), world_->shapes_.end());
78 for (Shape3D *s : shapes) {
79 if (s && s->getBody() == this) {
80 world_->forgetShape(s);
81 // Destroy the underlying shape before invalidating the wrapper.
82 if (s->isValid()) b3DestroyShape(s->raw(), false);
83 s->invalidate();
84 }
85 }
86 b3Body_SetUserData(bodyId_, nullptr);
87 b3DestroyBody(bodyId_);
88 world_->forgetBody(this);
89 bodyId_ = {};
90 world_ = nullptr;
91}
92
93void Body3D::setPosition(float x, float y, float z) {
94 if (!isValid()) return;
95 b3Body_SetTransform(bodyId_, b3Pos{x, y, z}, b3Body_GetRotation(bodyId_));
96}
97
98float Body3D::getX() const {
99 if (!isValid()) return 0.f;
100 return static_cast<float>(b3Body_GetPosition(bodyId_).x);
101}
102
103float Body3D::getY() const {
104 if (!isValid()) return 0.f;
105 return static_cast<float>(b3Body_GetPosition(bodyId_).y);
106}
107
108float Body3D::getZ() const {
109 if (!isValid()) return 0.f;
110 return static_cast<float>(b3Body_GetPosition(bodyId_).z);
111}
112
113void Body3D::setRotation(float qx, float qy, float qz, float qw) {
114 if (!isValid()) return;
115 b3Quat q;
116 q.v = b3Vec3{qx, qy, qz};
117 q.s = qw;
118 float len = std::sqrt(qx * qx + qy * qy + qz * qz + qw * qw);
119 if (len > 1e-8f) {
120 q.v.x /= len;
121 q.v.y /= len;
122 q.v.z /= len;
123 q.s /= len;
124 } else {
125 q = b3Quat_identity;
126 }
127 b3Body_SetTransform(bodyId_, b3Body_GetPosition(bodyId_), q);
128}
129
130float Body3D::getRotX() const {
131 if (!isValid()) return 0.f;
132 return b3Body_GetRotation(bodyId_).v.x;
133}
134
135float Body3D::getRotY() const {
136 if (!isValid()) return 0.f;
137 return b3Body_GetRotation(bodyId_).v.y;
138}
139
140float Body3D::getRotZ() const {
141 if (!isValid()) return 0.f;
142 return b3Body_GetRotation(bodyId_).v.z;
143}
144
145float Body3D::getRotW() const {
146 if (!isValid()) return 1.f;
147 return b3Body_GetRotation(bodyId_).s;
148}
149
150void Body3D::setLinearVelocity(float vx, float vy, float vz) {
151 if (!isValid()) return;
152 b3Body_SetLinearVelocity(bodyId_, b3Vec3{vx, vy, vz});
153}
154
156 if (!isValid()) return 0.f;
157 return b3Body_GetLinearVelocity(bodyId_).x;
158}
159
161 if (!isValid()) return 0.f;
162 return b3Body_GetLinearVelocity(bodyId_).y;
163}
164
166 if (!isValid()) return 0.f;
167 return b3Body_GetLinearVelocity(bodyId_).z;
168}
169
170void Body3D::setAngularVelocity(float wx, float wy, float wz) {
171 if (!isValid()) return;
172 b3Body_SetAngularVelocity(bodyId_, b3Vec3{wx, wy, wz});
173}
174
176 if (!isValid()) return 0.f;
177 return b3Body_GetAngularVelocity(bodyId_).x;
178}
179
181 if (!isValid()) return 0.f;
182 return b3Body_GetAngularVelocity(bodyId_).y;
183}
184
186 if (!isValid()) return 0.f;
187 return b3Body_GetAngularVelocity(bodyId_).z;
188}
189
190void Body3D::applyForce(float fx, float fy, float fz) {
191 if (!isValid()) return;
192 b3Body_ApplyForceToCenter(bodyId_, b3Vec3{fx, fy, fz}, true);
193}
194
195void Body3D::applyForceAt(float fx, float fy, float fz, float x, float y, float z) {
196 if (!isValid()) return;
197 b3Body_ApplyForce(bodyId_, b3Vec3{fx, fy, fz}, b3Pos{x, y, z}, true);
198}
199
200void Body3D::applyLinearImpulse(float ix, float iy, float iz) {
201 if (!isValid()) return;
202 b3Body_ApplyLinearImpulseToCenter(bodyId_, b3Vec3{ix, iy, iz}, true);
203}
204
205void Body3D::applyAngularImpulse(float ix, float iy, float iz) {
206 if (!isValid()) return;
207 b3Body_ApplyAngularImpulse(bodyId_, b3Vec3{ix, iy, iz}, true);
208}
209
210void Body3D::setType(const std::string &bodyType) {
211 if (!isValid()) return;
212 b3Body_SetType(bodyId_, parseBodyType(bodyType));
213}
214
215std::string Body3D::getType() const {
216 if (!isValid()) return "static";
217 return bodyTypeName(b3Body_GetType(bodyId_));
218}
219
220void Body3D::setFixedRotation(bool fixed) {
221 if (!isValid()) return;
222 b3MotionLocks locks = b3Body_GetMotionLocks(bodyId_);
223 locks.angularX = fixed;
224 locks.angularY = fixed;
225 locks.angularZ = fixed;
226 b3Body_SetMotionLocks(bodyId_, locks);
227}
228
230 if (!isValid()) return false;
231 b3MotionLocks locks = b3Body_GetMotionLocks(bodyId_);
232 return locks.angularX && locks.angularY && locks.angularZ;
233}
234
236 if (!isValid()) return;
237 if (active)
238 b3Body_Enable(bodyId_);
239 else
240 b3Body_Disable(bodyId_);
241}
242
243bool Body3D::isActive() const { return isValid() ? b3Body_IsEnabled(bodyId_) : false; }
244
245void Body3D::setBullet(bool bullet) {
246 if (!isValid()) return;
247 b3Body_SetBullet(bodyId_, bullet);
248}
249
250bool Body3D::isBullet() const { return isValid() ? b3Body_IsBullet(bodyId_) : false; }
251
252void Body3D::setAwake(bool awake) {
253 if (!isValid()) return;
254 b3Body_SetAwake(bodyId_, awake);
255}
256
257bool Body3D::isAwake() const { return isValid() ? b3Body_IsAwake(bodyId_) : false; }
258
259Shape3D *Body3D::newBoxShape(float width, float height, float depth, float density,
260 float friction, float restitution) {
261 if (!isValid() || !world_) throw eve::Exception("Body3D.newBoxShape: body destroyed");
262 if (width <= 0.f || height <= 0.f || depth <= 0.f)
263 throw eve::Exception("Body3D.newBoxShape: width/height/depth must be > 0");
264
265 float hx = width * 0.5f;
266 float hy = height * 0.5f;
267 float hz = depth * 0.5f;
268
269 b3BoxHull box = b3MakeBoxHull(hx, hy, hz);
270 b3ShapeDef def = makeShapeDef(density, friction, restitution);
271 b3ShapeId id = b3CreateHullShape(bodyId_, &def, &box.base);
272
273 auto *shape = new Shape3D(world_, this, id, Shape3D::Kind::Box, hx, hy, hz);
274 b3Shape_SetUserData(id, shape);
275 world_->shapes_.insert(shape);
276 return shape;
277}
278
279Shape3D *Body3D::newSphereShape(float radius, float density, float friction, float restitution) {
280 if (!isValid() || !world_) throw eve::Exception("Body3D.newSphereShape: body destroyed");
281 if (radius <= 0.f) throw eve::Exception("Body3D.newSphereShape: radius must be > 0");
282
283 b3Sphere sphere;
284 sphere.center = b3Vec3_zero;
285 sphere.radius = radius;
286
287 b3ShapeDef def = makeShapeDef(density, friction, restitution);
288 b3ShapeId id = b3CreateSphereShape(bodyId_, &def, &sphere);
289
290 auto *shape = new Shape3D(world_, this, id, Shape3D::Kind::Sphere, radius, 0.f, 0.f);
291 b3Shape_SetUserData(id, shape);
292 world_->shapes_.insert(shape);
293 return shape;
294}
295
296Shape3D *Body3D::newCapsuleShape(float height, float radius, float density, float friction,
297 float restitution) {
298 if (!isValid() || !world_) throw eve::Exception("Body3D.newCapsuleShape: body destroyed");
299 if (height < 0.f || radius <= 0.f)
300 throw eve::Exception("Body3D.newCapsuleShape: height >= 0 and radius > 0 required");
301
302 float half = height * 0.5f;
303 b3Capsule capsule;
304 capsule.center1 = b3Vec3{0.f, -half, 0.f};
305 capsule.center2 = b3Vec3{0.f, half, 0.f};
306 capsule.radius = radius;
307
308 b3ShapeDef def = makeShapeDef(density, friction, restitution);
309 b3ShapeId id = b3CreateCapsuleShape(bodyId_, &def, &capsule);
310
311 auto *shape = new Shape3D(world_, this, id, Shape3D::Kind::Capsule, half, radius, 0.f);
312 b3Shape_SetUserData(id, shape);
313 world_->shapes_.insert(shape);
314 return shape;
315}
316
317} // namespace eve::physics
bool active
Definition CardTypes.cpp:34
std::string type
std::string id
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
float height
Definition Grass.cpp:235
int x
Definition Grass.cpp:135
float depth
int width
uint32_t s
Definition Weather.cpp:28
void setActive(bool active)
Disables/enables the body and its shapes.
Definition Body3D.cpp:235
void setRotation(float qx, float qy, float qz, float qw)
Orientation as quaternion (x, y, z, w).
Definition Body3D.cpp:113
bool isBullet() const
Definition Body3D.cpp:250
bool isActive() const
Definition Body3D.cpp:243
void applyForce(float fx, float fy, float fz)
Force applied at the center of mass.
Definition Body3D.cpp:190
float getLinearVelocityY() const
Definition Body3D.cpp:160
void applyLinearImpulse(float ix, float iy, float iz)
Instantaneous linear impulse.
Definition Body3D.cpp:200
void setFixedRotation(bool fixed)
Lock all angular axes (Box3D motion locks).
Definition Body3D.cpp:220
Body3D(World3D *world, b3BodyId bodyId, int id)
Internal: wraps a Box3D body (use World3D::newBody).
Definition Body3D.cpp:43
void applyAngularImpulse(float ix, float iy, float iz)
Instantaneous angular impulse.
Definition Body3D.cpp:205
float getX() const
Definition Body3D.cpp:98
bool isFixedRotation() const
Definition Body3D.cpp:229
void setBullet(bool bullet)
CCD bullet mode.
Definition Body3D.cpp:245
Shape3D * newBoxShape(float width, float height, float depth, float density=1.f, float friction=0.2f, float restitution=0.f)
Box extents are full width/height/depth in meters (same convention as Body.newRectangleFixture)....
Definition Body3D.cpp:259
bool isValid() const
True while the underlying Box3D body is still alive.
Definition Body3D.cpp:64
float getY() const
Definition Body3D.cpp:103
void setAwake(bool awake)
Wakes / sleeps the body manually.
Definition Body3D.cpp:252
void destroy()
Destroys the body inside its world.
Definition Body3D.cpp:72
float getRotZ() const
Definition Body3D.cpp:140
float getRotY() const
Definition Body3D.cpp:135
float getRotX() const
Definition Body3D.cpp:130
float getAngularVelocityZ() const
Definition Body3D.cpp:185
void invalidate()
Internal: marks the wrapper invalid after world destruction.
Definition Body3D.cpp:66
float getAngularVelocityX() const
Definition Body3D.cpp:175
std::string getType() const
Definition Body3D.cpp:215
float getRotW() const
Definition Body3D.cpp:145
void applyForceAt(float fx, float fy, float fz, float x, float y, float z)
Force applied at a world position.
Definition Body3D.cpp:195
Shape3D * newCapsuleShape(float height, float radius, float density=1.f, float friction=0.2f, float restitution=0.f)
Capsule along local Y; height is distance between hemisphere centers.
Definition Body3D.cpp:296
void setLinearVelocity(float vx, float vy, float vz)
Linear velocity in m/s.
Definition Body3D.cpp:150
bool isAwake() const
Definition Body3D.cpp:257
void setPosition(float x, float y, float z)
Position in meters.
Definition Body3D.cpp:93
void setType(const std::string &bodyType)
"static" | "kinematic" | "dynamic".
Definition Body3D.cpp:210
float getLinearVelocityZ() const
Definition Body3D.cpp:165
void setAngularVelocity(float wx, float wy, float wz)
Angular velocity in rad/s.
Definition Body3D.cpp:170
float getAngularVelocityY() const
Definition Body3D.cpp:180
friend class Shape3D
Definition Body3D.h:108
float getZ() const
Definition Body3D.cpp:108
Shape3D * newSphereShape(float radius, float density=1.f, float friction=0.2f, float restitution=0.f)
Definition Body3D.cpp:279
float getLinearVelocityX() const
Definition Body3D.cpp:155
3D shape (box/sphere/capsule) attached to a Body3D with material settings. Created via Body3D::new*Sh...
Definition Shape3D.h:14
Box3D rigid-body world. Script coordinates are meters (Box3D native), unlike 2D World which uses pixe...
Definition World3D.h:18
void forgetBody(Body3D *body)
Internal: wrapper teardown bookkeeping.
Definition World3D.cpp:118
bool isValid() const
True while the underlying Box3D world is alive.
Definition World3D.cpp:41
void forgetShape(Shape3D *shape)
Definition World3D.cpp:119