lovr/src/modules/physics/physics_ode.c

1381 lines
38 KiB
C
Raw Normal View History

2017-05-16 04:59:53 +00:00
#include "physics.h"
2022-03-22 07:13:21 +00:00
#include "util.h"
2022-03-31 05:01:51 +00:00
#include <ode/ode.h>
#include <stdatomic.h>
2017-05-16 04:59:53 +00:00
#include <stdlib.h>
2022-03-25 19:40:29 +00:00
struct World {
uint32_t ref;
dWorldID id;
dSpaceID space;
dJointGroupID contactGroup;
arr_t(Shape*) overlaps;
char* tags[MAX_TAGS];
uint32_t masks[MAX_TAGS];
2022-03-25 19:40:29 +00:00
Collider* head;
};
struct Collider {
uint32_t ref;
dBodyID body;
World* world;
Collider* prev;
Collider* next;
uint32_t tag;
arr_t(Shape*) shapes;
arr_t(Joint*) joints;
float friction;
float restitution;
};
struct Shape {
uint32_t ref;
ShapeType type;
dGeomID id;
Collider* collider;
void* vertices;
void* indices;
bool sensor;
};
struct Joint {
uint32_t ref;
JointType type;
dJointID id;
};
2017-05-19 21:04:34 +00:00
static void defaultNearCallback(void* data, dGeomID a, dGeomID b) {
2017-05-25 00:39:12 +00:00
lovrWorldCollide((World*) data, dGeomGetData(a), dGeomGetData(b), -1, -1);
2017-05-19 21:04:34 +00:00
}
2017-05-17 05:12:10 +00:00
2017-05-19 21:04:34 +00:00
static void customNearCallback(void* data, dGeomID shapeA, dGeomID shapeB) {
World* world = data;
arr_push(&world->overlaps, dGeomGetData(shapeA));
arr_push(&world->overlaps, dGeomGetData(shapeB));
2017-05-17 05:12:10 +00:00
}
2022-03-31 05:01:51 +00:00
typedef struct {
RaycastCallback callback;
void* userdata;
bool shouldStop;
2022-03-31 05:01:51 +00:00
} RaycastData;
static void raycastCallback(void* d, dGeomID a, dGeomID b) {
RaycastData* data = d;
if (data->shouldStop) return;
RaycastCallback callback = data->callback;
void* userdata = data->userdata;
2017-05-25 00:40:02 +00:00
Shape* shape = dGeomGetData(b);
if (!shape) {
return;
}
dContact contact[MAX_CONTACTS];
int count = dCollide(a, b, MAX_CONTACTS, &contact->geom, sizeof(dContact));
for (int i = 0; i < count; i++) {
dContactGeom g = contact[i].geom;
data->shouldStop = callback(
shape, g.pos[0], g.pos[1], g.pos[2], g.normal[0], g.normal[1], g.normal[2], userdata
);
2017-05-25 00:40:02 +00:00
}
}
2023-07-11 06:20:01 +00:00
typedef struct {
QueryCallback callback;
void* userdata;
bool called;
bool shouldStop;
2023-07-11 06:20:01 +00:00
} QueryData;
static void queryCallback(void* d, dGeomID a, dGeomID b) {
QueryData* data = d;
if (data->shouldStop) return;
2023-07-11 06:20:01 +00:00
Shape* shape = dGeomGetData(b);
2023-07-11 06:20:01 +00:00
if (!shape) {
return;
}
dContactGeom contact;
if (dCollide(a, b, 1 | CONTACTS_UNIMPORTANT, &contact, sizeof(contact))) {
if (data->callback) {
data->shouldStop = data->callback(shape, data->userdata);
} else {
data->shouldStop = true;
}
data->called = true;
2023-07-11 06:20:01 +00:00
}
}
2019-09-07 22:07:07 +00:00
// XXX slow, but probably fine (tag names are not on any critical path), could switch to hashing if needed
static uint32_t findTag(World* world, const char* name) {
2024-01-21 05:58:03 +00:00
if (name) {
for (uint32_t i = 0; i < MAX_TAGS && world->tags[i]; i++) {
if (!strcmp(world->tags[i], name)) {
return i;
}
2019-09-07 22:07:07 +00:00
}
}
return NO_TAG;
}
static void onErrorMessage(int num, const char* format, va_list args) {
char message[1024];
vsnprintf(message, 1024, format, args);
lovrLog(LOG_ERROR, "PHY", message);
}
static void onDebugMessage(int num, const char* format, va_list args) {
char message[1024];
vsnprintf(message, 1024, format, args);
lovrLog(LOG_DEBUG, "PHY", message);
}
static void onInfoMessage(int num, const char* format, va_list args) {
char message[1024];
vsnprintf(message, 1024, format, args);
lovrLog(LOG_INFO, "PHY", message);
}
static uint32_t ref;
2023-04-26 04:37:14 +00:00
bool lovrPhysicsInit(void) {
if (atomic_fetch_add(&ref, 1)) return false;
2017-05-16 04:59:53 +00:00
dInitODE();
dSetErrorHandler(onErrorMessage);
dSetDebugHandler(onDebugMessage);
dSetMessageHandler(onInfoMessage);
return true;
2017-05-16 04:59:53 +00:00
}
2023-04-26 04:37:14 +00:00
void lovrPhysicsDestroy(void) {
if (atomic_fetch_sub(&ref, 1) != 1) return;
2017-05-16 04:59:53 +00:00
dCloseODE();
}
2017-05-16 05:02:08 +00:00
World* lovrWorldCreate(WorldInfo* info) {
2024-03-11 21:38:00 +00:00
World* world = lovrCalloc(sizeof(World));
world->ref = 1;
2017-05-16 05:02:08 +00:00
world->id = dWorldCreate();
2017-05-17 05:12:10 +00:00
world->space = dHashSpaceCreate(0);
dHashSpaceSetLevels(world->space, -4, 8);
world->contactGroup = dJointGroupCreate(0);
arr_init(&world->overlaps);
2024-04-05 20:46:05 +00:00
lovrWorldSetGravity(world, info->gravity);
lovrWorldSetSleepingAllowed(world, info->allowSleep);
for (uint32_t i = 0; i < info->tagCount; i++) {
size_t size = strlen(info->tags[i]) + 1;
2024-03-11 21:38:00 +00:00
world->tags[i] = lovrMalloc(size);
memcpy(world->tags[i], info->tags[i], size);
2017-05-25 22:01:40 +00:00
}
2019-09-07 22:07:07 +00:00
memset(world->masks, 0xff, sizeof(world->masks));
2017-05-16 05:02:08 +00:00
return world;
}
2018-02-26 08:59:03 +00:00
void lovrWorldDestroy(void* ref) {
World* world = ref;
2017-05-20 04:09:33 +00:00
lovrWorldDestroyData(world);
arr_free(&world->overlaps);
2019-09-07 22:07:07 +00:00
for (uint32_t i = 0; i < MAX_TAGS && world->tags[i]; i++) {
2024-03-11 21:38:00 +00:00
lovrFree(world->tags[i]);
2019-09-07 22:07:07 +00:00
}
2024-03-11 21:38:00 +00:00
lovrFree(world);
2017-05-16 05:02:08 +00:00
}
2017-05-16 05:03:01 +00:00
2017-05-20 04:09:33 +00:00
void lovrWorldDestroyData(World* world) {
2018-08-22 16:29:34 +00:00
while (world->head) {
Collider* next = world->head->next;
lovrColliderDestroyData(world->head);
world->head = next;
}
2017-05-20 04:09:33 +00:00
if (world->contactGroup) {
2018-08-22 16:29:34 +00:00
dJointGroupDestroy(world->contactGroup);
2017-05-20 04:09:33 +00:00
world->contactGroup = NULL;
}
2017-05-20 04:09:33 +00:00
if (world->space) {
dSpaceDestroy(world->space);
world->space = NULL;
}
2017-05-20 04:09:33 +00:00
if (world->id) {
dWorldDestroy(world->id);
world->id = NULL;
}
}
2017-05-19 21:04:34 +00:00
void lovrWorldUpdate(World* world, float dt, CollisionResolver resolver, void* userdata) {
if (resolver) {
resolver(world, userdata);
} else {
dSpaceCollide(world->space, world, defaultNearCallback);
}
2017-08-27 20:23:40 +00:00
if (dt > 0) {
dWorldQuickStep(world->id, dt);
}
2017-05-17 05:12:10 +00:00
dJointGroupEmpty(world->contactGroup);
2017-05-16 05:04:05 +00:00
}
2017-05-16 05:09:32 +00:00
int lovrWorldGetStepCount(World* world) {
return dWorldGetQuickStepNumIterations(world->id);
}
void lovrWorldSetStepCount(World* world, int iterations) {
dWorldSetQuickStepNumIterations(world->id, iterations);
}
2017-05-19 21:04:34 +00:00
void lovrWorldComputeOverlaps(World* world) {
arr_clear(&world->overlaps);
2017-05-19 21:04:34 +00:00
dSpaceCollide(world->space, world, customNearCallback);
}
int lovrWorldGetNextOverlap(World* world, Shape** a, Shape** b) {
if (world->overlaps.length == 0) {
*a = *b = NULL;
return 0;
}
*a = arr_pop(&world->overlaps);
*b = arr_pop(&world->overlaps);
2017-05-19 21:04:34 +00:00
return 1;
}
2017-05-25 00:39:12 +00:00
int lovrWorldCollide(World* world, Shape* a, Shape* b, float friction, float restitution) {
2017-05-19 21:04:34 +00:00
if (!a || !b) {
2017-10-31 08:14:09 +00:00
return false;
2017-05-19 21:04:34 +00:00
}
2017-05-25 00:39:12 +00:00
Collider* colliderA = a->collider;
Collider* colliderB = b->collider;
2019-09-07 22:07:07 +00:00
uint32_t i = colliderA->tag;
uint32_t j = colliderB->tag;
2017-05-25 22:01:40 +00:00
2019-09-07 22:07:07 +00:00
if (i != NO_TAG && j != NO_TAG && !((world->masks[i] & (1 << j)) && (world->masks[j] & (1 << i)))) {
2017-10-31 08:14:09 +00:00
return false;
2017-05-25 22:01:40 +00:00
}
2017-05-25 00:39:12 +00:00
if (friction < 0.f) {
friction = sqrtf(colliderA->friction * colliderB->friction);
2017-05-25 00:39:12 +00:00
}
if (restitution < 0.f) {
2017-05-25 00:39:12 +00:00
restitution = MAX(colliderA->restitution, colliderB->restitution);
}
2017-05-25 22:01:40 +00:00
dContact contacts[MAX_CONTACTS];
2020-01-23 19:18:04 +00:00
for (int c = 0; c < MAX_CONTACTS; c++) {
contacts[c].surface.mode = 0;
contacts[c].surface.mu = friction;
contacts[c].surface.bounce = restitution;
if (restitution > 0) {
2020-01-23 19:18:04 +00:00
contacts[c].surface.mode |= dContactBounce;
}
2017-05-19 21:04:34 +00:00
}
int contactCount = dCollide(a->id, b->id, MAX_CONTACTS, &contacts[0].geom, sizeof(dContact));
2019-10-10 22:36:28 +00:00
if (!a->sensor && !b->sensor) {
2020-01-23 19:18:04 +00:00
for (int c = 0; c < contactCount; c++) {
dJointID joint = dJointCreateContact(world->id, world->contactGroup, &contacts[c]);
2019-10-10 22:36:28 +00:00
dJointAttach(joint, colliderA->body, colliderB->body);
}
2017-05-19 21:04:34 +00:00
}
return contactCount;
}
2022-03-31 05:32:20 +00:00
void lovrWorldGetContacts(World* world, Shape* a, Shape* b, Contact contacts[MAX_CONTACTS], uint32_t* count) {
dContactGeom info[MAX_CONTACTS];
int c = *count = dCollide(a->id, b->id, MAX_CONTACTS, info, sizeof(info[0]));
for (int i = 0; i < c; i++) {
contacts[i] = (Contact) {
.x = info[i].pos[0],
.y = info[i].pos[1],
.z = info[i].pos[2],
.nx = info[i].normal[0],
.ny = info[i].normal[1],
.nz = info[i].normal[2],
.depth = info[i].depth
};
}
}
2022-03-31 05:01:51 +00:00
void lovrWorldRaycast(World* world, float x1, float y1, float z1, float x2, float y2, float z2, RaycastCallback callback, void* userdata) {
RaycastData data = { .callback = callback, .userdata = userdata, .shouldStop = false };
2022-03-31 05:01:51 +00:00
float dx = x2 - x1;
float dy = y2 - y1;
float dz = z2 - z1;
float length = sqrtf(dx * dx + dy * dy + dz * dz);
dGeomID ray = dCreateRay(world->space, length);
dGeomRaySet(ray, x1, y1, z1, dx, dy, dz);
dSpaceCollide2(ray, (dGeomID) world->space, &data, raycastCallback);
dGeomDestroy(ray);
}
bool lovrWorldQueryBox(World* world, float position[3], float size[3], QueryCallback callback, void* userdata) {
QueryData data = { .callback = callback, .userdata = userdata, .called = false, .shouldStop = false };
2023-07-11 06:20:01 +00:00
dGeomID box = dCreateBox(world->space, fabsf(size[0]), fabsf(size[1]), fabsf(size[2]));
dGeomSetPosition(box, position[0], position[1], position[2]);
dSpaceCollide2(box, (dGeomID) world->space, &data, queryCallback);
dGeomDestroy(box);
return data.called;
2023-07-11 06:20:01 +00:00
}
bool lovrWorldQuerySphere(World* world, float position[3], float radius, QueryCallback callback, void* userdata) {
QueryData data = { .callback = callback, .userdata = userdata, .called = false, .shouldStop = false };
2023-07-11 06:20:01 +00:00
dGeomID sphere = dCreateSphere(world->space, fabsf(radius));
dGeomSetPosition(sphere, position[0], position[1], position[2]);
dSpaceCollide2(sphere, (dGeomID) world->space, &data, queryCallback);
dGeomDestroy(sphere);
return data.called;
2023-07-11 06:20:01 +00:00
}
2020-05-11 18:05:30 +00:00
Collider* lovrWorldGetFirstCollider(World* world) {
return world->head;
}
2024-04-05 20:46:05 +00:00
void lovrWorldGetGravity(World* world, float gravity[3]) {
dReal g[4];
dWorldGetGravity(world->id, g);
gravity[0] = g[0];
gravity[1] = g[1];
gravity[2] = g[2];
2017-05-20 04:09:33 +00:00
}
2024-04-05 20:46:05 +00:00
void lovrWorldSetGravity(World* world, float gravity[3]) {
dWorldSetGravity(world->id, gravity[0], gravity[1], gravity[2]);
2017-05-20 04:09:33 +00:00
}
Get/set World and Joint simulation parameters Tightness parameter is amount of force is exerted on collider to resolve collisions and enforce joint operation. Low values make joints loose, high values make it tight and can cause collider to overshot the joint target. With tightness set to 0 the joint loses its function. Going above 1 puts even more energy into joint oscillations. Tightness parameter is called ERP in ODE manual. The responseTime affects the time constant of physics simulation, both for collisions and for joint inertia. Low responseTime values make simulation tight and fast, higher values make it sluggish. For collisions it affects how fast penetration is resolved, with higher values resulting in spongy objects with more surface penetration and slower collision resolving. For joints the responseTime is similar to inertia, with higher responseTime values resulting in slow oscillations. The oscillation frequency is also affected by collider mass, so responseTime can be used to tweak the joint to get desired frequency with specific collider mass. Values higher than 1 are often desirable, especially for very light objects. Unlike tightness, responseTime is tweaked in orders of magnitude with useful values (depending on mass) being between 10^-8 and 10^8. Both parameters can be applied to World for simulation-wide usage, or specified per-joint in case of distance and ball joints. Other joints don't allow customizing these parameters, and will use World settings instead..
2020-05-29 14:38:32 +00:00
float lovrWorldGetResponseTime(World* world) {
return dWorldGetCFM(world->id);
}
void lovrWorldSetResponseTime(World* world, float responseTime) {
dWorldSetCFM(world->id, responseTime);
}
float lovrWorldGetTightness(World* world) {
return dWorldGetERP(world->id);
}
void lovrWorldSetTightness(World* world, float tightness) {
dWorldSetERP(world->id, tightness);
}
2017-05-20 04:09:33 +00:00
void lovrWorldGetLinearDamping(World* world, float* damping, float* threshold) {
*damping = dWorldGetLinearDamping(world->id);
*threshold = dWorldGetLinearDampingThreshold(world->id);
}
void lovrWorldSetLinearDamping(World* world, float damping, float threshold) {
dWorldSetLinearDamping(world->id, damping);
dWorldSetLinearDampingThreshold(world->id, threshold);
}
void lovrWorldGetAngularDamping(World* world, float* damping, float* threshold) {
*damping = dWorldGetAngularDamping(world->id);
*threshold = dWorldGetAngularDampingThreshold(world->id);
}
void lovrWorldSetAngularDamping(World* world, float damping, float threshold) {
dWorldSetAngularDamping(world->id, damping);
dWorldSetAngularDampingThreshold(world->id, threshold);
}
2017-10-31 08:14:09 +00:00
bool lovrWorldIsSleepingAllowed(World* world) {
2017-05-20 04:09:33 +00:00
return dWorldGetAutoDisableFlag(world->id);
}
2017-10-31 08:14:09 +00:00
void lovrWorldSetSleepingAllowed(World* world, bool allowed) {
2017-05-20 04:09:33 +00:00
dWorldSetAutoDisableFlag(world->id, allowed);
}
2019-09-07 22:07:07 +00:00
const char* lovrWorldGetTagName(World* world, uint32_t tag) {
return (tag == NO_TAG) ? NULL : world->tags[tag];
2017-05-25 22:01:40 +00:00
}
void lovrWorldDisableCollisionBetween(World* world, const char* tag1, const char* tag2) {
2019-09-07 22:07:07 +00:00
uint32_t i = findTag(world, tag1);
uint32_t j = findTag(world, tag2);
2019-09-07 22:07:07 +00:00
if (i == NO_TAG || j == NO_TAG) {
return;
2017-05-25 22:01:40 +00:00
}
2019-09-07 22:07:07 +00:00
world->masks[i] &= ~(1 << j);
world->masks[j] &= ~(1 << i);
return;
2017-05-25 22:01:40 +00:00
}
void lovrWorldEnableCollisionBetween(World* world, const char* tag1, const char* tag2) {
2019-09-07 22:07:07 +00:00
uint32_t i = findTag(world, tag1);
uint32_t j = findTag(world, tag2);
2019-09-07 22:07:07 +00:00
if (i == NO_TAG || j == NO_TAG) {
return;
2017-05-25 22:01:40 +00:00
}
2019-09-07 22:07:07 +00:00
world->masks[i] |= (1 << j);
world->masks[j] |= (1 << i);
return;
2017-05-25 22:01:40 +00:00
}
bool lovrWorldIsCollisionEnabledBetween(World* world, const char* tag1, const char* tag2) {
2019-09-07 22:07:07 +00:00
uint32_t i = findTag(world, tag1);
uint32_t j = findTag(world, tag2);
2019-09-07 22:07:07 +00:00
if (i == NO_TAG || j == NO_TAG) {
return true;
2017-05-25 22:01:40 +00:00
}
2019-09-07 22:07:07 +00:00
return (world->masks[i] & (1 << j)) && (world->masks[j] & (1 << i));
2017-05-25 22:01:40 +00:00
}
Collider* lovrColliderCreate(World* world, float x, float y, float z) {
2024-03-11 21:38:00 +00:00
Collider* collider = lovrCalloc(sizeof(Collider));
collider->ref = 1;
2017-05-20 02:11:58 +00:00
collider->body = dBodyCreate(world->id);
collider->world = world;
collider->friction = INFINITY;
collider->restitution = 0;
2017-05-25 22:01:40 +00:00
collider->tag = NO_TAG;
2017-05-20 02:11:58 +00:00
dBodySetData(collider->body, collider);
arr_init(&collider->shapes);
arr_init(&collider->joints);
2017-05-16 05:09:32 +00:00
2017-06-10 21:17:59 +00:00
lovrColliderSetPosition(collider, x, y, z);
2018-08-19 04:16:19 +00:00
// Adjust the world's collider list
if (!collider->world->head) {
collider->world->head = collider;
} else {
collider->next = collider->world->head;
collider->next->prev = collider;
collider->world->head = collider;
}
// The world owns a reference to the collider
lovrRetain(collider);
2017-05-20 02:11:58 +00:00
return collider;
2017-05-16 05:09:32 +00:00
}
2018-02-26 08:59:03 +00:00
void lovrColliderDestroy(void* ref) {
Collider* collider = ref;
2018-08-22 16:29:34 +00:00
lovrColliderDestroyData(collider);
arr_free(&collider->shapes);
arr_free(&collider->joints);
2024-03-11 21:38:00 +00:00
lovrFree(collider);
2017-05-16 05:09:32 +00:00
}
2017-05-16 05:10:17 +00:00
2017-05-20 04:14:20 +00:00
void lovrColliderDestroyData(Collider* collider) {
2018-08-19 04:16:19 +00:00
if (!collider->body) {
return;
2017-05-20 04:14:20 +00:00
}
2018-08-19 04:16:19 +00:00
size_t count;
Shape** shapes = lovrColliderGetShapes(collider, &count);
for (size_t i = 0; i < count; i++) {
lovrColliderRemoveShape(collider, shapes[i]);
2018-08-22 16:29:34 +00:00
}
Joint** joints = lovrColliderGetJoints(collider, &count);
for (size_t i = 0; i < count; i++) {
2021-02-09 00:52:26 +00:00
lovrRelease(joints[i], lovrJointDestroy);
2018-08-22 16:29:34 +00:00
}
2018-08-19 04:16:19 +00:00
dBodyDestroy(collider->body);
collider->body = NULL;
if (collider->next) collider->next->prev = collider->prev;
if (collider->prev) collider->prev->next = collider->next;
if (collider->world->head == collider) collider->world->head = collider->next;
collider->next = collider->prev = NULL;
// If the Collider is destroyed, the world lets go of its reference to this Collider
2021-02-09 00:52:26 +00:00
lovrRelease(collider, lovrColliderDestroy);
2017-05-20 04:14:20 +00:00
}
2023-06-22 19:13:15 +00:00
bool lovrColliderIsDestroyed(Collider* collider) {
return !collider->body;
}
2020-04-30 20:15:36 +00:00
void lovrColliderInitInertia(Collider* collider, Shape* shape) {
// compute inertia matrix for default density
const float density = 1.0f;
float cx, cy, cz, mass, inertia[6];
lovrShapeGetMass(shape, density, &cx, &cy, &cz, &mass, inertia);
lovrColliderSetMassData(collider, cx, cy, cz, mass, inertia);
}
World* lovrColliderGetWorld(Collider* collider) {
return collider->world;
}
2022-03-25 19:40:29 +00:00
Collider* lovrColliderGetNext(Collider* collider) {
return collider->next;
}
2017-05-20 02:11:58 +00:00
void lovrColliderAddShape(Collider* collider, Shape* shape) {
2018-08-22 16:29:34 +00:00
lovrRetain(shape);
2017-05-20 02:11:58 +00:00
2018-08-22 16:29:34 +00:00
if (shape->collider) {
lovrColliderRemoveShape(shape->collider, shape);
2017-05-20 02:11:58 +00:00
}
2018-08-22 16:29:34 +00:00
shape->collider = collider;
dGeomSetBody(shape->id, collider->body);
dSpaceID newSpace = collider->world->space;
2017-05-20 02:11:58 +00:00
dSpaceAdd(newSpace, shape->id);
}
void lovrColliderRemoveShape(Collider* collider, Shape* shape) {
if (shape->collider == collider) {
dSpaceRemove(collider->world->space, shape->id);
dGeomSetBody(shape->id, 0);
2018-08-22 16:29:34 +00:00
shape->collider = NULL;
2021-02-09 00:52:26 +00:00
lovrRelease(shape, lovrShapeDestroy);
2017-05-20 02:11:58 +00:00
}
}
Shape** lovrColliderGetShapes(Collider* collider, size_t* count) {
arr_clear(&collider->shapes);
for (dGeomID geom = dBodyGetFirstGeom(collider->body); geom; geom = dBodyGetNextGeom(geom)) {
Shape* shape = dGeomGetData(geom);
if (shape) {
arr_push(&collider->shapes, shape);
}
}
*count = collider->shapes.length;
return collider->shapes.data;
2017-05-20 04:51:16 +00:00
}
Joint** lovrColliderGetJoints(Collider* collider, size_t* count) {
arr_clear(&collider->joints);
int jointCount = dBodyGetNumJoints(collider->body);
for (int i = 0; i < jointCount; i++) {
Joint* joint = dJointGetData(dBodyGetJoint(collider->body, i));
if (joint) {
arr_push(&collider->joints, joint);
}
}
*count = collider->joints.length;
return collider->joints.data;
2017-05-20 04:51:16 +00:00
}
2017-06-10 11:51:09 +00:00
const char* lovrColliderGetTag(Collider* collider) {
return lovrWorldGetTagName(collider->world, collider->tag);
}
2019-09-07 22:07:07 +00:00
bool lovrColliderSetTag(Collider* collider, const char* tag) {
if (!tag) {
2017-06-10 11:51:09 +00:00
collider->tag = NO_TAG;
2019-09-07 22:07:07 +00:00
return true;
2017-06-10 11:51:09 +00:00
}
2019-09-07 22:07:07 +00:00
collider->tag = findTag(collider->world, tag);
return collider->tag != NO_TAG;
2017-06-10 11:51:09 +00:00
}
float lovrColliderGetFriction(Collider* collider) {
return collider->friction;
}
void lovrColliderSetFriction(Collider* collider, float friction) {
collider->friction = friction;
}
float lovrColliderGetRestitution(Collider* collider) {
return collider->restitution;
}
void lovrColliderSetRestitution(Collider* collider, float restitution) {
collider->restitution = restitution;
}
2017-10-31 08:14:09 +00:00
bool lovrColliderIsKinematic(Collider* collider) {
return dBodyIsKinematic(collider->body);
}
2017-10-31 08:14:09 +00:00
void lovrColliderSetKinematic(Collider* collider, bool kinematic) {
if (kinematic) {
dBodySetKinematic(collider->body);
} else {
dBodySetDynamic(collider->body);
}
}
2024-04-06 02:05:25 +00:00
bool lovrColliderIsContinuous(Collider* collider) {
return false;
}
void lovrColliderSetContinuous(Collider* collider, bool continuous) {
//
}
float lovrColliderGetGravityScale(Collider* collider) {
return dBodyGetGravityMode(collider->body) ? 1.f : 0.f;
}
void lovrColliderSetGravityScale(Collider* collider, float scale) {
dBodySetGravityMode(collider->body, scale == 0.f ? false : true);
}
2017-10-31 08:14:09 +00:00
bool lovrColliderIsSleepingAllowed(Collider* collider) {
return dBodyGetAutoDisableFlag(collider->body);
}
2017-10-31 08:14:09 +00:00
void lovrColliderSetSleepingAllowed(Collider* collider, bool allowed) {
dBodySetAutoDisableFlag(collider->body, allowed);
}
2017-10-31 08:14:09 +00:00
bool lovrColliderIsAwake(Collider* collider) {
return dBodyIsEnabled(collider->body);
}
2017-10-31 08:14:09 +00:00
void lovrColliderSetAwake(Collider* collider, bool awake) {
if (awake) {
dBodyEnable(collider->body);
} else {
dBodyDisable(collider->body);
}
}
float lovrColliderGetMass(Collider* collider) {
dMass m;
dBodyGetMass(collider->body, &m);
return m.mass;
}
void lovrColliderSetMass(Collider* collider, float mass) {
dMass m;
dBodyGetMass(collider->body, &m);
dMassAdjust(&m, mass);
dBodySetMass(collider->body, &m);
}
void lovrColliderGetMassData(Collider* collider, float* cx, float* cy, float* cz, float* mass, float inertia[6]) {
dMass m;
dBodyGetMass(collider->body, &m);
*cx = m.c[0];
*cy = m.c[1];
*cz = m.c[2];
*mass = m.mass;
// Diagonal
inertia[0] = m.I[0];
inertia[1] = m.I[5];
inertia[2] = m.I[10];
// Lower triangular
inertia[3] = m.I[4];
inertia[4] = m.I[8];
inertia[5] = m.I[9];
}
2022-03-25 19:14:49 +00:00
void lovrColliderSetMassData(Collider* collider, float cx, float cy, float cz, float mass, float inertia[6]) {
dMass m;
dBodyGetMass(collider->body, &m);
dMassSetParameters(&m, mass, cx, cy, cz, inertia[0], inertia[1], inertia[2], inertia[3], inertia[4], inertia[5]);
dBodySetMass(collider->body, &m);
}
2017-05-20 02:11:58 +00:00
void lovrColliderGetPosition(Collider* collider, float* x, float* y, float* z) {
const dReal* position = dBodyGetPosition(collider->body);
2017-05-16 05:10:17 +00:00
*x = position[0];
*y = position[1];
*z = position[2];
}
2017-05-20 02:11:58 +00:00
void lovrColliderSetPosition(Collider* collider, float x, float y, float z) {
dBodySetPosition(collider->body, x, y, z);
2017-05-16 05:10:17 +00:00
}
void lovrColliderGetOrientation(Collider* collider, float* orientation) {
2017-05-20 02:11:58 +00:00
const dReal* q = dBodyGetQuaternion(collider->body);
orientation[0] = q[1];
orientation[1] = q[2];
orientation[2] = q[3];
orientation[3] = q[0];
}
void lovrColliderSetOrientation(Collider* collider, float* orientation) {
dReal q[4] = { orientation[3], orientation[0], orientation[1], orientation[2] };
2017-05-20 02:11:58 +00:00
dBodySetQuaternion(collider->body, q);
}
2017-05-20 02:11:58 +00:00
void lovrColliderGetLinearVelocity(Collider* collider, float* x, float* y, float* z) {
const dReal* velocity = dBodyGetLinearVel(collider->body);
*x = velocity[0];
*y = velocity[1];
*z = velocity[2];
}
2017-05-20 02:11:58 +00:00
void lovrColliderSetLinearVelocity(Collider* collider, float x, float y, float z) {
dBodyEnable(collider->body);
2017-05-20 02:11:58 +00:00
dBodySetLinearVel(collider->body, x, y, z);
}
2017-05-20 02:11:58 +00:00
void lovrColliderGetAngularVelocity(Collider* collider, float* x, float* y, float* z) {
const dReal* velocity = dBodyGetAngularVel(collider->body);
*x = velocity[0];
*y = velocity[1];
*z = velocity[2];
}
2017-05-20 02:11:58 +00:00
void lovrColliderSetAngularVelocity(Collider* collider, float x, float y, float z) {
dBodyEnable(collider->body);
2017-05-20 02:11:58 +00:00
dBodySetAngularVel(collider->body, x, y, z);
}
2017-05-20 02:11:58 +00:00
void lovrColliderGetLinearDamping(Collider* collider, float* damping, float* threshold) {
*damping = dBodyGetLinearDamping(collider->body);
*threshold = dBodyGetLinearDampingThreshold(collider->body);
}
2017-05-20 02:11:58 +00:00
void lovrColliderSetLinearDamping(Collider* collider, float damping, float threshold) {
dBodySetLinearDamping(collider->body, damping);
dBodySetLinearDampingThreshold(collider->body, threshold);
}
2017-05-20 02:11:58 +00:00
void lovrColliderGetAngularDamping(Collider* collider, float* damping, float* threshold) {
*damping = dBodyGetAngularDamping(collider->body);
*threshold = dBodyGetAngularDampingThreshold(collider->body);
}
2017-05-20 02:11:58 +00:00
void lovrColliderSetAngularDamping(Collider* collider, float damping, float threshold) {
dBodySetAngularDamping(collider->body, damping);
dBodySetAngularDampingThreshold(collider->body, threshold);
}
2017-05-16 05:15:22 +00:00
2017-05-20 02:11:58 +00:00
void lovrColliderApplyForce(Collider* collider, float x, float y, float z) {
dBodyEnable(collider->body);
2017-05-20 02:11:58 +00:00
dBodyAddForce(collider->body, x, y, z);
2017-05-16 05:15:22 +00:00
}
2017-05-20 02:11:58 +00:00
void lovrColliderApplyForceAtPosition(Collider* collider, float x, float y, float z, float cx, float cy, float cz) {
dBodyEnable(collider->body);
2017-05-20 02:11:58 +00:00
dBodyAddForceAtPos(collider->body, x, y, z, cx, cy, cz);
2017-05-16 05:15:22 +00:00
}
2017-05-20 02:11:58 +00:00
void lovrColliderApplyTorque(Collider* collider, float x, float y, float z) {
dBodyEnable(collider->body);
2017-05-20 02:11:58 +00:00
dBodyAddTorque(collider->body, x, y, z);
2017-05-16 05:15:22 +00:00
}
2017-05-16 05:15:50 +00:00
2024-04-06 02:16:39 +00:00
void lovrColliderApplyLinearImpulse(Collider* collider, float impulse[3]) {
//
}
void lovrColliderApplyLinearImpulseAtPosition(Collider* collider, float impulse[3], float position[3]) {
//
}
void lovrColliderApplyAngularImpulse(Collider* collider, float impulse[3]) {
//
}
2017-05-20 03:51:43 +00:00
void lovrColliderGetLocalCenter(Collider* collider, float* x, float* y, float* z) {
dMass m;
dBodyGetMass(collider->body, &m);
*x = m.c[0];
*y = m.c[1];
*z = m.c[2];
}
2017-05-20 02:11:58 +00:00
void lovrColliderGetLocalPoint(Collider* collider, float wx, float wy, float wz, float* x, float* y, float* z) {
2022-03-25 19:14:49 +00:00
dReal local[4];
2017-05-20 02:11:58 +00:00
dBodyGetPosRelPoint(collider->body, wx, wy, wz, local);
*x = local[0];
*y = local[1];
*z = local[2];
}
2017-05-20 02:11:58 +00:00
void lovrColliderGetWorldPoint(Collider* collider, float x, float y, float z, float* wx, float* wy, float* wz) {
2022-03-25 19:14:49 +00:00
dReal world[4];
2017-05-20 02:11:58 +00:00
dBodyGetRelPointPos(collider->body, x, y, z, world);
*wx = world[0];
*wy = world[1];
*wz = world[2];
}
2017-05-20 02:11:58 +00:00
void lovrColliderGetLocalVector(Collider* collider, float wx, float wy, float wz, float* x, float* y, float* z) {
2022-03-25 19:14:49 +00:00
dReal local[4];
2017-05-20 02:11:58 +00:00
dBodyVectorFromWorld(collider->body, wx, wy, wz, local);
*x = local[0];
*y = local[1];
*z = local[2];
}
2017-05-20 02:11:58 +00:00
void lovrColliderGetWorldVector(Collider* collider, float x, float y, float z, float* wx, float* wy, float* wz) {
2022-03-25 19:14:49 +00:00
dReal world[4];
2017-05-20 02:11:58 +00:00
dBodyVectorToWorld(collider->body, x, y, z, world);
*wx = world[0];
*wy = world[1];
*wz = world[2];
}
2017-05-20 02:11:58 +00:00
void lovrColliderGetLinearVelocityFromLocalPoint(Collider* collider, float x, float y, float z, float* vx, float* vy, float* vz) {
2022-03-25 19:14:49 +00:00
dReal velocity[4];
2017-05-20 02:11:58 +00:00
dBodyGetRelPointVel(collider->body, x, y, z, velocity);
*vx = velocity[0];
*vy = velocity[1];
*vz = velocity[2];
}
2017-05-20 02:11:58 +00:00
void lovrColliderGetLinearVelocityFromWorldPoint(Collider* collider, float wx, float wy, float wz, float* vx, float* vy, float* vz) {
2022-03-25 19:14:49 +00:00
dReal velocity[4];
2017-05-20 02:11:58 +00:00
dBodyGetPointVel(collider->body, wx, wy, wz, velocity);
*vx = velocity[0];
*vy = velocity[1];
*vz = velocity[2];
}
2017-05-25 01:10:39 +00:00
void lovrColliderGetAABB(Collider* collider, float aabb[6]) {
dGeomID shape = dBodyGetFirstGeom(collider->body);
if (!shape) {
memset(aabb, 0, 6 * sizeof(float));
return;
}
dGeomGetAABB(shape, aabb);
float otherAABB[6];
while ((shape = dBodyGetNextGeom(shape)) != NULL) {
dGeomGetAABB(shape, otherAABB);
aabb[0] = MIN(aabb[0], otherAABB[0]);
2017-06-22 01:45:46 +00:00
aabb[1] = MAX(aabb[1], otherAABB[1]);
2017-05-25 01:10:39 +00:00
aabb[2] = MIN(aabb[2], otherAABB[2]);
aabb[3] = MAX(aabb[3], otherAABB[3]);
aabb[4] = MIN(aabb[4], otherAABB[4]);
aabb[5] = MAX(aabb[5], otherAABB[5]);
}
}
2018-02-26 08:59:03 +00:00
void lovrShapeDestroy(void* ref) {
Shape* shape = ref;
2017-05-20 04:24:23 +00:00
lovrShapeDestroyData(shape);
2024-03-11 21:38:00 +00:00
lovrFree(shape);
2017-05-16 18:24:49 +00:00
}
2017-05-20 04:24:23 +00:00
void lovrShapeDestroyData(Shape* shape) {
if (shape->id) {
2020-09-13 10:34:36 +00:00
if (shape->type == SHAPE_MESH) {
dTriMeshDataID dataID = dGeomTriMeshGetData(shape->id);
dGeomTriMeshDataDestroy(dataID);
2024-03-11 21:38:00 +00:00
lovrFree(shape->vertices);
lovrFree(shape->indices);
2022-08-12 05:04:59 +00:00
} else if (shape->type == SHAPE_TERRAIN) {
dHeightfieldDataID dataID = dGeomHeightfieldGetHeightfieldData(shape->id);
dGeomHeightfieldDataDestroy(dataID);
2020-09-13 10:34:36 +00:00
}
2017-05-20 04:24:23 +00:00
dGeomDestroy(shape->id);
shape->id = NULL;
}
}
2017-05-16 18:17:01 +00:00
ShapeType lovrShapeGetType(Shape* shape) {
return shape->type;
}
2017-05-20 02:11:58 +00:00
Collider* lovrShapeGetCollider(Shape* shape) {
return shape->collider;
2017-05-16 18:23:13 +00:00
}
2017-10-31 08:14:09 +00:00
bool lovrShapeIsEnabled(Shape* shape) {
2017-05-16 18:29:18 +00:00
return dGeomIsEnabled(shape->id);
}
2017-10-31 08:14:09 +00:00
void lovrShapeSetEnabled(Shape* shape, bool enabled) {
2017-05-16 18:29:18 +00:00
if (enabled) {
dGeomEnable(shape->id);
} else {
dGeomDisable(shape->id);
}
2017-05-16 18:17:01 +00:00
}
2017-05-16 18:33:55 +00:00
2019-10-10 22:36:28 +00:00
bool lovrShapeIsSensor(Shape* shape) {
return shape->sensor;
}
void lovrShapeSetSensor(Shape* shape, bool sensor) {
shape->sensor = sensor;
}
2017-05-16 18:46:15 +00:00
void lovrShapeGetPosition(Shape* shape, float* x, float* y, float* z) {
const dReal* position = dGeomGetOffsetPosition(shape->id);
*x = position[0];
*y = position[1];
*z = position[2];
}
void lovrShapeSetPosition(Shape* shape, float x, float y, float z) {
dGeomSetOffsetPosition(shape->id, x, y, z);
}
void lovrShapeGetOrientation(Shape* shape, float* orientation) {
2017-05-17 05:11:53 +00:00
dReal q[4];
dGeomGetOffsetQuaternion(shape->id, q);
orientation[0] = q[1];
orientation[1] = q[2];
orientation[2] = q[3];
orientation[3] = q[0];
}
void lovrShapeSetOrientation(Shape* shape, float* orientation) {
dReal q[4] = { orientation[3], orientation[0], orientation[1], orientation[2] };
2017-05-17 05:11:53 +00:00
dGeomSetOffsetQuaternion(shape->id, q);
}
2017-05-16 20:26:09 +00:00
2017-05-25 00:47:59 +00:00
void lovrShapeGetMass(Shape* shape, float density, float* cx, float* cy, float* cz, float* mass, float inertia[6]) {
2017-05-17 00:25:08 +00:00
dMass m;
dMassSetZero(&m);
switch (shape->type) {
case SHAPE_SPHERE: {
dMassSetSphere(&m, density, dGeomSphereGetRadius(shape->id));
break;
}
case SHAPE_BOX: {
2022-03-25 19:14:49 +00:00
dReal lengths[4];
2017-05-17 00:25:08 +00:00
dGeomBoxGetLengths(shape->id, lengths);
dMassSetBox(&m, density, lengths[0], lengths[1], lengths[2]);
break;
}
case SHAPE_CAPSULE: {
dReal radius, length;
dGeomCapsuleGetParams(shape->id, &radius, &length);
dMassSetCapsule(&m, density, 3, radius, length);
break;
}
case SHAPE_CYLINDER: {
dReal radius, length;
dGeomCylinderGetParams(shape->id, &radius, &length);
dMassSetCylinder(&m, density, 3, radius, length);
break;
}
2020-09-13 10:34:36 +00:00
case SHAPE_MESH: {
dMassSetTrimesh(&m, density, shape->id);
dGeomSetPosition(shape->id, -m.c[0], -m.c[1], -m.c[2]);
dMassTranslate(&m, -m.c[0], -m.c[1], -m.c[2]);
break;
}
2022-11-10 06:22:26 +00:00
case SHAPE_TERRAIN: {
break;
}
2017-05-17 00:25:08 +00:00
}
const dReal* position = dGeomGetOffsetPosition(shape->id);
dMassTranslate(&m, position[0], position[1], position[2]);
const dReal* rotation = dGeomGetOffsetRotation(shape->id);
dMassRotate(&m, rotation);
*cx = m.c[0];
*cy = m.c[1];
*cz = m.c[2];
*mass = m.mass;
2017-05-17 01:13:38 +00:00
// Diagonal
2017-05-17 00:25:08 +00:00
inertia[0] = m.I[0];
2017-05-17 01:13:38 +00:00
inertia[1] = m.I[5];
inertia[2] = m.I[10];
// Lower triangular
inertia[3] = m.I[4];
inertia[4] = m.I[8];
2017-05-17 00:25:08 +00:00
inertia[5] = m.I[9];
}
2017-05-25 00:47:59 +00:00
void lovrShapeGetAABB(Shape* shape, float aabb[6]) {
dGeomGetAABB(shape->id, aabb);
}
SphereShape* lovrSphereShapeCreate(float radius) {
lovrCheck(radius > 0.f, "SphereShape radius must be positive");
2024-03-11 21:38:00 +00:00
SphereShape* sphere = lovrCalloc(sizeof(SphereShape));
sphere->ref = 1;
2017-05-16 21:21:10 +00:00
sphere->type = SHAPE_SPHERE;
sphere->id = dCreateSphere(0, radius);
2017-05-19 21:04:34 +00:00
dGeomSetData(sphere->id, sphere);
2017-05-16 21:21:10 +00:00
return sphere;
}
float lovrSphereShapeGetRadius(SphereShape* sphere) {
return dGeomSphereGetRadius(sphere->id);
}
void lovrSphereShapeSetRadius(SphereShape* sphere, float radius) {
lovrCheck(radius > 0.f, "SphereShape radius must be positive");
2017-05-16 21:21:10 +00:00
dGeomSphereSetRadius(sphere->id, radius);
}
2017-05-16 21:37:05 +00:00
BoxShape* lovrBoxShapeCreate(float w, float h, float d) {
2024-03-11 21:38:00 +00:00
BoxShape* box = lovrCalloc(sizeof(BoxShape));
box->ref = 1;
2017-05-16 21:37:05 +00:00
box->type = SHAPE_BOX;
box->id = dCreateBox(0, w, h, d);
2017-05-19 21:04:34 +00:00
dGeomSetData(box->id, box);
2017-05-16 21:37:05 +00:00
return box;
}
void lovrBoxShapeGetDimensions(BoxShape* box, float* w, float* h, float* d) {
2022-03-25 19:14:49 +00:00
dReal dimensions[4];
2017-05-16 21:37:05 +00:00
dGeomBoxGetLengths(box->id, dimensions);
*w = dimensions[0];
*h = dimensions[1];
*d = dimensions[2];
2017-05-16 21:37:05 +00:00
}
void lovrBoxShapeSetDimensions(BoxShape* box, float w, float h, float d) {
lovrCheck(w > 0.f && h > 0.f && d > 0.f, "BoxShape dimensions must be positive");
dGeomBoxSetLengths(box->id, w, h, d);
2017-05-16 21:37:05 +00:00
}
2017-05-16 21:52:41 +00:00
CapsuleShape* lovrCapsuleShapeCreate(float radius, float length) {
lovrCheck(radius > 0.f && length > 0.f, "CapsuleShape dimensions must be positive");
2024-03-11 21:38:00 +00:00
CapsuleShape* capsule = lovrCalloc(sizeof(CapsuleShape));
capsule->ref = 1;
2017-05-16 21:52:41 +00:00
capsule->type = SHAPE_CAPSULE;
capsule->id = dCreateCapsule(0, radius, length);
2017-05-19 21:04:34 +00:00
dGeomSetData(capsule->id, capsule);
2017-05-16 21:52:41 +00:00
return capsule;
}
float lovrCapsuleShapeGetRadius(CapsuleShape* capsule) {
dReal radius, length;
2017-05-16 21:52:41 +00:00
dGeomCapsuleGetParams(capsule->id, &radius, &length);
return radius;
}
void lovrCapsuleShapeSetRadius(CapsuleShape* capsule, float radius) {
lovrCheck(radius > 0.f, "CapsuleShape dimensions must be positive");
2017-05-16 21:52:41 +00:00
dGeomCapsuleSetParams(capsule->id, radius, lovrCapsuleShapeGetLength(capsule));
}
float lovrCapsuleShapeGetLength(CapsuleShape* capsule) {
dReal radius, length;
2017-05-16 21:52:41 +00:00
dGeomCapsuleGetParams(capsule->id, &radius, &length);
return length;
}
void lovrCapsuleShapeSetLength(CapsuleShape* capsule, float length) {
lovrCheck(length > 0.f, "CapsuleShape dimensions must be positive");
2017-05-16 21:52:41 +00:00
dGeomCapsuleSetParams(capsule->id, lovrCapsuleShapeGetRadius(capsule), length);
}
2017-05-16 21:56:20 +00:00
CylinderShape* lovrCylinderShapeCreate(float radius, float length) {
lovrCheck(radius > 0.f && length > 0.f, "CylinderShape dimensions must be positive");
2024-03-11 21:38:00 +00:00
CylinderShape* cylinder = lovrCalloc(sizeof(CylinderShape));
cylinder->ref = 1;
2017-05-16 21:56:20 +00:00
cylinder->type = SHAPE_CYLINDER;
cylinder->id = dCreateCylinder(0, radius, length);
2017-05-19 21:04:34 +00:00
dGeomSetData(cylinder->id, cylinder);
2017-05-16 21:56:20 +00:00
return cylinder;
}
float lovrCylinderShapeGetRadius(CylinderShape* cylinder) {
dReal radius, length;
2017-05-16 21:56:20 +00:00
dGeomCylinderGetParams(cylinder->id, &radius, &length);
return radius;
}
void lovrCylinderShapeSetRadius(CylinderShape* cylinder, float radius) {
lovrCheck(radius > 0.f, "CylinderShape dimensions must be positive");
2017-05-16 21:56:20 +00:00
dGeomCylinderSetParams(cylinder->id, radius, lovrCylinderShapeGetLength(cylinder));
}
float lovrCylinderShapeGetLength(CylinderShape* cylinder) {
dReal radius, length;
2017-05-16 21:56:20 +00:00
dGeomCylinderGetParams(cylinder->id, &radius, &length);
return length;
}
void lovrCylinderShapeSetLength(CylinderShape* cylinder, float length) {
lovrCheck(length > 0.f, "CylinderShape dimensions must be positive");
2017-05-16 21:56:20 +00:00
dGeomCylinderSetParams(cylinder->id, lovrCylinderShapeGetRadius(cylinder), length);
}
MeshShape* lovrMeshShapeCreate(int vertexCount, float* vertices, int indexCount, dTriIndex* indices) {
2024-03-11 21:38:00 +00:00
MeshShape* mesh = lovrCalloc(sizeof(MeshShape));
mesh->ref = 1;
2020-09-13 10:34:36 +00:00
dTriMeshDataID dataID = dGeomTriMeshDataCreate();
2020-11-21 21:37:47 +00:00
dGeomTriMeshDataBuildSingle(dataID, vertices, 3 * sizeof(float), vertexCount, indices, indexCount, 3 * sizeof(dTriIndex));
2020-09-13 10:34:36 +00:00
dGeomTriMeshDataPreprocess2(dataID, (1U << dTRIDATAPREPROCESS_BUILD_FACE_ANGLES), NULL);
mesh->id = dCreateTriMesh(0, dataID, 0, 0, 0);
mesh->type = SHAPE_MESH;
mesh->vertices = vertices;
mesh->indices = indices;
2020-09-13 10:34:36 +00:00
dGeomSetData(mesh->id, mesh);
return mesh;
}
2024-04-06 07:12:50 +00:00
TerrainShape* lovrTerrainShapeCreate(float* vertices, uint32_t n, float scaleXZ, float scaleY) {
2022-08-12 05:04:59 +00:00
const float thickness = 10.f;
2024-03-11 21:38:00 +00:00
TerrainShape* terrain = lovrCalloc(sizeof(TerrainShape));
2022-08-12 05:04:59 +00:00
terrain->ref = 1;
dHeightfieldDataID dataID = dGeomHeightfieldDataCreate();
2024-04-06 07:12:50 +00:00
dGeomHeightfieldDataBuildSingle(dataID, vertices, 1, scaleXZ, scaleXZ, n, n, scaleY, 0.f, thickness, 0);
2022-08-12 05:04:59 +00:00
terrain->id = dCreateHeightfield(0, dataID, 1);
terrain->type = SHAPE_TERRAIN;
dGeomSetData(terrain->id, terrain);
return terrain;
}
2018-02-26 08:59:03 +00:00
void lovrJointDestroy(void* ref) {
Joint* joint = ref;
lovrJointDestroyData(joint);
2024-03-11 21:38:00 +00:00
lovrFree(joint);
}
void lovrJointDestroyData(Joint* joint) {
if (joint->id) {
dJointDestroy(joint->id);
joint->id = NULL;
}
}
JointType lovrJointGetType(Joint* joint) {
return joint->type;
}
void lovrJointGetColliders(Joint* joint, Collider** a, Collider** b) {
dBodyID bodyA = dJointGetBody(joint->id, 0);
dBodyID bodyB = dJointGetBody(joint->id, 1);
if (bodyA) {
*a = dBodyGetData(bodyA);
}
if (bodyB) {
*b = dBodyGetData(bodyB);
}
}
2019-11-12 00:27:30 +00:00
bool lovrJointIsEnabled(Joint* joint) {
return dJointIsEnabled(joint->id);
}
void lovrJointSetEnabled(Joint* joint, bool enable) {
if (enable) {
dJointEnable(joint->id);
} else {
dJointDisable(joint->id);
}
}
BallJoint* lovrBallJointCreate(Collider* a, Collider* b, float anchor[3]) {
2024-03-10 07:22:32 +00:00
lovrCheck(a->world == b->world, "Joint bodies must exist in same World");
2024-03-11 21:38:00 +00:00
BallJoint* joint = lovrCalloc(sizeof(BallJoint));
joint->ref = 1;
joint->type = JOINT_BALL;
joint->id = dJointCreateBall(a->world->id, 0);
dJointSetData(joint->id, joint);
dJointAttach(joint->id, a->body, b->body);
lovrBallJointSetAnchor(joint, anchor);
2018-08-22 16:29:34 +00:00
lovrRetain(joint);
2017-06-10 22:13:19 +00:00
return joint;
}
void lovrBallJointGetAnchors(BallJoint* joint, float anchor1[3], float anchor2[3]) {
2022-03-25 19:14:49 +00:00
dReal anchor[4];
2017-06-10 22:13:19 +00:00
dJointGetBallAnchor(joint->id, anchor);
anchor1[0] = anchor[0];
anchor1[1] = anchor[1];
anchor1[2] = anchor[2];
2017-06-10 22:13:19 +00:00
dJointGetBallAnchor2(joint->id, anchor);
anchor2[0] = anchor[0];
anchor2[1] = anchor[1];
anchor2[2] = anchor[2];
2017-06-10 22:13:19 +00:00
}
void lovrBallJointSetAnchor(BallJoint* joint, float anchor[3]) {
dJointSetBallAnchor(joint->id, anchor[0], anchor[1], anchor[2]);
2017-06-10 22:13:19 +00:00
}
Get/set World and Joint simulation parameters Tightness parameter is amount of force is exerted on collider to resolve collisions and enforce joint operation. Low values make joints loose, high values make it tight and can cause collider to overshot the joint target. With tightness set to 0 the joint loses its function. Going above 1 puts even more energy into joint oscillations. Tightness parameter is called ERP in ODE manual. The responseTime affects the time constant of physics simulation, both for collisions and for joint inertia. Low responseTime values make simulation tight and fast, higher values make it sluggish. For collisions it affects how fast penetration is resolved, with higher values resulting in spongy objects with more surface penetration and slower collision resolving. For joints the responseTime is similar to inertia, with higher responseTime values resulting in slow oscillations. The oscillation frequency is also affected by collider mass, so responseTime can be used to tweak the joint to get desired frequency with specific collider mass. Values higher than 1 are often desirable, especially for very light objects. Unlike tightness, responseTime is tweaked in orders of magnitude with useful values (depending on mass) being between 10^-8 and 10^8. Both parameters can be applied to World for simulation-wide usage, or specified per-joint in case of distance and ball joints. Other joints don't allow customizing these parameters, and will use World settings instead..
2020-05-29 14:38:32 +00:00
float lovrBallJointGetResponseTime(Joint* joint) {
return dJointGetBallParam(joint->id, dParamCFM);
}
void lovrBallJointSetResponseTime(Joint* joint, float responseTime) {
dJointSetBallParam(joint->id, dParamCFM, responseTime);
}
float lovrBallJointGetTightness(Joint* joint) {
return dJointGetBallParam(joint->id, dParamERP);
}
void lovrBallJointSetTightness(Joint* joint, float tightness) {
dJointSetBallParam(joint->id, dParamERP, tightness);
}
DistanceJoint* lovrDistanceJointCreate(Collider* a, Collider* b, float anchor1[3], float anchor2[3]) {
2024-03-10 07:22:32 +00:00
lovrCheck(a->world == b->world, "Joint bodies must exist in same World");
2024-03-11 21:38:00 +00:00
DistanceJoint* joint = lovrCalloc(sizeof(DistanceJoint));
joint->ref = 1;
2017-06-10 22:13:19 +00:00
joint->type = JOINT_DISTANCE;
joint->id = dJointCreateDBall(a->world->id, 0);
dJointSetData(joint->id, joint);
dJointAttach(joint->id, a->body, b->body);
lovrDistanceJointSetAnchors(joint, anchor1, anchor2);
2018-08-22 16:29:34 +00:00
lovrRetain(joint);
return joint;
}
void lovrDistanceJointGetAnchors(DistanceJoint* joint, float anchor1[3], float anchor2[3]) {
2022-03-25 19:14:49 +00:00
dReal anchor[4];
2017-06-10 22:13:19 +00:00
dJointGetDBallAnchor1(joint->id, anchor);
anchor1[0] = anchor[0];
anchor1[1] = anchor[1];
anchor1[2] = anchor[2];
2017-06-10 22:13:19 +00:00
dJointGetDBallAnchor2(joint->id, anchor);
anchor2[0] = anchor[0];
anchor2[1] = anchor[1];
anchor2[2] = anchor[2];
}
void lovrDistanceJointSetAnchors(DistanceJoint* joint, float anchor1[3], float anchor2[3]) {
dJointSetDBallAnchor1(joint->id, anchor1[0], anchor1[1], anchor1[2]);
dJointSetDBallAnchor2(joint->id, anchor2[0], anchor2[1], anchor2[2]);
2017-06-10 22:13:19 +00:00
}
float lovrDistanceJointGetDistance(DistanceJoint* joint) {
return dJointGetDBallDistance(joint->id);
}
void lovrDistanceJointSetDistance(DistanceJoint* joint, float distance) {
dJointSetDBallDistance(joint->id, distance);
}
2017-05-25 06:51:27 +00:00
Get/set World and Joint simulation parameters Tightness parameter is amount of force is exerted on collider to resolve collisions and enforce joint operation. Low values make joints loose, high values make it tight and can cause collider to overshot the joint target. With tightness set to 0 the joint loses its function. Going above 1 puts even more energy into joint oscillations. Tightness parameter is called ERP in ODE manual. The responseTime affects the time constant of physics simulation, both for collisions and for joint inertia. Low responseTime values make simulation tight and fast, higher values make it sluggish. For collisions it affects how fast penetration is resolved, with higher values resulting in spongy objects with more surface penetration and slower collision resolving. For joints the responseTime is similar to inertia, with higher responseTime values resulting in slow oscillations. The oscillation frequency is also affected by collider mass, so responseTime can be used to tweak the joint to get desired frequency with specific collider mass. Values higher than 1 are often desirable, especially for very light objects. Unlike tightness, responseTime is tweaked in orders of magnitude with useful values (depending on mass) being between 10^-8 and 10^8. Both parameters can be applied to World for simulation-wide usage, or specified per-joint in case of distance and ball joints. Other joints don't allow customizing these parameters, and will use World settings instead..
2020-05-29 14:38:32 +00:00
float lovrDistanceJointGetResponseTime(Joint* joint) {
return dJointGetDBallParam(joint->id, dParamCFM);
}
void lovrDistanceJointSetResponseTime(Joint* joint, float responseTime) {
dJointSetDBallParam(joint->id, dParamCFM, responseTime);
}
float lovrDistanceJointGetTightness(Joint* joint) {
return dJointGetDBallParam(joint->id, dParamERP);
}
void lovrDistanceJointSetTightness(Joint* joint, float tightness) {
dJointSetDBallParam(joint->id, dParamERP, tightness);
}
HingeJoint* lovrHingeJointCreate(Collider* a, Collider* b, float anchor[3], float axis[3]) {
2024-03-10 07:22:32 +00:00
lovrCheck(a->world == b->world, "Joint bodies must exist in same World");
2024-03-11 21:38:00 +00:00
HingeJoint* joint = lovrCalloc(sizeof(HingeJoint));
joint->ref = 1;
2017-05-25 06:51:27 +00:00
joint->type = JOINT_HINGE;
joint->id = dJointCreateHinge(a->world->id, 0);
dJointSetData(joint->id, joint);
dJointAttach(joint->id, a->body, b->body);
lovrHingeJointSetAnchor(joint, anchor);
lovrHingeJointSetAxis(joint, axis);
2018-08-22 16:29:34 +00:00
lovrRetain(joint);
2017-05-25 06:51:27 +00:00
return joint;
}
void lovrHingeJointGetAnchors(HingeJoint* joint, float anchor1[3], float anchor2[3]) {
2022-03-25 19:14:49 +00:00
dReal anchor[4];
2017-06-10 22:13:19 +00:00
dJointGetHingeAnchor(joint->id, anchor);
anchor1[0] = anchor[0];
anchor1[1] = anchor[1];
anchor1[2] = anchor[2];
2017-06-10 22:13:19 +00:00
dJointGetHingeAnchor2(joint->id, anchor);
anchor2[0] = anchor[0];
anchor2[1] = anchor[1];
anchor2[2] = anchor[2];
2017-05-25 06:51:27 +00:00
}
void lovrHingeJointSetAnchor(HingeJoint* joint, float anchor[3]) {
dJointSetHingeAnchor(joint->id, anchor[0], anchor[1], anchor[2]);
2017-05-25 06:51:27 +00:00
}
void lovrHingeJointGetAxis(HingeJoint* joint, float axis[3]) {
dReal daxis[4];
dJointGetHingeAxis(joint->id, daxis);
axis[0] = daxis[0];
axis[1] = daxis[1];
axis[2] = daxis[2];
2017-05-25 06:51:27 +00:00
}
void lovrHingeJointSetAxis(HingeJoint* joint, float axis[3]) {
dJointSetHingeAxis(joint->id, axis[0], axis[1], axis[2]);
2017-05-25 06:51:27 +00:00
}
2017-06-10 22:13:19 +00:00
float lovrHingeJointGetAngle(HingeJoint* joint) {
return dJointGetHingeAngle(joint->id);
2017-05-25 06:51:27 +00:00
}
2017-05-25 07:48:02 +00:00
2017-06-10 22:13:19 +00:00
float lovrHingeJointGetLowerLimit(HingeJoint* joint) {
return dJointGetHingeParam(joint->id, dParamLoStop);
2017-05-25 22:20:55 +00:00
}
2017-06-10 22:13:19 +00:00
void lovrHingeJointSetLowerLimit(HingeJoint* joint, float limit) {
dJointSetHingeParam(joint->id, dParamLoStop, limit);
2017-05-25 22:20:55 +00:00
}
2017-06-10 22:13:19 +00:00
float lovrHingeJointGetUpperLimit(HingeJoint* joint) {
return dJointGetHingeParam(joint->id, dParamHiStop);
2017-05-25 22:20:55 +00:00
}
2017-06-10 22:13:19 +00:00
void lovrHingeJointSetUpperLimit(HingeJoint* joint, float limit) {
dJointSetHingeParam(joint->id, dParamHiStop, limit);
2017-05-25 22:20:55 +00:00
}
SliderJoint* lovrSliderJointCreate(Collider* a, Collider* b, float axis[3]) {
2024-03-10 07:22:32 +00:00
lovrCheck(a->world == b->world, "Joint bodies must exist in the same world");
2024-03-11 21:38:00 +00:00
SliderJoint* joint = lovrCalloc(sizeof(SliderJoint));
joint->ref = 1;
2017-05-25 07:48:02 +00:00
joint->type = JOINT_SLIDER;
joint->id = dJointCreateSlider(a->world->id, 0);
dJointSetData(joint->id, joint);
dJointAttach(joint->id, a->body, b->body);
lovrSliderJointSetAxis(joint, axis);
2018-08-22 16:29:34 +00:00
lovrRetain(joint);
2017-05-25 07:48:02 +00:00
return joint;
}
void lovrSliderJointGetAxis(SliderJoint* joint, float axis[3]) {
dReal daxis[4];
2017-06-10 22:13:19 +00:00
dJointGetSliderAxis(joint->id, axis);
axis[0] = daxis[0];
axis[1] = daxis[1];
axis[2] = daxis[2];
2017-05-25 07:48:02 +00:00
}
void lovrSliderJointSetAxis(SliderJoint* joint, float axis[3]) {
dJointSetSliderAxis(joint->id, axis[0], axis[1], axis[2]);
2017-05-25 07:48:02 +00:00
}
2017-06-10 22:13:19 +00:00
float lovrSliderJointGetPosition(SliderJoint* joint) {
return dJointGetSliderPosition(joint->id);
2017-05-25 07:48:02 +00:00
}
2017-05-25 22:20:55 +00:00
2017-06-10 22:13:19 +00:00
float lovrSliderJointGetLowerLimit(SliderJoint* joint) {
return dJointGetSliderParam(joint->id, dParamLoStop);
2017-05-25 22:20:55 +00:00
}
2017-06-10 22:13:19 +00:00
void lovrSliderJointSetLowerLimit(SliderJoint* joint, float limit) {
dJointSetSliderParam(joint->id, dParamLoStop, limit);
2017-05-25 22:20:55 +00:00
}
2017-06-10 22:13:19 +00:00
float lovrSliderJointGetUpperLimit(SliderJoint* joint) {
return dJointGetSliderParam(joint->id, dParamHiStop);
2017-05-25 22:20:55 +00:00
}
2017-06-10 22:13:19 +00:00
void lovrSliderJointSetUpperLimit(SliderJoint* joint, float limit) {
dJointSetSliderParam(joint->id, dParamHiStop, limit);
2017-05-25 22:20:55 +00:00
}