载入中...
搜索中...
未找到
World3D.cpp
浏览该文件的文档.
1#include "physics/World3D.h"
2#include "physics/Body3D.h"
3#include "physics/Shape3D.h"
4
5#include "common/Exception.h"
6#include "event/Event.h"
7
8#include <box3d/box3d.h>
9
10#include <algorithm>
11#include <cmath>
12#include <unordered_set>
13
14namespace eve::physics {
15namespace {
16
17b3BodyType parseBodyType(const std::string &type) {
18 if (type == "static") return b3_staticBody;
19 if (type == "kinematic") return b3_kinematicBody;
20 if (type == "dynamic") return b3_dynamicBody;
21 throw eve::Exception("World3D.newBody: unknown body type '%s' (use static|kinematic|dynamic)",
22 type.c_str());
23}
24
25Body3D *bodyFromShape(b3ShapeId shapeId) {
26 if (!b3Shape_IsValid(shapeId)) return nullptr;
27 return static_cast<Body3D *>(b3Body_GetUserData(b3Shape_GetBody(shapeId)));
28}
29
30} // namespace
31
32World3D::World3D(float gravityX, float gravityY, float gravityZ, bool sleep) {
33 b3WorldDef def = b3DefaultWorldDef();
34 def.gravity = b3Vec3{gravityX, gravityY, gravityZ};
35 def.enableSleep = sleep;
36 worldId_ = b3CreateWorld(&def);
37}
38
40
41bool World3D::isValid() const { return !destroyed_ && b3World_IsValid(worldId_); }
42
44 if (destroyed_) return;
45 destroyed_ = true;
46
47 std::vector<Body3D *> bodies(bodies_.begin(), bodies_.end());
48 for (Body3D *b : bodies) {
49 if (b) b->invalidate();
50 }
51 bodies_.clear();
52
53 std::vector<Shape3D *> shapes(shapes_.begin(), shapes_.end());
54 for (Shape3D *s : shapes) {
55 if (s) s->invalidate();
56 }
57 shapes_.clear();
58
59 if (b3World_IsValid(worldId_)) {
60 b3DestroyWorld(worldId_);
61 }
62 worldId_ = {};
63}
64
65void World3D::update(float dt) { updateFull(dt, 4); }
66
67void World3D::updateFull(float dt, int subStepCount) {
68 if (!isValid()) return;
69 if (dt < 0.f) dt = 0.f;
70 if (dt > 0.05f) dt = 0.05f;
71 if (subStepCount < 1) subStepCount = 1;
72 b3World_Step(worldId_, dt, subStepCount);
74}
75
76void World3D::setGravity(float gx, float gy, float gz) {
77 if (!isValid()) return;
78 b3World_SetGravity(worldId_, b3Vec3{gx, gy, gz});
79}
80
81float World3D::getGravityX() const {
82 if (!isValid()) return 0.f;
83 return b3World_GetGravity(worldId_).x;
84}
85
86float World3D::getGravityY() const {
87 if (!isValid()) return 0.f;
88 return b3World_GetGravity(worldId_).y;
89}
90
91float World3D::getGravityZ() const {
92 if (!isValid()) return 0.f;
93 return b3World_GetGravity(worldId_).z;
94}
95
96int World3D::nextBodyId() { return nextId_++; }
97
98Body3D *World3D::newBody(const std::string &bodyType, float x, float y, float z) {
99 if (!isValid()) throw eve::Exception("World3D.newBody: world destroyed");
100
101 b3BodyDef def = b3DefaultBodyDef();
102 def.type = parseBodyType(bodyType);
103 def.position = b3Pos{x, y, z};
104 def.rotation = b3Quat_identity;
105
106 b3BodyId raw = b3CreateBody(worldId_, &def);
107 Body3D *body = new Body3D(this, raw, nextBodyId());
108 b3Body_SetUserData(raw, body);
109 bodies_.insert(body);
110 return body;
111}
112
114 if (!body) return;
115 body->destroy();
116}
117
118void World3D::forgetBody(Body3D *body) { bodies_.erase(body); }
119void World3D::forgetShape(Shape3D *shape) { shapes_.erase(shape); }
120
122 if (!isValid()) return;
123
124 auto *ev = eve::ModuleManager::getInstance<eve::event::Event>("Event");
125 if (!ev) return;
126
127 b3ContactEvents contacts = b3World_GetContactEvents(worldId_);
128 for (int i = 0; i < contacts.beginCount; ++i) {
129 Body3D *a = bodyFromShape(contacts.beginEvents[i].shapeIdA);
130 Body3D *b = bodyFromShape(contacts.beginEvents[i].shapeIdB);
131 if (!a || !b) continue;
132 std::vector<eve::event::Variant> args = {eve::event::Variant::makeInt(a->getId()),
134 ev->push(new eve::event::Message("begincontact3d", args));
135 }
136 for (int i = 0; i < contacts.endCount; ++i) {
137 Body3D *a = bodyFromShape(contacts.endEvents[i].shapeIdA);
138 Body3D *b = bodyFromShape(contacts.endEvents[i].shapeIdB);
139 if (!a || !b) continue;
140 std::vector<eve::event::Variant> args = {eve::event::Variant::makeInt(a->getId()),
142 ev->push(new eve::event::Message("endcontact3d", args));
143 }
144}
145
146int World3D::rayCast(float x1, float y1, float z1, float x2, float y2, float z2) {
147 rayHitBodyId_ = -1;
148 rayHitX_ = 0.f;
149 rayHitY_ = 0.f;
150 rayHitZ_ = 0.f;
151 rayHitNormalX_ = 0.f;
152 rayHitNormalY_ = 0.f;
153 rayHitNormalZ_ = 0.f;
154 rayHitFraction_ = 0.f;
155 if (!isValid()) return -1;
156
157 b3Pos origin{x1, y1, z1};
158 b3Vec3 translation{x2 - x1, y2 - y1, z2 - z1};
159 b3QueryFilter filter = b3DefaultQueryFilter();
160 b3RayResult hit = b3World_CastRayClosest(worldId_, origin, translation, filter);
161 if (!hit.hit) return -1;
162
163 Body3D *body = bodyFromShape(hit.shapeId);
164 if (!body) return -1;
165
166 rayHitBodyId_ = body->getId();
167 rayHitX_ = static_cast<float>(hit.point.x);
168 rayHitY_ = static_cast<float>(hit.point.y);
169 rayHitZ_ = static_cast<float>(hit.point.z);
170 rayHitNormalX_ = hit.normal.x;
171 rayHitNormalY_ = hit.normal.y;
172 rayHitNormalZ_ = hit.normal.z;
173 rayHitFraction_ = hit.fraction;
174 return rayHitBodyId_;
175}
176
177int World3D::queryAABB(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
178 queryBodyIds_.clear();
179 if (!isValid()) return 0;
180
181 struct Collector {
182 std::vector<int> *ids = nullptr;
183 std::unordered_set<int> seen;
184
185 static bool callback(b3ShapeId shapeId, void *context) {
186 auto *self = static_cast<Collector *>(context);
187 Body3D *b = bodyFromShape(shapeId);
188 if (!b) return true;
189 int id = b->getId();
190 if (self->seen.insert(id).second) self->ids->push_back(id);
191 return true;
192 }
193 } cb;
194 cb.ids = &queryBodyIds_;
195
196 b3AABB aabb;
197 aabb.lowerBound = b3Vec3{std::min(minX, maxX), std::min(minY, maxY), std::min(minZ, maxZ)};
198 aabb.upperBound = b3Vec3{std::max(minX, maxX), std::max(minY, maxY), std::max(minZ, maxZ)};
199 b3QueryFilter filter = b3DefaultQueryFilter();
200 b3World_OverlapAABB(worldId_, aabb, filter, &Collector::callback, &cb);
201 return static_cast<int>(queryBodyIds_.size());
202}
203
204int World3D::getQueryBodyId(int index) const {
205 if (index < 0 || index >= static_cast<int>(queryBodyIds_.size()))
206 throw eve::Exception("World3D.getQueryBodyId: index out of range");
207 return queryBodyIds_[static_cast<size_t>(index)];
208}
209
210} // namespace eve::physics
std::string type
int y
Definition Grass.cpp:135
int z
Definition Grass.cpp:135
int x
Definition Grass.cpp:135
JobFunc body
uint32_t a
uint32_t b
std::string filter
uint32_t s
Definition Weather.cpp:28
A named event carrying an ordered list of Variant payloads. Pushed messages are heap-allocated; the q...
Definition Event.h:56
3D rigid body (Box3D) in meter-space coordinates (+Y up by convention). Owned by a World3D; create sh...
Definition Body3D.h:16
3D shape (box/sphere/capsule) attached to a Body3D with material settings. Created via Body3D::new*Sh...
Definition Shape3D.h:14
void setGravity(float gx, float gy, float gz)
Gravity in m/s².
Definition World3D.cpp:76
int getQueryBodyId(int index) const
Body id of the i-th query hit.
Definition World3D.cpp:204
void update(float dt)
Steps the simulation by dt seconds (default substep count).
Definition World3D.cpp:65
void forgetBody(Body3D *body)
Internal: wrapper teardown bookkeeping.
Definition World3D.cpp:118
float getGravityX() const
Definition World3D.cpp:81
int nextBodyId()
Internal: next stable body id.
Definition World3D.cpp:96
float getGravityY() const
Definition World3D.cpp:86
int rayCast(float x1, float y1, float z1, float x2, float y2, float z2)
Closest raycast from (x1,y1,z1) to (x2,y2,z2) in meters. Returns hit body id, or -1....
Definition World3D.cpp:146
friend class Body3D
Definition World3D.h:86
b3WorldId raw() const
Raw Box3D world id.
Definition World3D.h:73
bool isValid() const
True while the underlying Box3D world is alive.
Definition World3D.cpp:41
int queryAABB(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)
Query shapes overlapping an AABB in meters (min/max corners). Returns match count; read ids with getQ...
Definition World3D.cpp:177
void updateFull(float dt, int subStepCount)
Steps with an explicit substep count.
Definition World3D.cpp:67
void destroyBody(Body3D *body)
Destroys a body (null is ignored).
Definition World3D.cpp:113
void forgetShape(Shape3D *shape)
Definition World3D.cpp:119
float getGravityZ() const
Definition World3D.cpp:91
void destroy()
Destroys the world and invalidates all wrappers.
Definition World3D.cpp:43
World3D(float gravityX, float gravityY, float gravityZ, bool sleep)
Definition World3D.cpp:32
void emitContactEvents()
Internal: collects Box3D contact events into the event buffers.
Definition World3D.cpp:121
Body3D * newBody(const std::string &bodyType, float x, float y, float z)
bodyType: "static" | "kinematic" | "dynamic". Position in meters.
Definition World3D.cpp:98
static Variant makeInt(int64_t v)
Constructs an integer variant.
Definition Event.h:29