Fx2D
A C++20 2D rigid-body physics engine
Loading...
Searching...
No Matches
Joints.h
Go to the documentation of this file.
1#pragma once
2
3#include "Fx2D/Entity.h"
4#include "Fx2D/Solver.h"
5#include <memory>
6#include <string>
7#include <unordered_map>
8
9// Motor control mode: track a position, velocity, or direct effort target.
11
12// Base joint class that manages relationships between entities and constraints
13class FxJoint {
14 protected:
15 std::shared_ptr<FxEntity> entity1;
16 std::shared_ptr<FxEntity> entity2;
17 std::string m_name; // Joint name for identification
18 std::vector<std::shared_ptr<FxConstraint>> m_constraints; // Constraints that define this joint
19
20 // PID control parameters
21 FxVec3f m_pid{1.0f, 0.0f, 0.0f}; // {p, i, d}
22 float m_integral = 0.0f; // Integral accumulator
23 float m_previous_error = 0.0f; // Previous error for derivative calculation
24 float m_target_effort = 0.0f; // Shared effort target for torque/force control
25 bool m_instant = true; // Whether to apply controls instantly or use PID
26
27 // Renames owned constraints to <joint>_<Type>, appending _N on same-type repeats.
28 // Joint names are unique in the registry, so constraint names are unique by construction
29 // and stable under entity renames.
33 float eval_pid(float error, double dt);
34 float clamp_effort(float effort) const;
35
36 public:
37 bool enabled = true; // Whether joint is enabled
38 bool entities_collide = false; // Whether connected entities should collide
39
40 FxJoint(const std::string& name, const std::shared_ptr<FxEntity>& e1,
41 const std::shared_ptr<FxEntity>& e2);
42 virtual ~FxJoint() = default;
43
44 // Accessor method for name
45 const std::string& get_name() const { return m_name; }
46 // Clears integrator windup and derivative history; gains and targets are settings, kept.
47 void reset() { reset_pid_state(); }
48
49 // Entity accessor methods (read-only)
50 const std::shared_ptr<FxEntity>& get_entity1() const { return entity1; }
51 const std::shared_ptr<FxEntity>& get_entity2() const { return entity2; }
52
53 // Entity name accessor methods
54 std::string get_entity1_name() const;
55 std::string get_entity2_name() const;
56
57 // Constraints accessor
58 const std::vector<std::shared_ptr<FxConstraint>>& get_constraints() const {
59 return m_constraints;
60 }
61
62 // PID control methods
63 void set_pid(const FxVec3f& pid);
64 FxVec3f get_pid() const { return m_pid; }
65 void set_p(float p) { m_pid.x() = p; }
66 void set_i(float i) { m_pid.y() = i; }
67 void set_d(float d) { m_pid.z() = d; }
68 float get_p() const { return m_pid.x(); }
69 float get_i() const { return m_pid.y(); }
70 float get_d() const { return m_pid.z(); }
71 void set_instant(bool instant) { m_instant = instant; }
72 bool get_instant() const { return m_instant; }
73 void set_effort(float effort);
74 float get_effort() const { return m_target_effort; }
75 void set_max_effort(float max_effort);
76 float get_max_effort() const { return m_max_effort; }
78 ControlMode get_control_mode() const { return m_control_mode; }
79
80 // Type checking methods
81 virtual bool is_revolute() const { return false; }
82 virtual bool is_prismatic() const { return false; }
83
84 // Virtual apply_controls method for applying joint controls
85 virtual void apply_controls(double dt) = 0;
86
87 private:
88 float m_max_effort = FxInfinityf;
89 ControlMode m_control_mode = ControlMode::POSITION;
90};
91
92// Revolute joint with anchor and angular limit constraints
93class FxRevoluteJoint : public FxJoint {
94 private:
95 // The angular limits are not held here: the FxAngularLimitConstraint this joint builds owns
96 // them, and a second copy on the joint was written once and never read again.
97 float m_target_theta = 0.0f; // Target angle for PID control
98 float m_target_omega = 0.0f; // Target angular velocity for PID control
99
100 void apply_torque_effort(float torque);
101
102 public:
103 FxRevoluteJoint(const std::string& name, const std::shared_ptr<FxEntity>& e1,
104 const std::shared_ptr<FxEntity>& e2, const FxVec2f& anchor_point,
105 float angle_min = -3.14159f, float angle_max = 3.14159f);
106
107 // Type checking override
108 bool is_revolute() const override { return true; }
109
110 // Control methods - set targets for PID control
111 void set_theta(float angle, bool instant = true);
112 void set_omega(float omega, bool instant = true);
113 void set_torque(float torque);
114
115 // Query methods
116 float get_theta() const;
117 float get_omega() const;
118 void set_max_torque(float max_torque) { set_max_effort(max_torque); }
119 float get_max_torque() const { return get_max_effort(); }
120
121 // Apply controls method
122 void apply_controls(double dt) override;
123};
124
125// Prismatic joint with motion, separation, and angle lock constraints
126class FxPrismaticJoint : public FxJoint {
127 private:
128 FxVec2f m_axis; // Local axis on entity1 (normalized)
129 float m_initial_distance; // Initial distance projection along axis
130 float m_position_min, m_position_max; // Position limits along axis
131 float m_target_position = 0.0f; // Target position for PID control
132 float m_target_velocity = 0.0f; // Target velocity for PID control
133
134 void apply_force_effort(float force);
135
136 public:
137 FxPrismaticJoint(const std::string& name, const std::shared_ptr<FxEntity>& e1,
138 const std::shared_ptr<FxEntity>& e2, const FxVec2f& local_axis,
139 float position_min = -1000.0f, float position_max = 1000.0f);
140
141 // Type checking override
142 bool is_prismatic() const override { return true; }
143
144 // Control methods - set targets for PID control
145 void set_position(float position, bool instant = true); // Set target position along axis
146 void set_velocity(float velocity, bool instant = true); // Set target velocity along axis
147 void set_force(float force); // Apply force along axis
148
149 // Query methods
150 float get_position() const; // Get current relative position along axis
151 float get_velocity() const; // Get current relative velocity along axis
152 void set_max_force(float max_force) { set_max_effort(max_force); }
153 float get_max_force() const { return get_max_effort(); }
154
155 // Apply controls method
156 void apply_controls(double dt) override;
157};
158
159// A soft spring from a world point to an anchor on one body, for click-dragging. Not an
160// FxJoint: it pairs a body with a point rather than two bodies, so it lives outside the joint
161// registry and is transient. FxScene owns one; reset() and deleting the held entity release it.
162//
163// Tuned like a damped spring rather than by raw compliance, so a drag feels the same on a
164// 0.1 kg ball and a 50 kg crate: stiffness and damping scale with the body's mass on attach.
166 private:
167 std::shared_ptr<FxEntity> m_entity;
168 FxVec2f m_local_anchor{0.0f, 0.0f};
169 FxVec2f m_target{0.0f, 0.0f};
170 double m_compliance = 0.0; // XPBD alpha for the attached body, from frequency and mass
171 double m_beta = 0.0; // XPBD damping coefficient, from damping ratio and mass
172 float m_max_lambda = FxInfinityf; // force cap expressed as a Lagrange-multiplier bound
173
174 public:
175 // Undamped natural frequency of the spring, in Hz. Higher pulls harder.
176 float frequency_hz = 5.0f;
177 // 0 oscillates freely, 1 is critically damped.
178 float damping_ratio = 0.7f;
179 // Force cap as a multiple of the body's weight-equivalent (mass x this, in N per kg), so
180 // a body pinned against a wall cannot be driven through it.
181 float max_force_per_kg = 1000.0f;
182
183 // Grabs `entity` at `world_point`, which becomes the anchor. Static (zero inverse mass),
184 // disabled and sensor bodies are refused. Returns whether the joint is now attached.
185 bool attach(const std::shared_ptr<FxEntity>& entity, const FxVec2f& world_point);
186 // Moves the point the anchor is pulled toward.
187 void set_target(const FxVec2f& world_point) { m_target = world_point; }
188 void release();
189
190 bool attached() const { return m_entity != nullptr; }
191 const std::shared_ptr<FxEntity>& entity() const { return m_entity; }
192 const FxVec2f& target() const { return m_target; }
193 const FxVec2f& local_anchor() const { return m_local_anchor; }
194 // Anchor in world coordinates at the body's current pose; zero when detached.
196
197 // One damped XPBD position correction toward the target. Called by FxScene each substep.
198 void resolve(double dt);
199};
ControlMode
Definition Joints.h:10
Base type shared by engine joints.
Definition Joints.h:13
void namespace_constraints()
void set_i(float i)
Definition Joints.h:66
float get_effort() const
Definition Joints.h:74
float get_p() const
Definition Joints.h:68
const std::shared_ptr< FxEntity > & get_entity1() const
Definition Joints.h:50
bool entities_collide
Definition Joints.h:38
void set_pid(const FxVec3f &pid)
void set_control_mode(ControlMode mode)
float m_target_effort
Definition Joints.h:24
float clamp_effort(float effort) const
virtual bool is_revolute() const
Definition Joints.h:81
bool enabled
Definition Joints.h:37
float m_integral
Definition Joints.h:22
const std::vector< std::shared_ptr< FxConstraint > > & get_constraints() const
Definition Joints.h:58
void reset()
Definition Joints.h:47
bool get_instant() const
Definition Joints.h:72
FxVec3f get_pid() const
Definition Joints.h:64
void set_d(float d)
Definition Joints.h:67
void set_effort(float effort)
void set_instant(bool instant)
Definition Joints.h:71
void wake_entities()
std::string m_name
Definition Joints.h:17
const std::shared_ptr< FxEntity > & get_entity2() const
Definition Joints.h:51
bool m_instant
Definition Joints.h:25
std::shared_ptr< FxEntity > entity2
Definition Joints.h:16
const std::string & get_name() const
Definition Joints.h:45
float get_d() const
Definition Joints.h:70
std::vector< std::shared_ptr< FxConstraint > > m_constraints
Definition Joints.h:18
void set_p(float p)
Definition Joints.h:65
virtual bool is_prismatic() const
Definition Joints.h:82
std::shared_ptr< FxEntity > entity1
Definition Joints.h:15
float get_max_effort() const
Definition Joints.h:76
virtual void apply_controls(double dt)=0
float m_previous_error
Definition Joints.h:23
virtual ~FxJoint()=default
std::string get_entity1_name() const
FxVec3f m_pid
Definition Joints.h:21
float eval_pid(float error, double dt)
FxJoint(const std::string &name, const std::shared_ptr< FxEntity > &e1, const std::shared_ptr< FxEntity > &e2)
ControlMode get_control_mode() const
Definition Joints.h:78
float get_i() const
Definition Joints.h:69
void set_max_effort(float max_effort)
std::string get_entity2_name() const
void reset_pid_state()
void release()
float frequency_hz
Definition Joints.h:176
const std::shared_ptr< FxEntity > & entity() const
Definition Joints.h:191
bool attach(const std::shared_ptr< FxEntity > &entity, const FxVec2f &world_point)
void resolve(double dt)
float max_force_per_kg
Definition Joints.h:181
FxVec2f anchor_world() const
float damping_ratio
Definition Joints.h:178
void set_target(const FxVec2f &world_point)
Definition Joints.h:187
const FxVec2f & local_anchor() const
Definition Joints.h:193
bool attached() const
Definition Joints.h:190
const FxVec2f & target() const
Definition Joints.h:192
A sliding joint with limits and motor control.
Definition Joints.h:126
void set_max_force(float max_force)
Definition Joints.h:152
float get_position() const
float get_velocity() const
void set_velocity(float velocity, bool instant=true)
bool is_prismatic() const override
Definition Joints.h:142
FxPrismaticJoint(const std::string &name, const std::shared_ptr< FxEntity > &e1, const std::shared_ptr< FxEntity > &e2, const FxVec2f &local_axis, float position_min=-1000.0f, float position_max=1000.0f)
void set_force(float force)
float get_max_force() const
Definition Joints.h:153
void set_position(float position, bool instant=true)
void apply_controls(double dt) override
A pivot joint with angular limits and motor control.
Definition Joints.h:93
void set_omega(float omega, bool instant=true)
bool is_revolute() const override
Definition Joints.h:108
void apply_controls(double dt) override
float get_max_torque() const
Definition Joints.h:119
float get_theta() const
float get_omega() const
void set_max_torque(float max_torque)
Definition Joints.h:118
void set_theta(float angle, bool instant=true)
FxRevoluteJoint(const std::string &name, const std::shared_ptr< FxEntity > &e1, const std::shared_ptr< FxEntity > &e2, const FxVec2f &anchor_point, float angle_min=-3.14159f, float angle_max=3.14159f)
void set_torque(float torque)
Single-precision 2D vector.
Definition Math.h:47
Single-precision three-component vector, including poses.
Definition Math.h:159
float & z()
Definition Math.h:169
float & x()
Definition Math.h:167
float & y()
Definition Math.h:168