Overview
Joints (also called constraints) connect rigidbodies together or to the world, restricting their relative motion. Atlas provides three joint types through the Bezel physics layer.
Joints are defined in both bezel/bezel.h (low-level) and atlas/physics.h (high-level components).
Joint Types
Atlas supports three joint types:
- FixedJoint - Locks bodies together with no relative motion
- HingeJoint - Allows rotation around a single axis (door, wheel)
- SpringJoint - Distance constraint with optional spring behavior
Joint Endpoints
Joints connect two endpoints (from atlas/physics.h:178):
using JointChild = std::variant<GameObject *, WorldBody>;
Each joint has:
- Parent - First body or world
- Child - Second body or world
Connecting Two Bodies
auto joint = std::make_shared<HingeJoint>();
joint->parent = parentObject;
joint->child = childObject;
Connecting to World
auto joint = std::make_shared<HingeJoint>();
joint->parent = parentObject;
joint->child = WorldBody{}; // Attach to world
Common Joint Properties
All joints share these properties (from atlas/physics.h:230-243):
class Joint : public Component {
public:
JointChild parent; // First endpoint
JointChild child; // Second endpoint
Space space = Space::Global; // Local or global coordinates
Position3d anchor = Position3d::invalid(); // Anchor point
float breakForce = 0.0f; // Force threshold to break (0 = unbreakable)
float breakTorque = 0.0f; // Torque threshold to break (0 = unbreakable)
};
Coordinate Space
enum class Space {
Local, // Relative to parent object
Global // World-space coordinates
};
Breaking Joints
Joints can break when forces exceed thresholds:
auto joint = std::make_shared<HingeJoint>();
joint->breakForce = 1000.0f; // Break at 1000N force
joint->breakTorque = 500.0f; // Break at 500Nm torque
From the test example (test/main.cpp:124-130):
void update(float) override {
if (Window::mainWindow->isKeyClicked(Key::N)) {
auto hinge = object->getComponent<HingeJoint>();
if (hinge) {
std::cout << "Breaking hinge joint\n";
hinge->breakJoint();
}
}
}
FixedJoint
Fixed joints completely lock two bodies together with no relative motion.
Definition (atlas/physics.h:258-267)
class FixedJoint final : public Joint {
private:
std::shared_ptr<bezel::FixedJoint> joint;
public:
void beforePhysics() override;
void breakJoint() override;
};
Usage
// Create fixed joint between two objects
auto fixed = std::make_shared<FixedJoint>();
fixed->parent = parentObject;
fixed->child = childObject;
fixed->anchor = {0.0f, 1.0f, 0.0f}; // Connection point
Example: Welded Connection
// Weld two boxes together
auto boxA = std::make_shared<Rigidbody>();
boxA->setMotionType(MotionType::Dynamic);
boxA->addBoxCollider({0.5f, 0.5f, 0.5f});
auto boxB = std::make_shared<Rigidbody>();
boxB->setMotionType(MotionType::Dynamic);
boxB->addBoxCollider({0.5f, 0.5f, 0.5f});
// Attach with fixed joint
auto fixed = std::make_shared<FixedJoint>();
fixed->parent = objectA;
fixed->child = objectB;
fixed->space = Space::Global;
fixed->anchor = {0.0f, 0.5f, 0.0f};
Fixed joints are useful for compound objects that need to move as one unit.
HingeJoint
Hinge joints allow rotation around a single axis, like doors or wheels.
Definition (atlas/physics.h:272-287)
class HingeJoint final : public Joint {
private:
std::shared_ptr<bezel::HingeJoint> joint;
public:
Normal3d axis1 = Normal3d::up(); // Parent axis
Normal3d axis2 = Normal3d::up(); // Child axis
AngleLimits limits; // Optional angle constraints
Motor motor; // Optional motor actuation
void beforePhysics() override;
void breakJoint() override;
};
Basic Usage
// Create hinge joint
auto hinge = std::make_shared<HingeJoint>();
hinge->parent = doorFrame;
hinge->child = door;
hinge->anchor = {0.0f, 1.0f, 0.0f}; // Hinge position
hinge->axis1 = {0.0f, 1.0f, 0.0f}; // Rotation axis (Y-up)
hinge->axis2 = {0.0f, 1.0f, 0.0f};
Example: Door (from test/main.cpp)
class BallBehavior : public Component {
public:
void init() override {
if (object && object->rigidbody) {
object->rigidbody->applyImpulse({0.0f, 0.0f, 20.0f});
}
}
void update(float) override {
if (Window::mainWindow->isKeyClicked(Key::N)) {
auto hinge = object->getComponent<HingeJoint>();
if (hinge) {
std::cout << "Breaking hinge joint\n";
hinge->breakJoint();
}
}
}
};
Angle Limits
Constrain rotation range (from bezel.h:261-265):
struct AngleLimits {
bool enabled = false;
float minAngle = 0.0f; // Minimum angle
float maxAngle = 0.0f; // Maximum angle
};
// Create door with 90-degree opening limit
auto hinge = std::make_shared<HingeJoint>();
hinge->parent = doorFrame;
hinge->child = door;
hinge->anchor = {-1.0f, 0.0f, 0.0f};
hinge->axis1 = Normal3d::up();
hinge->axis2 = Normal3d::up();
// Set angle limits
hinge->limits.enabled = true;
hinge->limits.minAngle = 0.0f; // Closed
hinge->limits.maxAngle = 90.0f; // Fully open
Motors
Motors actively drive joint rotation (from bezel.h:270-274):
struct Motor {
bool enabled = false;
float maxForce = 0.0f; // Maximum force (N)
float maxTorque = 0.0f; // Maximum torque (Nm)
};
// Powered hinge (like an electric motor)
auto hinge = std::make_shared<HingeJoint>();
hinge->motor.enabled = true;
hinge->motor.maxTorque = 100.0f; // 100 Nm
Example: Wheel
// Create freely rotating wheel
auto wheel = std::make_shared<Rigidbody>();
wheel->setMotionType(MotionType::Dynamic);
wheel->addCapsuleCollider(0.3f, 0.2f);
auto hinge = std::make_shared<HingeJoint>();
hinge->parent = carBody;
hinge->child = wheel;
hinge->anchor = {1.0f, -0.5f, 1.5f}; // Wheel position
hinge->axis1 = {1.0f, 0.0f, 0.0f}; // Roll axis (X)
hinge->axis2 = {1.0f, 0.0f, 0.0f};
// No limits = freely rotating
SpringJoint
Spring joints maintain a distance between bodies with spring-like behavior.
Definition (atlas/physics.h:292-311)
class SpringJoint final : public Joint {
private:
std::shared_ptr<bezel::SpringJoint> joint;
public:
Position3d anchorB = Position3d::invalid(); // Second anchor
float restLength = 1.0f; // Natural spring length
bool useLimits = false;
float minLength = 0.0f; // Minimum extension
float maxLength = 0.0f; // Maximum extension
Spring spring; // Spring behavior
void beforePhysics() override;
void breakJoint() override;
};
Spring Settings
Springs support two configuration modes (from bezel.h:244-255):
struct Spring {
bool enabled = false;
SpringMode mode = SpringMode::FrequencyAndDamping;
// Mode 1: Frequency and damping ratio
float frequencyHz = 0.0f;
float dampingRatio = 0.0f;
// Mode 2: Stiffness and damping
float stiffness = 0.0f;
float damping = 0.0f;
};
enum class SpringMode {
FrequencyAndDamping, // Physical spring model
StiffnessAndDamping // Direct spring constants
};
Basic Usage
// Simple distance constraint (no spring)
auto spring = std::make_shared<SpringJoint>();
spring->parent = objectA;
spring->child = objectB;
spring->anchor = {0.0f, 0.0f, 0.0f};
spring->anchorB = {0.0f, 0.0f, 0.0f};
spring->restLength = 2.0f; // Keep 2m apart
Example: Suspension
// Car suspension spring
auto suspension = std::make_shared<SpringJoint>();
suspension->parent = carBody;
suspension->child = wheel;
suspension->anchor = {1.0f, 0.0f, 1.5f};
suspension->anchorB = {0.0f, 0.0f, 0.0f};
// Set rest length
suspension->restLength = 0.5f;
// Configure spring
suspension->spring.enabled = true;
suspension->spring.mode = SpringMode::FrequencyAndDamping;
suspension->spring.frequencyHz = 1.5f; // Soft suspension
suspension->spring.dampingRatio = 0.5f; // Medium damping
// Set limits
suspension->useLimits = true;
suspension->minLength = 0.3f; // Maximum compression
suspension->maxLength = 0.7f; // Maximum extension
Example: Rope
// Create rope between two objects
auto rope = std::make_shared<SpringJoint>();
rope->parent = hook;
rope->child = weight;
rope->anchor = {0.0f, -0.5f, 0.0f};
rope->anchorB = {0.0f, 0.5f, 0.0f};
rope->restLength = 5.0f; // 5m rope
// Soft spring for natural swinging
rope->spring.enabled = true;
rope->spring.mode = SpringMode::FrequencyAndDamping;
rope->spring.frequencyHz = 2.0f;
rope->spring.dampingRatio = 0.3f;
// Max length prevents overstretching
rope->useLimits = true;
rope->maxLength = 6.0f; // Allow 1m stretch
Example: Elastic Band
// Stretchy elastic connecting two objects
auto elastic = std::make_shared<SpringJoint>();
elastic->parent = objectA;
elastic->child = objectB;
elastic->anchor = {0.0f, 0.0f, 0.0f};
elastic->anchorB = {0.0f, 0.0f, 0.0f};
elastic->restLength = 1.0f;
// Stiff spring
elastic->spring.enabled = true;
elastic->spring.mode = SpringMode::StiffnessAndDamping;
elastic->spring.stiffness = 100.0f; // Very stiff
elastic->spring.damping = 5.0f;
Joint Lifecycle
Creation and Attachment
Joints are components that update during physics:
// Create joint component
auto hinge = std::make_shared<HingeJoint>();
hinge->parent = objectA;
hinge->child = objectB;
hinge->anchor = {0.0f, 1.0f, 0.0f};
// Add to GameObject
objectA->addComponent(hinge);
// Joint is created during beforePhysics()
Breaking Joints
Joints can be broken programmatically or by exceeding force/torque limits:
// Manual break (from test/main.cpp:128)
hinge->breakJoint();
// Automatic break on excessive force
hinge->breakForce = 1000.0f;
hinge->breakTorque = 500.0f;
Low-Level Bezel Joints
For advanced use, you can create Bezel joints directly:
Bezel HingeJoint (bezel.h:306-316)
auto world = std::make_shared<bezel::PhysicsWorld>();
world->init();
auto bezelHinge = std::make_shared<bezel::HingeJoint>();
bezelHinge->parent = rigidbodyA.get();
bezelHinge->child = rigidbodyB.get();
bezelHinge->anchor = {0.0f, 1.0f, 0.0f};
bezelHinge->axis1 = Normal3d::up();
bezelHinge->axis2 = Normal3d::up();
bezelHinge->create(world);
Complete Example: Chain
Here’s a complete example creating a chain of connected objects:
// Create chain segments
std::vector<std::shared_ptr<Rigidbody>> links;
for (int i = 0; i < 10; i++) {
auto link = std::make_shared<Rigidbody>();
link->setMotionType(MotionType::Dynamic);
link->setMass(0.5f);
link->addBoxCollider({0.1f, 0.3f, 0.1f});
links.push_back(link);
}
// Connect links with hinge joints
for (size_t i = 0; i < links.size() - 1; i++) {
auto hinge = std::make_shared<HingeJoint>();
hinge->parent = links[i];
hinge->child = links[i + 1];
hinge->anchor = {0.0f, 0.3f, 0.0f}; // Bottom of link
hinge->axis1 = {1.0f, 0.0f, 0.0f}; // Allow swinging
hinge->axis2 = {1.0f, 0.0f, 0.0f};
// Add joint to first link
// links[i]->addComponent(hinge);
}
// Attach first link to world
auto anchor = std::make_shared<HingeJoint>();
anchor->parent = WorldBody{};
anchor->child = links[0];
anchor->anchor = {0.0f, 5.0f, 0.0f};
anchor->axis1 = {1.0f, 0.0f, 0.0f};
anchor->axis2 = {1.0f, 0.0f, 0.0f};
API Reference
Joint Base Class (atlas/physics.h:230-253)
| Property | Type | Description |
|---|
parent | JointChild | First endpoint (object or world) |
child | JointChild | Second endpoint (object or world) |
space | Space | Local or global coordinates |
anchor | Position3d | Connection point |
breakForce | float | Force threshold to break (0 = unbreakable) |
breakTorque | float | Torque threshold to break (0 = unbreakable) |
| Method | Description |
|---|
beforePhysics() | Create/update constraint (called by engine) |
breakJoint() | Manually break the joint |
HingeJoint (atlas/physics.h:272-287)
| Property | Type | Description |
|---|
axis1 | Normal3d | Rotation axis for parent |
axis2 | Normal3d | Rotation axis for child |
limits | AngleLimits | Optional angle constraints |
motor | Motor | Optional motor actuation |
SpringJoint (atlas/physics.h:292-311)
| Property | Type | Description |
|---|
anchorB | Position3d | Second anchor point |
restLength | float | Natural spring length |
useLimits | bool | Enable min/max length limits |
minLength | float | Minimum extension |
maxLength | float | Maximum extension |
spring | Spring | Spring behavior settings |
See Also