Box3D-0.1.0.0: cbits/box3d_shim.c
// SPDX-License-Identifier: BSD-3-Clause
//
// See box3d_shim.h for the rationale behind this file.
#include "box3d_shim.h"
// The Haskell bindings pass ids by value as Word32/Word64; that pun is only
// sound while the ids stay register-sized integer structs.
_Static_assert(sizeof(b3WorldId) == 4, "b3WorldId is passed as Word32");
_Static_assert(sizeof(b3BodyId) == 8, "b3BodyId is passed as Word64");
_Static_assert(sizeof(b3ShapeId) == 8, "b3ShapeId is passed as Word64");
_Static_assert(sizeof(b3JointId) == 8, "b3JointId is passed as Word64");
// base.h -----------------------------------------------------------------
void hs_b3GetVersion(b3Version* out)
{
*out = b3GetVersion();
}
// math_functions.h -------------------------------------------------------
bool hs_b3IsValidVec3(const b3Vec3* a)
{
return b3IsValidVec3(*a);
}
bool hs_b3IsValidQuat(const b3Quat* q)
{
return b3IsValidQuat(*q);
}
bool hs_b3IsValidTransform(const b3Transform* a)
{
return b3IsValidTransform(*a);
}
bool hs_b3IsValidMatrix3(const b3Matrix3* a)
{
return b3IsValidMatrix3(*a);
}
bool hs_b3IsValidAABB(const b3AABB* a)
{
return b3IsValidAABB(*a);
}
bool hs_b3IsValidPlane(const b3Plane* a)
{
return b3IsValidPlane(*a);
}
void hs_b3ComputeCosSin(float radians, b3CosSin* out)
{
*out = b3ComputeCosSin(radians);
}
void hs_b3ComputeQuatBetweenUnitVectors(const b3Vec3* v1, const b3Vec3* v2, b3Quat* out)
{
*out = b3ComputeQuatBetweenUnitVectors(*v1, *v2);
}
// types.h: default definition constructors --------------------------------
void hs_b3DefaultWorldDef(b3WorldDef* out)
{
*out = b3DefaultWorldDef();
}
void hs_b3DefaultBodyDef(b3BodyDef* out)
{
*out = b3DefaultBodyDef();
}
void hs_b3DefaultShapeDef(b3ShapeDef* out)
{
*out = b3DefaultShapeDef();
}
void hs_b3DefaultFilter(b3Filter* out)
{
*out = b3DefaultFilter();
}
void hs_b3DefaultSurfaceMaterial(b3SurfaceMaterial* out)
{
*out = b3DefaultSurfaceMaterial();
}
void hs_b3DefaultQueryFilter(b3QueryFilter* out)
{
*out = b3DefaultQueryFilter();
}
void hs_b3DefaultExplosionDef(b3ExplosionDef* out)
{
*out = b3DefaultExplosionDef();
}
void hs_b3DefaultDebugDraw(b3DebugDraw* out)
{
*out = b3DefaultDebugDraw();
}
// types.h: joint definition defaults ---------------------------------------
void hs_b3DefaultDistanceJointDef(b3DistanceJointDef* out)
{
*out = b3DefaultDistanceJointDef();
}
void hs_b3DefaultMotorJointDef(b3MotorJointDef* out)
{
*out = b3DefaultMotorJointDef();
}
void hs_b3DefaultParallelJointDef(b3ParallelJointDef* out)
{
*out = b3DefaultParallelJointDef();
}
void hs_b3DefaultPrismaticJointDef(b3PrismaticJointDef* out)
{
*out = b3DefaultPrismaticJointDef();
}
void hs_b3DefaultRevoluteJointDef(b3RevoluteJointDef* out)
{
*out = b3DefaultRevoluteJointDef();
}
void hs_b3DefaultSphericalJointDef(b3SphericalJointDef* out)
{
*out = b3DefaultSphericalJointDef();
}
void hs_b3DefaultWeldJointDef(b3WeldJointDef* out)
{
*out = b3DefaultWeldJointDef();
}
void hs_b3DefaultWheelJointDef(b3WheelJointDef* out)
{
*out = b3DefaultWheelJointDef();
}
void hs_b3DefaultFilterJointDef(b3FilterJointDef* out)
{
*out = b3DefaultFilterJointDef();
}
// Callback trampolines ------------------------------------------------------
// GHC callbacks cannot receive structs by value, so these fixed C functions
// take the by-value b3Pos/b3Vec3 arguments and forward pointers to a
// Haskell-wrapped function carried in the context slot.
typedef float (*hs_b3CastResultHsFcn)(b3ShapeId shapeId, const b3Pos* point, const b3Vec3* normal, float fraction,
uint64_t userMaterialId, int triangleIndex, int childIndex, void* context);
typedef bool (*hs_b3PreSolveHsFcn)(b3ShapeId shapeIdA, b3ShapeId shapeIdB, const b3Pos* point, const b3Vec3* normal,
void* context);
float hs_b3CastResultTrampoline(b3ShapeId shapeId, b3Pos point, b3Vec3 normal, float fraction, uint64_t userMaterialId,
int triangleIndex, int childIndex, void* context)
{
hs_b3Closure* c = context;
return ((hs_b3CastResultHsFcn)c->fn)(shapeId, &point, &normal, fraction, userMaterialId, triangleIndex, childIndex,
c->context);
}
bool hs_b3PreSolveTrampoline(b3ShapeId shapeIdA, b3ShapeId shapeIdB, b3Pos point, b3Vec3 normal, void* context)
{
hs_b3Closure* c = context;
return ((hs_b3PreSolveHsFcn)c->fn)(shapeIdA, shapeIdB, &point, &normal, c->context);
}
typedef bool (*hs_b3MeshQueryHsFcn)(const b3Vec3* a, const b3Vec3* b, const b3Vec3* c, int triangleIndex,
void* context);
bool hs_b3MeshQueryTrampoline(b3Vec3 a, b3Vec3 b, b3Vec3 c, int triangleIndex, void* context)
{
hs_b3Closure* cl = context;
return ((hs_b3MeshQueryHsFcn)cl->fn)(&a, &b, &c, triangleIndex, cl->context);
}
// b3DebugDraw trampolines ---------------------------------------------------
// The Haskell side fills hs_b3DrawClosure field by field through hsc2hs, so
// the struct below is the single definition of its layout. A NULL slot means
// the renderer left that primitive unset; skipping it here keeps the call
// from entering Haskell at all.
typedef void (*hs_b3DrawShapeHsFcn)(void* userShape, const b3WorldTransform* transform, b3HexColor color,
void* context);
typedef void (*hs_b3DrawSegmentHsFcn)(const b3Pos* p1, const b3Pos* p2, b3HexColor color, void* context);
typedef void (*hs_b3DrawTransformHsFcn)(const b3WorldTransform* transform, void* context);
typedef void (*hs_b3DrawPointHsFcn)(const b3Pos* p, float size, b3HexColor color, void* context);
typedef void (*hs_b3DrawSphereHsFcn)(const b3Pos* p, float radius, b3HexColor color, float alpha, void* context);
typedef void (*hs_b3DrawCapsuleHsFcn)(const b3Pos* p1, const b3Pos* p2, float radius, b3HexColor color, float alpha,
void* context);
typedef void (*hs_b3DrawBoundsHsFcn)(const b3AABB* aabb, b3HexColor color, void* context);
typedef void (*hs_b3DrawBoxHsFcn)(const b3Vec3* extents, const b3WorldTransform* transform, b3HexColor color,
void* context);
typedef void (*hs_b3DrawStringHsFcn)(const b3Pos* p, const char* s, b3HexColor color, void* context);
void hs_b3DrawShapeTrampoline(void* userShape, b3WorldTransform transform, b3HexColor color, void* context)
{
hs_b3DrawClosure* c = context;
if (c->shape != NULL)
{
((hs_b3DrawShapeHsFcn)c->shape)(userShape, &transform, color, c->context);
}
}
void hs_b3DrawSegmentTrampoline(b3Pos p1, b3Pos p2, b3HexColor color, void* context)
{
hs_b3DrawClosure* c = context;
if (c->segment != NULL)
{
((hs_b3DrawSegmentHsFcn)c->segment)(&p1, &p2, color, c->context);
}
}
void hs_b3DrawTransformTrampoline(b3WorldTransform transform, void* context)
{
hs_b3DrawClosure* c = context;
if (c->transform != NULL)
{
((hs_b3DrawTransformHsFcn)c->transform)(&transform, c->context);
}
}
void hs_b3DrawPointTrampoline(b3Pos p, float size, b3HexColor color, void* context)
{
hs_b3DrawClosure* c = context;
if (c->point != NULL)
{
((hs_b3DrawPointHsFcn)c->point)(&p, size, color, c->context);
}
}
void hs_b3DrawSphereTrampoline(b3Pos p, float radius, b3HexColor color, float alpha, void* context)
{
hs_b3DrawClosure* c = context;
if (c->sphere != NULL)
{
((hs_b3DrawSphereHsFcn)c->sphere)(&p, radius, color, alpha, c->context);
}
}
void hs_b3DrawCapsuleTrampoline(b3Pos p1, b3Pos p2, float radius, b3HexColor color, float alpha, void* context)
{
hs_b3DrawClosure* c = context;
if (c->capsule != NULL)
{
((hs_b3DrawCapsuleHsFcn)c->capsule)(&p1, &p2, radius, color, alpha, c->context);
}
}
void hs_b3DrawBoundsTrampoline(b3AABB aabb, b3HexColor color, void* context)
{
hs_b3DrawClosure* c = context;
if (c->bounds != NULL)
{
((hs_b3DrawBoundsHsFcn)c->bounds)(&aabb, color, c->context);
}
}
void hs_b3DrawBoxTrampoline(b3Vec3 extents, b3WorldTransform transform, b3HexColor color, void* context)
{
hs_b3DrawClosure* c = context;
if (c->box != NULL)
{
((hs_b3DrawBoxHsFcn)c->box)(&extents, &transform, color, c->context);
}
}
void hs_b3DrawStringTrampoline(b3Pos p, const char* s, b3HexColor color, void* context)
{
hs_b3DrawClosure* c = context;
if (c->string != NULL)
{
((hs_b3DrawStringHsFcn)c->string)(&p, s, color, c->context);
}
}
// shape queries -------------------------------------------------------------
// hs_b3Shape_TestPoint needs the shape's GJK proxy, which the public API does
// not expose. These internal headers give it the same access
// b3Shape_GetClosestPoint uses; b3GetShape is static in shape.c, so its
// two-line lookup is inlined here.
#include "body.h"
#include "physics_world.h"
#include "shape.h"
bool hs_b3Shape_TestPoint(b3ShapeId shapeId, const b3Pos* point)
{
b3World* world = b3GetWorld(shapeId.world0);
if (world == NULL)
{
return false;
}
int id = shapeId.index1 - 1;
b3Shape* shape = b3Array_Get(world->shapes, id);
B3_ASSERT(shape->id == id && shape->generation == shapeId.generation);
// b3MakeShapeProxy only supports the convex shapes; report false for the
// rest like 2D's b2Shape_TestPoint does for its unsupported types
// (instead of tripping the assert in b3MakeShapeProxy's default branch).
if (shape->type != b3_sphereShape && shape->type != b3_capsuleShape && shape->type != b3_hullShape)
{
return false;
}
b3Body* body = b3Array_Get(world->bodies, shape->bodyId);
b3Transform transform = b3ToRelativeTransform(b3GetBodyTransformQuick(world, body), *point);
b3Vec3 target = b3Vec3_zero;
b3DistanceInput input;
input.proxyA = b3MakeShapeProxy(shape);
input.proxyB = (b3ShapeProxy){ &target, 1, 0.0f };
input.transform = b3InvMulTransforms(transform, b3Transform_identity);
input.useRadii = true;
b3SimplexCache cache = { 0 };
b3DistanceOutput output = b3ShapeDistance(&input, &cache, NULL, 0);
// With radii applied the distance clamps to zero inside the shape even
// though the witness point stays on the perimeter.
return output.distance == 0.0f;
}
// debug shapes --------------------------------------------------------------
void hs_b3NoopDestroyDebugShape(void* userShape, void* userContext)
{
(void)userShape;
(void)userContext;
}