Merge pull request #6933 from odin-lang/bill/box3d

`vendor:box3d`
This commit is contained in:
Jeroen van Rijn
2026-07-03 21:35:30 +02:00
committed by GitHub
19 changed files with 13573 additions and 19 deletions

View File

@@ -244,27 +244,18 @@ quaternion_mul_quaternion :: proc "contextless" (q1, q2: $Q) -> Q where IS_QUATE
@(require_results)
quaternion64_mul_vector3 :: proc "contextless" (q: $Q/quaternion64, v: $V/[3]$F/f16) -> V {
q := transmute(runtime.Raw_Quaternion64_Vector_Scalar)q
v := v
t := cross(2*q.vector, v)
return V(v + q.scalar*t + cross(q.vector, t))
t := cross(2*q.xyz, v)
return V(v + q.w*t + cross(q.xyz, t))
}
@(require_results)
quaternion128_mul_vector3 :: proc "contextless" (q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
q := transmute(runtime.Raw_Quaternion128_Vector_Scalar)q
v := v
t := cross(2*q.vector, v)
return V(v + q.scalar*t + cross(q.vector, t))
t := cross(2*q.xyz, v)
return V(v + q.w*t + cross(q.xyz, t))
}
@(require_results)
quaternion256_mul_vector3 :: proc "contextless" (q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
q := transmute(runtime.Raw_Quaternion256_Vector_Scalar)q
v := v
t := cross(2*q.vector, v)
return V(v + q.scalar*t + cross(q.vector, t))
t := cross(2*q.xyz, v)
return V(v + q.w*t + cross(q.xyz, t))
}
quaternion_mul_vector3 :: proc{quaternion64_mul_vector3, quaternion128_mul_vector3, quaternion256_mul_vector3}

View File

@@ -6,5 +6,6 @@ package all
@(require) import "vendor:wgpu/sdl2glue"
@(require) import "vendor:wgpu"
@(require) import "vendor:box2d"
@(require) import "vendor:box3d"
@(require) import "vendor:windows/GameInput"
@(require) import "vendor:windows/XAudio2"

View File

@@ -1206,18 +1206,19 @@ gb_internal lbValue lb_emit_struct_ep_internal(lbProcedure *p, lbValue s, i32 in
i32 original_index = index;
index = lb_convert_struct_index(p->module, t, index);
lbModule *m = p->module;
if (lb_is_const(s)) {
// NOTE(bill): this cannot be replaced with lb_emit_epi
lbModule *m = p->module;
lbValue res = {};
LLVMValueRef indices[2] = {llvm_zero(m), LLVMConstInt(lb_type(m, t_i32), index, false)};
res.value = LLVMConstGEP2(lb_type(m, type_deref(s.type)), s.value, indices, gb_count_of(indices));
res.type = alloc_type_pointer(result_type);
res.value = LLVMConstGEP2(lb_type(m, type_deref(s.type)), s.value, indices, gb_count_of(indices));
res.value = LLVMConstPointerCast(res.value, lb_type(m, res.type));
return res;
} else {
lbValue res = {};
LLVMTypeRef st = lb_type(p->module, type_deref(s.type));
LLVMTypeRef st = lb_type(m, type_deref(s.type));
// gb_printf_err("%s\n", type_to_string(s.type));
// gb_printf_err("%s\n", LLVMPrintTypeToString(LLVMTypeOf(s.value)));
// gb_printf_err("%d\n", index);
@@ -1225,8 +1226,9 @@ gb_internal lbValue lb_emit_struct_ep_internal(lbProcedure *p, lbValue s, i32 in
unsigned count = LLVMCountStructElementTypes(st);
GB_ASSERT_MSG(count >= cast(unsigned)index, "%u %d %d", count, index, original_index);
res.value = LLVMBuildStructGEP2(p->builder, st, s.value, cast(unsigned)index, "");
res.type = alloc_type_pointer(result_type);
res.value = LLVMBuildStructGEP2(p->builder, st, s.value, cast(unsigned)index, "");
res.value = LLVMBuildPointerCast(p->builder, res.value, lb_type(m, res.type), "");
return res;
}
}
@@ -1274,6 +1276,17 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
case 1: result_type = ft; break;
case 2: result_type = ft; break;
case 3: result_type = ft; break;
case -1:
{
lbValue res = {};
res.type = alloc_type_pointer(alloc_type_array(ft, 3));
if (lb_is_const(s)) {
res.value = LLVMConstPointerCast(s.value, lb_type(p->module, res.type));
} else {
res.value = LLVMBuildPointerCast(p->builder, s.value, lb_type(p->module, res.type), "");
}
return res;
}
}
} else if (is_type_slice(t)) {
switch (index) {
@@ -1443,6 +1456,11 @@ gb_internal lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index) {
case 1: result_type = ft; break;
case 2: result_type = ft; break;
case 3: result_type = ft; break;
case -1: {
lbValue ptr_q = lb_address_from_load_or_generate_local(p, s);
lbValue ptr_xyz = lb_emit_struct_ep(p, ptr_q, -1);
return lb_emit_load(p, ptr_xyz);
}
}
break;
}

View File

@@ -3981,6 +3981,9 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
gb_local_persist Entity *entity__y = alloc_entity_field(nullptr, make_token_ident(y), t_f16, false, 1);
gb_local_persist Entity *entity__z = alloc_entity_field(nullptr, make_token_ident(z), t_f16, false, 2);
gb_local_persist String xyz = str_lit("xyz");
gb_local_persist Entity *entity__xyz = alloc_entity_field(nullptr, make_token_ident(xyz), alloc_type_array(t_f16, 3), false, -1);
String n = field_name.string();
if (n == w) {
selection_add_index(&sel, 3);
@@ -3998,6 +4001,10 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
selection_add_index(&sel, 2);
sel.entity = entity__z;
return sel;
} else if (n == xyz) {
selection_add_index(&sel, -1);
sel.entity = entity__xyz;
return sel;
}
} break;
@@ -4012,6 +4019,9 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
gb_local_persist Entity *entity__y = alloc_entity_field(nullptr, make_token_ident(y), t_f32, false, 1);
gb_local_persist Entity *entity__z = alloc_entity_field(nullptr, make_token_ident(z), t_f32, false, 2);
gb_local_persist String xyz = str_lit("xyz");
gb_local_persist Entity *entity__xyz = alloc_entity_field(nullptr, make_token_ident(xyz), alloc_type_array(t_f32, 3), false, -1);
String n = field_name.string();
if (n == w) {
selection_add_index(&sel, 3);
@@ -4029,6 +4039,10 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
selection_add_index(&sel, 2);
sel.entity = entity__z;
return sel;
} else if (n == xyz) {
selection_add_index(&sel, -1);
sel.entity = entity__xyz;
return sel;
}
} break;
@@ -4043,6 +4057,9 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
gb_local_persist Entity *entity__y = alloc_entity_field(nullptr, make_token_ident(y), t_f64, false, 1);
gb_local_persist Entity *entity__z = alloc_entity_field(nullptr, make_token_ident(z), t_f64, false, 2);
gb_local_persist String xyz = str_lit("xyz");
gb_local_persist Entity *entity__xyz = alloc_entity_field(nullptr, make_token_ident(xyz), alloc_type_array(t_f64, 3), false, -1);
String n = field_name.string();
if (n == w) {
selection_add_index(&sel, 3);
@@ -4060,6 +4077,10 @@ gb_internal Selection lookup_field_with_selection(Type *type_, InternedString fi
selection_add_index(&sel, 2);
sel.entity = entity__z;
return sel;
} else if (n == xyz) {
selection_add_index(&sel, -1);
sel.entity = entity__xyz;
return sel;
}
} break;

1827
vendor/box3d/box3d.odin vendored Normal file

File diff suppressed because it is too large Load Diff

615
vendor/box3d/box3d_collision.odin vendored Normal file
View File

@@ -0,0 +1,615 @@
package vendor_box3d
import "core:c"
// Query callback.
MeshQueryFcn :: proc "c" (x, y, z: Vec3, triangleIndex: c.int, ctx: rawptr) -> bool
@(link_prefix="b3", default_calling_convention="c", require_results)
foreign lib {
/**
* @addtogroup tree
* @{
*/
// Constructing the tree initializes the node pool.
DynamicTree_Create :: proc(proxyCapacity: c.int) -> DynamicTree ---
// Destroy the tree, freeing the node pool.
DynamicTree_Destroy :: proc(tree: ^DynamicTree) ---
// Create a proxy. Provide an AABB and a userData value.
DynamicTree_CreateProxy :: proc(tree: ^DynamicTree, aabb: AABB, categoryBits: u64, userData: u64) -> c.int ---
// Destroy a proxy. This asserts if the id is invalid.
DynamicTree_DestroyProxy :: proc(tree: ^DynamicTree, proxyId: c.int) ---
// Move a proxy to a new AABB by removing and reinserting into the tree.
DynamicTree_MoveProxy :: proc(tree: ^DynamicTree, proxyId: c.int, aabb: AABB) ---
// Enlarge a proxy and enlarge ancestors as necessary.
DynamicTree_EnlargeProxy :: proc(tree: ^DynamicTree, proxyId: c.int, aabb: AABB) ---
// Modify the category bits on a proxy. This is an expensive operation.
DynamicTree_SetCategoryBits :: proc(tree: ^DynamicTree, proxyId: c.int, categoryBits: u64) ---
// Get the category bits on a proxy.
DynamicTree_GetCategoryBits :: proc(tree: ^DynamicTree, proxyId: c.int) -> u64 ---
// Query an AABB for overlapping proxies. The callback function is called for each proxy that overlaps the supplied AABB.
// @return performance data
DynamicTree_Query :: proc(#by_ptr tree: DynamicTree, aabb: AABB, maskBits: u64, requireAllBits: bool, callback: TreeQueryCallbackFcn, ctx: rawptr) -> TreeStats ---
// Query an AABB for the closest object. The callback function is called for each proxy that might be closest to the supplied point.
// @param tree the dynamic tree to query
// @param point the query point
// @param maskBits nodes are skipped if the bit-wise AND with the node category bits is zero
// @param requireAllBits nodes are skipped if the bit-wise AND with the node category bits does not equal the maskBits
// @param callback a user provided instance of TreeQueryClosestCallbackFcn
// @param context a user context object that is provided to the callback
// @param minDistanceSqr the initial and final minimum squared distance. Provide a small initial to restrict the search and
// improve performance. If the value is large this query has performance that scales linearly with the number of proxies and
// would be slower than a brute force search.
// @return performance data
DynamicTree_QueryClosest :: proc(#by_ptr tree: DynamicTree, point: Vec3, maskBits: u64, requireAllBits: bool, callback: TreeQueryClosestCallbackFcn, ctx: rawptr, minDistanceSqr: ^f32) -> TreeStats ---
// Ray cast against the proxies in the tree. This relies on the callback
// to perform an exact ray cast in the case where the proxy contains a shape.
// The callback also performs any collision filtering. This has performance
// roughly equal to k * log(n), where k is the number of collisions and n is the
// number of proxies in the tree.
// Bit-wise filtering using mask bits can greatly improve performance in some scenarios.
// However, this filtering may be approximate, so the user should still apply filtering to results.
// @param tree the dynamic tree to ray cast
// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1)
// @param maskBits bit mask test: `bool accept = (maskBits & node->categoryBits) != 0;`
// @param requireAllBits modifies bit mask test: `bool accept = (maskBits & node->categoryBits) == maskBits;`
// @param callback a callback function that is called for each proxy that is hit by the ray
// @param context user context that is passed to the callback
// @return performance data
DynamicTree_RayCast :: proc(#by_ptr tree: DynamicTree, #by_ptr input: RayCastInput, maskBits: u64, requireAllBits: bool, callback: TreeRayCastCallbackFcn, ctx: rawptr) -> TreeStats ---
// Sweep an AABB through the tree. The box is in the tree's world float frame and the callback
// re-differences each shape at full precision against the query origin. Used by the large world
// spatial queries so the tree traversal stays float while the narrow phase stays precise.
DynamicTree_BoxCast :: proc(#by_ptr tree: DynamicTree, #by_ptr input: BoxCastInput, maskBits: u64, requireAllBits: bool, callback: TreeBoxCastCallbackFcn, ctx: rawptr) -> TreeStats ---
// Validate this tree. For testing.
DynamicTree_Validate :: proc(#by_ptr tree: DynamicTree) ---
// Get the height of the binary tree.
DynamicTree_GetHeight :: proc(#by_ptr tree: DynamicTree) -> c.int ---
// Get the ratio of the sum of the node areas to the root area.
DynamicTree_GetAreaRatio :: proc(#by_ptr tree: DynamicTree) -> f32 ---
// Get the bounding box that contains the entire tree
DynamicTree_GetRootBounds :: proc(#by_ptr tree: DynamicTree) -> AABB ---
// Get the number of proxies created
DynamicTree_GetProxyCount :: proc(#by_ptr tree: DynamicTree) -> c.int ---
// Rebuild the tree while retaining subtrees that haven't changed. Returns the number of boxes sorted.
DynamicTree_Rebuild :: proc(tree: ^DynamicTree, fullBuild: bool) -> c.int ---
// Get the number of bytes used by this tree
DynamicTree_GetByteCount :: proc(#by_ptr tree: DynamicTree) -> c.int ---
// Validate this tree has no enlarged AABBs. For testing.
DynamicTree_ValidateNoEnlarged :: proc(#by_ptr tree: DynamicTree) ---
// Save this tree to a file for debugging
DynamicTree_Save :: proc(#by_ptr tree: DynamicTree, fileName: cstring) ---
// Load a file for debugging
DynamicTree_Load :: proc(fileName: cstring, scale: f32) -> DynamicTree ---
/**@}*/ // tree
/**
* @addtogroup hull
* @{
*/
// Create a tessellated cylinder as a hull.
CreateCylinder :: proc(height: f32, radius: f32, yOffset: f32, sides: c.int) -> ^HullData ---
// Create a tessellated cone as a hull.
CreateCone :: proc(height: f32, radius1: f32, radius2: f32, slices: c.int) -> ^HullData ---
// Create a rock shaped hull.
CreateRock :: proc(radius: f32) -> ^HullData ---
// Create a generic convex hull.
CreateHull :: proc(points: [^]Vec3, pointCount: c.int, maxVertexCount: c.int) -> ^HullData ---
// Deep clone a hull.
CloneHull :: proc(#by_ptr hull: HullData) -> ^HullData ---
// Clone and transform a hull. Supports non-uniform and mirroring scale.
CloneAndTransformHull :: proc(#by_ptr original: HullData, transform: Transform, scale: Vec3) -> ^HullData ---
// Destroy a hull.
DestroyHull :: proc(hull: ^HullData) ---
// Make a cube as a hull. Do not call DestroyHull on this.
MakeCubeHull :: proc(halfWidth: f32) -> BoxHull ---
// Make a box as a hull. Do not call DestroyHull on this.
MakeBoxHull :: proc(hx, hy, hz: f32) -> BoxHull ---
// Make an offset box as a hull. Do not call DestroyHull on this.
MakeOffsetBoxHull :: proc(hx, hy, hz: f32, offset: Vec3) -> BoxHull ---
// Make a transformed box as a hull. Do not call DestroyHull on this.
// @param hx, hy, hz positive half widths
// @param transform local transform of box
MakeTransformedBoxHull :: proc(hx, hy, hz: f32, transform: Transform) -> BoxHull ---
// This makes a transformed box hull with post scaling. This is useful for boxes that are scaled in
// a level editor. Such scaling can have reflection and shear. In the case of shear the result
// may be approximate. If you need to support shear consider using CreateHull.
// Do not call DestroyHull on this.
// @param halfWidths positive half widths
// @param transform local transform of box
// @param postScale scale applied after the transform, may be negative
MakeScaledBoxHull :: proc(halfWidths: Vec3, transform: Transform, postScale: Vec3) -> BoxHull ---
// This takes a box with a transform and post scale and converts it into a box with the post scale
// resolved with new half-widths and transform. This accepts non-uniform and negative scale.
// This is approximate if there is shear.
// @param halfWidths [in/out] the box half widths
// @param transform [in/out] the box transform with rotation and translation
// @param postScale the post scale being applied to the box after the transform
// @param minHalfWidth the minimum half width after scale is applied
ScaleBox :: proc(halfWidths: ^Vec3, transform: ^Transform, postScale: Vec3, minHalfWidth: f32) ---
/**@}*/ // hull
/**
* @addtogroup mesh
* @{
*/
// Create a grid mesh along the x and z axes.
// @param xCount the number of rows in the x direction
// @param zCount the number of rows in the z direction
// @param cellWidth the width of each cell
// @param materialCount the number of materials to generate
// @param identifyEdges compute adjacency information
CreateGridMesh :: proc(xCount: c.int, zCount: c.int, cellWidth: f32, materialCount: c.int, identifyEdges: bool) -> ^MeshData ---
// Create a wave mesh along the x and z axes.
CreateWaveMesh :: proc(xCount: c.int, zCount: c.int, cellWidth: f32, amplitude: f32, rowFrequency: f32, columnFrequency: f32) -> ^MeshData ---
// Create a torus mesh.
CreateTorusMesh :: proc(radialResolution: c.int, tubularResolution: c.int, radius: f32, thickness: f32) -> ^MeshData ---
// Create a box mesh.
CreateBoxMesh :: proc(center: Vec3, extent: Vec3, identifyEdges: bool) -> ^MeshData ---
// Create a hollow box mesh.
CreateHollowBoxMesh :: proc(center: Vec3, extent: Vec3) -> ^MeshData ---
// Create a platform mesh. A truncated pyramid.
CreatePlatformMesh :: proc(center: Vec3, height, topWidth, bottomWidth: f32) -> ^MeshData ---
// Create a generic mesh.
CreateMesh :: proc(#by_ptr def: MeshDef, degenerateTriangleIndices: [^]c.int, degenerateCapacity: c.int) -> ^MeshData ---
// Destroy a mesh.
DestroyMesh :: proc(mesh: ^MeshData) ---
// Get the height of the mesh BVH.
GetHeight :: proc(#by_ptr mesh: MeshData) -> c.int ---
/**@}*/ // mesh
/**
* @addtogroup height_field
* @{
*/
// Create a generic height field.
CreateHeightField :: proc(#by_ptr data: HeightFieldDef) -> ^HeightFieldData ---
// Create a grid as a height field.
CreateGrid :: proc(rowCount, columnCount: c.int, scale: Vec3, makeHoles: bool) -> ^HeightFieldData ---
// Create a wave grid as a height field.
CreateWave :: proc(rowCount, columnCount: c.int, scale: Vec3, rowFrequency: f32, columnFrequency: f32, makeHoles: bool) -> ^HeightFieldData ---
// Destroy a height field.
DestroyHeightField :: proc(heightField: ^HeightFieldData) ---
// Save input height data to a file
DumpHeightData :: proc(#by_ptr data: HeightFieldDef, fileName: cstring) ---
// Create a height field by loading a previously saved height data
LoadHeightField :: proc(fileName: cstring) -> ^HeightFieldData ---
/**@}*/ // height_field
/**
* @addtogroup compound
* @{
*/
// Get a child shape of a compound.
GetCompoundChild :: proc(#by_ptr compound: CompoundData, childIndex: c.int) -> ChildShape ---
// Query a compound shape for children that overlap an AABB.
QueryCompound :: proc(#by_ptr compound: CompoundData, aabb: AABB, fcn: CompoundQueryFcn, ctx: rawptr) ---
// Access a child capsule by index.
GetCompoundCapsule :: proc(#by_ptr compound: CompoundData, index: c.int) -> CompoundCapsule ---
// Access a child hull by index.
GetCompoundHull :: proc(#by_ptr compound: CompoundData, index: c.int) -> CompoundHull ---
// Access a child mesh by index.
GetCompoundMesh :: proc(#by_ptr compound: CompoundData, index: c.int) -> CompoundMesh ---
// Access a child sphere by index.
GetCompoundSphere :: proc(#by_ptr compound: CompoundData, index: c.int) -> CompoundSphere ---
// Access the compound material array.
GetCompoundMaterials :: proc(#by_ptr compound: CompoundData) -> ^SurfaceMaterial ---
// Create a compound shape. All input data in the definition is cloned into the resulting compound.
CreateCompound :: proc(#by_ptr def: CompoundDef) -> ^CompoundData ---
// Destroy a compound shape.
DestroyCompound :: proc(compound: ^CompoundData) ---
// If bytes is null then this returns the number of required bytes. This clones all the
// data into the bytes buffer. This is expected to run offline or asynchronously.
// This mutates the compound to nullify pointers, leaving the compound in an unusable state.
ConvertCompoundToBytes :: proc(compound: ^CompoundData) -> [^]u8 ---
// Convert bytes to compound. This does not clone. The bytes must remain in scope while the
// compound is used. This is done to improve run-time performance and allow for instancing.
// The bytes are mutated to fixup pointers.
ConvertBytesToCompound :: proc(bytes: [^]u8, byteCount: c.int) -> ^CompoundData ---
/**@}*/ // compound
/**
* @addtogroup geometry
* @{
*/
// Compute mass properties of a sphere
ComputeSphereMass :: proc(#by_ptr shape: Sphere, density: f32) -> MassData ---
// Compute mass properties of a capsule
ComputeCapsuleMass :: proc(#by_ptr shape: Capsule, density: f32) -> MassData ---
// Compute mass properties of a hull
ComputeHullMass :: proc(#by_ptr shape: HullData, density: f32) -> MassData ---
// Compute the bounding box of a transformed sphere
ComputeSphereAABB :: proc(#by_ptr shape: Sphere, transform: Transform) -> AABB ---
// Compute the bounding box of a transformed capsule
ComputeCapsuleAABB :: proc(#by_ptr shape: Capsule, transform: Transform) -> AABB ---
// Compute the bounding box of a transformed hull
ComputeHullAABB :: proc(#by_ptr shape: HullData, transform: Transform) -> AABB ---
// Compute the bounding box of a transformed mesh. Scale may be non-uniform and have negative components.
ComputeMeshAABB :: proc(#by_ptr shape: MeshData, transform: Transform, scale: Vec3) -> AABB ---
// Compute the bounding box of a transformed height-field
ComputeHeightFieldAABB :: proc(#by_ptr shape: HeightFieldData, transform: Transform) -> AABB ---
// Compute the bounding box of a compound
ComputeCompoundAABB :: proc(#by_ptr shape: CompoundData, transform: Transform) -> AABB ---
/**@}*/ // geometry
/**
* @addtogroup query
* @{
*/
// Use this to ensure your ray cast input is valid and avoid internal assertions.
IsValidRay :: proc(#by_ptr input: RayCastInput) -> bool ---
// Overlap shape versus capsule
OverlapCapsule :: proc(#by_ptr shape: Capsule, shapeTransform: Transform, #by_ptr proxy: ShapeProxy) -> bool ---
// Overlap shape versus compound
OverlapCompound :: proc(#by_ptr shape: CompoundData, shapeTransform: Transform, #by_ptr proxy: ShapeProxy) -> bool ---
// Overlap shape versus height field
OverlapHeightField :: proc(#by_ptr shape: HeightFieldData, shapeTransform: Transform, #by_ptr proxy: ShapeProxy) -> bool ---
// Overlap shape versus hull
OverlapHull :: proc(#by_ptr shape: HullData, shapeTransform: Transform, #by_ptr proxy: ShapeProxy) -> bool ---
// Overlap shape versus mesh
OverlapMesh :: proc(#by_ptr shape: Mesh, shapeTransform: Transform, #by_ptr proxy: ShapeProxy) -> bool ---
// Overlap shape versus sphere
OverlapSphere :: proc(#by_ptr shape: Sphere, shapeTransform: Transform, #by_ptr proxy: ShapeProxy) -> bool ---
// Ray cast versus sphere in local space. A zero length ray is a point query. Initial overlap
// reports a hit at the ray origin with zero fraction and zero normal.
RayCastSphere :: proc(#by_ptr shape: Sphere, #by_ptr input: RayCastInput) -> CastOutput ---
// Ray cast versus a hollow sphere shell in local space. Unlike the solid sphere a ray starting
// inside is not an overlap: it passes through and hits the far wall.
RayCastHollowSphere :: proc(#by_ptr shape: Sphere, #by_ptr input: RayCastInput) -> CastOutput ---
// Ray cast versus capsule in local space. A zero length ray is a point query. Initial overlap
// reports a hit at the ray origin with zero fraction and zero normal.
RayCastCapsule :: proc(#by_ptr shape: Capsule, #by_ptr input: RayCastInput) -> CastOutput ---
// Ray cast versus compound in local space. A zero length ray is a point query. Initial overlap
// with a child reports a hit at the ray origin with zero fraction and zero normal.
RayCastCompound :: proc(#by_ptr shape: CompoundData, #by_ptr input: RayCastInput) -> CastOutput ---
// Ray cast versus hull shape in local space. A zero length ray is a point query. Initial overlap
// reports a hit at the ray origin with zero fraction and zero normal.
RayCastHull :: proc(#by_ptr shape: HullData, #by_ptr input: RayCastInput) -> CastOutput ---
// Ray cast versus mesh in local space. A thin surface with no interior, so there is no overlap case.
RayCastMesh :: proc(#by_ptr shape: Mesh, #by_ptr input: RayCastInput) -> CastOutput ---
// Ray cast versus height field in local space. A thin surface with no interior, so there is no overlap case.
RayCastHeightField :: proc(#by_ptr shape: HeightFieldData, #by_ptr input: RayCastInput) -> CastOutput ---
// Shape cast versus a sphere. Initial overlap is treated as a miss.
ShapeCastSphere :: proc(#by_ptr shape: Sphere, #by_ptr input: ShapeCastInput) -> CastOutput ---
// Shape cast versus a capsule. Initial overlap is treated as a miss.
ShapeCastCapsule :: proc(#by_ptr shape: Capsule, #by_ptr input: ShapeCastInput) -> CastOutput ---
// Shape cast versus compound. Initial overlap is treated as a miss.
ShapeCastCompound :: proc(#by_ptr shape: CompoundData, #by_ptr input: ShapeCastInput) -> CastOutput ---
// Shape cast versus a hull. Initial overlap is treated as a miss.
ShapeCastHull :: proc(#by_ptr shape: HullData, #by_ptr input: ShapeCastInput) -> CastOutput ---
// Shape cast versus a mesh. Initial overlap is treated as a miss.
ShapeCastMesh :: proc(#by_ptr shape: Mesh, #by_ptr input: ShapeCastInput) -> CastOutput ---
// Shape cast versus a height field. Initial overlap is treated as a miss.
ShapeCastHeightField :: proc(#by_ptr shape: HeightFieldData, #by_ptr input: ShapeCastInput) -> CastOutput ---
// Query a mesh for triangles overlapping a bounding box in local space. May have false positives. Useful for debug draw.
// @param mesh the mesh to query, includes scale
// @param bounds the bounding box in local space
// @param fcn a user function to collect triangles
// @param context the context sent to the user function.
QueryMesh :: proc(#by_ptr mesh: Mesh, bounds: AABB, fcn: MeshQueryFcn, ctx: rawptr) ---
// Query a height field for triangles overlapping a bounding box in local space. May have false positives. Useful for debug draw.
// @param heightField the height field to query
// @param bounds the bounding box in local space
// @param fcn a user function to collect triangles
// @param context the context sent to the user function.
QueryHeightField :: proc(#by_ptr heightField: HeightFieldData, bounds: AABB, fcn: MeshQueryFcn, ctx: rawptr) ---
// Compute the closest points between two shapes represented as point clouds.
// SimplexCache cache is input/output. On the first call set SimplexCache.count to zero.
// The query runs in frame A, so the witness points and normal are returned in frame A.
// The underlying GJK algorithm may be debugged by passing in debug simplexes and capacity. You may pass in NULL and 0 for these.
ShapeDistance :: proc(#by_ptr input: DistanceInput, cache: ^SimplexCache, simplexes: [^]Simplex, simplexCapacity: c.int) -> DistanceOutput ---
// Perform a linear shape cast of shape B moving and shape A fixed. Determines the hit point, normal, and translation fraction.
// The query runs in frame A, so the hit point and normal are returned in frame A. Initially touching shapes are a miss.
ShapeCast :: proc(#by_ptr input: ShapeCastPairInput) -> CastOutput ---
// Evaluate the transform sweep at a specific time.
GetSweepTransform :: proc(#by_ptr sweep: Sweep, time: f32) -> Transform ---
// Compute the upper bound on time before two shapes penetrate. Time is represented as
// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate,
// non-tunneling collisions. If you change the time interval, you should call this function
// again.
TimeOfImpact :: proc(#by_ptr input: TOIInput) -> TOIOutput ---
/**@}*/ // query
/**
* @addtogroup collision
* @{
*/
// Collide two spheres.
CollideSpheres :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr sphereA, sphereB: Sphere, transformBtoA: Transform) ---
// Collide a capsule and a sphere.
CollideCapsuleAndSphere :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr capsuleA: Capsule, #by_ptr sphereB: Sphere, transformBtoA: Transform) ---
// Collide a hull and a sphere.
CollideHullAndSphere :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr hullA: HullData, #by_ptr sphereB: Sphere, transformBtoA: Transform, cache: ^SimplexCache) ---
// Collide two capsules.
CollideCapsules :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr capsuleA, capsuleB: Capsule, transformBtoA: Transform) ---
// Collide a hull and a capsule.
CollideHullAndCapsule :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr hullA: HullData, #by_ptr capsuleB: Capsule, transformBtoA: Transform, cache: ^SimplexCache) ---
// Collide two hulls.
CollideHulls :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr hullA: HullData, #by_ptr hullB: HullData, transformBtoA: Transform, cache: ^SATCache) ---
// Collide a capsule and a triangle.
CollideCapsuleAndTriangle :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr capsuleA: Capsule, #by_ptr triangleB: [3]Vec3, cache: ^SimplexCache) ---
// Collide a hull and a triangle.
CollideHullAndTriangle :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr hullA: HullData, v1, v2, v3: Vec3, triangleFlags: c.int, cache: ^SATCache) ---
// Collide a sphere and a triangle.
CollideSphereAndTriangle :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr sphereA: Sphere, #by_ptr triangleB: [3]Vec3) ---
/**@}*/ // collision
/**
* @addtogroup character
* @{
*/
// Solves the position of a mover that satisfies the given collision planes.
// @param targetDelta the desired translation from the position used to generate the collision planes
// @param planes the collision planes
// @param count the number of collision planes
SolvePlanes :: proc(targetDelta: Vec3, planes: [^]CollisionPlane, count: c.int) -> PlaneSolverResult ---
// Clips the velocity against the given collision planes. Planes with zero push or clipVelocity
// set to false are skipped.
ClipVector :: proc(vector: Vec3, planes: [^]CollisionPlane, count: c.int) -> Vec3 ---
/**@}*/ // character
}
// Get proxy user data
@(require_results)
DynamicTree_GetUserData :: proc "c" (#by_ptr tree: DynamicTree, proxyId: c.int) -> u64 {
return tree.nodes[proxyId].userData
}
// Get the AABB of a proxy
@(require_results)
DynamicTree_GetAABB :: proc "c" (#by_ptr tree: DynamicTree, proxyId: c.int) -> AABB {
return tree.nodes[proxyId].aabb
}
// Get read only hull vertices.
@(require_results)
GetHullVertices :: proc "c" (hull: ^HullData) -> Maybe(^HullVertex) {
if hull.vertexOffset == 0 {
return nil
}
return (^HullVertex)(uintptr(hull) + uintptr(hull.vertexOffset))
}
// Get read only hull points.
@(require_results)
GetHullPoints :: proc "c" (hull: ^HullData) -> Maybe(^Vec3) {
if hull.pointOffset == 0 {
return nil
}
return (^Vec3)(uintptr(hull) + uintptr(hull.pointOffset))
}
// Get read only hull half edges.
@(require_results)
GetHullEdges :: proc "c" (hull: ^HullData) -> Maybe(^HullHalfEdge) {
if hull.edgeOffset == 0 {
return nil
}
return (^HullHalfEdge)(uintptr(hull) + uintptr(hull.edgeOffset))
}
// Get read only hull faces.
@(require_results)
GetHullFaces :: proc "c" (hull: ^HullData) -> Maybe(^HullFace) {
if hull.faceOffset == 0 {
return nil
}
return (^HullFace)(uintptr(hull) + uintptr(hull.faceOffset))
}
// Get read only hull planes.
@(require_results)
GetHullPlanes :: proc "c" (hull: ^HullData) -> Maybe(^Plane) {
if hull.planeOffset == 0 {
return nil
}
return (^Plane)(uintptr(hull) + uintptr(hull.planeOffset))
}
// Get read only mesh BVH nodes.
@(require_results)
GetMeshNodes :: proc "c" (mesh: ^MeshData) -> Maybe(^MeshNode) {
if mesh.nodeOffset == 0 {
return nil
}
return (^MeshNode)(uintptr(mesh) + uintptr(mesh.nodeOffset))
}
// Get read only mesh vertices.
@(require_results)
GetMeshVertices :: proc "c" (mesh: ^MeshData) -> Maybe(^Vec3) {
if mesh.vertexOffset == 0 {
return nil
}
return (^Vec3)(uintptr(mesh) + uintptr(mesh.vertexOffset))
}
// Get read only mesh triangles.
@(require_results)
GetMeshTriangles :: proc "c" (mesh: ^MeshData) -> Maybe(^MeshTriangle) {
if mesh.triangleOffset == 0 {
return nil
}
return (^MeshTriangle)(uintptr(mesh) + uintptr(mesh.triangleOffset))
}
// Get read only mesh materials. The count is equal to the triangle count.
@(require_results)
GetMeshMaterialIndices :: proc "c" (mesh: ^MeshData) -> Maybe([^]u8) {
if mesh.materialOffset == 0 {
return nil
}
return ([^]u8)(uintptr(mesh) + uintptr(mesh.materialOffset))
}
// Get read only mesh flags. The count is equal to the triangle count.
@(require_results)
GetMeshFlags :: proc "c" (mesh: ^MeshData) -> [^]u8 {
if mesh.flagsOffset == 0 {
return nil
}
return ([^]u8)(uintptr(mesh) + uintptr(mesh.flagsOffset))
}
// Get read only compressed heights. One u16 per grid point.
@(require_results)
GetHeightFieldCompressedHeights :: proc "c" (hf: ^HeightFieldData) -> [^]u16 {
if hf.heightsOffset == 0 {
return nil
}
return ([^]u16)(uintptr(hf) + uintptr(hf.heightsOffset))
}
// Get read only material indices. One u8 per cell.
@(require_results)
GetHeightFieldMaterialIndices :: proc "c" (hf: ^HeightFieldData) -> [^]u8 {
if hf.materialOffset == 0 {
return nil
}
return ([^]u8)(uintptr(hf) + uintptr(hf.materialOffset))
}
// Get read only triangle flags. One u8 per triangle.
@(require_results)
GetHeightFieldFlags :: proc "c" (hf: ^HeightFieldData) -> [^]u8 {
if hf.flagsOffset == 0 {
return nil
}
return ([^]u8)(uintptr(hf) + uintptr(hf.flagsOffset))
}

140
vendor/box3d/box3d_constants.odin vendored Normal file
View File

@@ -0,0 +1,140 @@
package vendor_box3d
@(link_prefix="b3", default_calling_convention="c")
foreign lib {
// Box3D bases all length units on meters, but you may need different units for your game.
// You can set this value to use different units. This should be done at application startup
// and only modified once. Default value is 1.
// @warning This must be modified before any calls to Box3D
SetLengthUnitsPerMeter :: proc(lengthUnits: f32) ---
// Get the current length units per meter.
GetLengthUnitsPerMeter :: proc() -> f32 ---
// Set the threshold for logging stalls.
SetStallThreshold :: proc(seconds: f32) ---
// Get the threshold for logging stalls.
GetStallThreshold :: proc() -> f32 ---
}
// Used to detect bad values. In float mode positions greater than about 16km have precision
// problems, so 100km is a safe limit. Large world mode keeps coordinates accurate much farther
// from the origin, so the sanity limit widens to keep valid far-field positions from tripping it.
@(require_results)
HUGE :: #force_inline proc "c" () -> f32 {
when DOUBLE_PRECISION {
return 1.0e9 * GetLengthUnitsPerMeter()
} else {
return 1.0e5 * GetLengthUnitsPerMeter()
}
}
// Maximum parallel workers. Used for some fixed size arrays.
MAX_WORKERS :: 32
// Maximum number of tasks queued per world step. b3EnqueueTaskCallback will never be called
// more than this per world step. This is related to B3_MAX_WORKERS. With 32 workers,
// the maximum observed task count is 130. This allows an external task system to use a fixed
// size array for Box3D task, which may help with creating stable user task pointers.
MAX_TASKS :: 256
// Maximum number of colors in the constraint graph. Constraints that cannot
// find a color are added to the overflow set which are solved single-threaded.
// The compound barrel benchmark has minor overflow with 24 colors
GRAPH_COLOR_COUNT :: 24
// Number of contact point buckets for counting the number of contact points per
// shape contact pair. This is just for reporting and doesn't affect simulation.
CONTACT_MANIFOLD_COUNT_BUCKETS :: 8
// A small length used as a collision and constraint tolerance. Usually it is
// chosen to be numerically significant, but visually insignificant. In meters.
// @warning modifying this can have a significant impact on stability
@(require_results)
LINEAR_SLOP :: #force_inline proc "c" () -> f32 {
return 0.005 * GetLengthUnitsPerMeter()
}
@(require_results)
MIN_CAPSULE_LENGTH :: #force_inline proc "c" () -> f32 {
return LINEAR_SLOP()
}
// The distance between shapes where they are considered overlapped. This is needed
// because GJK may return small positive values for overlapped shapes in degenerate
// configurations.
@(require_results)
OVERLAP_SLOP :: #force_inline proc "c" () -> f32 {
return 0.1 * LINEAR_SLOP()
}
// Maximum number of simultaneous worlds that can be allocated
MAX_WORLDS :: 128
// The maximum rotation of a body per time step. This limit is very large and is used
// to prevent numerical problems. You shouldn't need to adjust this.
// @warning increasing this to 0.5f * B3_PI or greater will break continuous collision.
MAX_ROTATION :: 0.25 * PI
// @warning modifying this can have a significant impact on performance and stability
@(require_results)
SPECULATIVE_DISTANCE :: #force_inline proc "c" () -> f32 {
return 4.0 * LINEAR_SLOP()
}
// The rest offset is used for mesh contact to reduce ghost collisions and assist with CCD.
// The rest offset adjusts the contact point separation value, making the solver push the shapes
// apart by this distance.
// Must be at least B3_LINEAR_SLOP and less than B3_SPECULATIVE_DISTANCE.
@(require_results)
MESH_REST_OFFSET :: #force_inline proc "c" () -> f32 {
return 1.0 * LINEAR_SLOP()
}
// The default contact recycling distance.
@(require_results)
CONTACT_RECYCLE_DISTANCE :: #force_inline proc "c" () -> f32 {
return 10.0 * LINEAR_SLOP()
}
// The default contact recycling world angle threshold. For performance this value
// is cos(angle/2)^2. This value corresponds to 10 degrees.
CONTACT_RECYCLE_ANGULAR_DISTANCE :: 0.99240388
// This is used to fatten AABBs in the dynamic tree. This allows proxies
// to move by a small amount without triggering a tree adjustment. This is in meters.
// @warning modifying this can have a significant impact on performance
@(require_results)
MAX_AABB_MARGIN :: #force_inline proc "c" () -> f32 {
return 0.05 * GetLengthUnitsPerMeter()
}
// Per-shape AABB margin is a fraction of the shape extent (capped by B3_MAX_AABB_MARGIN).
// Small shapes get small margins; large shapes are clamped to the cap.
AABB_MARGIN_FRACTION :: 0.125
// The time that a body must be still before it will go to sleep. In seconds.
TIME_TO_SLEEP :: 0.5
// Maximum length of the body name. Can be 0 if you don't need names.
// Note: this gates recording capability.
BODY_NAME_LENGTH :: 18
// Maximum length of the shape name. Can be 0 if you don't need names.
// Note: this gates recording capability.
// todo waiting on this because it breaks existing recordings
SHAPE_NAME_LENGTH :: 18
// The maximum number of contact points between two touching shapes.
MAX_MANIFOLD_POINTS :: 4
// The maximum number points to use for shape cast proxies (swept point cloud).
MAX_SHAPE_CAST_POINTS :: 64
// These generous limits allow for easy hashing. See b3ShapePairKey.
SHAPE_POWER :: 22
CHILD_POWER :: 64 - 2 * SHAPE_POWER
MAX_SHAPES :: 1 << SHAPE_POWER
MAX_CHILD_SHAPES :: 1 << CHILD_POWER

129
vendor/box3d/box3d_id.odin vendored Normal file
View File

@@ -0,0 +1,129 @@
package vendor_box3d
/// World id references a world instance. This should be treated as an opaque handle.
WorldId :: struct {
index1: u16,
generation: u16,
}
/// Body id references a body instance. This should be treated as an opaque handle.
BodyId :: struct {
index1: i32,
world0: u16,
generation: u16,
}
/// Shape id references a shape instance. This should be treated as an opaque handle.
ShapeId :: struct {
index1: i32,
world0: u16,
generation: u16,
}
/// Joint id references a joint instance. This should be treated as an opaque handle.
JointId :: struct {
index1: i32,
world0: u16,
generation: u16,
}
/// Contact id references a contact instance. This should be treated as an opaque handle.
ContactId :: struct {
index1: i32,
world0: u16,
_: i16,
generation: u32,
}
/// Use these to make your identifiers null.
/// You may also use zero initialization to get null.
nullWorldId :: WorldId{}
nullBodyId :: BodyId{}
nullShapeId :: ShapeId{}
nullJointId :: JointId{}
nullContactId :: ContactId{}
/// Macro to determine if any id is null.
@(require_results)
IS_NULL :: #force_inline proc "c" (id: $T) -> bool {
return id.index1 == 0
}
/// Macro to determine if any id is non-null.
@(require_results)
IS_NON_NULL :: #force_inline proc "c" (id: $T) -> bool {
return id.index1 != 0
}
/// Compare two ids for equality. Doesn't work for b3WorldId. Don't mix types.
@(require_results)
ID_EQUALS :: #force_inline proc "c" (id1, id2: $T) -> bool {
return id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.generation == id2.generation
}
/// Store a world id into a u32.
@(require_results)
StoreWorldId :: #force_inline proc "c" (id: WorldId) -> u32 {
return u32(id.index1) << 16 | u32(id.generation)
}
/// Load a u32 into a world id.
@(require_results)
LoadWorldId :: #force_inline proc "c" (x: u32) -> WorldId {
return WorldId{u16(x >> 16), u16(x)}
}
/// Store a body id into a u64.
@(require_results)
StoreBodyId :: #force_inline proc "c" (id: BodyId) -> u64 {
return (u64(id.index1) << 32) | u64(id.world0) << 16 | u64(id.generation)
}
/// Load a u64 into a body id.
@(require_results)
LoadBodyId :: #force_inline proc "c" (x: u64) -> BodyId {
return BodyId{i32(x >> 32), u16(x >> 16), u16(x)}
}
/// Store a shape id into a u64.
@(require_results)
StoreShapeId :: #force_inline proc "c" (id: ShapeId) -> u64 {
return (u64(id.index1) << 32) | u64(id.world0) << 16 | u64(id.generation)
}
/// Load a u64 into a shape id.
@(require_results)
LoadShapeId :: #force_inline proc "c" (x: u64) -> ShapeId {
return ShapeId{i32(x >> 32), u16(x >> 16), u16(x)}
}
/// Store a joint id into a u64.
@(require_results)
StoreJointId :: #force_inline proc "c" (id: JointId) -> u64 {
return (u64(id.index1) << 32) | u64(id.world0) << 16 | u64(id.generation)
}
/// Load a u64 into a joint id.
@(require_results)
LoadJointId :: #force_inline proc "c" (x: u64) -> JointId {
return JointId{i32(x >> 32), u16(x >> 16), u16(x)}
}
/// Store a contact id into three uint32 values
@(require_results)
StoreContactId :: #force_inline proc "c" (id: ContactId) -> (values: [3]u32) {
values[0] = u32(id.index1)
values[1] = u32(id.world0)
values[2] = u32(id.generation)
return
}
/// Load a contact id from three uint32 values.
@(require_results)
LoadContactId :: #force_inline proc "c" (values: [3]u32) -> (id: ContactId) {
id.index1 = i32(values[0])
id.world0 = u16(values[1])
id.generation = u32(values[2])
return
}

747
vendor/box3d/box3d_math.odin vendored Normal file
View File

@@ -0,0 +1,747 @@
package vendor_box3d
import "base:builtin"
import "core:math"
import "core:math/linalg"
DOUBLE_PRECISION :: false
// https://en.wikipedia.org/wiki/Pi
PI :: math.PI
// Convenience constant to convert from degrees to radians.
DEG_TO_RAD :: math.RAD_PER_DEG
// Convenience constant to convert from radians to degrees.
RAD_TO_DEG :: math.DEG_PER_RAD
// Minimum scale used for scaling collision meshes, etc.
MIN_SCALE :: 0.01
Vec2 :: [2]f32
Vec3 :: [3]f32
// Cosine and sine pair.
// This uses a custom implementation designed for cross-platform determinism.
CosSin :: struct {
cosine: f32,
sine: f32,
}
Quat :: quaternion128
Transform :: struct {
p: Vec3,
q: Quat,
}
Pos :: [3]f64 when DOUBLE_PRECISION else [3]f32
WorldTransform :: struct {
p: Pos,
q: Quat,
}
Matrix3 :: matrix[3, 3]f32
// Axis aligned bounding box.
AABB :: struct {
lowerBound: Vec3,
upperBound: Vec3,
}
// A plane.
// separation = dot(normal, point) - offset
Plane :: struct {
normal: Vec3,
offset: f32,
}
Vec3_zero :: Vec3{0.0, 0.0, 0.0}
Vec3_one :: Vec3{1.0, 1.0, 1.0}
Vec3_axisX :: Vec3{1.0, 0.0, 0.0}
Vec3_axisY :: Vec3{0.0, 1.0, 0.0}
Vec3_axisZ :: Vec3{0.0, 0.0, 1.0}
Quat_identity :: Quat(1)
Transform_identity :: Transform{p={0, 0, 0}, q=1}
Mat3_zero :: Matrix3(0)
Mat3_identity :: Matrix3(1)
Pos_zero :: Pos{0.0, 0.0, 0.0}
WorldTransform_identity :: WorldTransform{p={0, 0, 0}, q=1}
// @return the minimum of two integers.
MinInt :: builtin.min
MaxInt :: builtin.max
ClampInt :: builtin.clamp
// The closest points between to segments or infinite lines.
SegmentDistanceResult :: struct {
point1: Vec3,
fraction1: f32,
point2: Vec3,
fraction2: f32,
}
@(link_prefix="b3", default_calling_convention="c", require_results)
foreign lib {
// @return is this float valid (finite and not NaN).
IsValidFloat :: proc(a: f32) -> bool ---
// Compute an approximate arctangent in the range [-pi, pi]
// This is hand coded for cross-platform determinism. The atan2f
// function in the standard library is not cross-platform deterministic.
// Accurate to around 0.0023 degrees.
Atan2 :: proc(y, x: f32) -> f32 ---
// Compute the cosine and sine of an angle in radians. Implemented
// for cross-platform determinism.
ComputeCosSin :: proc(radians: f32) -> CosSin ---
// Compute the closest point on the segment a-b to the target q.
PointToSegmentDistance :: proc(a, b: Vec3, q: Vec3) -> Vec3 ---
// Compute the closest points on two infinite lines.
LineDistance :: proc(p1, d1: Vec3, p2, d2: Vec3) -> SegmentDistanceResult ---
// Compute the closest points on two line segments.
SegmentDistance :: proc(p1, q1: Vec3, p2, q2: Vec3) -> SegmentDistanceResult ---
// Is this a valid vector? Not NaN or infinity.
IsValidVec3 :: proc(a: Vec3) -> bool ---
// Is this a valid quaternion? Not NaN or infinity. Is normalized.
IsValidQuat :: proc(q: Quat) -> bool ---
// Is this a valid transform? Not NaN or infinity. Is normalized.
IsValidTransform :: proc(a: Transform) -> bool ---
// Is this a valid matrix? Not NaN or infinity.
IsValidMatrix3 :: proc(a: Matrix3) -> bool ---
// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound.
IsValidAABB :: proc(a: AABB) -> bool ---
// Is this AABB reasonably close to the origin? See B3_HUGE.
IsBoundedAABB :: proc(a: AABB) -> bool ---
// Is this AABB valid and reasonable?
IsSaneAABB :: proc(a: AABB) -> bool ---
// Is this a valid plane? Normal is a unit vector. Not Nan or infinity.
IsValidPlane :: proc(a: Plane) -> bool ---
// Is this a valid world position? Not NaN or infinity.
IsValidPosition :: proc(p: Pos) -> bool ---
// Is this a valid world transform? Not NaN or infinity. Rotation is normalized.
IsValidWorldTransform :: proc(t: WorldTransform) -> bool ---
// Get the inertia tensor of an offset point.
// https://en.wikipedia.org/wiki/Parallel_axis_theorem
Steiner :: proc(mass: f32, origin: Vec3) -> Matrix3 ---
// Extract a quaternion from a rotation matrix.
MakeQuatFromMatrix :: proc(#by_ptr m: Matrix3) -> Quat ---
// Find a quaternion that rotates one vector to another.
ComputeQuatBetweenUnitVectors :: proc(v1, v2: Vec3) -> Quat ---
}
AbsFloat :: builtin.abs
MinFloat :: builtin.min
MaxFloat :: builtin.max
ClampFloat :: builtin.clamp
LerpFloat :: math.lerp
// Convert any angle into the range [-pi, pi].
@(require_results)
UnwindAngle :: #force_inline proc "c" (radians: f32) -> f32 {
// Assuming this is deterministic
return math.remainder(radians, 2.0 * PI)
}
// Vector dot product.
Dot :: linalg.dot
Length :: linalg.length
LengthSquared :: linalg.length2
// Distance between two points.
@(require_results)
Distance :: #force_inline proc "c" (a, b: Vec3) -> f32 {
dv := b - a
return Length(dv)
}
// Squared distance between two points.
@(require_results)
DistanceSquared :: #force_inline proc "c" (a, b: Vec3) -> f32 {
dv := b - a
return LengthSquared(dv)
}
FLT_EPSILON :: 1.1920928955078125e-07 /* 0x0.000002p0 */
// Normalize a vector. Returns a zero vector if the input vector is very small.
@(require_results)
Normalize :: proc "c" (a: Vec3) -> Vec3 {
lengthSquared := a.x * a.x + a.y * a.y + a.z * a.z
if lengthSquared > 1000.0 * min(f32) {
s := 1.0 / math.sqrt(lengthSquared)
return Vec3{ s * a.x, s * a.y, s * a.z }
}
return Vec3{0.0, 0.0, 0.0}
}
// Normalize a vector and return the length. Returns a zero vector
// if the input is very small.
@(require_results)
GetLengthAndNormalize :: proc "c" (a: Vec3) -> (length: f32, n: Vec3) {
length = Length(a)
if length < FLT_EPSILON {
return
}
invLength := 1.0 / length
n = {invLength * a.x, invLength * a.y, invLength * a.z}
return
}
// Get a unit vector that is perpendicular to the supplied vector.
@(require_results)
Perp :: proc "c" (a: Vec3) -> Vec3 {
// Suppose vector a has all equal components and is a unit vector: a = (s, s, s)
// Then 3*s*s = 1, s = sqrt(1/3) = 0.57735. This means that at least one component
// of a unit vector must be greater or equal to 0.57735.
p: Vec3
if a.x < -0.5 || 0.5 < a.x {
p = {a.y, -a.x, 0.0}
} else {
p = {0.0, a.z, -a.y}
}
return Normalize(p)
}
// Is a vector normalized? In other words, does it have unit length?
@(require_results)
IsNormalized :: proc "c" (a: Vec3) -> bool {
aa := Dot(a, a)
return AbsFloat(1.0 - aa) < 100.0 * FLT_EPSILON
}
// https://en.wikipedia.org/wiki/Cross_product
Cross :: linalg.cross
// Linearly interpolate between two vectors.
Lerp :: linalg.lerp
// Blend two vectors: s * a + t * b
@(require_results)
Blend2 :: proc "c" (s: f32, a: Vec3, t: f32, b: Vec3) -> Vec3 {
return {
s * a.x + t * b.x,
s * a.y + t * b.y,
s * a.z + t * b.z,
}
}
// Component-wise absolute value.
Abs :: linalg.abs
// Component-wise -1 or 1 (1 if zero).
Sign :: linalg.sign
// Component-wise minimum value.
Min :: linalg.min
// Component-wise maximum value.
Max :: linalg.max
// Component-wise clamped value.
Clamp :: linalg.clamp
// Create a safe scaling value for scaling collision. This allows
// negative scale, but keeps scale sufficiently far from zero.
@(require_results)
SafeScale :: proc "c" (a: Vec3) -> Vec3 {
absScale := Abs(a)
minScale := Vec3{MIN_SCALE, MIN_SCALE, MIN_SCALE}
safeScale := Sign(a) * Max(absScale, minScale)
return safeScale
}
// Does the supplied quaternion have unit length?
@(require_results)
IsNormalizedQuat :: proc "c" (q: Quat) -> bool {
qq := q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w
return 1.0 - 20.0 * FLT_EPSILON < qq && qq < 1.0 + 20.0 * FLT_EPSILON
}
// Rotate a vector.
@(require_results)
RotateVector :: proc "c" (q: Quat, v: Vec3) -> Vec3 {
// v + 2 * cross(q.xyz, cross(q.xyz, v) + q.w * v)
// B3_ASSERT(IsNormalizedQuat(q));
t1 := Cross(q.xyz, v)
t2 := t1 * q.w + v
t3 := Cross(q.xyz, t2)
return v * 2.0 + t3
}
// Inverse rotate a vector.
@(require_results)
InvRotateVector :: proc "c" (q: Quat, v: Vec3) -> Vec3 {
// v + 2 * cross(q.xyz, cross(q.xyz, v) - q.w * v)
// B3_ASSERT(IsNormalizedQuat(q));
t1 := Cross(q.xyz, v)
t2 := t1 * q.w - v
t3 := Cross(q.xyz, t2)
return v * 2.0 + t3
}
// Compute dot product of two quaternions. Useful for polarity tests.
@(require_results)
DotQuat :: linalg.dot
// Multiply two quaternions.
@(require_results)
MulQuat :: proc "c" (q1, q2: Quat) -> Quat {
return q1 * q2
}
// Compute a relative quaternion.
// inv(q1) * q2
@(require_results)
InvMulQuat :: proc "c" (q1, q2: Quat) -> Quat {
t1 := Cross(q2.xyz, q1.xyz)
t2 := t1 * q1.w + q2.xyz
t3 := t2 * q2.w - q1.xyz
return quaternion(x=t3.x, y=t3.y, z=t3.z, w=q1.w * q2.w + Dot(q1.xyz, q2.xyz))
}
// Quaternion conjugate (cheap inverse).
Conjugate :: builtin.conj
// Component-wise quaternion negation.
@(require_results)
NegateQuat :: proc "c" (q: Quat) -> Quat {
return -q
}
// Normalize a quaternion.
@(require_results)
NormalizeQuat :: proc "c" (q: Quat) -> Quat {
lengthSq := DotQuat(q, q)
if lengthSq > 1000.0 * min(f32) {
s := 1.0 / math.sqrt(lengthSq)
return Quat(s) * q
}
return Quat_identity
}
// Make a quaternion that is equivalent to rotating around an axis by a specified angle.
@(require_results)
MakeQuatFromAxisAngle :: proc "c" (axis: Vec3, radians: f32) -> Quat {
ASSERT(IsNormalized(axis))
cs := ComputeCosSin(0.5 * radians)
return quaternion(x=cs.sine * axis.x, y=cs.sine * axis.y, z=cs.sine * axis.z, w=cs.cosine)
}
// Get the axis and angle from a quaternion. Assumes the quaternion is normalized.
@(require_results)
GetAxisAngle :: proc "c" (q: Quat) -> (radians: f32, axis: Vec3) {
length := math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z)
radians = 2.0 * Atan2(length, q.w)
if length > 0.0 {
invLength := 1.0 / length
axis = {invLength * q.x, invLength * q.y, invLength * q.z}
}
return
}
// Get the angle for a quaternion in radians
@(require_results)
GetQuatAngle :: proc "c" (q: Quat) -> f32 {
length := math.sqrt(q.x * q.x + q.y * q.y + q.z * q.z)
return 2.0 * Atan2(length, q.w)
}
// Twist angle around the z-axis, used for twist limit and revolute angle limit
@(require_results)
GetTwistAngle :: proc "c" (q: Quat) -> f32 {
// Account for polarity to keep the twist angle in range.
// This is simpler than asking the user to check polarity or unwinding.
twist := q.w < 0.0 ? Atan2(-q.z, -q.w) : Atan2(q.z, q.w)
twist *= 2.0
ASSERT(-PI <= twist && twist <= PI)
return twist
}
// Swing angle used for cone limit
@(require_results)
GetSwingAngle :: proc "c" (q: Quat) -> f32 {
// Polarity should not matter because all terms are squared.
x := math.sqrt(q.z * q.z + q.w * q.w)
y := math.sqrt(q.x * q.x + q.y * q.y)
swing := 2.0 * Atan2(y, x)
ASSERT(0.0 <= swing && swing <= PI)
return swing
}
// Linearly interpolate and normalize between two quaternions
@(require_results)
NLerp :: proc "c" (q1, q2: Quat, alpha: f32) -> Quat {
VALIDATE(0.0 <= alpha && alpha <= 1.0)
q1 := q1
if DotQuat(q1, q2) < 0.0 {
q1 = -q1
}
q: Quat
q.xyz = Lerp(q1.xyz, q2.xyz, alpha)
q.w = (1.0 - alpha) * q1.w + alpha * q2.w
return NormalizeQuat(q)
}
// Multiply two transforms. If the result is applied to a point p local to frame B,
// the transform would first convert p to a point local to frame A, then into a point
// in the world frame. This is useful if frame B is a child of frame A.
@(require_results)
MulTransforms :: proc "c" (a, b: Transform) -> (out: Transform) {
out.p = RotateVector(a.q, b.p) + a.p
out.q = a.q * b.q
return
}
// Creates a transform that converts a local point in frame B to a local point in frame A.
// This is useful for transforming points between the local spaces of two frames that are
// in world space.
@(require_results)
InvMulTransforms :: #force_inline proc "c" (a, b: Transform) -> (out: Transform) {
out.p = InvRotateVector(a.q, b.p - a.p)
out.q = InvMulQuat(a.q, b.q)
return
}
// Get the inverse of a transform.
@(require_results)
InvertTransform :: proc "c" (t: Transform) -> (out: Transform) {
out.p = InvRotateVector(t.q, -t.p)
out.q = Conjugate(t.q)
return
}
// Transform a point.
@(require_results)
TransformPoint :: proc "c" (t: Transform, v: Vec3) -> Vec3 {
rv := RotateVector(t.q, v)
return rv + t.p
}
// Inverse transform a point.
@(require_results)
InvTransformPoint :: proc "c" (t: Transform, v: Vec3) -> Vec3 {
return InvRotateVector(t.q, v - t.p)
}
// World position boundary. These cross between the double precision world space at the public
// boundary and the float interior. One set of bodies serves both modes: the typedefs collapse
// the types in float mode and the explicit float casts become no-ops.
// Convert a vector to a world position.
@(require_results)
ToPos :: proc "c" (v: Vec3) -> Pos {
T :: type_of(Pos{}.x)
return {T(v.x), T(v.y), T(v.z)}
}
// Lossy conversion of a world position to a float vector.
@(require_results)
ToVec3 :: proc "c" (p: Pos) -> Vec3 {
return {f32(p.x), f32(p.y), f32(p.z)}
}
// Narrow a world coordinate to float, rounding toward negative infinity. Use with
// RoundUpFloat to build a conservative float box that always contains the double bounds,
// where plain rounding far from the origin could clip. nextafterf is an exact IEEE operation,
// so this is cross-platform deterministic. With large world mode off this is a plain conversion.
@(require_results)
RoundDownFloat :: proc "c" (x: f64) -> f32 {
when DOUBLE_PRECISION {
f := f32(x)
return f64(f) > f64(x) ? math.nextafter(f, -max(f32)) : f
} else {
return f32(x)
}
}
// Narrow a world coordinate to float, rounding toward positive infinity.
@(require_results)
RoundUpFloat :: proc "c" (x: f32) -> f32 {
when DOUBLE_PRECISION {
f := f32(x)
return f64(f) < f64(x) ? math.nextafter(f, max(f32)) : f
} else {
return f32(x)
}
}
// p + d
@(require_results)
OffsetPos :: proc "c" (p: Pos, d: Vec3) -> Pos {
T :: type_of(Pos{}.x)
return {p.x + T(d.x), p.y + T(d.y), p.z + T(d.z)}
}
// World position interpolation for sweeps and sampling.
LerpPosition :: linalg.lerp
// Transform a local point to a world position. Rotation in float, translation in double.
@(require_results)
TransformWorldPoint :: proc "c" (t: WorldTransform, p: Vec3) -> Pos {
T :: type_of(Pos{}.x)
r := RotateVector(t.q, p)
return {T(t.p.x + r.x), T(t.p.y + r.y), T(t.p.z + r.z)}
}
// Transform a world position to a local point. One double subtraction, then float.
@(require_results)
InvTransformWorldPoint :: proc "c" (t: WorldTransform, p: Pos) -> Vec3 {
d := Vec3{f32(p.x - t.p.x), f32(p.y - t.p.y), f32(p.z - t.p.z)}
return InvRotateVector(t.q, d)
}
// Relative transform of frame B in frame A. The narrow phase boundary.
@(require_results)
InvMulWorldTransforms :: proc "c" (A, B: WorldTransform) -> (C: Transform) {
C.q = InvMulQuat(A.q, B.q)
d := Vec3{f32(B.p.x - A.p.x), f32(B.p.y - A.p.y), f32(B.p.z - A.p.z)}
C.p = InvRotateVector(A.q, d)
return
}
// Compose a world transform with a local transform.
@(require_results)
MulWorldTransforms :: proc "c" (A: WorldTransform, B: Transform) -> (C: WorldTransform) {
T :: type_of(Pos{}.x)
C.q = A.q * B.q
r := RotateVector(A.q, B.p)
C.p = Pos{T(A.p.x + r.x), T(A.p.y + r.y), T(A.p.z + r.z)}
return
}
// Shift a world transform into the frame of a base position.
@(require_results)
ToRelativeTransform :: proc "c" (t: WorldTransform, base: Pos) -> (r: Transform) {
r.q = t.q
r.p = {f32(t.p.x - base.x), f32(t.p.y - base.y), f32(t.p.z - base.z)}
return
}
// Promote a float transform to a world transform. Lossless.
@(require_results)
MakeWorldTransform :: proc "c" (t: Transform) -> (w: WorldTransform) {
w.p = ToPos(t.p)
w.q = t.q
return
}
// Translate a local AABB by a world origin, rounding outward so the float box always contains
// the double box. Far from the origin a plain conversion could clip a shape out of its own box.
// In float mode the origin is float and the rounding is a no-op.
@(require_results)
OffsetAABB :: proc "c" (localBox: AABB, origin: Pos) -> (out: AABB) {
out.lowerBound.x = RoundDownFloat(f64(origin.x + localBox.lowerBound.x))
out.lowerBound.y = RoundDownFloat(f64(origin.y + localBox.lowerBound.y))
out.lowerBound.z = RoundDownFloat(f64(origin.z + localBox.lowerBound.z))
out.upperBound.x = RoundUpFloat(f32(origin.x + localBox.upperBound.x))
out.upperBound.y = RoundUpFloat(f32(origin.y + localBox.upperBound.y))
out.upperBound.z = RoundUpFloat(f32(origin.z + localBox.upperBound.z))
return
}
// Compute the determinant of a 3-by-3 matrix.
Det :: linalg.determinant
// Matrix transpose.
Transpose :: linalg.transpose
// General matrix inverse.
@(require_results)
InvertMatrix :: proc "c" (m: Matrix3) -> Matrix3 {
det := Det(m)
if AbsFloat(det) > 1000.0 * min(f32) {
invDet := 1.0 / det
out: Matrix3
out[0] = invDet * Cross(m[1], m[2])
out[1] = invDet * Cross(m[2], m[0])
out[2] = invDet * Cross(m[0], m[1])
return Transpose(out)
}
return Mat3_zero
}
// Solve a matrix equation.
// @return inv(m) * a
@(require_results)
Solve3 :: proc "c" (m: Matrix3, a: Vec3) -> Vec3 {
return InvertMatrix(m) * a
}
// Invert a matrix.
@(require_results)
InvertT :: proc "c" (m: Matrix3) -> Matrix3 {
det := Det(m)
if AbsFloat(det) > 1000.0 * min(f32) {
invDet := 1.0 / det
out: Matrix3
out[0] = invDet * Cross(m[1], m[2])
out[1] = invDet * Cross(m[2], m[0])
out[2] = invDet * Cross(m[0], m[1])
return out
}
return Mat3_zero
}
// Get the component-wise absolute value of a matrix.
@(require_results)
AbsMatrix3 :: proc "c" (m: Matrix3) -> (out: Matrix3) {
out[0] = Abs(m[0])
out[1] = Abs(m[1])
out[2] = Abs(m[2])
return
}
// Make a matrix from a quaternion. This is useful if you need to
// rotate many vectors.
// The force inline improves the performance of ShapeDistance.
@(require_results)
MakeMatrixFromQuat :: proc "c" (q: Quat) -> (out: Matrix3) {
xx := q.x * q.x
yy := q.y * q.y
zz := q.z * q.z
xy := q.x * q.y
xz := q.x * q.z
xw := q.x * q.w
yz := q.y * q.z
yw := q.y * q.w
zw := q.z * q.w
out[0] = { 1.0 - 2.0 * (yy + zz), 2.0 * (xy + zw), 2.0 * (xz - yw) }
out[1] = { 2.0 * (xy - zw), 1.0 - 2.0 * (xx + zz), 2.0 * (yz + xw) }
out[2] = { 2.0 * (xz + yw), 2.0 * (yz - xw), 1.0 - 2.0 * (xx + yy) }
return
}
// Get the AABB of a point cloud.
@(require_results)
MakeAABB :: proc "c" (points: []Vec3, radius: f32) -> (a: AABB) #no_bounds_check {
ASSERT(len(points) > 0)
a = AABB{points[0], points[0]}
for i in 1..<len(points) {
a.lowerBound = Min(a.lowerBound, points[i])
a.upperBound = Max(a.upperBound, points[i])
}
a.lowerBound = a.lowerBound - radius
a.upperBound = a.upperBound + radius
return
}
// Does a fully contain b?
@(require_results)
AABB_Contains :: proc "c" (a, b: AABB) -> bool {
switch {
case a.lowerBound.x > b.lowerBound.x || b.upperBound.x > a.upperBound.x:
return false
case a.lowerBound.y > b.lowerBound.y || b.upperBound.y > a.upperBound.y:
return false
case a.lowerBound.z > b.lowerBound.z || b.upperBound.z > a.upperBound.z:
return false
}
return true
}
// Get the surface area of an axis-aligned bounding box.
@(require_results)
AABB_Area :: proc "c" (a: AABB) -> f32 {
delta := a.upperBound - a.lowerBound
return 2.0 * (delta.x * delta.y + delta.y * delta.z + delta.z * delta.x)
}
// Get the center of an axis-aligned bounding box.
@(require_results)
AABB_Center :: proc "c" (a: AABB) -> Vec3 {
return 0.5 * (a.upperBound + a.lowerBound)
}
// Get the extents (half-widths) of an axis-aligned bounding box.
@(require_results)
AABB_Extents :: proc "c" (a: AABB) -> Vec3 {
return 0.5 * (a.upperBound - a.lowerBound)
}
// Get the union of two axis-aligned bounding boxes.
@(require_results)
AABB_Union :: proc "c" (a, b: AABB) -> (out: AABB) {
out.lowerBound = Min(a.lowerBound, b.lowerBound)
out.upperBound = Max(a.upperBound, b.upperBound)
return
}
// Add uniform padding to an axis-aligned bounding box.
@(require_results)
AABB_Inflate :: proc "c" (a: AABB, extension: f32) -> (out: AABB) {
radius := Vec3{extension, extension, extension}
out.lowerBound = a.lowerBound - radius
out.upperBound = a.upperBound + radius
return
}
// Do two axis-aligned boxes overlap?
@(require_results)
AABB_Overlaps :: proc "c" (a, b: AABB) -> bool {
// No intersection if separated along one axis
switch {
case a.upperBound.x < b.lowerBound.x || a.lowerBound.x > b.upperBound.x:
return false
case a.upperBound.y < b.lowerBound.y || a.lowerBound.y > b.upperBound.y:
return false
case a.upperBound.z < b.lowerBound.z || a.lowerBound.z > b.upperBound.z:
return false
}
// Overlapping on all axis means bounds are intersecting
return true
}
// Transform an axis-aligned bounding box. This can create a larger box
// than if you recomputed the AABB of the original shape with the transform
// applied.
@(require_results)
AABB_Transform :: proc "c" (transform: Transform, a: AABB) -> AABB {
center := TransformPoint(transform, AABB_Center(a))
m := MakeMatrixFromQuat(transform.q)
extent := AbsMatrix3(m) * AABB_Extents(a)
return AABB{center - extent, center + extent}
}
// Get the closest point on an axis-aligned bounding box.
@(require_results)
ClosestPointToAABB :: proc "c" (point: Vec3, a: AABB) -> Vec3 {
return Clamp(point, a.lowerBound, a.upperBound)
}

2923
vendor/box3d/box3d_types.odin vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
vendor/box3d/lib/box3d.lib vendored Normal file

Binary file not shown.

193
vendor/box3d/src/include/box3d/base.h vendored Normal file
View File

@@ -0,0 +1,193 @@
// SPDX-FileCopyrightText: 2025 Erin Catto
// SPDX-License-Identifier: MIT
#pragma once
#include <stdbool.h>
#include <stdint.h>
// Compile-time options. Edit box3d/config.h, or define BOX3D_USER_CONFIG to
// point at your own copy.
#ifdef BOX3D_USER_CONFIG
#include BOX3D_USER_CONFIG
#endif
#include "config.h"
// clang-format off
//
// Shared library macros
// Predefine BOX3D_EXPORT to reuse an existing export/import scheme, for example
// when compiling Box3D into another shared library.
#ifndef BOX3D_EXPORT
#if defined(_WIN32) && defined(box3d_EXPORTS)
// build the Windows DLL
#define BOX3D_EXPORT __declspec(dllexport)
#elif defined(_WIN32) && defined(BOX3D_DLL)
// using the Windows DLL
#define BOX3D_EXPORT __declspec(dllimport)
#elif defined(box3d_EXPORTS)
// building or using the shared library
#define BOX3D_EXPORT __attribute__((visibility("default")))
#else
// static library
#define BOX3D_EXPORT
#endif
#endif
// C++ macros
#ifdef __cplusplus
#define B3_API extern "C" BOX3D_EXPORT
#define B3_INLINE inline
#if defined( _MSC_VER )
#define B3_FORCE_INLINE __forceinline
#elif defined( __GNUC__ ) || defined( __clang__ )
#define B3_FORCE_INLINE inline __attribute__((always_inline))
#else
#define B3_FORCE_INLINE inline
#endif
#define B3_LITERAL(T) T
#define B3_ZERO_INIT {}
#else
#define B3_API BOX3D_EXPORT
#define B3_INLINE static inline
#if defined( _MSC_VER )
#define B3_FORCE_INLINE static __forceinline
#elif defined( __GNUC__ ) || defined( __clang__ )
#define B3_FORCE_INLINE static inline __attribute__((always_inline))
#else
#define B3_FORCE_INLINE static inline
#endif
/// Used for C literals like (b3Vec3){1.0f, 2.0f, 3.0f} where C++ requires b3Vec3{1.0f, 2.0f, 3.0f}
#define B3_LITERAL(T) (T)
#define B3_ZERO_INIT {0}
#endif
// clang-format on
#if defined( BOX3D_VALIDATE ) && !defined( NDEBUG )
#define B3_ENABLE_VALIDATION 1
#else
#define B3_ENABLE_VALIDATION 0
#endif
/**
* @defgroup base Base
* Base functionality
* @{
*/
/// This is used to indicate null for interfaces that work with indices instead of pointers
#define B3_NULL_INDEX -1
/// Prototype for user allocation function.
/// @param size the allocation size in bytes
/// @param alignment the required alignment, guaranteed to be a power of 2
typedef void* b3AllocFcn( int32_t size, int32_t alignment );
/// Prototype for user free function.
/// @param mem the memory previously allocated through `b3AllocFcn`
typedef void b3FreeFcn( void* mem );
/// Prototype for the user assert callback. Return 0 to skip the debugger break.
typedef int b3AssertFcn( const char* condition, const char* fileName, int lineNumber );
/// Prototype for user log callback. Used to log warnings.
typedef void b3LogFcn( const char* message );
/// This allows the user to override the allocation functions. These should be
/// set during application startup.
B3_API void b3SetAllocator( b3AllocFcn* allocFcn, b3FreeFcn* freeFcn );
/// Total bytes allocated by Box3D
B3_API int32_t b3GetByteCount( void );
/// Override the default assert callback.
/// @param assertFcn a non-null assert callback
B3_API void b3SetAssertFcn( b3AssertFcn* assertFcn );
/// see https://github.com/scottt/debugbreak
#if defined( _MSC_VER )
/// Break to the debugger
#define B3_BREAKPOINT __debugbreak()
#elif defined( __GNUC__ ) || defined( __clang__ )
#define B3_BREAKPOINT __builtin_trap()
#else
/// Unknown compiler
#include <assert.h>
#define B3_BREAKPOINT assert( 0 )
#endif
#if !defined( NDEBUG ) || defined( B3_ENABLE_ASSERT )
/// Internal assertion handler. Allows for host intervention.
B3_API int b3InternalAssert( const char* condition, const char* fileName, int lineNumber );
/// Assert that a condition is true.
#define B3_ASSERT( condition ) \
( (void)( ( !!( condition ) ) || ( b3InternalAssert( #condition, __FILE__, (int)( __LINE__ ) ), 0 ) ) )
#else
#define B3_ASSERT( ... ) ( (void)0 )
#endif
#if B3_ENABLE_VALIDATION
/// Validation is typically only enabled in debug builds.
/// Floating point tolerance checks should use this instead of the regular assertion
#define B3_VALIDATE( condition ) B3_ASSERT( condition )
#else
/// Validation is typically only enabled in debug builds.
/// Floating point tolerance checks should use this instead of the regular assertion
#define B3_VALIDATE( ... ) ( (void)0 )
#endif
/// Override the default logging callback.
B3_API void b3SetLogFcn( b3LogFcn* logFcn );
/// Version numbering scheme.
/// See https://semver.org/
typedef struct b3Version
{
/// Significant changes
int major;
/// Incremental changes
int minor;
/// Bug fixes
int revision;
} b3Version;
/// Get the current version of Box3D
B3_API b3Version b3GetVersion( void );
/// @return true if the library was built with BOX3D_DOUBLE_PRECISION (large world mode)
B3_API bool b3IsDoublePrecision( void );
/**@}*/
//! @cond
/// Get the absolute number of system ticks. The value is platform specific.
B3_API uint64_t b3GetTicks( void );
/// Get the milliseconds passed from an initial tick value.
B3_API float b3GetMilliseconds( uint64_t ticks );
/// Get the milliseconds passed from an initial tick value.
B3_API float b3GetMillisecondsAndReset( uint64_t* ticks );
/// Yield to be used in a busy loop.
B3_API void b3Yield( void );
/// Sleep the current thread for a number of milliseconds.
B3_API void b3Sleep( int milliseconds );
// Simple djb2 hash function for determinism testing
#define B3_HASH_INIT 5381
B3_API uint32_t b3Hash( uint32_t hash, const uint8_t* data, int count );
// Dump file support functions
B3_API void b3WriteBinaryFile( void* data, int size, const char* fileName );
B3_API void* b3ReadBinaryFile( const char* prefix, const char* fileName, int* memSize );
//! @endcond

1730
vendor/box3d/src/include/box3d/box3d.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,645 @@
// SPDX-FileCopyrightText: 2025 Erin Catto
// SPDX-License-Identifier: MIT
#pragma once
#include "base.h"
#include "math_functions.h"
#include "types.h"
#include <stdbool.h>
#include <stddef.h>
/**
* @addtogroup tree
* @{
*/
/// Constructing the tree initializes the node pool.
B3_API b3DynamicTree b3DynamicTree_Create( int proxyCapacity );
/// Destroy the tree, freeing the node pool.
B3_API void b3DynamicTree_Destroy( b3DynamicTree* tree );
/// Create a proxy. Provide an AABB and a userData value.
B3_API int b3DynamicTree_CreateProxy( b3DynamicTree* tree, b3AABB aabb, uint64_t categoryBits, uint64_t userData );
/// Destroy a proxy. This asserts if the id is invalid.
B3_API void b3DynamicTree_DestroyProxy( b3DynamicTree* tree, int proxyId );
/// Move a proxy to a new AABB by removing and reinserting into the tree.
B3_API void b3DynamicTree_MoveProxy( b3DynamicTree* tree, int proxyId, b3AABB aabb );
/// Enlarge a proxy and enlarge ancestors as necessary.
B3_API void b3DynamicTree_EnlargeProxy( b3DynamicTree* tree, int proxyId, b3AABB aabb );
/// Modify the category bits on a proxy. This is an expensive operation.
B3_API void b3DynamicTree_SetCategoryBits( b3DynamicTree* tree, int proxyId, uint64_t categoryBits );
/// Get the category bits on a proxy.
B3_API uint64_t b3DynamicTree_GetCategoryBits( b3DynamicTree* tree, int proxyId );
/// Query an AABB for overlapping proxies. The callback function is called for each proxy that overlaps the supplied AABB.
/// @return performance data
B3_API b3TreeStats b3DynamicTree_Query( const b3DynamicTree* tree, b3AABB aabb, uint64_t maskBits, bool requireAllBits,
b3TreeQueryCallbackFcn* callback, void* context );
/// Query an AABB for the closest object. The callback function is called for each proxy that might be closest to the supplied point.
/// @param tree the dynamic tree to query
/// @param point the query point
/// @param maskBits nodes are skipped if the bit-wise AND with the node category bits is zero
/// @param requireAllBits nodes are skipped if the bit-wise AND with the node category bits does not equal the maskBits
/// @param callback a user provided instance of b3TreeQueryClosestCallbackFcn
/// @param context a user context object that is provided to the callback
/// @param minDistanceSqr the initial and final minimum squared distance. Provide a small initial to restrict the search and
/// improve performance. If the value is large this query has performance that scales linearly with the number of proxies and
/// would be slower than a brute force search.
/// @return performance data
B3_API b3TreeStats b3DynamicTree_QueryClosest( const b3DynamicTree* tree, b3Vec3 point, uint64_t maskBits, bool requireAllBits,
b3TreeQueryClosestCallbackFcn* callback, void* context, float* minDistanceSqr );
/// Ray cast against the proxies in the tree. This relies on the callback
/// to perform an exact ray cast in the case where the proxy contains a shape.
/// The callback also performs any collision filtering. This has performance
/// roughly equal to k * log(n), where k is the number of collisions and n is the
/// number of proxies in the tree.
/// Bit-wise filtering using mask bits can greatly improve performance in some scenarios.
/// However, this filtering may be approximate, so the user should still apply filtering to results.
/// @param tree the dynamic tree to ray cast
/// @param input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1)
/// @param maskBits bit mask test: `bool accept = (maskBits & node->categoryBits) != 0;`
/// @param requireAllBits modifies bit mask test: `bool accept = (maskBits & node->categoryBits) == maskBits;`
/// @param callback a callback function that is called for each proxy that is hit by the ray
/// @param context user context that is passed to the callback
/// @return performance data
B3_API b3TreeStats b3DynamicTree_RayCast( const b3DynamicTree* tree, const b3RayCastInput* input, uint64_t maskBits,
bool requireAllBits, b3TreeRayCastCallbackFcn* callback, void* context );
/// Sweep an AABB through the tree. The box is in the tree's world float frame and the callback
/// re-differences each shape at full precision against the query origin. Used by the large world
/// spatial queries so the tree traversal stays float while the narrow phase stays precise.
B3_API b3TreeStats b3DynamicTree_BoxCast( const b3DynamicTree* tree, const b3BoxCastInput* input, uint64_t maskBits,
bool requireAllBits, b3TreeBoxCastCallbackFcn* callback, void* context );
/// Validate this tree. For testing.
B3_API void b3DynamicTree_Validate( const b3DynamicTree* tree );
/// Get the height of the binary tree.
B3_API int b3DynamicTree_GetHeight( const b3DynamicTree* tree );
/// Get the ratio of the sum of the node areas to the root area.
B3_API float b3DynamicTree_GetAreaRatio( const b3DynamicTree* tree );
/// Get the bounding box that contains the entire tree
B3_API b3AABB b3DynamicTree_GetRootBounds( const b3DynamicTree* tree );
/// Get the number of proxies created
B3_API int b3DynamicTree_GetProxyCount( const b3DynamicTree* tree );
/// Rebuild the tree while retaining subtrees that haven't changed. Returns the number of boxes sorted.
B3_API int b3DynamicTree_Rebuild( b3DynamicTree* tree, bool fullBuild );
/// Get the number of bytes used by this tree
B3_API int b3DynamicTree_GetByteCount( const b3DynamicTree* tree );
/// Validate this tree. For testing.
B3_API void b3DynamicTree_Validate( const b3DynamicTree* tree );
/// Validate this tree has no enlarged AABBs. For testing.
B3_API void b3DynamicTree_ValidateNoEnlarged( const b3DynamicTree* tree );
/// Save this tree to a file for debugging
B3_API void b3DynamicTree_Save( const b3DynamicTree* tree, const char* fileName );
/// Load a file for debugging
B3_API b3DynamicTree b3DynamicTree_Load( const char* fileName, float scale );
/// Get proxy user data
B3_INLINE uint64_t b3DynamicTree_GetUserData( const b3DynamicTree* tree, int proxyId )
{
return tree->nodes[proxyId].userData;
}
/// Get the AABB of a proxy
B3_INLINE b3AABB b3DynamicTree_GetAABB( const b3DynamicTree* tree, int proxyId )
{
return tree->nodes[proxyId].aabb;
}
/**@}*/ // tree
/**
* @addtogroup hull
* @{
*/
/// Get read only hull vertices.
B3_INLINE const b3HullVertex* b3GetHullVertices( const b3HullData* hull )
{
if ( hull->vertexOffset == 0 )
{
return NULL;
}
return (const b3HullVertex*)( (intptr_t)hull + hull->vertexOffset );
}
/// Get read only hull points.
B3_INLINE const b3Vec3* b3GetHullPoints( const b3HullData* hull )
{
if ( hull->pointOffset == 0 )
{
return NULL;
}
return (const b3Vec3*)( (intptr_t)hull + hull->pointOffset );
}
/// Get read only hull half edges.
B3_INLINE const b3HullHalfEdge* b3GetHullEdges( const b3HullData* hull )
{
if ( hull->edgeOffset == 0 )
{
return NULL;
}
return (const b3HullHalfEdge*)( (intptr_t)hull + hull->edgeOffset );
}
/// Get read only hull faces.
B3_INLINE const b3HullFace* b3GetHullFaces( const b3HullData* hull )
{
if ( hull->faceOffset == 0 )
{
return NULL;
}
return (const b3HullFace*)( (intptr_t)hull + hull->faceOffset );
}
/// Get read only hull planes.
B3_INLINE const b3Plane* b3GetHullPlanes( const b3HullData* hull )
{
if ( hull->planeOffset == 0 )
{
return NULL;
}
return (const b3Plane*)( (intptr_t)hull + hull->planeOffset );
}
/// Create a tessellated cylinder as a hull.
B3_API b3HullData* b3CreateCylinder( float height, float radius, float yOffset, int sides );
/// Create a tessellated cone as a hull.
B3_API b3HullData* b3CreateCone( float height, float radius1, float radius2, int slices );
/// Create a rock shaped hull.
B3_API b3HullData* b3CreateRock( float radius );
/// Create a generic convex hull.
B3_API b3HullData* b3CreateHull( const b3Vec3* points, int pointCount, int maxVertexCount );
/// Deep clone a hull.
B3_API b3HullData* b3CloneHull( const b3HullData* hull );
/// Clone and transform a hull. Supports non-uniform and mirroring scale.
B3_API b3HullData* b3CloneAndTransformHull( const b3HullData* original, b3Transform transform, b3Vec3 scale );
/// Destroy a hull.
B3_API void b3DestroyHull( b3HullData* hull );
/// Make a cube as a hull. Do not call b3DestroyHull on this.
B3_API b3BoxHull b3MakeCubeHull( float halfWidth );
/// Make a box as a hull. Do not call b3DestroyHull on this.
B3_API b3BoxHull b3MakeBoxHull( float hx, float hy, float hz );
/// Make an offset box as a hull. Do not call b3DestroyHull on this.
B3_API b3BoxHull b3MakeOffsetBoxHull( float hx, float hy, float hz, b3Vec3 offset );
/// Make a transformed box as a hull. Do not call b3DestroyHull on this.
/// @param hx, hy, hz positive half widths
/// @param transform local transform of box
B3_API b3BoxHull b3MakeTransformedBoxHull( float hx, float hy, float hz, b3Transform transform );
/// This makes a transformed box hull with post scaling. This is useful for boxes that are scaled in
/// a level editor. Such scaling can have reflection and shear. In the case of shear the result
/// may be approximate. If you need to support shear consider using b3CreateHull.
/// Do not call b3DestroyHull on this.
/// @param halfWidths positive half widths
/// @param transform local transform of box
/// @param postScale scale applied after the transform, may be negative
B3_API b3BoxHull b3MakeScaledBoxHull( b3Vec3 halfWidths, b3Transform transform, b3Vec3 postScale );
/// This takes a box with a transform and post scale and converts it into a box with the post scale
/// resolved with new half-widths and transform. This accepts non-uniform and negative scale.
/// This is approximate if there is shear.
/// @param halfWidths [in/out] the box half widths
/// @param transform [in/out] the box transform with rotation and translation
/// @param postScale the post scale being applied to the box after the transform
/// @param minHalfWidth the minimum half width after scale is applied
B3_API void b3ScaleBox( b3Vec3* halfWidths, b3Transform* transform, b3Vec3 postScale, float minHalfWidth );
/**@}*/ // hull
/**
* @addtogroup mesh
* @{
*/
/// Get read only mesh BVH nodes.
B3_INLINE const b3MeshNode* b3GetMeshNodes( const b3MeshData* mesh )
{
if ( mesh->nodeOffset == 0 )
{
return NULL;
}
return (const b3MeshNode*)( (intptr_t)mesh + mesh->nodeOffset );
}
/// Get read only mesh vertices.
B3_INLINE const b3Vec3* b3GetMeshVertices( const b3MeshData* mesh )
{
if ( mesh->vertexOffset == 0 )
{
return NULL;
}
return (const b3Vec3*)( (intptr_t)mesh + mesh->vertexOffset );
}
/// Get read only mesh triangles.
B3_INLINE const b3MeshTriangle* b3GetMeshTriangles( const b3MeshData* mesh )
{
if ( mesh->triangleOffset == 0 )
{
return NULL;
}
return (const b3MeshTriangle*)( (intptr_t)mesh + mesh->triangleOffset );
}
/// Get read only mesh materials. The count is equal to the triangle count.
B3_INLINE const uint8_t* b3GetMeshMaterialIndices( const b3MeshData* mesh )
{
if ( mesh->materialOffset == 0 )
{
return NULL;
}
return (const uint8_t*)( (intptr_t)mesh + mesh->materialOffset );
}
/// Get read only mesh flags. The count is equal to the triangle count.
B3_INLINE const uint8_t* b3GetMeshFlags( const b3MeshData* mesh )
{
if ( mesh->flagsOffset == 0 )
{
return NULL;
}
return (const uint8_t*)( (intptr_t)mesh + mesh->flagsOffset );
}
/// Create a grid mesh along the x and z axes.
/// @param xCount the number of rows in the x direction
/// @param zCount the number of rows in the z direction
/// @param cellWidth the width of each cell
/// @param materialCount the number of materials to generate
/// @param identifyEdges compute adjacency information
B3_API b3MeshData* b3CreateGridMesh( int xCount, int zCount, float cellWidth, int materialCount, bool identifyEdges );
/// Create a wave mesh along the x and z axes.
B3_API b3MeshData* b3CreateWaveMesh( int xCount, int zCount, float cellWidth, float amplitude, float rowFrequency,
float columnFrequency );
/// Create a torus mesh.
B3_API b3MeshData* b3CreateTorusMesh( int radialResolution, int tubularResolution, float radius, float thickness );
/// Create a box mesh.
B3_API b3MeshData* b3CreateBoxMesh( b3Vec3 center, b3Vec3 extent, bool identifyEdges );
/// Create a hollow box mesh.
B3_API b3MeshData* b3CreateHollowBoxMesh( b3Vec3 center, b3Vec3 extent );
/// Create a platform mesh. A truncated pyramid.
B3_API b3MeshData* b3CreatePlatformMesh( b3Vec3 center, float height, float topWidth, float bottomWidth );
/// Create a generic mesh.
B3_API b3MeshData* b3CreateMesh( const b3MeshDef* def, int* degenerateTriangleIndices, int degenerateCapacity );
/// Destroy a mesh.
B3_API void b3DestroyMesh( b3MeshData* mesh );
/// Get the height of the mesh BVH.
B3_API int b3GetHeight( const b3MeshData* mesh );
/**@}*/ // mesh
/**
* @addtogroup height_field
* @{
*/
/// Get read only compressed heights. One uint16_t per grid point.
B3_INLINE const uint16_t* b3GetHeightFieldCompressedHeights( const b3HeightFieldData* hf )
{
if ( hf->heightsOffset == 0 )
{
return NULL;
}
return (const uint16_t*)( (intptr_t)hf + hf->heightsOffset );
}
/// Get read only material indices. One uint8_t per cell.
B3_INLINE const uint8_t* b3GetHeightFieldMaterialIndices( const b3HeightFieldData* hf )
{
if ( hf->materialOffset == 0 )
{
return NULL;
}
return (const uint8_t*)( (intptr_t)hf + hf->materialOffset );
}
/// Get read only triangle flags. One uint8_t per triangle.
B3_INLINE const uint8_t* b3GetHeightFieldFlags( const b3HeightFieldData* hf )
{
if ( hf->flagsOffset == 0 )
{
return NULL;
}
return (const uint8_t*)( (intptr_t)hf + hf->flagsOffset );
}
/// Create a generic height field.
B3_API b3HeightFieldData* b3CreateHeightField( const b3HeightFieldDef* data );
/// Create a grid as a height field.
B3_API b3HeightFieldData* b3CreateGrid( int rowCount, int columnCount, b3Vec3 scale, bool makeHoles );
/// Create a wave grid as a height field.
B3_API b3HeightFieldData* b3CreateWave( int rowCount, int columnCount, b3Vec3 scale, float rowFrequency, float columnFrequency,
bool makeHoles );
/// Destroy a height field.
B3_API void b3DestroyHeightField( b3HeightFieldData* heightField );
/// Save input height data to a file
B3_API void b3DumpHeightData( const b3HeightFieldDef* data, const char* fileName );
/// Create a height field by loading a previously saved height data
B3_API b3HeightFieldData* b3LoadHeightField( const char* fileName );
/**@}*/ // height_field
/**
* @addtogroup compound
* @{
*/
/// Get a child shape of a compound.
B3_API b3ChildShape b3GetCompoundChild( const b3CompoundData* compound, int childIndex );
/// Query a compound shape for children that overlap an AABB.
B3_API void b3QueryCompound( const b3CompoundData* compound, b3AABB aabb, b3CompoundQueryFcn* fcn, void* context );
/// Access a child capsule by index.
B3_API b3CompoundCapsule b3GetCompoundCapsule( const b3CompoundData* compound, int index );
/// Access a child hull by index.
B3_API b3CompoundHull b3GetCompoundHull( const b3CompoundData* compound, int index );
/// Access a child mesh by index.
B3_API b3CompoundMesh b3GetCompoundMesh( const b3CompoundData* compound, int index );
/// Access a child sphere by index.
B3_API b3CompoundSphere b3GetCompoundSphere( const b3CompoundData* compound, int index );
/// Access the compound material array.
B3_API const b3SurfaceMaterial* b3GetCompoundMaterials( const b3CompoundData* compound );
/// Create a compound shape. All input data in the definition is cloned into the resulting compound.
B3_API b3CompoundData* b3CreateCompound( const b3CompoundDef* def );
/// Destroy a compound shape.
B3_API void b3DestroyCompound( b3CompoundData* compound );
/// If bytes is null then this returns the number of required bytes. This clones all the
/// data into the bytes buffer. This is expected to run offline or asynchronously.
/// This mutates the compound to nullify pointers, leaving the compound in an unusable state.
B3_API uint8_t* b3ConvertCompoundToBytes( b3CompoundData* compound );
/// Convert bytes to compound. This does not clone. The bytes must remain in scope while the
/// compound is used. This is done to improve run-time performance and allow for instancing.
/// The bytes are mutated to fixup pointers.
B3_API b3CompoundData* b3ConvertBytesToCompound( uint8_t* bytes, int byteCount );
/**@}*/ // compound
/**
* @addtogroup geometry
* @{
*/
/// Compute mass properties of a sphere
B3_API b3MassData b3ComputeSphereMass( const b3Sphere* shape, float density );
/// Compute mass properties of a capsule
B3_API b3MassData b3ComputeCapsuleMass( const b3Capsule* shape, float density );
/// Compute mass properties of a hull
B3_API b3MassData b3ComputeHullMass( const b3HullData* shape, float density );
/// Compute the bounding box of a transformed sphere
B3_API b3AABB b3ComputeSphereAABB( const b3Sphere* shape, b3Transform transform );
/// Compute the bounding box of a transformed capsule
B3_API b3AABB b3ComputeCapsuleAABB( const b3Capsule* shape, b3Transform transform );
/// Compute the bounding box of a transformed hull
B3_API b3AABB b3ComputeHullAABB( const b3HullData* shape, b3Transform transform );
/// Compute the bounding box of a transformed mesh. Scale may be non-uniform and have negative components.
B3_API b3AABB b3ComputeMeshAABB( const b3MeshData* shape, b3Transform transform, b3Vec3 scale );
/// Compute the bounding box of a transformed height-field
B3_API b3AABB b3ComputeHeightFieldAABB( const b3HeightFieldData* shape, b3Transform transform );
/// Compute the bounding box of a compound
B3_API b3AABB b3ComputeCompoundAABB( const b3CompoundData* shape, b3Transform transform );
/**@}*/ // geometry
/**
* @addtogroup query
* @{
*/
/// Use this to ensure your ray cast input is valid and avoid internal assertions.
B3_API bool b3IsValidRay( const b3RayCastInput* input );
/// Overlap shape versus capsule
B3_API bool b3OverlapCapsule( const b3Capsule* shape, b3Transform shapeTransform, const b3ShapeProxy* proxy );
/// Overlap shape versus compound
B3_API bool b3OverlapCompound( const b3CompoundData* shape, b3Transform shapeTransform, const b3ShapeProxy* proxy );
/// Overlap shape versus height field
B3_API bool b3OverlapHeightField( const b3HeightFieldData* shape, b3Transform shapeTransform, const b3ShapeProxy* proxy );
/// Overlap shape versus hull
B3_API bool b3OverlapHull( const b3HullData* shape, b3Transform shapeTransform, const b3ShapeProxy* proxy );
/// Overlap shape versus mesh
B3_API bool b3OverlapMesh( const b3Mesh* shape, b3Transform shapeTransform, const b3ShapeProxy* proxy );
/// Overlap shape versus sphere
B3_API bool b3OverlapSphere( const b3Sphere* shape, b3Transform shapeTransform, const b3ShapeProxy* proxy );
/// Ray cast versus sphere in local space. A zero length ray is a point query. Initial overlap
/// reports a hit at the ray origin with zero fraction and zero normal.
B3_API b3CastOutput b3RayCastSphere( const b3Sphere* shape, const b3RayCastInput* input );
/// Ray cast versus a hollow sphere shell in local space. Unlike the solid sphere a ray starting
/// inside is not an overlap: it passes through and hits the far wall.
B3_API b3CastOutput b3RayCastHollowSphere( const b3Sphere* shape, const b3RayCastInput* input );
/// Ray cast versus capsule in local space. A zero length ray is a point query. Initial overlap
/// reports a hit at the ray origin with zero fraction and zero normal.
B3_API b3CastOutput b3RayCastCapsule( const b3Capsule* shape, const b3RayCastInput* input );
/// Ray cast versus compound in local space. A zero length ray is a point query. Initial overlap
/// with a child reports a hit at the ray origin with zero fraction and zero normal.
B3_API b3CastOutput b3RayCastCompound( const b3CompoundData* shape, const b3RayCastInput* input );
/// Ray cast versus hull shape in local space. A zero length ray is a point query. Initial overlap
/// reports a hit at the ray origin with zero fraction and zero normal.
B3_API b3CastOutput b3RayCastHull( const b3HullData* shape, const b3RayCastInput* input );
/// Ray cast versus mesh in local space. A thin surface with no interior, so there is no overlap case.
B3_API b3CastOutput b3RayCastMesh( const b3Mesh* shape, const b3RayCastInput* input );
/// Ray cast versus height field in local space. A thin surface with no interior, so there is no overlap case.
B3_API b3CastOutput b3RayCastHeightField( const b3HeightFieldData* shape, const b3RayCastInput* input );
/// Shape cast versus a sphere. Initial overlap is treated as a miss.
B3_API b3CastOutput b3ShapeCastSphere( const b3Sphere* shape, const b3ShapeCastInput* input );
/// Shape cast versus a capsule. Initial overlap is treated as a miss.
B3_API b3CastOutput b3ShapeCastCapsule( const b3Capsule* shape, const b3ShapeCastInput* input );
/// Shape cast versus compound. Initial overlap is treated as a miss.
B3_API b3CastOutput b3ShapeCastCompound( const b3CompoundData* shape, const b3ShapeCastInput* input );
/// Shape cast versus a hull. Initial overlap is treated as a miss.
B3_API b3CastOutput b3ShapeCastHull( const b3HullData* shape, const b3ShapeCastInput* input );
/// Shape cast versus a mesh. Initial overlap is treated as a miss.
B3_API b3CastOutput b3ShapeCastMesh( const b3Mesh* shape, const b3ShapeCastInput* input );
/// Shape cast versus a height field. Initial overlap is treated as a miss.
B3_API b3CastOutput b3ShapeCastHeightField( const b3HeightFieldData* shape, const b3ShapeCastInput* input );
/// Query callback.
typedef bool b3MeshQueryFcn( b3Vec3 a, b3Vec3 b, b3Vec3 c, int triangleIndex, void* context );
/// Query a mesh for triangles overlapping a bounding box in local space. May have false positives. Useful for debug draw.
/// @param mesh the mesh to query, includes scale
/// @param bounds the bounding box in local space
/// @param fcn a user function to collect triangles
/// @param context the context sent to the user function.
B3_API void b3QueryMesh( const b3Mesh* mesh, const b3AABB bounds, b3MeshQueryFcn* fcn, void* context );
/// Query a height field for triangles overlapping a bounding box in local space. May have false positives. Useful for debug draw.
/// @param heightField the height field to query
/// @param bounds the bounding box in local space
/// @param fcn a user function to collect triangles
/// @param context the context sent to the user function.
B3_API void b3QueryHeightField( const b3HeightFieldData* heightField, b3AABB bounds, b3MeshQueryFcn* fcn, void* context );
/// Compute the closest points between two shapes represented as point clouds.
/// b3SimplexCache cache is input/output. On the first call set b3SimplexCache.count to zero.
/// The query runs in frame A, so the witness points and normal are returned in frame A.
/// The underlying GJK algorithm may be debugged by passing in debug simplexes and capacity. You may pass in NULL and 0 for these.
B3_API b3DistanceOutput b3ShapeDistance( const b3DistanceInput* input, b3SimplexCache* cache, b3Simplex* simplexes,
int simplexCapacity );
/// Perform a linear shape cast of shape B moving and shape A fixed. Determines the hit point, normal, and translation fraction.
/// The query runs in frame A, so the hit point and normal are returned in frame A. Initially touching shapes are a miss.
B3_API b3CastOutput b3ShapeCast( const b3ShapeCastPairInput* input );
/// Evaluate the transform sweep at a specific time.
B3_API b3Transform b3GetSweepTransform( const b3Sweep* sweep, float time );
/// Compute the upper bound on time before two shapes penetrate. Time is represented as
/// a fraction between [0,tMax]. This uses a swept separating axis and may miss some intermediate,
/// non-tunneling collisions. If you change the time interval, you should call this function
/// again.
B3_API b3TOIOutput b3TimeOfImpact( const b3TOIInput* input );
/**@}*/ // query
/**
* @addtogroup collision
* @{
*/
/// Collide two spheres.
B3_API void b3CollideSpheres( b3LocalManifold* manifold, int capacity, const b3Sphere* sphereA, const b3Sphere* sphereB,
b3Transform transformBtoA );
/// Collide a capsule and a sphere.
B3_API void b3CollideCapsuleAndSphere( b3LocalManifold* manifold, int capacity, const b3Capsule* capsuleA,
const b3Sphere* sphereB, b3Transform transformBtoA );
/// Collide a hull and a sphere.
B3_API void b3CollideHullAndSphere( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, const b3Sphere* sphereB,
b3Transform transformBtoA, b3SimplexCache* cache );
/// Collide two capsules.
B3_API void b3CollideCapsules( b3LocalManifold* manifold, int capacity, const b3Capsule* capsuleA, const b3Capsule* capsuleB,
b3Transform transformBtoA );
/// Collide a hull and a capsule.
B3_API void b3CollideHullAndCapsule( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, const b3Capsule* capsuleB,
b3Transform transformBtoA, b3SimplexCache* cache );
/// Collide two hulls.
B3_API void b3CollideHulls( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, const b3HullData* hullB,
b3Transform transformBtoA, b3SATCache* cache );
/// Collide a capsule and a triangle.
B3_API void b3CollideCapsuleAndTriangle( b3LocalManifold* manifold, int capacity, const b3Capsule* capsuleA,
const b3Vec3* triangleB, b3SimplexCache* cache );
/// Collide a hull and a triangle.
B3_API void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, b3Vec3 v1, b3Vec3 v2,
b3Vec3 v3, int triangleFlags, b3SATCache* cache );
/// Collide a sphere and a triangle.
B3_API void b3CollideSphereAndTriangle( b3LocalManifold* manifold, int capacity, const b3Sphere* sphereA,
const b3Vec3* triangleB );
/**@}*/ // collision
/**
* @addtogroup character
* @{
*/
/// Solves the position of a mover that satisfies the given collision planes.
/// @param targetDelta the desired translation from the position used to generate the collision planes
/// @param planes the collision planes
/// @param count the number of collision planes
B3_API b3PlaneSolverResult b3SolvePlanes( b3Vec3 targetDelta, b3CollisionPlane* planes, int count );
/// Clips the velocity against the given collision planes. Planes with zero push or clipVelocity
/// set to false are skipped.
B3_API b3Vec3 b3ClipVector( b3Vec3 vector, const b3CollisionPlane* planes, int count );
/**@}*/ // character

31
vendor/box3d/src/include/box3d/config.h vendored Normal file
View File

@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: 2026 Erin Catto
// SPDX-License-Identifier: MIT
#pragma once
// Box3D compile-time options.
//
// Normally set by the CMake BOX3D_* options. If you build Box3D without its
// CMake (dropping the sources into another project), set them here so the
// library and your code agree. Edit this file, or keep your settings elsewhere
// and point Box3D at them from your build:
//
// #define BOX3D_USER_CONFIG "my_box3d_config.h"
//
// A define passed on the compiler command line still wins over this file.
// Large world mode. Stores world positions in double precision. Affects ABI.
//#define BOX3D_DOUBLE_PRECISION
// Build the scalar fallback instead of SSE2/NEON.
//#define BOX3D_DISABLE_SIMD
// Enable internal validation in debug builds.
//#define BOX3D_VALIDATE
// Decorate the public API with your own export macro instead of Box3D's
// box3d_EXPORTS/BOX3D_DLL scheme, for example when compiling Box3D into another
// shared library. A single value cannot switch between dllexport and dllimport,
// so this suits embedding more than shipping Box3D as its own DLL.
//#define BOX3D_EXPORT MYENGINE_API

View File

@@ -0,0 +1,123 @@
// SPDX-FileCopyrightText: 2025 Erin Catto
// SPDX-License-Identifier: MIT
#pragma once
#include "base.h"
/// Box3D bases all length units on meters, but you may need different units for your game.
/// You can set this value to use different units. This should be done at application startup
/// and only modified once. Default value is 1.
/// @warning This must be modified before any calls to Box3D
B3_API void b3SetLengthUnitsPerMeter( float lengthUnits );
/// Get the current length units per meter.
B3_API float b3GetLengthUnitsPerMeter( void );
/// Set the threshold for logging stalls.
B3_API void b3SetStallThreshold( float seconds );
/// Get the threshold for logging stalls.
B3_API float b3GetStallThreshold( void );
// Used to detect bad values. In float mode positions greater than about 16km have precision
// problems, so 100km is a safe limit. Large world mode keeps coordinates accurate much farther
// from the origin, so the sanity limit widens to keep valid far-field positions from tripping it.
#if defined( BOX3D_DOUBLE_PRECISION )
#define B3_HUGE ( 1.0e9f * b3GetLengthUnitsPerMeter() )
#else
#define B3_HUGE ( 1.0e5f * b3GetLengthUnitsPerMeter() )
#endif
/// Maximum parallel workers. Used for some fixed size arrays.
#define B3_MAX_WORKERS 32
/// Maximum number of tasks queued per world step. b3EnqueueTaskCallback will never be called
/// more than this per world step. This is related to B3_MAX_WORKERS. With 32 workers,
/// the maximum observed task count is 130. This allows an external task system to use a fixed
/// size array for Box3D task, which may help with creating stable user task pointers.
#define B3_MAX_TASKS 256
// Maximum number of colors in the constraint graph. Constraints that cannot
// find a color are added to the overflow set which are solved single-threaded.
// The compound barrel benchmark has minor overflow with 24 colors
#define B3_GRAPH_COLOR_COUNT 24
// Number of contact point buckets for counting the number of contact points per
// shape contact pair. This is just for reporting and doesn't affect simulation.
#define B3_CONTACT_MANIFOLD_COUNT_BUCKETS 8
// A small length used as a collision and constraint tolerance. Usually it is
// chosen to be numerically significant, but visually insignificant. In meters.
// @warning modifying this can have a significant impact on stability
#define B3_LINEAR_SLOP ( 0.005f * b3GetLengthUnitsPerMeter() )
#define B3_MIN_CAPSULE_LENGTH ( B3_LINEAR_SLOP )
/// The distance between shapes where they are considered overlapped. This is needed
/// because GJK may return small positive values for overlapped shapes in degenerate
/// configurations.
#define B3_OVERLAP_SLOP ( 0.1f * B3_LINEAR_SLOP )
/// Maximum number of simultaneous worlds that can be allocated
#ifndef B3_MAX_WORLDS
#define B3_MAX_WORLDS 128
#endif
/// The maximum rotation of a body per time step. This limit is very large and is used
/// to prevent numerical problems. You shouldn't need to adjust this.
/// @warning increasing this to 0.5f * B3_PI or greater will break continuous collision.
#define B3_MAX_ROTATION ( 0.25f * B3_PI )
/// @warning modifying this can have a significant impact on performance and stability
#define B3_SPECULATIVE_DISTANCE ( 4.0f * B3_LINEAR_SLOP )
/// The rest offset is used for mesh contact to reduce ghost collisions and assist with CCD.
/// The rest offset adjusts the contact point separation value, making the solver push the shapes
/// apart by this distance.
/// Must be at least B3_LINEAR_SLOP and less than B3_SPECULATIVE_DISTANCE.
#define B3_MESH_REST_OFFSET ( 1.0f * B3_LINEAR_SLOP )
/// The default contact recycling distance.
#define B3_CONTACT_RECYCLE_DISTANCE ( 10.0f * B3_LINEAR_SLOP )
/// The default contact recycling world angle threshold. For performance this value
/// is cos(angle/2)^2. This value corresponds to 10 degrees.
#define B3_CONTACT_RECYCLE_ANGULAR_DISTANCE ( 0.99240388f )
/// This is used to fatten AABBs in the dynamic tree. This allows proxies
/// to move by a small amount without triggering a tree adjustment. This is in meters.
/// @warning modifying this can have a significant impact on performance
#define B3_MAX_AABB_MARGIN ( 0.05f * b3GetLengthUnitsPerMeter() )
/// Per-shape AABB margin is a fraction of the shape extent (capped by B3_MAX_AABB_MARGIN).
/// Small shapes get small margins; large shapes are clamped to the cap.
#define B3_AABB_MARGIN_FRACTION 0.125f
/// The time that a body must be still before it will go to sleep. In seconds.
#define B3_TIME_TO_SLEEP 0.5f
/// Maximum length of the body name. Can be 0 if you don't need names.
/// Note: this gates recording capability.
#ifndef B3_BODY_NAME_LENGTH
#define B3_BODY_NAME_LENGTH 18
#endif
/// Maximum length of the shape name. Can be 0 if you don't need names.
/// Note: this gates recording capability.
/// todo waiting on this because it breaks existing recordings
#ifndef B3_SHAPE_NAME_LENGTH
#define B3_SHAPE_NAME_LENGTH 18
#endif
/// The maximum number of contact points between two touching shapes.
#define B3_MAX_MANIFOLD_POINTS 4
/// The maximum number points to use for shape cast proxies (swept point cloud).
#define B3_MAX_SHAPE_CAST_POINTS 64
/// These generous limits allow for easy hashing. See b3ShapePairKey.
#define B3_SHAPE_POWER 22
#define B3_CHILD_POWER ( 64 - 2 * B3_SHAPE_POWER )
#define B3_MAX_SHAPES ( 1 << B3_SHAPE_POWER )
#define B3_MAX_CHILD_SHAPES ( 1 << B3_CHILD_POWER )

178
vendor/box3d/src/include/box3d/id.h vendored Normal file
View File

@@ -0,0 +1,178 @@
// SPDX-FileCopyrightText: 2026 Erin Catto
// SPDX-License-Identifier: MIT
#pragma once
#include <stdint.h>
// Note: this file should be stand-alone
/**
* @defgroup id Ids
* These ids serve as handles to internal Box3D objects.
* These should be considered opaque data and passed by value.
* Include this header if you need the id types and not the whole Box3D API.
* All ids are considered null if initialized to zero.
*
* For example in C++:
*
* @code{.cxx}
* b3WorldId worldId = {};
* @endcode
*
* Or in C:
*
* @code{.c}
* b3WorldId worldId = {0};
* @endcode
*
* These are both considered null.
*
* @warning Do not use the internals of these ids. They are subject to change. Ids should be treated as opaque objects.
* @warning You should use ids to access objects in Box3D. Do not access files within the src folder. Such usage is unsupported.
* @{
*/
/// World id references a world instance. This should be treated as an opaque handle.
typedef struct b3WorldId
{
uint16_t index1;
uint16_t generation;
} b3WorldId;
/// Body id references a body instance. This should be treated as an opaque handle.
typedef struct b3BodyId
{
int32_t index1;
uint16_t world0;
uint16_t generation;
} b3BodyId;
/// Shape id references a shape instance. This should be treated as an opaque handle.
typedef struct b3ShapeId
{
int32_t index1;
uint16_t world0;
uint16_t generation;
} b3ShapeId;
/// Joint id references a joint instance. This should be treated as an opaque handle.
typedef struct b3JointId
{
int32_t index1;
uint16_t world0;
uint16_t generation;
} b3JointId;
/// Contact id references a contact instance. This should be treated as an opaque handle.
typedef struct b3ContactId
{
int32_t index1;
uint16_t world0;
int16_t padding;
uint32_t generation;
} b3ContactId;
// clang-format off
#ifdef __cplusplus
/// A null id. Works for any id type.
#define B3_NULL_ID {}
#define B3_ID_INLINE inline
#else
/// A null id. Works for any id type.
#define B3_NULL_ID { 0 }
/// This macro bridges C and C++ inline functions. C++ has the one definition rule that C lacks.
#define B3_ID_INLINE static inline
#endif
// clang-format on
/// Use these to make your identifiers null.
/// You may also use zero initialization to get null.
static const b3WorldId b3_nullWorldId = B3_NULL_ID;
static const b3BodyId b3_nullBodyId = B3_NULL_ID;
static const b3ShapeId b3_nullShapeId = B3_NULL_ID;
static const b3JointId b3_nullJointId = B3_NULL_ID;
static const b3ContactId b3_nullContactId = B3_NULL_ID;
/// Macro to determine if any id is null.
#define B3_IS_NULL( id ) ( id.index1 == 0 )
/// Macro to determine if any id is non-null.
#define B3_IS_NON_NULL( id ) ( id.index1 != 0 )
/// Compare two ids for equality. Doesn't work for b3WorldId. Don't mix types.
#define B3_ID_EQUALS( id1, id2 ) ( id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.generation == id2.generation )
/// Store a world id into a uint32_t.
B3_ID_INLINE uint32_t b3StoreWorldId( b3WorldId id )
{
return ( (uint32_t)id.index1 << 16 ) | (uint32_t)id.generation;
}
/// Load a uint32_t into a world id.
B3_ID_INLINE b3WorldId b3LoadWorldId( uint32_t x )
{
b3WorldId id = { (uint16_t)( x >> 16 ), (uint16_t)( x ) };
return id;
}
/// Store a body id into a uint64_t.
B3_ID_INLINE uint64_t b3StoreBodyId( b3BodyId id )
{
return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;
}
/// Load a uint64_t into a body id.
B3_ID_INLINE b3BodyId b3LoadBodyId( uint64_t x )
{
b3BodyId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
return id;
}
/// Store a shape id into a uint64_t.
B3_ID_INLINE uint64_t b3StoreShapeId( b3ShapeId id )
{
return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;
}
/// Load a uint64_t into a shape id.
B3_ID_INLINE b3ShapeId b3LoadShapeId( uint64_t x )
{
b3ShapeId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
return id;
}
/// Store a joint id into a uint64_t.
B3_ID_INLINE uint64_t b3StoreJointId( b3JointId id )
{
return ( (uint64_t)id.index1 << 32 ) | ( (uint64_t)id.world0 ) << 16 | (uint64_t)id.generation;
}
/// Load a uint64_t into a joint id.
B3_ID_INLINE b3JointId b3LoadJointId( uint64_t x )
{
b3JointId id = { (int32_t)( x >> 32 ), (uint16_t)( x >> 16 ), (uint16_t)( x ) };
return id;
}
/// Store a contact id into three uint32 values
B3_ID_INLINE void b3StoreContactId( b3ContactId id, uint32_t values[3] )
{
values[0] = (uint32_t)id.index1;
values[1] = (uint32_t)id.world0;
values[2] = (uint32_t)id.generation;
}
/// Load a contact id from three uint32 values.
B3_ID_INLINE b3ContactId b3LoadContactId( uint32_t values[3] )
{
b3ContactId id;
id.index1 = (int32_t)values[0];
id.world0 = (uint16_t)values[1];
id.padding = 0;
id.generation = (uint32_t)values[2];
return id;
}
/**@}*/

File diff suppressed because it is too large Load Diff

3034
vendor/box3d/src/include/box3d/types.h vendored Normal file

File diff suppressed because it is too large Load Diff