mirror of
https://github.com/odin-lang/Odin.git
synced 2026-07-10 18:09:32 +00:00
Add vendor:box3d bindings
This commit is contained in:
1820
vendor/box3d/box3d.odin
vendored
Normal file
1820
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
615
vendor/box3d/box3d_collision.odin
vendored
Normal 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
140
vendor/box3d/box3d_constants.odin
vendored
Normal 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
129
vendor/box3d/box3d_id.odin
vendored
Normal 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
747
vendor/box3d/box3d_math.odin
vendored
Normal 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
2923
vendor/box3d/box3d_types.odin
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user