Skip to main content

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:
  1. FixedJoint - Locks bodies together with no relative motion
  2. HingeJoint - Allows rotation around a single axis (door, wheel)
  3. 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)

PropertyTypeDescription
parentJointChildFirst endpoint (object or world)
childJointChildSecond endpoint (object or world)
spaceSpaceLocal or global coordinates
anchorPosition3dConnection point
breakForcefloatForce threshold to break (0 = unbreakable)
breakTorquefloatTorque threshold to break (0 = unbreakable)
MethodDescription
beforePhysics()Create/update constraint (called by engine)
breakJoint()Manually break the joint

HingeJoint (atlas/physics.h:272-287)

PropertyTypeDescription
axis1Normal3dRotation axis for parent
axis2Normal3dRotation axis for child
limitsAngleLimitsOptional angle constraints
motorMotorOptional motor actuation

SpringJoint (atlas/physics.h:292-311)

PropertyTypeDescription
anchorBPosition3dSecond anchor point
restLengthfloatNatural spring length
useLimitsboolEnable min/max length limits
minLengthfloatMinimum extension
maxLengthfloatMaximum extension
springSpringSpring behavior settings

See Also

Build docs developers (and LLMs) love