Backlog/lib/zphysics/libs/JoltC/JoltPhysicsC_Tests.c

1146 lines
41 KiB
C

#include "JoltPhysicsC.h"
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
// Object layers
#define NUM_OBJ_LAYERS 2
#define OBJ_LAYER_NON_MOVING 0
#define OBJ_LAYER_MOVING 1
// Broad phase layers
#define NUM_BP_LAYERS 2
#define BP_LAYER_NON_MOVING 0
#define BP_LAYER_MOVING 1
//--------------------------------------------------------------------------------------------------
// BPLayerInterface
//--------------------------------------------------------------------------------------------------
typedef struct BPLayerInterfaceImpl
{
const JPC_BroadPhaseLayerInterfaceVTable *vtable; // VTable has to be the first field in the struct.
JPC_BroadPhaseLayer object_to_broad_phase[NUM_OBJ_LAYERS];
} BPLayerInterfaceImpl;
static uint32_t
BPLayerInterface_GetNumBroadPhaseLayers(const void *in_self)
{
#ifdef PRINT_OUTPUT
fprintf(stderr, "BPLayerInterface_GetNumBroadPhaseLayers()\n");
#endif
return NUM_BP_LAYERS;
}
#ifdef _MSC_VER
static const JPC_BroadPhaseLayer *
BPLayerInterface_GetBroadPhaseLayer(const void *in_self, JPC_BroadPhaseLayer *out_layer, JPC_ObjectLayer in_layer)
#else
static JPC_BroadPhaseLayer
BPLayerInterface_GetBroadPhaseLayer(const void *in_self, JPC_ObjectLayer in_layer)
#endif
{
#ifdef PRINT_OUTPUT
fprintf(stderr, "BPLayerInterface_GetBroadPhaseLayer()\n");
#endif
assert(in_layer < NUM_BP_LAYERS);
const BPLayerInterfaceImpl *self = (BPLayerInterfaceImpl *)in_self;
#ifdef _MSC_VER
*out_layer = self->object_to_broad_phase[in_layer];
return out_layer;
#else
return self->object_to_broad_phase[in_layer];
#endif
}
static BPLayerInterfaceImpl
BPLayerInterface_Init(void)
{
static const JPC_BroadPhaseLayerInterfaceVTable vtable =
{
.GetNumBroadPhaseLayers = BPLayerInterface_GetNumBroadPhaseLayers,
.GetBroadPhaseLayer = BPLayerInterface_GetBroadPhaseLayer,
};
BPLayerInterfaceImpl impl =
{
.vtable = &vtable,
};
impl.object_to_broad_phase[OBJ_LAYER_NON_MOVING] = BP_LAYER_NON_MOVING;
impl.object_to_broad_phase[OBJ_LAYER_MOVING] = BP_LAYER_MOVING;
return impl;
}
//--------------------------------------------------------------------------------------------------
// MyContactListener
//--------------------------------------------------------------------------------------------------
typedef struct MyContactListener
{
const JPC_ContactListenerVTable *vtable; // VTable has to be the first field in the struct.
} MyContactListener;
static JPC_ValidateResult
MyContactListener_OnContactValidate(void *in_self,
const JPC_Body *in_body1,
const JPC_Body *in_body2,
const JPC_Real in_base_offset[3],
const JPC_CollideShapeResult *in_collision_result)
{
const JPC_BodyID body1_id = JPC_Body_GetID(in_body1);
const JPC_BodyID body2_id = JPC_Body_GetID(in_body2);
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tOnContactValidate(): First BodyID is (%d, %d), second BodyID is (%d, %d)\n",
JPC_BodyID_GetSequenceNumber(body1_id), JPC_BodyID_GetIndex(body1_id),
JPC_BodyID_GetSequenceNumber(body2_id), JPC_BodyID_GetIndex(body2_id));
fprintf(stderr,
"\tOnContactValidate(): in_base_offset (%f, %f, %f)\n",
in_base_offset[0], in_base_offset[1], in_base_offset[2]);
fprintf(stderr, "\tOnContactValidate(): penetration_depth (%f)\n", in_collision_result->penetration_depth);
fprintf(stderr, "\tOnContactValidate(): shape1_sub_shape_id (%d)\n", in_collision_result->shape1_sub_shape_id.id);
fprintf(stderr, "\tOnContactValidate(): shape2_sub_shape_id (%d)\n", in_collision_result->shape2_sub_shape_id.id);
fprintf(stderr, "\tOnContactValidate(): body2_id (%d)\n", in_collision_result->body2_id.id);
#endif
return JPC_VALIDATE_RESULT_ACCEPT_ALL_CONTACTS;
}
static void
MyContactListener_OnContactAdded(void *in_self,
const JPC_Body *in_body1,
const JPC_Body *in_body2,
const JPC_ContactManifold *in_manifold,
JPC_ContactSettings *io_settings)
{
const JPC_BodyID body1_id = JPC_Body_GetID(in_body1);
const JPC_BodyID body2_id = JPC_Body_GetID(in_body2);
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tOnContactAdded(): First BodyID is (%d, %d), second BodyID is (%d, %d)\n",
JPC_BodyID_GetSequenceNumber(body1_id), JPC_BodyID_GetIndex(body1_id),
JPC_BodyID_GetSequenceNumber(body2_id), JPC_BodyID_GetIndex(body2_id));
#endif
}
static void
MyContactListener_OnContactPersisted(void *in_self,
const JPC_Body *in_body1,
const JPC_Body *in_body2,
const JPC_ContactManifold *in_manifold,
JPC_ContactSettings *io_settings)
{
const JPC_BodyID body1_id = JPC_Body_GetID(in_body1);
const JPC_BodyID body2_id = JPC_Body_GetID(in_body2);
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tOnContactPersisted(): First BodyID is (%d, %d), second BodyID is (%d, %d)\n",
JPC_BodyID_GetSequenceNumber(body1_id), JPC_BodyID_GetIndex(body1_id),
JPC_BodyID_GetSequenceNumber(body2_id), JPC_BodyID_GetIndex(body2_id));
#endif
}
static void
MyContactListener_OnContactRemoved(void *in_self, const JPC_SubShapeIDPair *in_sub_shape_pair)
{
const JPC_BodyID body1_id = in_sub_shape_pair->first.body_id;
const JPC_BodyID body2_id = in_sub_shape_pair->second.body_id;
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tOnContactRemoved(): First BodyID is (%d, %d), second BodyID is (%d, %d)\n",
JPC_BodyID_GetSequenceNumber(body1_id), JPC_BodyID_GetIndex(body1_id),
JPC_BodyID_GetSequenceNumber(body2_id), JPC_BodyID_GetIndex(body2_id));
#endif
}
static MyContactListener
MyContactListener_Init(void)
{
static const JPC_ContactListenerVTable vtable =
{
.OnContactValidate = MyContactListener_OnContactValidate,
.OnContactAdded = MyContactListener_OnContactAdded,
.OnContactPersisted = MyContactListener_OnContactPersisted,
.OnContactRemoved = MyContactListener_OnContactRemoved,
};
MyContactListener impl =
{
.vtable = &vtable,
};
return impl;
}
//--------------------------------------------------------------------------------------------------
// MyDebugRenderer
//--------------------------------------------------------------------------------------------------
#if JPC_DEBUG_RENDERER == 1
typedef struct MyRenderPrimitive
{
// Actual render data goes here
int foobar;
} MyRenderPrimitive;
typedef struct MyDebugRenderer
{
const JPC_DebugRendererVTable *vtable; // VTable has to be the first field in the struct.
struct MyRenderPrimitive prims[32];
int prim_head;
} MyDebugRenderer;
static void
MyDebugRenderer_DrawLine(void *in_self, JPC_Real in_from[3], JPC_Real in_to[3], JPC_Color in_color)
{
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tDebugRenderer: DrawLine called.\n");
#endif
}
static void
MyDebugRenderer_DrawTriangle(void *in_self,
JPC_Real in_v1[3],
JPC_Real in_v2[3],
JPC_Real in_v3[3],
JPC_Color in_color,
JPC_CastShadow in_cast_shadow)
{
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tDebugRenderer: DrawTriangle called.\n");
#endif
}
static JPC_DebugRenderer_TriangleBatch *
MyDebugRenderer_CreateTriangleBatch(void *in_self,
const JPC_DebugRenderer_Triangle *in_triangles,
uint32_t in_triangle_count)
{
struct MyDebugRenderer *self = (struct MyDebugRenderer*) in_self;
struct MyRenderPrimitive *prim = &self->prims[self->prim_head++];
JPC_DebugRenderer_TriangleBatch *batch = JPC_DebugRenderer_TriangleBatch_Create(
(JPC_DebugRenderer_Primitive *)prim
);
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tDebugRenderer: CreateTriangleBatch called. Created primitive %llx\n", (uint64_t)prim);
#endif
return batch;
}
static JPC_DebugRenderer_TriangleBatch *
MyDebugRenderer_CreateTriangleBatchIndexed(void *in_self,
const JPC_DebugRenderer_Vertex *in_vertices,
uint32_t in_vertex_count,
const uint32_t *in_indices,
uint32_t in_index_count)
{
struct MyDebugRenderer *self = (struct MyDebugRenderer*) in_self;
struct MyRenderPrimitive *prim = &self->prims[self->prim_head++];
JPC_DebugRenderer_TriangleBatch *batch = JPC_DebugRenderer_TriangleBatch_Create(
(JPC_DebugRenderer_Primitive *)prim
);
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tDebugRenderer: CreateTriangleBatchIndexed called. Created primitive %llx\n", (uint64_t)prim);
#endif
return batch;
}
static void
MyDebugRenderer_DestroyTriangleBatch(void *in_self, void *in_batch)
{
struct MyDebugRenderer *self = (struct MyDebugRenderer*) in_self;
struct MyRenderPrimitive *prim = (MyRenderPrimitive*)in_batch;
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tDebugRenderer: DestroyTriangleBatch called. Destroyed primitive %llx\n", (uint64_t)prim);
#endif
}
static void
MyDebugRenderer_DrawGeometry(void *in_self,
const JPC_RMatrix *inMatrix,
const JPC_AABox *inWorldSpaceBounds,
float inLODScaleSq,
JPC_Color inColor,
const JPC_DebugRenderer_Geometry *in_geometry,
JPC_CullMode in_cull_mode,
JPC_CastShadow in_cast_shadow,
JPC_DrawMode in_draw_mode)
{
if (in_geometry->num_LODs == 0) return;
JPC_DebugRenderer_TriangleBatch *batch = in_geometry->LODs[0].batch;
const JPC_DebugRenderer_Primitive *prim = JPC_DebugRenderer_TriangleBatch_GetPrimitive(batch);
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tDebugRenderer: DrawGeometry called for %llx\n", (uint64_t)prim);
#endif
}
static void
MyDebugRenderer_DrawText3D(void *in_self,
JPC_Real in_position[3],
const char *in_string,
JPC_Color in_color,
float in_height)
{
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tDebugRenderer: DrawText3D called.\n");
#endif
}
static MyDebugRenderer
MyDebugRenderer_Init(void)
{
static const JPC_DebugRendererVTable vtable =
{
.DrawLine = MyDebugRenderer_DrawLine,
.DrawTriangle = MyDebugRenderer_DrawTriangle,
.CreateTriangleBatch = MyDebugRenderer_CreateTriangleBatch,
.CreateTriangleBatchIndexed = MyDebugRenderer_CreateTriangleBatchIndexed,
.DestroyTriangleBatch = MyDebugRenderer_DestroyTriangleBatch,
.DrawGeometry = MyDebugRenderer_DrawGeometry,
.DrawText3D = MyDebugRenderer_DrawText3D,
};
MyDebugRenderer impl =
{
.vtable = &vtable,
.prim_head = 0
};
return impl;
}
/// This function can be passed to JPC_PhysicsSystem.DrawBodies as the filter, deciding which bodies draw.
static bool BodyShouldDraw(const JPC_Body *) {
return true;
}
#endif //JPC_DEBUG_RENDERER
//--------------------------------------------------------------------------------------------------
// MyActivationListener
//--------------------------------------------------------------------------------------------------
typedef struct MyActivationListener
{
const JPC_BodyActivationListenerVTable *vtable; // VTable has to be the first field in the struct.
} MyActivationListener;
static void
MyActivationListener_OnBodyActivated(void *in_self, const JPC_BodyID *in_body_id, uint64_t in_user_data)
{
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tOnBodyActivated(): BodyID is (%d, %d)\n",
JPC_BodyID_GetSequenceNumber(*in_body_id),
JPC_BodyID_GetIndex(*in_body_id));
#endif
}
static void
MyActivationListener_OnBodyDeactivated(void *in_self, const JPC_BodyID *in_body_id, uint64_t in_user_data)
{
#ifdef PRINT_OUTPUT
fprintf(stderr, "\tOnBodyDeactivated(): BodyID is (%d, %d)\n",
JPC_BodyID_GetSequenceNumber(*in_body_id),
JPC_BodyID_GetIndex(*in_body_id));
#endif
}
static MyActivationListener
MyActivationListener_Init(void)
{
static const JPC_BodyActivationListenerVTable vtable =
{
.OnBodyActivated = MyActivationListener_OnBodyActivated,
.OnBodyDeactivated = MyActivationListener_OnBodyDeactivated,
};
MyActivationListener impl =
{
.vtable = &vtable,
};
return impl;
}
//--------------------------------------------------------------------------------------------------
// MyObjectFilter
//--------------------------------------------------------------------------------------------------
typedef struct MyObjectFilter
{
const JPC_ObjectLayerPairFilterVTable *vtable; // VTable has to be the first field in the struct.
} MyObjectFilter;
static bool
MyObjectFilter_ShouldCollide(const void *in_self, JPC_ObjectLayer in_object1, JPC_ObjectLayer in_object2)
{
switch (in_object1)
{
case OBJ_LAYER_NON_MOVING:
return in_object2 == OBJ_LAYER_MOVING;
case OBJ_LAYER_MOVING:
return true;
default:
assert(false);
return false;
}
}
static MyObjectFilter
MyObjectFilter_Init(void)
{
static const JPC_ObjectLayerPairFilterVTable vtable =
{
.ShouldCollide = MyObjectFilter_ShouldCollide,
};
MyObjectFilter impl =
{
.vtable = &vtable,
};
return impl;
}
//--------------------------------------------------------------------------------------------------
// MyBroadPhaseFilter
//--------------------------------------------------------------------------------------------------
typedef struct MyBroadPhaseFilter
{
const JPC_ObjectVsBroadPhaseLayerFilterVTable *vtable; // VTable has to be the first field in the struct.
} MyBroadPhaseFilter;
static bool
MyBroadPhaseFilter_ShouldCollide(const void *in_self, JPC_ObjectLayer in_layer1, JPC_BroadPhaseLayer in_layer2)
{
switch (in_layer1)
{
case OBJ_LAYER_NON_MOVING:
return in_layer2 == BP_LAYER_MOVING;
case OBJ_LAYER_MOVING:
return true;
default:
assert(false);
return false;
}
}
static MyBroadPhaseFilter
MyBroadPhaseFilter_Init(void)
{
static const JPC_ObjectVsBroadPhaseLayerFilterVTable vtable =
{
.ShouldCollide = MyBroadPhaseFilter_ShouldCollide,
};
MyBroadPhaseFilter impl =
{
.vtable = &vtable,
};
return impl;
}
//--------------------------------------------------------------------------------------------------
// Basic1
//--------------------------------------------------------------------------------------------------
uint32_t
JoltCTest_Basic1(void)
{
JPC_RegisterDefaultAllocator();
JPC_CreateFactory();
JPC_RegisterTypes();
const uint32_t max_bodies = 1024;
const uint32_t num_body_mutexes = 0;
const uint32_t max_body_pairs = 1024;
const uint32_t max_contact_constraints = 1024;
BPLayerInterfaceImpl *broad_phase_layer_interface = malloc(sizeof(BPLayerInterfaceImpl));
*broad_phase_layer_interface = BPLayerInterface_Init();
MyBroadPhaseFilter *broad_phase_filter = malloc(sizeof(MyBroadPhaseFilter));
*broad_phase_filter = MyBroadPhaseFilter_Init();
MyObjectFilter *object_filter = malloc(sizeof(MyObjectFilter));
*object_filter = MyObjectFilter_Init();
JPC_PhysicsSystem *physics_system = JPC_PhysicsSystem_Create(
max_bodies,
num_body_mutexes,
max_body_pairs,
max_contact_constraints,
broad_phase_layer_interface,
broad_phase_filter,
object_filter);
JPC_BoxShapeSettings *box_settings = JPC_BoxShapeSettings_Create((float[]){ 10.0, 20.0, 30.0 });
if (JPC_ShapeSettings_GetRefCount((JPC_ShapeSettings *)box_settings) != 1) return 0;
JPC_ShapeSettings_AddRef((JPC_ShapeSettings *)box_settings);
JPC_ShapeSettings_Release((JPC_ShapeSettings *)box_settings);
if (JPC_ShapeSettings_GetRefCount((JPC_ShapeSettings *)box_settings) != 1) return 0;
JPC_BoxShapeSettings_SetConvexRadius(box_settings, 1.0);
if (JPC_BoxShapeSettings_GetConvexRadius(box_settings) != 1.0) return 0;
JPC_ConvexShapeSettings_SetDensity((JPC_ConvexShapeSettings *)box_settings, 100.0);
if (JPC_ConvexShapeSettings_GetDensity((JPC_ConvexShapeSettings *)box_settings) != 100.0) return 0;
JPC_Shape *box_shape = JPC_ShapeSettings_CreateShape((JPC_ShapeSettings *)box_settings);
if (box_shape == NULL) return 0;
if (JPC_Shape_GetType(box_shape) != JPC_SHAPE_TYPE_CONVEX) return 0;
if (JPC_Shape_GetSubType(box_shape) != JPC_SHAPE_SUB_TYPE_BOX) return 0;
if (JPC_Shape_GetRefCount(box_shape) != 2) return 0;
JPC_ShapeSettings_CreateShape((JPC_ShapeSettings *)box_settings);
if (JPC_Shape_GetRefCount(box_shape) != 3) return 0;
JPC_Shape_Release(box_shape);
if (JPC_Shape_GetRefCount(box_shape) != 2) return 0;
if (JPC_ShapeSettings_GetRefCount((JPC_ShapeSettings *)box_settings) != 1) return 0;
JPC_ShapeSettings_Release((JPC_ShapeSettings *)box_settings);
box_settings = NULL;
if (JPC_Shape_GetRefCount(box_shape) != 1) return 0;
JPC_Shape_Release(box_shape);
box_shape = NULL;
JPC_PhysicsSystem_Destroy(physics_system);
physics_system = NULL;
JPC_DestroyFactory();
return 1;
}
//--------------------------------------------------------------------------------------------------
// Basic2
//--------------------------------------------------------------------------------------------------
uint32_t
JoltCTest_Basic2(void)
{
JPC_RegisterDefaultAllocator();
JPC_CreateFactory();
JPC_RegisterTypes();
JPC_TempAllocator *temp_allocator = JPC_TempAllocator_Create(10 * 1024 * 1024);
JPC_JobSystem *job_system = JPC_JobSystem_Create(JPC_MAX_PHYSICS_JOBS, JPC_MAX_PHYSICS_BARRIERS, -1);
const uint32_t max_bodies = 1024;
const uint32_t num_body_mutexes = 0;
const uint32_t max_body_pairs = 1024;
const uint32_t max_contact_constraints = 1024;
BPLayerInterfaceImpl broad_phase_layer_interface = BPLayerInterface_Init();
MyBroadPhaseFilter broad_phase_filter = MyBroadPhaseFilter_Init();
MyObjectFilter object_filter = MyObjectFilter_Init();
JPC_PhysicsSystem *physics_system = JPC_PhysicsSystem_Create(
max_bodies,
num_body_mutexes,
max_body_pairs,
max_contact_constraints,
&broad_phase_layer_interface,
&broad_phase_filter,
&object_filter);
MyActivationListener body_activation_listener = MyActivationListener_Init();
JPC_PhysicsSystem_SetBodyActivationListener(physics_system, &body_activation_listener);
MyContactListener contact_listener = MyContactListener_Init();
JPC_PhysicsSystem_SetContactListener(physics_system, &contact_listener);
JPC_PhysicsSystem_SetContactListener(physics_system, NULL);
JPC_PhysicsSystem_SetContactListener(physics_system, &contact_listener);
JPC_BoxShapeSettings *floor_shape_settings = JPC_BoxShapeSettings_Create((float[]){ 100.0f, 1.0f, 100.0f });
if (JPC_ShapeSettings_GetRefCount((JPC_ShapeSettings *)floor_shape_settings) != 1) return 0;
JPC_Shape *floor_shape = JPC_ShapeSettings_CreateShape((JPC_ShapeSettings *)floor_shape_settings);
if (floor_shape == NULL) return 0;
if (JPC_ShapeSettings_GetRefCount((JPC_ShapeSettings *)floor_shape_settings) != 1) return 0;
if (JPC_Shape_GetRefCount(floor_shape) != 2) return 0;
JPC_BodyCreationSettings floor_settings;
JPC_BodyCreationSettings_Set(
&floor_settings,
floor_shape,
(JPC_Real[]){ 0.0f, -1.0f, 0.0f },
(float[]){ 0.0f, 0.0f, 0.0f, 1.0f },
JPC_MOTION_TYPE_STATIC,
OBJ_LAYER_NON_MOVING);
JPC_BodyInterface *body_interface = JPC_PhysicsSystem_GetBodyInterface(physics_system);
JPC_Body *floor = JPC_BodyInterface_CreateBody(body_interface, &floor_settings);
if (floor == NULL) return 0;
const JPC_BodyID floor_id = JPC_Body_GetID(floor);
if (((floor_id.id & JPC_BODY_ID_SEQUENCE_BITS) >> JPC_BODY_ID_SEQUENCE_SHIFT) != 1) return 0;
if ((floor_id.id & JPC_BODY_ID_INDEX_BITS) != 0) return 0;
if (JPC_Body_IsStatic(floor) == false) return 0;
if (JPC_Body_IsDynamic(floor) == true) return 0;
if (JPC_Shape_GetRefCount(floor_shape) != 3) return 0;
JPC_Body *floor1 = JPC_BodyInterface_CreateBody(body_interface, &floor_settings);
if (floor1 == NULL) return 0;
const JPC_BodyID floor1_id = JPC_Body_GetID(floor1);
if (((floor1_id.id & JPC_BODY_ID_SEQUENCE_BITS) >> JPC_BODY_ID_SEQUENCE_SHIFT) != 1) return 0;
if ((floor1_id.id & JPC_BODY_ID_INDEX_BITS) != 1) return 0;
if (JPC_BodyInterface_IsAdded(body_interface, floor_id) != false) return 0;
if (JPC_BodyInterface_IsAdded(body_interface, floor1_id) != false) return 0;
JPC_BodyInterface_AddBody(body_interface, floor_id, JPC_ACTIVATION_ACTIVATE);
if (JPC_BodyInterface_IsAdded(body_interface, floor_id) != true) return 0;
JPC_PhysicsSystem_OptimizeBroadPhase(physics_system);
JPC_PhysicsUpdateError update_err = JPC_PhysicsSystem_Update(physics_system, 1.0f / 60.0f, 1, temp_allocator, job_system);
if (update_err != JPC_PHYSICS_UPDATE_NO_ERROR) return 0;
JPC_BodyInterface_RemoveBody(body_interface, floor_id);
if (JPC_BodyInterface_IsAdded(body_interface, floor_id) != false) return 0;
if (JPC_Shape_GetRefCount(floor_shape) != 4) return 0;
if (JPC_ShapeSettings_GetRefCount((JPC_ShapeSettings *)floor_shape_settings) != 1) return 0;
JPC_ShapeSettings_Release((JPC_ShapeSettings *)floor_shape_settings);
if (JPC_Shape_GetRefCount(floor_shape) != 3) return 0;
JPC_BodyInterface_DestroyBody(body_interface, floor_id);
if (JPC_Shape_GetRefCount(floor_shape) != 2) return 0;
JPC_BodyInterface_DestroyBody(body_interface, floor1_id);
if (JPC_Shape_GetRefCount(floor_shape) != 1) return 0;
JPC_Shape_Release(floor_shape);
JPC_PhysicsSystem_Destroy(physics_system);
JPC_JobSystem_Destroy(job_system);
JPC_TempAllocator_Destroy(temp_allocator);
JPC_DestroyFactory();
return 1;
}
//--------------------------------------------------------------------------------------------------
// HelloWorld
//--------------------------------------------------------------------------------------------------
uint32_t
JoltCTest_HelloWorld(void)
{
JPC_RegisterDefaultAllocator();
JPC_CreateFactory();
JPC_RegisterTypes();
JPC_TempAllocator *temp_allocator = JPC_TempAllocator_Create(10 * 1024 * 1024);
JPC_JobSystem *job_system = JPC_JobSystem_Create(JPC_MAX_PHYSICS_JOBS, JPC_MAX_PHYSICS_BARRIERS, -1);
const uint32_t max_bodies = 1024;
const uint32_t num_body_mutexes = 0;
const uint32_t max_body_pairs = 1024;
const uint32_t max_contact_constraints = 1024;
BPLayerInterfaceImpl broad_phase_layer_interface = BPLayerInterface_Init();
MyBroadPhaseFilter broad_phase_filter = MyBroadPhaseFilter_Init();
MyObjectFilter object_filter = MyObjectFilter_Init();
JPC_PhysicsSystem *physics_system = JPC_PhysicsSystem_Create(
max_bodies,
num_body_mutexes,
max_body_pairs,
max_contact_constraints,
&broad_phase_layer_interface,
&broad_phase_filter,
&object_filter);
MyActivationListener body_activation_listener = MyActivationListener_Init();
JPC_PhysicsSystem_SetBodyActivationListener(physics_system, &body_activation_listener);
MyContactListener contact_listener = MyContactListener_Init();
JPC_PhysicsSystem_SetContactListener(physics_system, &contact_listener);
JPC_BodyInterface *body_interface = JPC_PhysicsSystem_GetBodyInterface(physics_system);
#if JPC_DEBUG_RENDERER == 1
MyDebugRenderer debug_renderer = MyDebugRenderer_Init();
enum JPC_DebugRendererResult result = JPC_CreateDebugRendererSingleton(&debug_renderer);
assert(result == JPC_DEBUGRENDERER_SUCCESS);
JPC_BodyManager_DrawSettings *body_draw_settings = JPC_BodyManager_DrawSettings_Create();
JPC_BodyDrawFilter *body_draw_filter = JPC_BodyDrawFilter_Create(&BodyShouldDraw);
#endif //JPC_DEBUG_RENDERER
//
// Static floor
//
JPC_BoxShapeSettings *floor_shape_settings = JPC_BoxShapeSettings_Create((float[]){ 100.0, 1.0, 100.0 });
JPC_Shape *floor_shape = JPC_ShapeSettings_CreateShape((JPC_ShapeSettings *)floor_shape_settings);
JPC_BodyCreationSettings floor_settings;
JPC_BodyCreationSettings_Set(
&floor_settings,
floor_shape,
(JPC_Real[]){ 0.0f, -1.0f, 0.0f },
(float[]){ 0.0f, 0.0f, 0.0f, 1.0f },
JPC_MOTION_TYPE_STATIC,
OBJ_LAYER_NON_MOVING);
JPC_Body *floor = JPC_BodyInterface_CreateBody(body_interface, &floor_settings);
const JPC_BodyID floor_id = JPC_Body_GetID(floor);
JPC_BodyInterface_AddBody(body_interface, floor_id, JPC_ACTIVATION_DONT_ACTIVATE);
//
// Falling sphere
//
JPC_SphereShapeSettings *sphere_shape_settings = JPC_SphereShapeSettings_Create(0.5f);
JPC_Shape *sphere_shape = JPC_ShapeSettings_CreateShape((JPC_ShapeSettings *)sphere_shape_settings);
JPC_BodyCreationSettings sphere_settings;
JPC_BodyCreationSettings_Set(
&sphere_settings,
sphere_shape,
(JPC_Real[]){ 0.0f, 2.0f, 0.0f },
(float[]){ 0.0f, 0.0f, 0.0f, 1.0f },
JPC_MOTION_TYPE_DYNAMIC,
OBJ_LAYER_MOVING);
const JPC_BodyID sphere_id = JPC_BodyInterface_CreateAndAddBody(
body_interface,
&sphere_settings,
JPC_ACTIVATION_ACTIVATE);
if (JPC_Body_IsStatic(floor) == false) return 0;
if (JPC_Body_IsDynamic(floor) == true) return 0;
JPC_BodyInterface_SetLinearVelocity(body_interface, sphere_id, (float[]){ 0.0f, -5.0f, 0.0f });
JPC_PhysicsSystem_OptimizeBroadPhase(physics_system);
// Test JPC_PhysicsSystem_GetBodyIDs()
{
JPC_BodyID body_ids[2];
uint32_t num_body_ids = 0;
JPC_PhysicsSystem_GetBodyIDs(physics_system, 2, &num_body_ids, &body_ids[0]);
if (num_body_ids != 2) return 0;
if (!JPC_ID_EQ(body_ids[0], floor_id)) return 0;
if (!JPC_ID_EQ(body_ids[1], sphere_id)) return 0;
}
// Test JPC_PhysicsSystem_GetActiveBodyIDs()
{
JPC_BodyID body_ids[2];
uint32_t num_body_ids = 0;
JPC_PhysicsSystem_GetActiveBodyIDs(physics_system, 2, &num_body_ids, &body_ids[0]);
if (num_body_ids != 1) return 0;
if (!JPC_ID_EQ(body_ids[0], sphere_id)) return 0;
}
#ifdef PRINT_OUTPUT
if (sizeof(JPC_Real) == 8)
fprintf(stderr, "Using double precision computation...\n");
#endif
uint32_t step = 0;
while (JPC_BodyInterface_IsActive(body_interface, sphere_id))
{
step += 1;
JPC_Real position[3];
JPC_BodyInterface_GetCenterOfMassPosition(body_interface, sphere_id, &position[0]);
float velocity[3];
JPC_BodyInterface_GetLinearVelocity(body_interface, sphere_id, &velocity[0]);
const float delta_time = 1.0f / 60.0f;
const int collision_steps = 1;
JPC_PhysicsUpdateError update_err = JPC_PhysicsSystem_Update(
physics_system,
delta_time,
collision_steps,
temp_allocator,
job_system);
if (update_err != JPC_PHYSICS_UPDATE_NO_ERROR) {
return 0;
}
#ifdef PRINT_OUTPUT
fprintf(stderr, "Step %d\n\tPosition = (%f, %f, %f), Velocity(%f, %f, %f)\n",
step,
position[0], position[1], position[2],
velocity[0], velocity[1], velocity[2]);
#endif
// Safe, lock protected way of accessing all bodies (use when you interact with Jolt from multiple threads).
{
JPC_BodyID body_ids[16]; // You can use JPC_PhysicsSystem_GetMaxBodies() to pre-allocate storage
uint32_t num_body_ids = 0;
JPC_PhysicsSystem_GetBodyIDs(physics_system, 16, &num_body_ids, &body_ids[0]);
const JPC_BodyLockInterface *lock_iface = JPC_PhysicsSystem_GetBodyLockInterface(physics_system);
//const JPC_BodyLockInterface *lock_iface = JPC_PhysicsSystem_GetBodyLockInterfaceNoLock(physics_system);
for (uint32_t i = 0; i < num_body_ids; ++i)
{
JPC_BodyLockRead lock;
JPC_BodyLockInterface_LockRead(lock_iface, body_ids[i], &lock);
//JPC_BodyLockWrite lock;
//JPC_BodyLockInterface_LockWrite(lock_iface, body_ids[i], &lock);
if (lock.body)
{
// Body has been locked, you can safely use `JPC_Body_*()` functions.
}
JPC_BodyLockInterface_UnlockRead(lock_iface, &lock);
//JPC_BodyLockInterface_UnlockWrite(lock_iface, &lock);
}
}
// Low-level, advanced way of accessing body data. Not protected by a lock, no function calls overhead.
// Use when you interact with Jolt only from one thread or when you are sure that JPC_PhysicsSystem_Update()
// has already completed in a given frame.
{
JPC_Body **bodies = JPC_PhysicsSystem_GetBodiesUnsafe(physics_system);
// Access a single body (get the body pointer from a body id).
{
JPC_Body *sphere = JPC_TRY_GET_BODY(bodies, sphere_id);
if (sphere)
{
sphere->friction = 0.2f;
}
}
// Access all body pointers.
for (uint32_t i = 0; i < JPC_PhysicsSystem_GetNumBodies(physics_system); ++i)
{
JPC_Body *body = bodies[i];
if (JPC_IS_VALID_BODY_POINTER(body))
{
// Body pointer is valid (not freed) you can access the data.
}
}
}
// Test body access.
{
JPC_Body **bodies = JPC_PhysicsSystem_GetBodiesUnsafe(physics_system);
const JPC_BodyLockInterface *lock_iface = JPC_PhysicsSystem_GetBodyLockInterface(physics_system);
JPC_BodyLockRead lock;
JPC_BodyLockInterface_LockRead(lock_iface, sphere_id, &lock);
if (lock.body)
{
JPC_Body *body = bodies[sphere_id.id & JPC_BODY_ID_INDEX_BITS];
if (!JPC_IS_VALID_BODY_POINTER(body)) return 0;
if (JPC_Body_IsDynamic(body) != true) return 0;
JPC_Body *body_checked = JPC_TRY_GET_BODY(bodies, sphere_id);
if (body_checked == NULL) return 0;
if (body_checked != body) return 0;
if (!JPC_ID_EQ(body_checked->id, body->id)) return 0;
if (body != lock.body) return 0;
if (!JPC_ID_EQ(body->id, sphere_id)) return 0;
}
JPC_BodyLockInterface_UnlockRead(lock_iface, &lock);
}
#if JPC_DEBUG_RENDERER == 1
// Test Debug Renderer - should get calls to DrawGeometry each frame, once for floor, once for ball.
JPC_PhysicsSystem_DrawBodies(physics_system, body_draw_settings, body_draw_filter);
#endif // JPC_DEBUG_RENDERER
}
#if JPC_DEBUG_RENDERER == 1
// Test other drawing functions besides DrawBodies at least once at the end
JPC_PhysicsSystem_DrawConstraints(physics_system);
JPC_PhysicsSystem_DrawConstraintLimits(physics_system);
JPC_PhysicsSystem_DrawConstraintReferenceFrame(physics_system);
#endif // JPC_DEBUG_RENDERER
#if JPC_DEBUG_RENDERER == 1
JPC_BodyDrawFilter_Destroy(body_draw_filter);
JPC_BodyManager_DrawSettings_Destroy(body_draw_settings);
JPC_DestroyDebugRendererSingleton();
#endif // JPC_DEBUG_RENDERER
JPC_BodyInterface_RemoveBody(body_interface, sphere_id);
JPC_BodyInterface_DestroyBody(body_interface, sphere_id);
JPC_BodyInterface_RemoveBody(body_interface, floor_id);
JPC_BodyInterface_DestroyBody(body_interface, floor_id);
JPC_ShapeSettings_Release((JPC_ShapeSettings *)floor_shape_settings);
if (JPC_Shape_GetRefCount(floor_shape) != 1) return 0;
JPC_Shape_Release(floor_shape);
JPC_ShapeSettings_Release((JPC_ShapeSettings *)sphere_shape_settings);
if (JPC_Shape_GetRefCount(sphere_shape) != 1) return 0;
JPC_Shape_Release(sphere_shape);
JPC_PhysicsSystem_Destroy(physics_system);
JPC_JobSystem_Destroy(job_system);
JPC_TempAllocator_Destroy(temp_allocator);
JPC_DestroyFactory();
return 1;
}
//--------------------------------------------------------------------------------------------------
// Serialization
//--------------------------------------------------------------------------------------------------
typedef struct BufferStreamOutImpl
{
const JPC_StreamOutVTable *vtable;
void* buffer;
size_t len;
bool failed;
} BufferStreamOutImpl;
static void
BufferedStreamOutImpl_WriteBytes(void *in_self, const void *in_data, size_t in_num_bytes)
{
BufferStreamOutImpl* self = (BufferStreamOutImpl*)in_self;
if (self->failed)
{
return;
}
#ifdef PRINT_OUTPUT
fprintf(stderr, "BufferedStreamOutImpl_WriteBytes(%llx, %llu)\n", (size_t)in_data, in_num_bytes);
#endif
const size_t start = self->len;
self->len += in_num_bytes;
void* new_buffer = realloc(self->buffer, self->len);
if (!new_buffer)
{
free(self->buffer);
self->failed = true;
}
self->buffer = new_buffer;
memcpy(self->buffer + start, in_data, in_num_bytes);
}
static bool
BufferedStreamOutImpl_IsFailed(const void *in_self)
{
const BufferStreamOutImpl* self = (BufferStreamOutImpl*)in_self;
#ifdef PRINT_OUTPUT
fprintf(stderr, "BufferedStreamOutImpl_IsFailed()\n");
#endif
return self->failed;
}
static BufferStreamOutImpl
BufferedStreamOutImpl_Init(void)
{
static const JPC_StreamOutVTable vtable =
{
.WriteBytes = BufferedStreamOutImpl_WriteBytes,
.IsFailed = BufferedStreamOutImpl_IsFailed,
};
BufferStreamOutImpl impl =
{
.vtable = &vtable,
.buffer = NULL,
.len = 0,
.failed = false,
};
return impl;
}
typedef struct BufferStreamInImpl
{
const JPC_StreamInVTable *vtable;
void* buffer;
size_t len;
size_t next;
bool eof;
} BufferStreamInImpl;
static void
BufferedStreamInImpl_ReadBytes(void *in_self, void *out_data, size_t in_num_bytes)
{
BufferStreamInImpl* self = (BufferStreamInImpl*)in_self;
#ifdef PRINT_OUTPUT
fprintf(stderr, "BufferedStreamInImpl_ReadBytes(%llx, %llu)\n", (size_t)out_data, in_num_bytes);
#endif
const size_t remaining = self->len - self->next;
size_t copy_len = in_num_bytes;
if (copy_len > remaining)
{
self->eof = true;
copy_len = remaining;
}
if (copy_len > 0)
{
memcpy(out_data, self->buffer + self->next, copy_len);
self->next += copy_len;
}
}
static bool
BufferedStreamInImpl_IsEOF(const void *in_self)
{
BufferStreamInImpl* self = (BufferStreamInImpl*)in_self;
#ifdef PRINT_OUTPUT
fprintf(stderr, "BufferedStreamInImpl_IsEOF()\n");
#endif
return self->eof;
}
static bool
BufferedStreamInImpl_IsFailed(const void *in_self)
{
#ifdef PRINT_OUTPUT
fprintf(stderr, "BufferedStreamInImpl_IsFailed()\n");
#endif
return false;
}
static BufferStreamInImpl
BufferedStreamInImpl_Init(void)
{
static const JPC_StreamInVTable vtable =
{
.ReadBytes = BufferedStreamInImpl_ReadBytes,
.IsEOF = BufferedStreamInImpl_IsEOF,
.IsFailed = BufferedStreamInImpl_IsFailed,
};
BufferStreamInImpl impl =
{
.vtable = &vtable,
.buffer = NULL,
.len = 0,
.next = 0,
};
return impl;
}
uint32_t
JoltCTest_Serialization(void)
{
JPC_RegisterDefaultAllocator();
JPC_CreateFactory();
JPC_RegisterTypes();
JPC_TempAllocator *temp_allocator = JPC_TempAllocator_Create(10 * 1024 * 1024);
const float half_extent[3] = { 1.f, 2.f, 3.f };
// Single shape
{
JPC_BoxShapeSettings* box_shape_settings = JPC_BoxShapeSettings_Create(half_extent);
JPC_Shape* box_shape = JPC_ShapeSettings_CreateShape((JPC_ShapeSettings*)box_shape_settings);
BufferStreamOutImpl stream_out = BufferedStreamOutImpl_Init();
JPC_Shape_SaveBinaryState(box_shape, &stream_out);
assert(!stream_out.failed);
BufferStreamInImpl stream_in = BufferedStreamInImpl_Init();
stream_in.buffer = stream_out.buffer;
stream_in.len = stream_out.len;
JPC_Shape* box_restored = JPC_Shape_sRestoreFromBinaryState(&stream_in);
assert(JPC_SHAPE_SUB_TYPE_BOX == JPC_Shape_GetSubType(box_restored));
float half_extent_restored[3] = { 0, 0, 0 };
JPC_BoxShape_GetHalfExtent((JPC_BoxShape*)box_restored, half_extent_restored);
assert(memcmp(half_extent, half_extent_restored, 3 * sizeof(float)) == 0);
free(stream_in.buffer);
}
// Compound shapes (save all)
{
const float translation[3] = { 4.f, 5.f, 6.f };
BufferStreamOutImpl stream_out = BufferedStreamOutImpl_Init();
{
JPC_BoxShapeSettings* box_shape_settings = JPC_BoxShapeSettings_Create(half_extent);
JPC_DecoratedShapeSettings* decorated_shape_settings = JPC_RotatedTranslatedShapeSettings_Create(
(JPC_ShapeSettings*)box_shape_settings,
(float[4]) { 0, 0, 0, 1 },
translation);
JPC_Shape* shape = JPC_ShapeSettings_CreateShape((JPC_ShapeSettings*)decorated_shape_settings);
JPC_Shape_SaveWithChildren_All(shape, &stream_out);
assert(!stream_out.failed);
}
BufferStreamInImpl stream_in = BufferedStreamInImpl_Init();
stream_in.buffer = stream_out.buffer;
stream_in.len = stream_out.len;
{
JPC_Shape* shape = JPC_Shape_sRestoreWithChildren_All(&stream_in);
assert(JPC_SHAPE_SUB_TYPE_ROTATED_TRANSLATED == JPC_Shape_GetSubType(shape));
JPC_DecoratedShape* decorated = (JPC_DecoratedShape*)shape;
const JPC_Shape* inner = JPC_DecoratedShape_GetInnerShape(decorated);
assert(JPC_SHAPE_SUB_TYPE_BOX == JPC_Shape_GetSubType(inner));
float half_extent_restored[3] = { 0, 0, 0 };
JPC_BoxShape_GetHalfExtent((const JPC_BoxShape*)inner, half_extent_restored);
assert(memcmp(half_extent, half_extent_restored, 3 * sizeof(float)) == 0);
float translation_restored[3];
JPC_RotatedTranslatedShape_GetPosition((JPC_RotatedTranslatedShape*)shape, translation_restored);
assert(memcmp(translation, translation_restored, 3 * sizeof(float)) == 0);
}
free(stream_in.buffer);
}
// Compound shapes
{
BufferStreamOutImpl stream_out = BufferedStreamOutImpl_Init();
const float translation[3] = { 7.f, 8.f, 9.f };
{
JPC_BoxShapeSettings* box_shape_settings = JPC_BoxShapeSettings_Create(half_extent);
JPC_DecoratedShapeSettings* decorated_shape_settings = JPC_RotatedTranslatedShapeSettings_Create(
(JPC_ShapeSettings*)box_shape_settings,
(float[4]) { 0, 0, 0, 1 },
translation);
JPC_Shape* shape = JPC_ShapeSettings_CreateShape((JPC_ShapeSettings*)decorated_shape_settings);
const JPC_Shape* inner_shape = JPC_DecoratedShape_GetInnerShape((JPC_DecoratedShape*)shape);
JPC_ShapeToIDMap* shape_to_id = JPC_ShapeToIDMap_Create();
JPC_MaterialToIDMap* material_to_id = JPC_MaterialToIDMap_Create();
JPC_ShapeToIDMap_Add(shape_to_id, &inner_shape, 1);
JPC_Shape_SaveWithChildren(shape, &stream_out, shape_to_id, material_to_id);
assert(!stream_out.failed);
JPC_ShapeToIDMap_Destroy(shape_to_id);
JPC_MaterialToIDMap_Destroy(material_to_id);
}
BufferStreamInImpl stream_in = BufferedStreamInImpl_Init();
stream_in.buffer = stream_out.buffer;
stream_in.len = stream_out.len;
{
JPC_IDToShapeMap* id_to_shape = JPC_IDToShapeMap_Create();
JPC_IDToMaterialMap* id_to_material = JPC_IDToMaterialMap_Create();
const float half_extent_alt[3] = { 4.f, 5.f, 6.f };
JPC_BoxShapeSettings* box_shape_settings = JPC_BoxShapeSettings_Create(half_extent_alt);
JPC_Shape* box_shape = JPC_ShapeSettings_CreateShape((JPC_ShapeSettings*)box_shape_settings);
JPC_IDToShapeMap_Add(id_to_shape, &box_shape, 1);
JPC_Shape* shape = JPC_Shape_sRestoreWithChildren(&stream_in, id_to_shape, id_to_material);
assert(JPC_SHAPE_SUB_TYPE_ROTATED_TRANSLATED == JPC_Shape_GetSubType(shape));
JPC_DecoratedShape* decorated = (JPC_DecoratedShape*)shape;
const JPC_Shape* inner = JPC_DecoratedShape_GetInnerShape(decorated);
assert(JPC_SHAPE_SUB_TYPE_BOX == JPC_Shape_GetSubType(inner));
float half_extent_restored[3] = { 0, 0, 0 };
JPC_BoxShape_GetHalfExtent((const JPC_BoxShape*)inner, half_extent_restored);
assert(memcmp(half_extent_alt, half_extent_restored, 3 * sizeof(float)) == 0);
float translation_restored[3];
JPC_RotatedTranslatedShape_GetPosition((JPC_RotatedTranslatedShape*)shape, translation_restored);
assert(memcmp(translation, translation_restored, 3 * sizeof(float)) == 0);
JPC_ShapeToIDMap_Destroy(id_to_shape);
JPC_MaterialToIDMap_Destroy(id_to_material);
}
free(stream_in.buffer);
}
JPC_TempAllocator_Destroy(temp_allocator);
JPC_DestroyFactory();
return 1;
}
//--------------------------------------------------------------------------------------------------