载入中...
搜索中...
未找到
Joint2D.cpp
浏览该文件的文档.
1#include "physics/Joint2D.h"
2
3#include "physics/Body.h"
4#include "physics/World.h"
5#include "common/Exception.h"
6
7#include <Box2D/Box2D.h>
8
9#include <cmath>
10#include <vector>
11
12namespace eve::physics {
13namespace {
14
15void nonNegative(float value, const char *operation, const char *name) {
16 if (!std::isfinite(value) || value < 0.f)
17 throw eve::Exception("%s: %s must be finite and >= 0", operation, name);
18}
19
20void finite(float value, const char *operation, const char *name) {
21 if (!std::isfinite(value)) throw eve::Exception("%s: %s must be finite", operation, name);
22}
23
24} // namespace
25
26Joint2D::Joint2D(World *world, Body *bodyA, Body *bodyB, b2Joint *joint,
27 PhysicsJointHandle runtimeHandle, Kind kind, int id)
28 : world_(world),
29 bodyA_(bodyA),
30 bodyB_(bodyB),
31 joint_(joint),
32 runtimeHandle_(runtimeHandle),
33 kind_(kind),
34 id_(id) {}
35
37
38bool Joint2D::isValid() const { return joint_ != nullptr && world_ != nullptr && world_->raw() != nullptr; }
39
41 if (world_) world_->forgetJoint(this);
42 joint_ = nullptr;
43 world_ = nullptr;
44 bodyA_ = nullptr;
45 bodyB_ = nullptr;
46 runtimeHandle_ = PhysicsJointHandle::invalid();
47 gearJoint1_ = nullptr;
48 gearJoint2_ = nullptr;
49}
50
52 if (!isValid()) {
53 invalidate();
54 return;
55 }
56 // Gear joints that depend on this joint must be destroyed first.
57 if (kind_ != Kind::Gear && world_) {
58 std::vector<Joint2D *> gears;
59 for (Joint2D *candidate : world_->joints_) {
60 if (!candidate || candidate->kind_ != Kind::Gear || !candidate->isValid()) continue;
61 if (candidate->gearJoint1_ == this || candidate->gearJoint2_ == this)
62 gears.push_back(candidate);
63 }
64 for (Joint2D *gear : gears) gear->destroy();
65 }
66 world_->raw()->DestroyJoint(joint_);
67 if (world_) world_->forgetJoint(this);
68 invalidate();
69}
70
71void Joint2D::setGearMembers(Joint2D *joint1, Joint2D *joint2) {
72 gearJoint1_ = joint1;
73 gearJoint2_ = joint2;
74}
75
76std::string Joint2D::getKind() const {
77 switch (kind_) {
78 case Kind::Distance: return "distance";
79 case Kind::Revolute: return "revolute";
80 case Kind::Prismatic: return "prismatic";
81 case Kind::Weld: return "weld";
82 case Kind::Wheel: return "wheel";
83 case Kind::Motor: return "motor";
84 case Kind::Gear: return "gear";
85 }
86 return "distance";
87}
88
89int Joint2D::getBodyAId() const { return bodyA_ ? bodyA_->getId() : -1; }
90int Joint2D::getBodyBId() const { return bodyB_ ? bodyB_->getId() : -1; }
91
92void Joint2D::setCollideConnected(bool collide) {
93 if (!isValid()) return;
94 // Box2D stores collideConnected on the joint; there is no setter after creation in 2.3,
95 // so recreate is required. Keep a soft no-op with a clear exception for scripts.
96 if (collide != joint_->GetCollideConnected())
97 throw eve::Exception(
98 "Joint2D.setCollideConnected: collideConnected is fixed at creation for Box2D joints");
99}
100
102 return isValid() ? joint_->GetCollideConnected() : false;
103}
104
105void Joint2D::requireKind(Kind expected, const char *operation) const {
106 if (!isValid()) throw eve::Exception("%s: joint destroyed", operation);
107 if (kind_ != expected) throw eve::Exception("%s: incompatible joint kind", operation);
108}
109
110void Joint2D::setDistanceLength(float lengthPixels) {
111 requireKind(Kind::Distance, "Joint2D.setDistanceLength");
112 nonNegative(lengthPixels, "Joint2D.setDistanceLength", "length");
113 static_cast<b2DistanceJoint *>(joint_)->SetLength(world_->toMeters(lengthPixels));
114}
116 requireKind(Kind::Distance, "Joint2D.getDistanceLength");
117 return world_->toPixels(static_cast<b2DistanceJoint *>(joint_)->GetLength());
118}
119void Joint2D::setDistanceSpring(float frequencyHz, float dampingRatio) {
120 requireKind(Kind::Distance, "Joint2D.setDistanceSpring");
121 nonNegative(frequencyHz, "Joint2D.setDistanceSpring", "frequencyHz");
122 nonNegative(dampingRatio, "Joint2D.setDistanceSpring", "dampingRatio");
123 auto *distance = static_cast<b2DistanceJoint *>(joint_);
124 distance->SetFrequency(frequencyHz);
125 distance->SetDampingRatio(dampingRatio);
126}
128 requireKind(Kind::Distance, "Joint2D.getDistanceFrequency");
129 return static_cast<b2DistanceJoint *>(joint_)->GetFrequency();
130}
132 requireKind(Kind::Distance, "Joint2D.getDistanceDampingRatio");
133 return static_cast<b2DistanceJoint *>(joint_)->GetDampingRatio();
134}
135
136void Joint2D::setRevoluteLimits(bool enabled, float lower, float upper) {
137 requireKind(Kind::Revolute, "Joint2D.setRevoluteLimits");
138 finite(lower, "Joint2D.setRevoluteLimits", "lower");
139 finite(upper, "Joint2D.setRevoluteLimits", "upper");
140 if (lower > upper)
141 throw eve::Exception("Joint2D.setRevoluteLimits: lower must be <= upper");
142 auto *revolute = static_cast<b2RevoluteJoint *>(joint_);
143 revolute->SetLimits(lower, upper);
144 revolute->EnableLimit(enabled);
145}
146void Joint2D::setRevoluteMotor(bool enabled, float speed, float maxTorque) {
147 requireKind(Kind::Revolute, "Joint2D.setRevoluteMotor");
148 finite(speed, "Joint2D.setRevoluteMotor", "speed");
149 nonNegative(maxTorque, "Joint2D.setRevoluteMotor", "maxTorque");
150 auto *revolute = static_cast<b2RevoluteJoint *>(joint_);
151 revolute->SetMotorSpeed(speed);
152 revolute->SetMaxMotorTorque(maxTorque);
153 revolute->EnableMotor(enabled);
154}
156 requireKind(Kind::Revolute, "Joint2D.getRevoluteAngle");
157 return static_cast<b2RevoluteJoint *>(joint_)->GetJointAngle();
158}
160 requireKind(Kind::Revolute, "Joint2D.getRevoluteSpeed");
161 return static_cast<b2RevoluteJoint *>(joint_)->GetJointSpeed();
162}
164 requireKind(Kind::Revolute, "Joint2D.getRevoluteMotorTorque");
165 return static_cast<b2RevoluteJoint *>(joint_)->GetMotorTorque(1.f / 60.f);
166}
167
168void Joint2D::setPrismaticLimits(bool enabled, float lowerPixels, float upperPixels) {
169 requireKind(Kind::Prismatic, "Joint2D.setPrismaticLimits");
170 finite(lowerPixels, "Joint2D.setPrismaticLimits", "lower");
171 finite(upperPixels, "Joint2D.setPrismaticLimits", "upper");
172 if (lowerPixels > upperPixels)
173 throw eve::Exception("Joint2D.setPrismaticLimits: lower must be <= upper");
174 auto *prismatic = static_cast<b2PrismaticJoint *>(joint_);
175 prismatic->SetLimits(world_->toMeters(lowerPixels), world_->toMeters(upperPixels));
176 prismatic->EnableLimit(enabled);
177}
178void Joint2D::setPrismaticMotor(bool enabled, float speedPixels, float maxForcePixels) {
179 requireKind(Kind::Prismatic, "Joint2D.setPrismaticMotor");
180 finite(speedPixels, "Joint2D.setPrismaticMotor", "speed");
181 nonNegative(maxForcePixels, "Joint2D.setPrismaticMotor", "maxForce");
182 auto *prismatic = static_cast<b2PrismaticJoint *>(joint_);
183 prismatic->SetMotorSpeed(world_->toMeters(speedPixels));
184 prismatic->SetMaxMotorForce(world_->toMeters(maxForcePixels));
185 prismatic->EnableMotor(enabled);
186}
188 requireKind(Kind::Prismatic, "Joint2D.getPrismaticTranslation");
189 return world_->toPixels(static_cast<b2PrismaticJoint *>(joint_)->GetJointTranslation());
190}
192 requireKind(Kind::Prismatic, "Joint2D.getPrismaticSpeed");
193 return world_->toPixels(static_cast<b2PrismaticJoint *>(joint_)->GetJointSpeed());
194}
195
196void Joint2D::setWeldSpring(float frequencyHz, float dampingRatio) {
197 requireKind(Kind::Weld, "Joint2D.setWeldSpring");
198 nonNegative(frequencyHz, "Joint2D.setWeldSpring", "frequencyHz");
199 nonNegative(dampingRatio, "Joint2D.setWeldSpring", "dampingRatio");
200 auto *weld = static_cast<b2WeldJoint *>(joint_);
201 weld->SetFrequency(frequencyHz);
202 weld->SetDampingRatio(dampingRatio);
203}
205 requireKind(Kind::Weld, "Joint2D.getWeldFrequency");
206 return static_cast<b2WeldJoint *>(joint_)->GetFrequency();
207}
209 requireKind(Kind::Weld, "Joint2D.getWeldDampingRatio");
210 return static_cast<b2WeldJoint *>(joint_)->GetDampingRatio();
211}
212
213void Joint2D::setWheelSpring(float frequencyHz, float dampingRatio) {
214 requireKind(Kind::Wheel, "Joint2D.setWheelSpring");
215 nonNegative(frequencyHz, "Joint2D.setWheelSpring", "frequencyHz");
216 nonNegative(dampingRatio, "Joint2D.setWheelSpring", "dampingRatio");
217 auto *wheel = static_cast<b2WheelJoint *>(joint_);
218 wheel->SetSpringFrequencyHz(frequencyHz);
219 wheel->SetSpringDampingRatio(dampingRatio);
220}
221void Joint2D::setWheelMotor(bool enabled, float speed, float maxTorque) {
222 requireKind(Kind::Wheel, "Joint2D.setWheelMotor");
223 finite(speed, "Joint2D.setWheelMotor", "speed");
224 nonNegative(maxTorque, "Joint2D.setWheelMotor", "maxTorque");
225 auto *wheel = static_cast<b2WheelJoint *>(joint_);
226 wheel->SetMotorSpeed(speed);
227 wheel->SetMaxMotorTorque(maxTorque);
228 wheel->EnableMotor(enabled);
229}
231 requireKind(Kind::Wheel, "Joint2D.getWheelTranslation");
232 return world_->toPixels(static_cast<b2WheelJoint *>(joint_)->GetJointTranslation());
233}
235 requireKind(Kind::Wheel, "Joint2D.getWheelSpeed");
236 return static_cast<b2WheelJoint *>(joint_)->GetJointSpeed();
237}
238
239void Joint2D::setMotorLinearOffset(float xPixels, float yPixels) {
240 requireKind(Kind::Motor, "Joint2D.setMotorLinearOffset");
241 finite(xPixels, "Joint2D.setMotorLinearOffset", "x");
242 finite(yPixels, "Joint2D.setMotorLinearOffset", "y");
243 static_cast<b2MotorJoint *>(joint_)->SetLinearOffset(
244 b2Vec2(world_->toMeters(xPixels), world_->toMeters(yPixels)));
245}
247 requireKind(Kind::Motor, "Joint2D.setMotorAngularOffset");
248 finite(radians, "Joint2D.setMotorAngularOffset", "radians");
249 static_cast<b2MotorJoint *>(joint_)->SetAngularOffset(radians);
250}
251void Joint2D::setMotorLimits(float maxForcePixels, float maxTorque) {
252 requireKind(Kind::Motor, "Joint2D.setMotorLimits");
253 nonNegative(maxForcePixels, "Joint2D.setMotorLimits", "maxForce");
254 nonNegative(maxTorque, "Joint2D.setMotorLimits", "maxTorque");
255 auto *motor = static_cast<b2MotorJoint *>(joint_);
256 motor->SetMaxForce(world_->toMeters(maxForcePixels));
257 motor->SetMaxTorque(maxTorque);
258}
260 requireKind(Kind::Motor, "Joint2D.getMotorLinearOffsetX");
261 return world_->toPixels(static_cast<b2MotorJoint *>(joint_)->GetLinearOffset().x);
262}
264 requireKind(Kind::Motor, "Joint2D.getMotorLinearOffsetY");
265 return world_->toPixels(static_cast<b2MotorJoint *>(joint_)->GetLinearOffset().y);
266}
268 requireKind(Kind::Motor, "Joint2D.getMotorAngularOffset");
269 return static_cast<b2MotorJoint *>(joint_)->GetAngularOffset();
270}
271
272void Joint2D::setGearRatio(float ratio) {
273 requireKind(Kind::Gear, "Joint2D.setGearRatio");
274 if (!std::isfinite(ratio) || std::fabs(ratio) < 1e-8f)
275 throw eve::Exception("Joint2D.setGearRatio: ratio must be finite and non-zero");
276 static_cast<b2GearJoint *>(joint_)->SetRatio(ratio);
277}
279 requireKind(Kind::Gear, "Joint2D.getGearRatio");
280 return static_cast<b2GearJoint *>(joint_)->GetRatio();
281}
282
283} // namespace eve::physics
ActionParameterOperation operation
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
TokenKind kind
std::string name
float distance
bool finite
World3D * world
std::string id
Definition PlayHost.cpp:108
EVENGINE_API_FOUNDATION public API.
Definition Exception.h:13
static constexpr RuntimeHandle invalid() noexcept
Returns the canonical invalid handle.
2D rigid body (Box2D) in pixel-space coordinates. Owned by a World; create shapes with newRectangleFi...
Definition Body.h:21
int getId() const
Stable id used by contact/impact events.
Definition Body.h:32
Script-facing Box2D joint owned by a World.
Definition Joint2D.h:21
void setWheelSpring(float frequencyHz, float dampingRatio)
Configures wheel suspension spring.
Definition Joint2D.cpp:213
void setGearRatio(float ratio)
Changes gear ratio.
Definition Joint2D.cpp:272
float getMotorAngularOffset() const
Motor angular offset in radians.
Definition Joint2D.cpp:267
std::string getKind() const
Joint kind string for script/debug.
Definition Joint2D.cpp:76
float getRevoluteSpeed() const
Current revolute speed in radians/second.
Definition Joint2D.cpp:159
void setPrismaticMotor(bool enabled, float speedPixels, float maxForcePixels)
Configures a prismatic motor in pixels/second and pixel-force units.
Definition Joint2D.cpp:178
float getWeldFrequency() const
Weld spring frequency in Hertz.
Definition Joint2D.cpp:204
void setMotorAngularOffset(float radians)
Sets motor-joint angular offset in radians.
Definition Joint2D.cpp:246
float getDistanceLength() const
Distance-joint rest length in pixels.
Definition Joint2D.cpp:115
void destroy()
Destroys the backend joint and invalidates this wrapper.
Definition Joint2D.cpp:51
void setWeldSpring(float frequencyHz, float dampingRatio)
Configures weld rotational spring frequency and damping.
Definition Joint2D.cpp:196
float getMotorLinearOffsetX() const
Motor linear offset X in pixels.
Definition Joint2D.cpp:259
void setPrismaticLimits(bool enabled, float lowerPixels, float upperPixels)
Configures prismatic translation limits in pixels.
Definition Joint2D.cpp:168
void invalidate()
Internal invalidation used by body/world destruction.
Definition Joint2D.cpp:40
void setCollideConnected(bool collide)
Enables or disables collision between the attached bodies.
Definition Joint2D.cpp:92
void setMotorLimits(float maxForcePixels, float maxTorque)
Sets motor max force (pixel-force) and torque (N·m).
Definition Joint2D.cpp:251
void setWheelMotor(bool enabled, float speed, float maxTorque)
Configures wheel spin motor in radians/second and newton-metres.
Definition Joint2D.cpp:221
float getPrismaticSpeed() const
Current prismatic speed in pixels/second.
Definition Joint2D.cpp:191
int getBodyBId() const
Stable ID of attached body B, or -1 after invalidation.
Definition Joint2D.cpp:90
void setDistanceSpring(float frequencyHz, float dampingRatio)
Configures distance spring frequency (Hz) and damping ratio.
Definition Joint2D.cpp:119
Kind
Supported joint geometry.
Definition Joint2D.h:27
bool isValid() const
Whether the backend joint still exists.
Definition Joint2D.cpp:38
bool getCollideConnected() const
Whether attached bodies may collide.
Definition Joint2D.cpp:101
float getDistanceFrequency() const
Distance spring frequency in Hertz.
Definition Joint2D.cpp:127
float getWheelTranslation() const
Current wheel translation along the suspension axis in pixels.
Definition Joint2D.cpp:230
int getBodyAId() const
Stable ID of attached body A, or -1 after invalidation.
Definition Joint2D.cpp:89
void setRevoluteMotor(bool enabled, float speed, float maxTorque)
Configures the revolute motor in radians/second and newton-metres.
Definition Joint2D.cpp:146
float getRevoluteAngle() const
Current revolute angle in radians.
Definition Joint2D.cpp:155
float getMotorLinearOffsetY() const
Motor linear offset Y in pixels.
Definition Joint2D.cpp:263
void setRevoluteLimits(bool enabled, float lower, float upper)
Configures revolute angular limits in radians.
Definition Joint2D.cpp:136
float getGearRatio() const
Current gear ratio.
Definition Joint2D.cpp:278
Joint2D(World *world, Body *bodyA, Body *bodyB, b2Joint *joint, PhysicsJointHandle runtimeHandle, Kind kind, int id)
Internal wrapper constructor; use World::new*Joint.
Definition Joint2D.cpp:26
float getWeldDampingRatio() const
Weld spring damping ratio.
Definition Joint2D.cpp:208
float getPrismaticTranslation() const
Current prismatic translation in pixels.
Definition Joint2D.cpp:187
void setDistanceLength(float lengthPixels)
Changes distance-joint rest length in pixels.
Definition Joint2D.cpp:110
float getWheelSpeed() const
Current wheel spin speed in radians/second.
Definition Joint2D.cpp:234
float getDistanceDampingRatio() const
Distance spring damping ratio.
Definition Joint2D.cpp:131
void setMotorLinearOffset(float xPixels, float yPixels)
Sets motor-joint linear offset in pixels (bodyA frame).
Definition Joint2D.cpp:239
float getRevoluteMotorTorque() const
Current revolute motor torque in newton-metres.
Definition Joint2D.cpp:163
Box2D world wrapper (2D physics) with pixel-space coordinates. Handles stepping, gravity,...
Definition World.h:41
void forgetJoint(Joint2D *joint)
Internal: removes a joint wrapper from ownership bookkeeping.
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
bool enabled