packages feed

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;
}