Skip to content

Instantly share code, notes, and snippets.

@Lightnet
Created October 27, 2025 22:16
Show Gist options
  • Save Lightnet/bfe05c2d89d19c6de3242cf77b4de2b7 to your computer and use it in GitHub Desktop.
Save Lightnet/bfe05c2d89d19c6de3242cf77b4de2b7 to your computer and use it in GitHub Desktop.
raylib ODE capsule controller test.
#include "raylib.h"
#include <ode/ode.h>
#include <stdio.h>
#include <math.h> // Ensure math.h is included for PI
// Global ODE variables
dWorldID world;
dSpaceID space;
dJointGroupID contactgroup;
dGeomID floorGeom, cube1Geom, cube2Geom;
dBodyID cube1Body, cube2Body;
Model cubeModel;
// capsule variables
dGeomID capsuleGeom;
dBodyID capsuleBody;
Model capsuleModel;
// Model capsuleModel2; //test
// Global variable for the fixed joint
dJointID capsuleFixedJoint;
// Global variables for camera
bool cameraControlEnabled = false; // Toggle for mouse controls
float cameraAngleX = 233.0f; // Horizontal angle (degrees, yaw)
float cameraAngleY = -33.0f; // Vertical angle (degrees, pitch)
// Contact callback for collision handling
void nearCallback(void *data, dGeomID o1, dGeomID o2) {
dBodyID b1 = dGeomGetBody(o1);
dBodyID b2 = dGeomGetBody(o2);
dContact contact;
contact.surface.mode = dContactBounce;
// contact.surface.mu = dInfinity; // Friction
contact.surface.mu = 50.0f; // Friction
contact.surface.bounce = 0.5f; // Restitution
if (dCollide(o1, o2, 1, &contact.geom, sizeof(dContact))) {
dJointID c = dJointCreateContact(world, contactgroup, &contact);
dJointAttach(c, b1, b2);
}
}
float NormalizeAngle(float angle) {
// Normalize angle to [-π, π]
angle = fmod(angle, 2.0f * PI);
if (angle > PI) {
angle -= 2.0f * PI;
} else if (angle < -PI) {
angle += 2.0f * PI;
}
return angle;
}
float NormalizeAngleDegrees(float angle) {
// Normalize angle to [-180, 180]
angle = fmod(angle, 360.0f);
if (angle > 180.0f) {
angle -= 360.0f;
} else if (angle < -180.0f) {
angle += 360.0f;
}
return angle;
}
// Convert ODE quaternion to Raylib Matrix
Matrix QuaternionToMatrix(dQuaternion q) {
float w = (float)q[0], x = (float)q[1], y = (float)q[2], z = (float)q[3];
float x2 = x * x, y2 = y * y, z2 = z * z;
float xy = x * y, xz = x * z, yz = y * z;
float wx = w * x, wy = w * y, wz = w * z;
// Create 4x4 rotation matrix (column-major for Raylib)
Matrix mat = {
1.0f - 2.0f * (y2 + z2), 2.0f * (xy - wz), 2.0f * (xz + wy), 0.0f,
2.0f * (xy + wz), 1.0f - 2.0f * (x2 + z2), 2.0f * (yz - wx), 0.0f,
2.0f * (xz - wy), 2.0f * (yz + wx), 1.0f - 2.0f * (x2 + y2), 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
};
return mat;
}
// generate capsule, z axis to match ODE capsule
Mesh GenMeshCapsule(float radius, float height, int slices, int rings) {
Mesh mesh = { 0 };
float halfHeight = height / 2.0f;
int ringsPerHemisphere = rings / 2; // Rings per hemisphere, excluding poles
// Vertex count: 1 pole + ringsPerHemisphere * (slices + 1) for top hemisphere,
// + (ringsPerHemisphere + 1) * (slices + 1) for bottom hemisphere (include last ring),
// + 2 cylinder rings
int vertexCount = 1 + ringsPerHemisphere * (slices + 1) + (ringsPerHemisphere + 1) * (slices + 1) + 2 * (slices + 1) + 1;
// Triangle count: (slices + (ringsPerHemisphere - 1) * slices * 2) for top hemisphere
// + (slices + ringsPerHemisphere * slices * 2) for bottom hemisphere + slices * 2 for cylinder
int triangleCount = (slices + (ringsPerHemisphere - 1) * slices * 2) + (slices + ringsPerHemisphere * slices * 2) + slices * 2;
mesh.vertexCount = vertexCount;
mesh.triangleCount = triangleCount;
mesh.vertices = (float *)RL_MALLOC(vertexCount * 3 * sizeof(float));
mesh.normals = (float *)RL_MALLOC(vertexCount * 3 * sizeof(float));
mesh.texcoords = (float *)RL_MALLOC(vertexCount * 2 * sizeof(float));
mesh.indices = (unsigned short *)RL_MALLOC(triangleCount * 3 * sizeof(unsigned short));
int v = 0, i = 0;
// Top pole (single vertex at z = halfHeight + radius)
printf("Top Pole Vertex:\n");
mesh.vertices[v * 3 + 0] = 0.0f;
mesh.vertices[v * 3 + 1] = 0.0f;
mesh.vertices[v * 3 + 2] = halfHeight + radius;
mesh.normals[v * 3 + 0] = 0.0f;
mesh.normals[v * 3 + 1] = 0.0f;
mesh.normals[v * 3 + 2] = 1.0f;
mesh.texcoords[v * 2 + 0] = 0.5f;
mesh.texcoords[v * 2 + 1] = 0.0f;
printf("Vertex %d: (%.4f, %.4f, %.4f)\n", v, mesh.vertices[v * 3 + 0], mesh.vertices[v * 3 + 1], mesh.vertices[v * 3 + 2]);
v++;
// Top hemisphere (z from halfHeight to halfHeight + radius, excluding pole)
printf("\nTop Hemisphere Vertices:\n");
for (int j = 1; j <= ringsPerHemisphere; j++) {
float phi = (float)j * PI / 2.0f / ringsPerHemisphere; // 0 to PI/2
for (int k = 0; k <= slices; k++) {
float theta = (float)k * 2.0f * PI / slices;
float sinPhi = sinf(phi);
float x = radius * sinPhi * cosf(theta);
float y = radius * sinPhi * sinf(theta);
float z = radius * cosf(phi) + halfHeight;
mesh.vertices[v * 3 + 0] = x;
mesh.vertices[v * 3 + 1] = y;
mesh.vertices[v * 3 + 2] = z;
mesh.normals[v * 3 + 0] = x / radius;
mesh.normals[v * 3 + 1] = y / radius;
mesh.normals[v * 3 + 2] = (z - halfHeight) / radius;
mesh.texcoords[v * 2 + 0] = (float)k / slices;
mesh.texcoords[v * 2 + 1] = (float)j / (ringsPerHemisphere * 2);
printf("Vertex %d: (%.4f, %.4f, %.4f)\n", v, x, y, z);
v++;
}
}
// Cylinder top ring (at z = halfHeight)
printf("\nCylinder Top Ring Vertices:\n");
for (int k = 0; k <= slices; k++) {
float theta = (float)k * 2.0f * PI / slices;
float x = radius * cosf(theta);
float y = radius * sinf(theta);
float z = halfHeight;
mesh.vertices[v * 3 + 0] = x;
mesh.vertices[v * 3 + 1] = y;
mesh.vertices[v * 3 + 2] = z;
mesh.normals[v * 3 + 0] = x / radius;
mesh.normals[v * 3 + 1] = y / radius;
mesh.normals[v * 3 + 2] = 0.0f;
mesh.texcoords[v * 2 + 0] = (float)k / slices;
mesh.texcoords[v * 2 + 1] = 0.5f;
printf("Vertex %d: (%.4f, %.4f, %.4f)\n", v, x, y, z);
v++;
}
// Cylinder bottom ring (at z = -halfHeight)
printf("\nCylinder Bottom Ring Vertices:\n");
for (int k = 0; k <= slices; k++) {
float theta = (float)k * 2.0f * PI / slices;
float x = radius * cosf(theta);
float y = radius * sinf(theta);
float z = -halfHeight;
mesh.vertices[v * 3 + 0] = x;
mesh.vertices[v * 3 + 1] = y;
mesh.vertices[v * 3 + 2] = z;
mesh.normals[v * 3 + 0] = x / radius;
mesh.normals[v * 3 + 1] = y / radius;
mesh.normals[v * 3 + 2] = 0.0f;
mesh.texcoords[v * 2 + 0] = (float)k / slices;
mesh.texcoords[v * 2 + 1] = 0.5f;
printf("Vertex %d: (%.4f, %.4f, %.4f)\n", v, x, y, z);
v++;
}
// Bottom hemisphere (z from -halfHeight to just before -halfHeight - radius)
printf("\nBottom Hemisphere Vertices:\n");
for (int j = 0; j <= ringsPerHemisphere; j++) { // Include last ring
float phi = PI / 2.0f + (float)j * PI / 2.0f / ringsPerHemisphere; // PI/2 to PI
for (int k = 0; k <= slices; k++) {
float theta = (float)k * 2.0f * PI / slices;
float sinPhi = sinf(phi);
float x = radius * sinPhi * cosf(theta);
float y = radius * sinPhi * sinf(theta);
float z = radius * cosf(phi) - halfHeight;
mesh.vertices[v * 3 + 0] = x;
mesh.vertices[v * 3 + 1] = y;
mesh.vertices[v * 3 + 2] = z;
mesh.normals[v * 3 + 0] = x / radius;
mesh.normals[v * 3 + 1] = y / radius;
mesh.normals[v * 3 + 2] = (z + halfHeight) / radius;
mesh.texcoords[v * 2 + 0] = (float)k / slices;
mesh.texcoords[v * 2 + 1] = (float)(j + ringsPerHemisphere) / (ringsPerHemisphere * 2);
printf("Vertex %d: (%.4f, %.4f, %.4f)\n", v, x, y, z);
v++;
}
}
// Bottom pole (single vertex at z = -halfHeight - radius)
printf("\nBottom Pole Vertex:\n");
mesh.vertices[v * 3 + 0] = 0.0f;
mesh.vertices[v * 3 + 1] = 0.0f;
mesh.vertices[v * 3 + 2] = -halfHeight - radius;
mesh.normals[v * 3 + 0] = 0.0f;
mesh.normals[v * 3 + 1] = 0.0f;
mesh.normals[v * 3 + 2] = -1.0f;
mesh.texcoords[v * 2 + 0] = 0.5f;
mesh.texcoords[v * 2 + 1] = 1.0f;
printf("Vertex %d: (%.4f, %.4f, %.4f)\n", v, mesh.vertices[v * 3 + 0], mesh.vertices[v * 3 + 1], mesh.vertices[v * 3 + 2]);
v++;
// Indices for top hemisphere (including pole)
int topHemisphereStart = 1; // After top pole
printf("\nTop Hemisphere Indices:\n");
// Pole triangles (connect pole to first ring)
for (int k = 0; k < slices; k++) {
int v0 = 0; // Top pole
int v1 = topHemisphereStart + k;
int v2 = topHemisphereStart + (k + 1) % (slices + 1);
mesh.indices[i++] = v0;
mesh.indices[i++] = v1;
mesh.indices[i++] = v2;
printf("Triangle %d: (%d, %d, %d)\n", i/3-1, v0, v1, v2);
}
// Remaining top hemisphere quads
for (int j = 0; j < ringsPerHemisphere - 1; j++) {
for (int k = 0; k < slices; k++) {
int v0 = topHemisphereStart + j * (slices + 1) + k;
int v1 = v0 + 1;
int v2 = topHemisphereStart + (j + 1) * (slices + 1) + k;
int v3 = v2 + 1;
mesh.indices[i++] = v0;
mesh.indices[i++] = v2;
mesh.indices[i++] = v1;
mesh.indices[i++] = v1;
mesh.indices[i++] = v2;
mesh.indices[i++] = v3;
printf("Triangle %d: (%d, %d, %d)\n", i/3-2, v0, v2, v1);
printf("Triangle %d: (%d, %d, %d)\n", i/3-1, v1, v2, v3);
}
}
// Indices for cylinder (connect top ring to bottom ring)
int topRingOffset = topHemisphereStart + ringsPerHemisphere * (slices + 1);
int bottomRingOffset = topRingOffset + (slices + 1);
printf("\nCylinder Indices:\n");
for (int k = 0; k < slices; k++) {
int v0 = topRingOffset + k;
int v1 = v0 + 1;
int v2 = bottomRingOffset + k;
int v3 = v2 + 1;
mesh.indices[i++] = v0;
mesh.indices[i++] = v2;
mesh.indices[i++] = v1;
mesh.indices[i++] = v1;
mesh.indices[i++] = v2;
mesh.indices[i++] = v3;
printf("Triangle %d: (%d, %d, %d)\n", i/3-2, v0, v2, v1);
printf("Triangle %d: (%d, %d, %d)\n", i/3-1, v1, v2, v3);
}
// Indices for bottom hemisphere (including pole)
int bottomHemisphereOffset = bottomRingOffset + (slices + 1);
int bottomPole = bottomHemisphereOffset + ringsPerHemisphere * (slices + 1);
printf("\nBottom Hemisphere Indices:\n");
// Bottom hemisphere quads
for (int j = 0; j < ringsPerHemisphere; j++) {
for (int k = 0; k < slices; k++) {
int v0 = bottomHemisphereOffset + j * (slices + 1) + k;
int v1 = v0 + 1;
int v2 = bottomHemisphereOffset + (j + 1) * (slices + 1) + k;
int v3 = v2 + 1;
mesh.indices[i++] = v0;
mesh.indices[i++] = v2;
mesh.indices[i++] = v1;
mesh.indices[i++] = v1;
mesh.indices[i++] = v2;
mesh.indices[i++] = v3;
printf("Triangle %d: (%d, %d, %d)\n", i/3-2, v0, v2, v1);
printf("Triangle %d: (%d, %d, %d)\n", i/3-1, v1, v2, v3);
}
}
// Pole triangles (connect last ring to bottom pole)
for (int k = 0; k < slices; k++) {
int v0 = bottomPole;
int v1 = bottomHemisphereOffset + ringsPerHemisphere * (slices + 1) + k;
int v2 = bottomHemisphereOffset + ringsPerHemisphere * (slices + 1) + (k + 1) % (slices + 1);
mesh.indices[i++] = v0;
mesh.indices[i++] = v2; // Reversed for correct winding
mesh.indices[i++] = v1;
printf("Triangle %d: (%d, %d, %d)\n", i/3-1, v0, v2, v1);
}
// Upload mesh data to GPU
UploadMesh(&mesh, false);
return mesh;
}
// main
int main(void) {
// Initialize window
InitWindow(800, 600, "3D Physics Cubes with Quaternion Rotation - Raylib with ODE");
SetTargetFPS(60);
// Initialize 3D camera
Camera3D camera = { 0 };
camera.position = (Vector3){ 10.0f, 10.0f, 10.0f };
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };
camera.fovy = 45.0f;
camera.projection = CAMERA_PERSPECTIVE;
// Create cube model
Mesh cubeMesh = GenMeshCube(1.0f, 1.0f, 1.0f);
cubeModel = LoadModelFromMesh(cubeMesh);
// Create capsule model
Mesh capsuleMesh = GenMeshCapsule(0.5f, 1.0f, 8, 8); // Radius 0.5, cylinder height 1.0
capsuleModel = LoadModelFromMesh(capsuleMesh);
// capsuleModel2 = capsuleModel; // Assuming capsuleModel2 is for testing; copy the model
// Initialize ODE
dInitODE();
world = dWorldCreate();
space = dHashSpaceCreate(0);
contactgroup = dJointGroupCreate(0);
dWorldSetGravity(world, 0, -9.81f, 0); // Gravity along Y-axis
// Create static floor (cube)
floorGeom = dCreateBox(space, 10.0f, 1.0f, 10.0f);
dGeomSetPosition(floorGeom, 0.0f, -0.5f, 0.0f);
// Store initial capsule position for reset
Vector3 initialCube1Pos = (Vector3){ -1.0f, 1.5f, 0.0f };
Vector3 initialCube2Pos = (Vector3){ -2.0f, 3.5f, 0.0f }; // Adjusted to stack cubes
Vector3 initialCapsulePos = (Vector3){ 0.0f, 2.5f, 0.0f };
// Create first dynamic cube
cube1Body = dBodyCreate(world);
cube1Geom = dCreateBox(space, 1.0f, 1.0f, 1.0f);
dMass mass1;
dMassSetBox(&mass1, 1.0f, 1.0f, 1.0f, 1.0f); // Density = 1.0
dBodySetMass(cube1Body, &mass1);
dGeomSetBody(cube1Geom, cube1Body);
dBodySetPosition(cube1Body, initialCube1Pos.x, initialCube1Pos.y, initialCube1Pos.z);
// Create second dynamic cube (stacked on top)
cube2Body = dBodyCreate(world);
cube2Geom = dCreateBox(space, 1.0f, 1.0f, 1.0f);
dMass mass2;
dMassSetBox(&mass2, 1.0f, 1.0f, 1.0f, 1.0f); // Density = 1.0
dBodySetMass(cube2Body, &mass2);
dGeomSetBody(cube2Geom, cube2Body);
dBodySetPosition(cube2Body, initialCube2Pos.x, initialCube2Pos.y, initialCube2Pos.z);
// Create capsule in ODE
capsuleBody = dBodyCreate(world);
capsuleGeom = dCreateCapsule(space, 0.5f, 1.0f); // Radius 0.5, cylinder length 1.0
dMass massCapsule;
dMassSetCapsule(&massCapsule, 1.0f, 2, 0.5f, 1.0f); // Density = 1.0, direction along y-axis (2)
dBodySetMass(capsuleBody, &massCapsule);
dGeomSetBody(capsuleGeom, capsuleBody);
dQuaternion yAxisQuat;
dQFromAxisAndAngle(yAxisQuat, 1.0f, 0.0f, 0.0f, PI / 2.0f); // Rotate z to y
dBodySetQuaternion(capsuleBody, yAxisQuat);
dBodySetPosition(capsuleBody, initialCapsulePos.x, initialCapsulePos.y, initialCapsulePos.z);
dBodySetMaxAngularSpeed(capsuleBody, 0.0); // Prevent rotation for capsule
// Add toggle for capsule movement
bool capsuleMovementEnabled = false;
while (!WindowShouldClose()) {
// Step ODE simulation
dSpaceCollide(space, 0, &nearCallback);
dWorldQuickStep(world, 0.016f); // ~60 FPS
dJointGroupEmpty(contactgroup);
// Toggle capsule movement with '1' key
if (IsKeyPressed(KEY_ONE)) {
capsuleMovementEnabled = !capsuleMovementEnabled;
}
// Reset both cubes and capsule on 'R' key press
if (IsKeyPressed(KEY_R)) {
dBodySetPosition(cube1Body, initialCube1Pos.x, initialCube1Pos.y, initialCube1Pos.z);
dBodySetLinearVel(cube1Body, 0, 0, 0);
dBodySetAngularVel(cube1Body, 0, 0, 0);
dQuaternion identity1 = { 1.0f, 0.0f, 0.0f, 0.0f };
dBodySetQuaternion(cube1Body, identity1);
dBodySetPosition(cube2Body, initialCube2Pos.x, initialCube2Pos.y, initialCube2Pos.z);
dBodySetLinearVel(cube2Body, 0, 0, 0);
dBodySetAngularVel(cube2Body, 0, 0, 0);
dQuaternion identity2 = { 1.0f, 0.0f, 0.0f, 0.0f };
dBodySetQuaternion(cube2Body, identity2);
dBodySetPosition(capsuleBody, initialCapsulePos.x, initialCapsulePos.y, initialCapsulePos.z);
dBodySetLinearVel(capsuleBody, 0, 0, 0);
dBodySetAngularVel(capsuleBody, 0, 0, 0);
dQFromAxisAndAngle(yAxisQuat, 1.0f, 0.0f, 0.0f, PI / 2.0f);
dBodySetQuaternion(capsuleBody, yAxisQuat);
}
// Toggle camera controls with Tab key
if (IsKeyPressed(KEY_TAB)) {
cameraControlEnabled = !cameraControlEnabled;
if (cameraControlEnabled) {
DisableCursor();
} else {
EnableCursor();
}
}
// Update camera if controls are enabled
Vector3 forward = { 0 };
Vector3 right = { 0 };
if (cameraControlEnabled) {
Vector2 mouseDelta = GetMouseDelta();
cameraAngleX -= mouseDelta.x * 0.3f * -1;
cameraAngleX = NormalizeAngleDegrees(cameraAngleX);
cameraAngleY -= mouseDelta.y * 0.3f;
if (cameraAngleY > 89.0f) cameraAngleY = 89.0f;
if (cameraAngleY < -89.0f) cameraAngleY = -89.0f;
float yawRad = cameraAngleX * DEG2RAD;
float pitchRad = cameraAngleY * DEG2RAD;
forward = (Vector3){
cosf(pitchRad) * cosf(yawRad),
sinf(pitchRad),
cosf(pitchRad) * sinf(yawRad)
};
float len = sqrtf(forward.x * forward.x + forward.y * forward.y + forward.z * forward.z);
if (len > 0) {
forward.x /= len;
forward.y /= len;
forward.z /= len;
}
Vector3 up = { 0.0f, 1.0f, 0.0f };
right = (Vector3){
-sinf(yawRad),
0.0f,
cosf(yawRad)
};
len = sqrtf(right.x * right.x + right.y * right.y + right.z * right.z);
if (len > 0) {
right.x /= len;
right.y /= len;
right.z /= len;
}
float moveSpeed = 5.0f * GetFrameTime();
if (IsKeyDown(KEY_W)) {
camera.position.x += forward.x * moveSpeed;
camera.position.y += forward.y * moveSpeed;
camera.position.z += forward.z * moveSpeed;
}
if (IsKeyDown(KEY_S)) {
camera.position.x -= forward.x * moveSpeed;
camera.position.y -= forward.y * moveSpeed;
camera.position.z -= forward.z * moveSpeed;
}
if (IsKeyDown(KEY_A)) {
camera.position.x -= right.x * moveSpeed;
camera.position.y -= right.y * moveSpeed;
camera.position.z -= right.z * moveSpeed;
}
if (IsKeyDown(KEY_D)) {
camera.position.x += right.x * moveSpeed;
camera.position.y += right.y * moveSpeed;
camera.position.z += right.z * moveSpeed;
}
if (IsKeyDown(KEY_SPACE) && !capsuleMovementEnabled) { // Only move camera up if capsule movement is off
camera.position.y += moveSpeed;
}
if (IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT)) {
camera.position.y -= moveSpeed;
}
camera.target.x = camera.position.x + forward.x;
camera.target.y = camera.position.y + forward.y;
camera.target.z = camera.position.z + forward.z;
} else {
float yawRad = cameraAngleX * DEG2RAD;
float pitchRad = cameraAngleY * DEG2RAD;
forward = (Vector3){
cosf(pitchRad) * cosf(yawRad),
sinf(pitchRad),
cosf(pitchRad) * sinf(yawRad)
};
float len = sqrtf(forward.x * forward.x + forward.y * forward.y + forward.z * forward.z);
if (len > 0) {
forward.x /= len;
forward.y /= len;
forward.z /= len;
}
right = (Vector3){
-sinf(yawRad),
0.0f,
cosf(yawRad)
};
len = sqrtf(right.x * right.x + right.y * right.y + right.z * right.z);
if (len > 0) {
right.x /= len;
right.y /= len;
right.z /= len;
}
}
// Move capsule based on camera direction if enabled
if (capsuleMovementEnabled) {
float forceMagnitude = 10.0f;
Vector3 force = { 0.0f, 0.0f, 0.0f };
Vector3 forwardXZ = { forward.x, 0.0f, forward.z };
float len = sqrtf(forwardXZ.x * forwardXZ.x + forwardXZ.z * forwardXZ.z);
if (len > 0) {
forwardXZ.x /= len;
forwardXZ.z /= len;
}
Vector3 rightXZ = { right.x, 0.0f, right.z };
len = sqrtf(rightXZ.x * rightXZ.x + rightXZ.z * rightXZ.z);
if (len > 0) {
rightXZ.x /= len;
rightXZ.z /= len;
}
if (IsKeyDown(KEY_W)) {
force.x += forwardXZ.x * forceMagnitude;
force.z += forwardXZ.z * forceMagnitude;
}
if (IsKeyDown(KEY_S)) {
force.x -= forwardXZ.x * forceMagnitude;
force.z -= forwardXZ.z * forceMagnitude;
}
if (IsKeyDown(KEY_A)) {
force.x -= rightXZ.x * forceMagnitude;
force.z -= rightXZ.z * forceMagnitude;
}
if (IsKeyDown(KEY_D)) {
force.x += rightXZ.x * forceMagnitude;
force.z += rightXZ.z * forceMagnitude;
}
// Jump logic
if (IsKeyPressed(KEY_SPACE)) {
const dReal *capsulePos = dBodyGetPosition(capsuleBody);
float groundLevel = -0.5f + 0.5f; // Floor at y=-0.5, capsule radius=0.5
float heightThreshold = 1.0f; // Small threshold to allow jumping
printf(" position: %f\n", capsulePos[1]);
printf(" ground: %f\n", (groundLevel + heightThreshold));
if (capsulePos[1] < groundLevel + heightThreshold) {
float jumpForce = 500.0f; // Adjust as needed
dBodyAddForce(capsuleBody, 0.0f, jumpForce, 0.0f);
}
}
dBodyAddForce(capsuleBody, force.x, force.y, force.z);
}
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode3D(camera);
DrawCube((Vector3){ 0.0f, -0.5f, 0.0f }, 10.0f, 1.0f, 10.0f, GRAY);
DrawCubeWires((Vector3){ 0.0f, -0.5f, 0.0f }, 10.0f, 1.0f, 10.0f, BLACK);
const dReal *cube1Pos = dBodyGetPosition(cube1Body);
const dReal *cube1QuatPtr = dBodyGetQuaternion(cube1Body);
dQuaternion cube1Quat = { cube1QuatPtr[0], cube1QuatPtr[1], cube1QuatPtr[2], cube1QuatPtr[3] };
Matrix cube1RotMatrix = QuaternionToMatrix(cube1Quat);
cube1RotMatrix.m12 = (float)cube1Pos[0];
cube1RotMatrix.m13 = (float)cube1Pos[1];
cube1RotMatrix.m14 = (float)cube1Pos[2];
cubeModel.transform = cube1RotMatrix;
DrawModel(cubeModel, (Vector3){0, 0, 0}, 1.0f, RED);
DrawModelWires(cubeModel, (Vector3){0, 0, 0}, 1.0f, BLACK);
const dReal *cube2Pos = dBodyGetPosition(cube2Body);
const dReal *cube2QuatPtr = dBodyGetQuaternion(cube2Body);
dQuaternion cube2Quat = { cube2QuatPtr[0], cube2QuatPtr[1], cube2QuatPtr[2], cube2QuatPtr[3] };
Matrix cube2RotMatrix = QuaternionToMatrix(cube2Quat);
cube2RotMatrix.m12 = (float)cube2Pos[0];
cube2RotMatrix.m13 = (float)cube2Pos[1];
cube2RotMatrix.m14 = (float)cube2Pos[2];
cubeModel.transform = cube2RotMatrix;
DrawModel(cubeModel, (Vector3){0, 0, 0}, 1.0f, BLUE);
DrawModelWires(cubeModel, (Vector3){0, 0, 0}, 1.0f, BLACK);
const dReal *capsulePos = dBodyGetPosition(capsuleBody);
const dReal *capsuleQuatPtr = dBodyGetQuaternion(capsuleBody);
dQuaternion capsuleQuat = { capsuleQuatPtr[0], capsuleQuatPtr[1], capsuleQuatPtr[2], capsuleQuatPtr[3] };
Matrix capsuleRotMatrix = QuaternionToMatrix(capsuleQuat);
capsuleRotMatrix.m12 = (float)capsulePos[0];
capsuleRotMatrix.m13 = (float)capsulePos[1];
capsuleRotMatrix.m14 = (float)capsulePos[2];
capsuleModel.transform = capsuleRotMatrix;
DrawModel(capsuleModel, (Vector3){0, 0, 0}, 1.0f, YELLOW);
DrawModelWires(capsuleModel, (Vector3){0, 0, 0}, 1.0f, BLACK);
// DrawModel(capsuleModel2, (Vector3){0, 0, 0}, 1.0f, BLUE);
// DrawModelWires(capsuleModel2, (Vector3){0, 0, 0}, 1.0f, BLACK);
EndMode3D();
// Draw instructions
DrawText("Press 'R' to reset cubes", 10, 10, 20, BLACK);
DrawText("Press 'TAB' to toggle camera controls", 10, 30, 20, BLACK);
DrawText("Press '1' to toggle capsule movement", 10, 50, 20, BLACK);
DrawText(capsuleMovementEnabled ? "Capsule Movement: ON" : "Capsule Movement: OFF", 10, 70, 20, BLACK);
DrawText(cameraControlEnabled ? "Camera: Mouse active" : "Camera: Mouse inactive", 10, 90, 20, BLACK);
DrawText(TextFormat("Angle X: %0.02f", cameraAngleX), 10, 110, 20, BLACK);
DrawText(TextFormat("Angle Y: %0.02f", cameraAngleY), 10, 130, 20, BLACK);
DrawText("Press 'SPACE' to jump (when movement enabled)", 10, 150, 20, BLACK);
EndDrawing();
}
// Clean up ODE
dGeomDestroy(floorGeom);
dGeomDestroy(cube1Geom);
dGeomDestroy(cube2Geom);
dGeomDestroy(capsuleGeom);
dBodyDestroy(cube1Body);
dBodyDestroy(cube2Body);
dBodyDestroy(capsuleBody);
dJointGroupDestroy(contactgroup);
dSpaceDestroy(space);
dWorldDestroy(world);
dCloseODE();
// Clean up Raylib
UnloadModel(cubeModel);
UnloadModel(capsuleModel);
// UnloadModel(capsuleModel2);
CloseWindow();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment