载入中...
搜索中...
未找到
Body.cpp
浏览该文件的文档.
1#include "physics/Body.h"
2#include "physics/Fixture.h"
3#include "physics/Joint2D.h"
4#include "physics/World.h"
5
6#include "common/Exception.h"
7
8#include <Box2D/Box2D.h>
9
10#include <cmath>
11#include <cstring>
12#include <vector>
13
14namespace eve::physics {
15namespace {
16
17b2BodyType parseBodyType(const std::string &type) {
18 if (type == "static") return b2_staticBody;
19 if (type == "kinematic") return b2_kinematicBody;
20 if (type == "dynamic") return b2_dynamicBody;
21 throw eve::Exception("Body.setType: unknown body type '%s'", type.c_str());
22}
23
24const char *bodyTypeName(b2BodyType t) {
25 switch (t) {
26 case b2_staticBody: return "static";
27 case b2_kinematicBody: return "kinematic";
28 case b2_dynamicBody: return "dynamic";
29 default: return "static";
30 }
31}
32
33} // namespace
34
35Body::Body(World *world, b2Body *body, int id, PhysicsBodyHandle runtimeHandle)
36 : world_(world), body_(body), id_(id), runtimeHandle_(runtimeHandle) {}
37
39 if (body_ && world_ && world_->raw()) {
40 std::vector<Joint2D *> joints(world_->joints_.begin(), world_->joints_.end());
41 for (Joint2D *joint : joints) {
42 if (joint && joint->isValid() && (joint->bodyA_ == this || joint->bodyB_ == this))
43 joint->destroy();
44 }
45 // Invalidate fixture wrappers before DestroyBody frees b2Fixtures.
46 b2Fixture *f = body_->GetFixtureList();
47 while (f) {
48 b2Fixture *next = f->GetNext();
49 auto *wrap = static_cast<Fixture *>(f->GetUserData());
50 if (wrap) {
51 world_->forgetFixture(wrap);
52 wrap->invalidate();
53 }
54 f = next;
55 }
56 body_->SetUserData(nullptr);
57 world_->raw()->DestroyBody(body_);
58 world_->forgetBody(this);
59 }
60 body_ = nullptr;
61 world_ = nullptr;
62 runtimeHandle_ = PhysicsBodyHandle::invalid();
63}
64
66 if (body_) body_->SetUserData(nullptr);
67 body_ = nullptr;
68 world_ = nullptr;
69 runtimeHandle_ = PhysicsBodyHandle::invalid();
70}
71
73 if (!body_ || !world_ || !world_->raw()) {
74 invalidate();
75 return;
76 }
77 std::vector<Joint2D *> joints(world_->joints_.begin(), world_->joints_.end());
78 for (Joint2D *joint : joints) {
79 if (joint && joint->isValid() && (joint->bodyA_ == this || joint->bodyB_ == this))
80 joint->destroy();
81 }
82 b2Fixture *f = body_->GetFixtureList();
83 while (f) {
84 b2Fixture *next = f->GetNext();
85 auto *wrap = static_cast<Fixture *>(f->GetUserData());
86 if (wrap) {
87 world_->forgetFixture(wrap);
88 wrap->invalidate();
89 }
90 f = next;
91 }
92 body_->SetUserData(nullptr);
93 world_->raw()->DestroyBody(body_);
94 world_->forgetBody(this);
95 body_ = nullptr;
96 world_ = nullptr;
97 runtimeHandle_ = PhysicsBodyHandle::invalid();
98}
99
100void Body::setPosition(float x, float y) {
101 if (!body_ || !world_) return;
102 body_->SetTransform(b2Vec2(world_->toMeters(x), world_->toMeters(y)), body_->GetAngle());
103}
104
105float Body::getX() const {
106 if (!body_ || !world_) return 0.f;
107 return world_->toPixels(body_->GetPosition().x);
108}
109
110float Body::getY() const {
111 if (!body_ || !world_) return 0.f;
112 return world_->toPixels(body_->GetPosition().y);
113}
114
115void Body::setAngle(float radians) {
116 if (!body_) return;
117 body_->SetTransform(body_->GetPosition(), radians);
118}
119
120float Body::getAngle() const {
121 if (!body_) return 0.f;
122 return body_->GetAngle();
123}
124
125void Body::setLinearVelocity(float vx, float vy) {
126 if (!body_ || !world_) return;
127 body_->SetLinearVelocity(b2Vec2(world_->toMeters(vx), world_->toMeters(vy)));
128}
129
131 if (!body_ || !world_) return 0.f;
132 return world_->toPixels(body_->GetLinearVelocity().x);
133}
134
136 if (!body_ || !world_) return 0.f;
137 return world_->toPixels(body_->GetLinearVelocity().y);
138}
139
140float Body::getLinearSpeed() const {
141 if (!body_ || !world_) return 0.f;
142 return world_->toPixels(body_->GetLinearVelocity().Length());
143}
144
145float Body::getMass() const { return body_ ? body_->GetMass() : 0.f; }
146
148 if (!body_ || !world_) return 0.f;
149 return world_->toPixels(body_->GetWorldCenter().x);
150}
151
153 if (!body_ || !world_) return 0.f;
154 return world_->toPixels(body_->GetWorldCenter().y);
155}
156
157void Body::setAngularVelocity(float omega) {
158 if (!body_) return;
159 body_->SetAngularVelocity(omega);
160}
161
163 if (!body_) return 0.f;
164 return body_->GetAngularVelocity();
165}
166
167void Body::applyForce(float fx, float fy) {
168 if (!body_ || !world_) return;
169 // Force in Newtons ≈ (pixels/s² * mass) with mass in kg; convert like LÖVE.
170 body_->ApplyForceToCenter(b2Vec2(world_->toMeters(fx), world_->toMeters(fy)), true);
171}
172
173void Body::applyForceAt(float fx, float fy, float x, float y) {
174 if (!body_ || !world_) return;
175 body_->ApplyForce(b2Vec2(world_->toMeters(fx), world_->toMeters(fy)),
176 b2Vec2(world_->toMeters(x), world_->toMeters(y)), true);
177}
178
179void Body::applyLinearImpulse(float ix, float iy) {
180 if (!body_ || !world_) return;
181 body_->ApplyLinearImpulse(b2Vec2(world_->toMeters(ix), world_->toMeters(iy)),
182 body_->GetWorldCenter(), true);
183}
184
185void Body::applyAngularImpulse(float impulse) {
186 if (!body_) return;
187 body_->ApplyAngularImpulse(impulse, true);
188}
189
190void Body::setType(const std::string &bodyType) {
191 if (!body_) return;
192 body_->SetType(parseBodyType(bodyType));
193}
194
195std::string Body::getType() const {
196 if (!body_) return "static";
197 return bodyTypeName(body_->GetType());
198}
199
200void Body::setFixedRotation(bool fixed) {
201 if (!body_) return;
202 body_->SetFixedRotation(fixed);
203}
204
205bool Body::isFixedRotation() const { return body_ ? body_->IsFixedRotation() : false; }
206
208 if (!body_) return;
209 body_->SetActive(active);
210}
211
212bool Body::isActive() const { return body_ ? body_->IsActive() : false; }
213
215 if (!body_) return;
216 body_->SetBullet(bullet);
217}
218
219bool Body::isBullet() const { return body_ ? body_->IsBullet() : false; }
220
222 if (!body_) return;
223 body_->SetAwake(awake);
224}
225
226bool Body::isAwake() const { return body_ ? body_->IsAwake() : false; }
227
228Fixture *Body::newRectangleFixture(float width, float height, float density, float friction,
229 float restitution) {
230 return newRectangleFixtureAt(width, height, 0.f, 0.f, density, friction, restitution);
231}
232
234 float density, float friction, float restitution) {
235 if (!body_ || !world_) throw eve::Exception("Body.newRectangleFixture: body destroyed");
236 if (width <= 0.f || height <= 0.f)
237 throw eve::Exception("Body.newRectangleFixture: width/height must be > 0");
238
239 b2PolygonShape shape;
240 shape.SetAsBox(world_->toMeters(width) * 0.5f, world_->toMeters(height) * 0.5f,
241 b2Vec2(world_->toMeters(offsetX), world_->toMeters(offsetY)), 0.f);
242
243 b2FixtureDef def;
244 def.shape = &shape;
245 def.density = density;
246 def.friction = friction;
247 def.restitution = restitution;
248
249 b2Fixture *raw = body_->CreateFixture(&def);
250 auto *fx = new Fixture(world_, this, raw);
251 raw->SetUserData(fx);
252 world_->fixtures_.insert(fx);
253 return fx;
254}
255
256Fixture *Body::newCircleFixture(float radius, float density, float friction, float restitution) {
257 if (!body_ || !world_) throw eve::Exception("Body.newCircleFixture: body destroyed");
258 if (radius <= 0.f) throw eve::Exception("Body.newCircleFixture: radius must be > 0");
259
260 b2CircleShape shape;
261 shape.m_radius = world_->toMeters(radius);
262
263 b2FixtureDef def;
264 def.shape = &shape;
265 def.density = density;
266 def.friction = friction;
267 def.restitution = restitution;
268
269 b2Fixture *raw = body_->CreateFixture(&def);
270 auto *fx = new Fixture(world_, this, raw);
271 raw->SetUserData(fx);
272 world_->fixtures_.insert(fx);
273 return fx;
274}
275
276Fixture *Body::newPolygonFixture(const std::vector<float> &vertices, float density,
277 float friction, float restitution) {
278 if (!body_ || !world_) throw eve::Exception("Body.newPolygonFixture: body destroyed");
279 if (vertices.size() < 6 || vertices.size() > 16 || vertices.size() % 2 != 0)
280 throw eve::Exception("Body.newPolygonFixture: expected 3..8 packed XY vertices");
281 std::vector<b2Vec2> points;
282 points.reserve(vertices.size() / 2);
283 for (size_t index = 0; index < vertices.size(); index += 2) {
284 if (!std::isfinite(vertices[index]) || !std::isfinite(vertices[index + 1]))
285 throw eve::Exception("Body.newPolygonFixture: vertices must be finite");
286 points.emplace_back(world_->toMeters(vertices[index]), world_->toMeters(vertices[index + 1]));
287 }
288 b2PolygonShape shape;
289 shape.Set(points.data(), static_cast<int32>(points.size()));
290 if (shape.GetVertexCount() < 3)
291 throw eve::Exception("Body.newPolygonFixture: vertices do not form a convex polygon");
292 b2FixtureDef def;
293 def.shape = &shape;
294 def.density = density;
295 def.friction = friction;
296 def.restitution = restitution;
297 b2Fixture *raw = body_->CreateFixture(&def);
298 if (!raw) throw eve::Exception("Body.newPolygonFixture: Box2D rejected the fixture");
299 auto *fixture = new Fixture(world_, this, raw);
300 raw->SetUserData(fixture);
301 world_->fixtures_.insert(fixture);
302 return fixture;
303}
304
305Fixture *Body::newChainFixture(const std::vector<float> &vertices, bool loop, float friction,
306 float restitution) {
307 if (!body_ || !world_) throw eve::Exception("Body.newChainFixture: body destroyed");
308 const size_t count = vertices.size() / 2;
309 if (vertices.size() % 2 != 0 || count < (loop ? 3U : 2U) || count > 100000U)
310 throw eve::Exception("Body.newChainFixture: invalid packed XY vertex count");
311 std::vector<b2Vec2> points;
312 points.reserve(count);
313 for (size_t index = 0; index < vertices.size(); index += 2) {
314 if (!std::isfinite(vertices[index]) || !std::isfinite(vertices[index + 1]))
315 throw eve::Exception("Body.newChainFixture: vertices must be finite");
316 const b2Vec2 point(world_->toMeters(vertices[index]), world_->toMeters(vertices[index + 1]));
317 if (!points.empty() && point == points.back())
318 throw eve::Exception("Body.newChainFixture: consecutive vertices must be distinct");
319 points.push_back(point);
320 }
321 if (loop && points.front() == points.back())
322 throw eve::Exception("Body.newChainFixture: loop endpoint is implicit and must not be repeated");
323 b2ChainShape shape;
324 if (loop)
325 shape.CreateLoop(points.data(), static_cast<int32>(points.size()));
326 else
327 shape.CreateChain(points.data(), static_cast<int32>(points.size()));
328 b2FixtureDef def;
329 def.shape = &shape;
330 def.friction = friction;
331 def.restitution = restitution;
332 b2Fixture *raw = body_->CreateFixture(&def);
333 if (!raw) throw eve::Exception("Body.newChainFixture: Box2D rejected the fixture");
334 auto *fixture = new Fixture(world_, this, raw);
335 raw->SetUserData(fixture);
336 world_->fixtures_.insert(fixture);
337 return fixture;
338}
339
340} // namespace eve::physics
bool & active
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
ShaderImageInput shape
std::uint32_t height
std::uint32_t width
float f
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
std::uint32_t count
double restitution
float offsetX
float offsetY
std::string body
uint32_t index
glm::vec3 point
float vy
bool awake
float vx
bool bullet
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
static constexpr RuntimeHandle invalid() noexcept
Returns the canonical invalid handle.
Fixture * newRectangleFixture(float width, float height, float density=1.f, float friction=0.2f, float restitution=0.f)
Creates a rectangle fixture in pixel-space units.
Definition Body.cpp:228
float getLinearVelocityX() const
Returns the linear velocity x.
Definition Body.cpp:130
void applyForceAt(float fx, float fy, float x, float y)
Force applied at a world pixel position.
Definition Body.cpp:173
void setBullet(bool bullet)
CCD bullet mode (recommended for fast small bodies).
Definition Body.cpp:214
float getX() const
Returns the x.
Definition Body.cpp:105
void setAwake(bool awake)
Wakes / sleeps the body manually.
Definition Body.cpp:221
void setType(const std::string &bodyType)
"static" | "kinematic" | "dynamic".
Definition Body.cpp:190
void setAngle(float radians)
Rotation in radians.
Definition Body.cpp:115
float getLinearVelocityY() const
Returns the linear velocity y.
Definition Body.cpp:135
bool isActive() const
True when active.
Definition Body.cpp:212
void applyLinearImpulse(float ix, float iy)
Instantaneous linear impulse.
Definition Body.cpp:179
Fixture * newPolygonFixture(const std::vector< float > &vertices, float density=1.f, float friction=0.2f, float restitution=0.f)
Create a bounded convex polygon fixture from packed local pixel-space XY vertices.
Definition Body.cpp:276
Fixture * newCircleFixture(float radius, float density=1.f, float friction=0.2f, float restitution=0.f)
Creates a circular fixture in pixel-space units.
Definition Body.cpp:256
void setFixedRotation(bool fixed)
Locks rotation so the body cannot spin.
Definition Body.cpp:200
Fixture * newChainFixture(const std::vector< float > &vertices, bool loop=false, float friction=0.2f, float restitution=0.f)
Create an open chain or closed loop from packed local pixel-space XY vertices.
Definition Body.cpp:305
void applyForce(float fx, float fy)
Force (pixels/s² * kg) applied at the center of mass.
Definition Body.cpp:167
bool isAwake() const
True when awake.
Definition Body.cpp:226
bool isFixedRotation() const
True when fixed rotation.
Definition Body.cpp:205
bool isBullet() const
True when bullet.
Definition Body.cpp:219
friend class Fixture
Definition Body.h:211
void setAngularVelocity(float omega)
Angular velocity in radians/s.
Definition Body.cpp:157
b2Body * raw()
Exposes the underlying Box2D body for tightly-scoped backend integration.
Definition Body.h:194
float getAngularVelocity() const
Returns the angular velocity.
Definition Body.cpp:162
void destroy()
Destroys the body inside its world.
Definition Body.cpp:72
void applyAngularImpulse(float impulse)
Instantaneous angular impulse.
Definition Body.cpp:185
float getWorldCenterY() const
Returns the world center y.
Definition Body.cpp:152
float getLinearSpeed() const
Magnitude of the linear velocity.
Definition Body.cpp:140
float getAngle() const
Returns the angle.
Definition Body.cpp:120
std::string getType() const
Returns the type.
Definition Body.cpp:195
void invalidate()
Internal: marks the wrapper invalid after world destruction.
Definition Body.cpp:65
void setPosition(float x, float y)
Position in pixels.
Definition Body.cpp:100
float getWorldCenterX() const
World center of mass in pixels.
Definition Body.cpp:147
Body(World *world, b2Body *body, int id, PhysicsBodyHandle runtimeHandle)
Internal: wraps a Box2D body (use World::newBody).
Definition Body.cpp:35
Fixture * newRectangleFixtureAt(float width, float height, float offsetX, float offsetY, float density=1.f, float friction=0.2f, float restitution=0.f)
Creates an offset rectangle fixture in pixel-space units.
Definition Body.cpp:233
float getMass() const
Mass in kilograms (Box2D units).
Definition Body.cpp:145
void setActive(bool active)
Disables/enables the body and its fixtures.
Definition Body.cpp:207
void setLinearVelocity(float vx, float vy)
Linear velocity in pixels/s.
Definition Body.cpp:125
float getY() const
Returns the y.
Definition Body.cpp:110
2D fixture: a shape attached to a Body with material + filter settings. Also carries a string tag use...
Definition Fixture.h:18
Script-facing Box2D joint owned by a World.
Definition Joint2D.h:21
Box2D world wrapper (2D physics) with pixel-space coordinates. Handles stepping, gravity,...
Definition World.h:41
void forgetBody(Body *body)
Definition World.cpp:467
void forgetFixture(Fixture *fixture)
Definition World.cpp:478
float toPixels(float meters) const
Converts a meter-space length to pixels.
Definition World.cpp:421
b2World * raw()
Exposes the underlying Box2D world for tightly-scoped backend integration.
Definition World.h:378
float toMeters(float pixels) const
Converts a pixel-space length to meters.
Definition World.cpp:420
Optional physics backend for vehicle mobility and body attach.
Definition Climbing.h:36