载入中...
搜索中...
未找到
WorldJoints2D.cpp
浏览该文件的文档.
1#include "physics/World.h"
2
3#include "physics/Body.h"
4#include "physics/Joint2D.h"
6#include "common/Exception.h"
7
8#include <Box2D/Box2D.h>
9
10#include <cmath>
11
12namespace eve::physics {
13namespace {
14
15void requireFiniteParams(const float *values, int count, const char *operation) {
16 for (int i = 0; i < count; ++i) {
17 if (!std::isfinite(values[i]))
18 throw eve::Exception("%s: parameters must be finite", operation);
19 }
20}
21
22void requireDistinctBodies(World *world, Body *bodyA, Body *bodyB, const char *operation) {
23 if (!world || !world->isValid() || !bodyA || !bodyB || !bodyA->isValid() || !bodyB->isValid() ||
24 bodyA->getWorld() != world || bodyB->getWorld() != world || bodyA == bodyB)
25 throw eve::Exception("%s: bodies must be distinct and belong to this world", operation);
26}
27
28b2Vec2 normalizedAxisPixels(float x, float y, const char *operation) {
29 const float length = std::sqrt(x * x + y * y);
30 if (length <= 1e-8f) throw eve::Exception("%s: axis length must be > 0", operation);
31 return b2Vec2(x / length, y / length);
32}
33
34} // namespace
35
36Joint2D *World::adoptJoint(Body *bodyA, Body *bodyB, b2Joint *raw, Joint2D::Kind kind) {
38 auto *joint = new Joint2D(this, bodyA, bodyB, raw, runtimeHandle, kind, nextJointId());
39 raw->SetUserData(joint);
40 joints_.insert(joint);
41 jointHandles_[runtimeHandle] = joint;
42 return joint;
43}
44
46 if (!joint) return;
47 joints_.erase(joint);
48 if (joint->runtimeHandle().isValid()) jointHandles_.erase(joint->runtimeHandle());
49 for (auto it = jointHandles_.begin(); it != jointHandles_.end();) {
50 if (it->second == joint)
51 it = jointHandles_.erase(it);
52 else
53 ++it;
54 }
55}
56
58 if (!mechanism) return;
59 mechanisms_.erase(mechanism);
60}
61
62int World::nextJointId() { return nextJointId_++; }
63
64int World::nextMechanismId() { return nextMechanismId_++; }
65
67 if (nextJointHandleIndex_ == PhysicsJointHandle::invalidIndex)
68 throw eve::Exception("World.newJoint: process-local joint handle space exhausted");
69 return PhysicsJointHandle(nextJointHandleIndex_++, 1u);
70}
71
73 if (!isValid() || handle.isInvalid()) return nullptr;
74 const auto found = jointHandles_.find(handle);
75 if (found == jointHandles_.end() || !found->second || !found->second->isValid()) return nullptr;
76 return found->second;
77}
78
79Joint2D *World::newDistanceJoint(Body *bodyA, Body *bodyB, float anchorAX, float anchorAY,
80 float anchorBX, float anchorBY, float lengthPixels,
81 bool collideConnected) {
82 requireDistinctBodies(this, bodyA, bodyB, "World.newDistanceJoint");
83 const float values[] = {anchorAX, anchorAY, anchorBX, anchorBY, lengthPixels};
84 requireFiniteParams(values, 5, "World.newDistanceJoint");
85 if (lengthPixels < 0.f)
86 throw eve::Exception("World.newDistanceJoint: length must be >= 0");
87 b2DistanceJointDef def;
88 def.bodyA = bodyA->raw();
89 def.bodyB = bodyB->raw();
90 def.collideConnected = collideConnected;
91 def.localAnchorA = bodyA->raw()->GetLocalPoint(b2Vec2(toMeters(anchorAX), toMeters(anchorAY)));
92 def.localAnchorB = bodyB->raw()->GetLocalPoint(b2Vec2(toMeters(anchorBX), toMeters(anchorBY)));
93 def.length = toMeters(lengthPixels);
94 return adoptJoint(bodyA, bodyB, world_->CreateJoint(&def), Joint2D::Kind::Distance);
95}
96
97Joint2D *World::newRevoluteJoint(Body *bodyA, Body *bodyB, float anchorX, float anchorY,
98 bool collideConnected) {
99 requireDistinctBodies(this, bodyA, bodyB, "World.newRevoluteJoint");
100 const float values[] = {anchorX, anchorY};
101 requireFiniteParams(values, 2, "World.newRevoluteJoint");
102 b2RevoluteJointDef def;
103 def.Initialize(bodyA->raw(), bodyB->raw(), b2Vec2(toMeters(anchorX), toMeters(anchorY)));
104 def.collideConnected = collideConnected;
105 return adoptJoint(bodyA, bodyB, world_->CreateJoint(&def), Joint2D::Kind::Revolute);
106}
107
108Joint2D *World::newPrismaticJoint(Body *bodyA, Body *bodyB, float anchorX, float anchorY,
109 float axisX, float axisY, bool collideConnected) {
110 requireDistinctBodies(this, bodyA, bodyB, "World.newPrismaticJoint");
111 const float values[] = {anchorX, anchorY, axisX, axisY};
112 requireFiniteParams(values, 4, "World.newPrismaticJoint");
113 const b2Vec2 axis = normalizedAxisPixels(axisX, axisY, "World.newPrismaticJoint");
114 b2PrismaticJointDef def;
115 def.Initialize(bodyA->raw(), bodyB->raw(), b2Vec2(toMeters(anchorX), toMeters(anchorY)), axis);
116 def.collideConnected = collideConnected;
117 return adoptJoint(bodyA, bodyB, world_->CreateJoint(&def), Joint2D::Kind::Prismatic);
118}
119
120Joint2D *World::newWeldJoint(Body *bodyA, Body *bodyB, float anchorX, float anchorY,
121 bool collideConnected) {
122 requireDistinctBodies(this, bodyA, bodyB, "World.newWeldJoint");
123 const float values[] = {anchorX, anchorY};
124 requireFiniteParams(values, 2, "World.newWeldJoint");
125 b2WeldJointDef def;
126 def.Initialize(bodyA->raw(), bodyB->raw(), b2Vec2(toMeters(anchorX), toMeters(anchorY)));
127 def.collideConnected = collideConnected;
128 return adoptJoint(bodyA, bodyB, world_->CreateJoint(&def), Joint2D::Kind::Weld);
129}
130
131Joint2D *World::newWheelJoint(Body *bodyA, Body *bodyB, float anchorX, float anchorY, float axisX,
132 float axisY, bool collideConnected) {
133 requireDistinctBodies(this, bodyA, bodyB, "World.newWheelJoint");
134 const float values[] = {anchorX, anchorY, axisX, axisY};
135 requireFiniteParams(values, 4, "World.newWheelJoint");
136 const b2Vec2 axis = normalizedAxisPixels(axisX, axisY, "World.newWheelJoint");
137 b2WheelJointDef def;
138 def.Initialize(bodyA->raw(), bodyB->raw(), b2Vec2(toMeters(anchorX), toMeters(anchorY)), axis);
139 def.collideConnected = collideConnected;
140 return adoptJoint(bodyA, bodyB, world_->CreateJoint(&def), Joint2D::Kind::Wheel);
141}
142
143Joint2D *World::newMotorJoint(Body *bodyA, Body *bodyB, bool collideConnected) {
144 requireDistinctBodies(this, bodyA, bodyB, "World.newMotorJoint");
145 b2MotorJointDef def;
146 def.Initialize(bodyA->raw(), bodyB->raw());
147 def.collideConnected = collideConnected;
148 return adoptJoint(bodyA, bodyB, world_->CreateJoint(&def), Joint2D::Kind::Motor);
149}
150
151Joint2D *World::newGearJoint(Joint2D *joint1, Joint2D *joint2, float ratio) {
152 if (!isValid() || !joint1 || !joint2 || !joint1->isValid() || !joint2->isValid() ||
153 joint1->getWorld() != this || joint2->getWorld() != this || joint1 == joint2)
154 throw eve::Exception("World.newGearJoint: joints must be distinct and belong to this world");
155 if ((joint1->getKind() != "revolute" && joint1->getKind() != "prismatic") ||
156 (joint2->getKind() != "revolute" && joint2->getKind() != "prismatic"))
157 throw eve::Exception("World.newGearJoint: both joints must be revolute or prismatic");
158 if (!std::isfinite(ratio) || std::fabs(ratio) < 1e-8f)
159 throw eve::Exception("World.newGearJoint: ratio must be finite and non-zero");
160 // Gear joint attaches the two outer bodies of the child joints.
161 Body *bodyA = findBodyById(joint1->getBodyBId());
162 Body *bodyB = findBodyById(joint2->getBodyBId());
163 if (!bodyA) bodyA = findBodyById(joint1->getBodyAId());
164 if (!bodyB) bodyB = findBodyById(joint2->getBodyAId());
165 if (!bodyA || !bodyB)
166 throw eve::Exception("World.newGearJoint: child joints must reference live bodies");
167 b2GearJointDef def;
168 def.bodyA = bodyA->raw();
169 def.bodyB = bodyB->raw();
170 def.joint1 = joint1->raw();
171 def.joint2 = joint2->raw();
172 def.ratio = ratio;
173 Joint2D *gear = adoptJoint(bodyA, bodyB, world_->CreateJoint(&def), Joint2D::Kind::Gear);
174 gear->setGearMembers(joint1, joint2);
175 return gear;
176}
177
178Mechanism2D *World::newShaft(Body *support, Body *rotor, float anchorX, float anchorY,
179 bool collideConnected) {
180 requireDistinctBodies(this, support, rotor, "World.newShaft");
181 Joint2D *drive = newRevoluteJoint(support, rotor, anchorX, anchorY, collideConnected);
182 auto *mechanism =
183 new Mechanism2D(this, Mechanism2D::Kind::Shaft, nextMechanismId(), drive, nullptr, nullptr,
184 nullptr, 1, 0.f);
185 mechanisms_.insert(mechanism);
186 return mechanism;
187}
188
189Mechanism2D *World::newRatchet(Body *frame, Body *wheel, float anchorX, float anchorY, int direction,
190 float engagementTorque, bool collideConnected) {
191 requireDistinctBodies(this, frame, wheel, "World.newRatchet");
192 if (direction != 1 && direction != -1)
193 throw eve::Exception("World.newRatchet: direction must be +1 or -1");
194 if (!std::isfinite(engagementTorque) || engagementTorque < 0.f)
195 throw eve::Exception("World.newRatchet: engagementTorque must be finite and >= 0");
196 Joint2D *drive = newRevoluteJoint(frame, wheel, anchorX, anchorY, collideConnected);
197 auto *mechanism =
198 new Mechanism2D(this, Mechanism2D::Kind::Ratchet, nextMechanismId(), drive, nullptr,
199 nullptr, nullptr, direction, engagementTorque);
200 mechanisms_.insert(mechanism);
201 return mechanism;
202}
203
204Mechanism2D *World::newCrankSlider(Body *frame, Body *crank, Body *rod, Body *slider,
205 float crankAnchorX, float crankAnchorY, float crankPinX,
206 float crankPinY, float sliderPinX, float sliderPinY,
207 float slideAxisX, float slideAxisY, bool collideConnected) {
208 requireDistinctBodies(this, frame, crank, "World.newCrankSlider");
209 requireDistinctBodies(this, crank, rod, "World.newCrankSlider");
210 requireDistinctBodies(this, rod, slider, "World.newCrankSlider");
211 requireDistinctBodies(this, frame, slider, "World.newCrankSlider");
212 if (frame == rod || crank == slider)
213 throw eve::Exception("World.newCrankSlider: bodies must form a four-bar chain");
214 const float values[] = {crankAnchorX, crankAnchorY, crankPinX, crankPinY, sliderPinX,
215 sliderPinY, slideAxisX, slideAxisY};
216 requireFiniteParams(values, 8, "World.newCrankSlider");
217 (void)normalizedAxisPixels(slideAxisX, slideAxisY, "World.newCrankSlider");
218
219 Joint2D *drive = newRevoluteJoint(frame, crank, crankAnchorX, crankAnchorY, collideConnected);
220 Joint2D *crankPin = newRevoluteJoint(crank, rod, crankPinX, crankPinY, collideConnected);
221 Joint2D *sliderPin = newRevoluteJoint(rod, slider, sliderPinX, sliderPinY, collideConnected);
222 Joint2D *prismatic =
223 newPrismaticJoint(frame, slider, sliderPinX, sliderPinY, slideAxisX, slideAxisY,
224 collideConnected);
225 auto *mechanism =
226 new Mechanism2D(this, Mechanism2D::Kind::CrankSlider, nextMechanismId(), drive, crankPin,
227 sliderPin, prismatic, 1, 0.f);
228 mechanisms_.insert(mechanism);
229 return mechanism;
230}
231
232} // namespace eve::physics
ActionParameterOperation operation
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float length
Definition CaveMesh.cpp:94
std::map< std::string, Var > values
TokenKind kind
World3D * world
PrimitiveHandle handle
RoadLaneDirection direction
float axisY
Definition RockMesh.cpp:22
float axisX
Definition RockMesh.cpp:22
bool found
std::uint32_t count
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
constexpr bool isValid() const noexcept
Returns whether both coordinates are usable handle values.
static constexpr index_type invalidIndex
Reserved index value shared by all invalid handles.
2D rigid body (Box2D) in pixel-space coordinates. Owned by a World; create shapes with newRectangleFi...
Definition Body.h:21
b2Body * raw()
Exposes the underlying Box2D body for tightly-scoped backend integration.
Definition Body.h:194
Script-facing Box2D joint owned by a World.
Definition Joint2D.h:21
std::string getKind() const
Joint kind string for script/debug.
Definition Joint2D.cpp:76
b2Joint * raw() const
Internal raw Box2D joint.
Definition Joint2D.h:145
int getBodyBId() const
Stable ID of attached body B, or -1 after invalidation.
Definition Joint2D.cpp:90
Kind
Supported joint geometry.
Definition Joint2D.h:27
bool isValid() const
Whether the backend joint still exists.
Definition Joint2D.cpp:38
World * getWorld() const
Owning world, or null after joint destruction.
Definition Joint2D.h:53
PhysicsJointHandle runtimeHandle() const
Process-local generation-qualified handle owned by World.
Definition Joint2D.h:40
int getBodyAId() const
Stable ID of attached body A, or -1 after invalidation.
Definition Joint2D.cpp:89
Composed 2D mechanical assembly owned by a World.
Definition Mechanism2D.h:25
bool isValid() const
True while the underlying Box3D world is alive.
Definition World3D.cpp:430
void forgetMechanism(Mechanism2D *mechanism)
Internal: removes a mechanism wrapper from ownership bookkeeping.
Joint2D * newDistanceJoint(Body *bodyA, Body *bodyB, float anchorAX, float anchorAY, float anchorBX, float anchorBY, float lengthPixels, bool collideConnected=false)
Connects two pixel-space anchors with a distance constraint.
Joint2D * newWheelJoint(Body *bodyA, Body *bodyB, float anchorX, float anchorY, float axisX, float axisY, bool collideConnected=false)
Creates a wheel joint with a pixel-space suspension axis.
Body * findBodyById(int bodyId) const
Resolves a live body by its world-local stable event/query id.
Definition World.cpp:454
Joint2D * newWeldJoint(Body *bodyA, Body *bodyB, float anchorX, float anchorY, bool collideConnected=false)
Welds two bodies at a shared pixel-space anchor.
Joint2D * findJoint(PhysicsJointHandle handle) const
Resolves a live joint handle; returns null when stale or foreign.
void forgetJoint(Joint2D *joint)
Internal: removes a joint wrapper from ownership bookkeeping.
Joint2D * newPrismaticJoint(Body *bodyA, Body *bodyB, float anchorX, float anchorY, float axisX, float axisY, bool collideConnected=false)
Creates a prismatic slider along a pixel-space axis.
friend class Joint2D
Definition World.h:414
Joint2D * newRevoluteJoint(Body *bodyA, Body *bodyB, float anchorX, float anchorY, bool collideConnected=false)
Creates a revolute hinge at a shared pixel-space anchor.
int nextMechanismId()
Internal: next stable mechanism id.
b2World * raw()
Exposes the underlying Box2D world for tightly-scoped backend integration.
Definition World.h:378
Mechanism2D * newCrankSlider(Body *frame, Body *crank, Body *rod, Body *slider, float crankAnchorX, float crankAnchorY, float crankPinX, float crankPinY, float sliderPinX, float sliderPinY, float slideAxisX, float slideAxisY, bool collideConnected=false)
Builds a planar crank-slider from frame, crank, connecting rod and slider bodies.
int nextJointId()
Internal: next stable joint id.
friend class Mechanism2D
Definition World.h:415
PhysicsWorldHandle runtimeHandle() const noexcept
Process-local identity used by PhysicsLink; invalid after destruction.
Definition World.h:111
Joint2D * newGearJoint(Joint2D *joint1, Joint2D *joint2, float ratio)
Creates a gear joint that couples two revolute or prismatic joints.
Mechanism2D * newRatchet(Body *frame, Body *wheel, float anchorX, float anchorY, int direction, float engagementTorque, bool collideConnected=false)
Builds a one-way ratchet around a revolute hinge.
PhysicsJointHandle nextJointRuntimeHandle()
Internal: next generation-qualified joint handle.
bool isValid() const
True while the underlying Box2D world is alive.
Definition World.h:306
float toMeters(float pixels) const
Converts a pixel-space length to meters.
Definition World.cpp:420
Joint2D * newMotorJoint(Body *bodyA, Body *bodyB, bool collideConnected=false)
Creates a motor joint that drives relative pose between bodies.
Mechanism2D * newShaft(Body *support, Body *rotor, float anchorX, float anchorY, bool collideConnected=false)
Builds a shaft (support↔rotor revolute) with optional drive motor helpers.
Optional physics backend for vehicle mobility and body attach.
Definition Climbing.h:36
eve::RuntimeHandle< PhysicsJointHandleTag > PhysicsJointHandle
Generation-qualified runtime identity for a physics joint.