diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin index 7013de244..90ff301bd 100644 --- a/core/math/linalg/general.odin +++ b/core/math/linalg/general.odin @@ -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} diff --git a/examples/all/all_vendor_windows.odin b/examples/all/all_vendor_windows.odin index 8a0c29eaf..02db53b77 100644 --- a/examples/all/all_vendor_windows.odin +++ b/examples/all/all_vendor_windows.odin @@ -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" \ No newline at end of file diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index dfcb21874..e00254bc5 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -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; } diff --git a/src/types.cpp b/src/types.cpp index 67d757508..a71e22f9f 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -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; diff --git a/vendor/box3d/box3d.odin b/vendor/box3d/box3d.odin new file mode 100644 index 000000000..2eb792dea --- /dev/null +++ b/vendor/box3d/box3d.odin @@ -0,0 +1,1827 @@ +// Bindings for Box3D +package vendor_box3d + +import "base:intrinsics" +import "base:runtime" +import "core:c" + +ENABLE_VALIDATION :: false + +when ODIN_OS == .Windows { + @(export) + foreign import lib { + "lib/box3d.lib", + } +} else { + @(export) + foreign import lib { + "system:box3d", + } +} + +// This is used to indicate null for interfaces that work with indices instead of pointers +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 +AllocFcn :: proc "c" (size, alignment: i32) -> rawptr + +// Prototype for user free function. +// @param mem the memory previously allocated through `AllocFcn` +FreeFcn :: proc "c" (mem: rawptr) + +// Prototype for the user assert callback. Return 0 to skip the debugger break. +AssertFcn :: proc "c" (condition: cstring, fileName: cstring, lineNumber: c.int) -> c.int + +// Prototype for user log callback. Used to log warnings. +LogFcn :: proc "c" (message: rawptr) + + +BREAKPOINT :: intrinsics.debug_trap + +@(link_prefix="b3", default_calling_convention="c", require_results) +foreign lib { + // This allows the user to override the allocation functions. These should be + // set during application startup. + SetAllocator :: proc(allocFcn: AllocFcn, freeFcn: FreeFcn) --- + + // Total bytes allocated by Box3D + GetByteCount :: proc() -> i32 --- + + // Override the default assert callback. + // @param assertFcn a non-null assert callback + SetAssertFcn :: proc(assertFcn: AssertFcn) --- + + // Internal assertion handler. Allows for host intervention. + InternalAssert :: proc(condition: cstring, fileName: cstring, lineNumber: c.int) -> c.int --- + + // Override the default logging callback. + SetLogFcn :: proc(logFcn: LogFcn) --- +} + +// Version numbering scheme. +// See https://semver.org/ +Version :: struct { + // Significant changes + major: c.int, + + // Incremental changes + minor: c.int, + + // Bug fixes + revision: c.int, +} + +HASH_INIT :: 5381 + +@(link_prefix="b3", default_calling_convention="c", require_results) +foreign lib { + // Get the current version of Box3D + GetVersion :: proc() -> Version --- + + // @return true if the library was built with BOX3D_DOUBLE_PRECISION (large world mode) + IsDoublePrecision :: proc() -> bool --- + + // Get the absolute number of system ticks. The value is platform specific. + GetTicks :: proc() -> u64 --- + + // Get the milliseconds passed from an initial tick value. + GetMilliseconds :: proc(ticks: u64) -> f32 --- + + // Get the milliseconds passed from an initial tick value. + GetMillisecondsAndReset :: proc(ticks: ^u64) -> f32 --- + + // Yield to be used in a busy loop. + Yield :: proc() --- + + // Sleep the current thread for a number of milliseconds. + Sleep :: proc(milliseconds: c.int) --- + + // Simple djb2 hash function for determinism testing + Hash :: proc(hash: u32, data: [^]u8, count: c.int) -> u32 --- + + // Dump file support functions + WriteBinaryFile :: proc(data: rawptr, size: c.int, fileName: cstring) --- + ReadBinaryFile :: proc(prefix: cstring, fileName: cstring, memSize: ^c.int) -> rawptr --- +} + + +@(disabled=ODIN_DISABLE_ASSERT) +ASSERT :: proc "c" (condition: bool, message := #caller_expression(condition), loc := #caller_location) { + if !condition { + @(cold) + internal :: proc "c" (message: string, loc: runtime.Source_Code_Location) { + _ = InternalAssert(cstring(raw_data(message)), cstring(raw_data(loc.file_path)), loc.line) + } + internal(message, loc) + } +} + +@(disabled=!ENABLE_VALIDATION) +VALIDATE :: proc "c" (condition: bool, message := #caller_expression(condition), loc := #caller_location) { + ASSERT(condition, message, loc) +} + + + +/** + * @defgroup world World + * These functions allow you to create a simulation world. + * + * You can add rigid bodies and joint constraints to the world and run the simulation. You can get contact + * information to get contact points and normals as well as events. You can query the world, checking for overlaps and casting + * rays or shapes. There is also debugging information such as debug draw, timing information, and counters. You can find + * documentation here: https://box2d.org/ + * @{ + */ + + // Opaque recording handle. Create with CreateRecording, destroy with DestroyRecording. +Recording :: struct{} + + +// Opaque incremental replay player with a keyframe ring for O(interval) backward seek. +RecPlayer :: struct{} + +// Summary of a recording, read once at open so a viewer can frame and label it. +RecPlayerInfo :: struct { + frameCount: c.int, // total recorded steps + workerCount: c.int, // worker count requested for the replay world + timeStep: f32, // dt of the recorded steps + subStepCount: c.int, // recorded sub-steps + lengthScale: f32, // length units per meter in effect when recorded + bounds: AABB, // accumulated world bounds over the recording, zero-extent if unavailable +} + +// The kind of a recorded spatial query, matching the public query and cast functions. +RecQueryType :: enum c.int { + OverlapAABB, + OverlapShape, + CastRay, + CastShape, + CastRayClosest, + CastMover, + CollideMover, +} + +// A spatial query recorded during a replayed frame, exposed for inspection. +RecQueryInfo :: struct { + type: RecQueryType, + filter: QueryFilter, + aabb: AABB, // world-space bounds of the query, swept for casts + origin: Pos, // query origin (zero for overlap AABB) + translation: Vec3, // ray and cast translation + hitCount: c.int, // number of recorded results + key: u64, // identity key, the hash of (id, name), 0 if untagged + id: u64, // query id, 0 if none + name: cstring, // query label, NULL if none +} + +// One result of a recorded spatial query. +RecQueryHit :: struct { + shape: ShapeId, + point: Pos, + normal: Vec3, + fraction: f32, +} + + +@(link_prefix="b3", default_calling_convention="c", require_results) +foreign lib { + // Create a world for rigid body simulation. A world contains bodies, shapes, and constraints. You may create + // up to 128 worlds. Each world is completely independent and may be simulated in parallel. + // @return the world id. + CreateWorld :: proc(#by_ptr def: WorldDef) -> WorldId --- + + // Destroy a world + DestroyWorld :: proc(worldId: WorldId) --- + + // Get the current number of worlds + GetWorldCount :: proc() -> c.int --- + + // Get the maximum number of simultaneous worlds that have been created + GetMaxWorldCount :: proc() -> c.int --- + + // World id validation. Provides validation for up to 64K allocations. + World_IsValid :: proc(id: WorldId) -> bool --- + + // Simulate a world for one time step. This performs collision detection, integration, and constraint solution. + // @param worldId The world to simulate + // @param timeStep The amount of time to simulate, this should be a fixed number. Usually 1/60. + // @param subStepCount The number of sub-steps, increasing the sub-step count can increase accuracy. Usually 4. + World_Step :: proc(worldId: WorldId, timeStep: f32, subStepCount: c.int) --- + + // Call this to draw shapes and other debug draw data + World_Draw :: proc(worldId: WorldId, draw: ^DebugDraw, maskBits: u64) --- + + // Get the world's bounds. This is the bounding box that covers the current simulation. May have a small + // amount of padding. + World_GetBounds :: proc(worldId: WorldId) -> AABB --- + + // Get the body events for the current time step. The event data is transient. Do not store a reference to this data. + World_GetBodyEvents :: proc(worldId: WorldId) -> BodyEvents --- + + // Get sensor events for the current time step. The event data is transient. Do not store a reference to this data. + World_GetSensorEvents :: proc(worldId: WorldId) -> SensorEvents --- + + // Get contact events for this current time step. The event data is transient. Do not store a reference to this data. + World_GetContactEvents :: proc(worldId: WorldId) -> ContactEvents --- + + // Get the joint events for the current time step. The event data is transient. Do not store a reference to this data. + World_GetJointEvents :: proc(worldId: WorldId) -> JointEvents --- + + // Overlap test for all shapes that *potentially* overlap the provided AABB + World_OverlapAABB :: proc(worldId: WorldId, aabb: AABB, filter: QueryFilter, fcn: OverlapResultFcn, ctx: rawptr) -> TreeStats --- + + // Overlap test for all shapes that overlap the provided shape proxy. The proxy points are relative + // to the world origin, which lets the query stay precise far from the world origin. + World_OverlapShape :: proc(worldId: WorldId, origin: Pos, #by_ptr proxy: ShapeProxy, filter: QueryFilter, fcn: OverlapResultFcn, ctx: rawptr) -> TreeStats --- + + // Cast a ray into the world to collect shapes in the path of the ray. + // Your callback function controls whether you get the closest point, any point, or n-points. + // @note The callback function may receive shapes in any order + // @param worldId The world to cast the ray against + // @param origin The start point of the ray + // @param translation The translation of the ray from the start point to the end point + // @param filter Contains bit flags to filter unwanted shapes from the results + // @param fcn A user implemented callback function + // @param context A user context that is passed along to the callback function + // @return traversal performance counters + World_CastRay :: proc(worldId: WorldId, origin: Pos, translation: Vec3, filter: QueryFilter, fcn: CastResultFcn, ctx: rawptr) -> TreeStats --- + + // Cast a ray into the world to collect the closest hit. This is a convenience function. Ignores initial overlap. + // This is less general than World_CastRay() and does not allow for custom filtering. + World_CastRayClosest :: proc(worldId: WorldId, origin: Pos, translation: Vec3, filter: QueryFilter) -> RayResult --- + + // Cast a shape through the world. Similar to a cast ray except that a shape is cast instead of a point. + // The proxy points are relative to the origin and the hit points come back as world positions, so the + // cast stays precise far from the world origin. + // @see World_CastRay + World_CastShape :: proc(worldId: WorldId, origin: Pos, #by_ptr proxy: ShapeProxy, translation: Vec3, filter: QueryFilter, fcn: CastResultFcn, ctx: rawptr) -> TreeStats --- + + // Cast a capsule mover through the world. This is a special shape cast that handles sliding along other shapes while reducing + // clipping. This is not a good source of information about what the mover is touching. Instead use the planes returned by + // World_CollideMover. + // @param worldId World to cast the mover against + // @param origin World position the mover capsule is relative to + // @param mover Capsule mover, relative to the origin + // @param translation Desired mover translation + // @param filter Contains bit flags to filter unwanted shapes from the results + // @param fcn Optional callback for custom shape filtering + // @param context A user context that is passed along to the callback function + // @return the translation fraction + World_CastMover :: proc(worldId: WorldId, origin: Pos, #by_ptr mover: Capsule, translation: Vec3, filter: QueryFilter, fcn: MoverFilterFcn, ctx: rawptr) -> f32 --- + + // Collide a capsule mover with the world, gathering collision planes that can be fed to SolvePlanes. Useful for + // kinematic character movement. The mover and the returned planes are relative to the origin. + World_CollideMover :: proc(worldId: WorldId, origin: Pos, #by_ptr mover: Capsule, filter: QueryFilter, fcn: PlaneResultFcn, ctx: rawptr) --- + + // Enable/disable sleep. If your application does not need sleeping, you can gain some performance + // by disabling sleep completely at the world level. + // @see WorldDef + World_EnableSleeping :: proc(worldId: WorldId, flag: bool) --- + + // Is body sleeping enabled? + World_IsSleepingEnabled :: proc(worldId: WorldId) -> bool --- + + // Enable/disable continuous collision between dynamic and static bodies. Generally you should keep continuous + // collision enabled to prevent fast moving objects from going through static objects. The performance gain from + // disabling continuous collision is minor. + // @see WorldDef + World_EnableContinuous :: proc(worldId: WorldId, flag: bool) --- + + // Is continuous collision enabled? + World_IsContinuousEnabled :: proc(worldId: WorldId) -> bool --- + + // Adjust the restitution threshold. It is recommended not to make this value very small + // because it will prevent bodies from sleeping. Usually in meters per second. + // @see WorldDef + World_SetRestitutionThreshold :: proc(worldId: WorldId, value: f32) --- + + // Get the restitution speed threshold. Usually in meters per second. + World_GetRestitutionThreshold :: proc(worldId: WorldId) -> f32 --- + + // Adjust the hit event threshold. This controls the collision speed needed to generate a ContactHitEvent. + // Usually in meters per second. + // @see WorldDef::hitEventThreshold + World_SetHitEventThreshold :: proc(worldId: WorldId, value: f32) --- + + // Get the hit event speed threshold. Usually in meters per second. + World_GetHitEventThreshold :: proc(worldId: WorldId) -> f32 --- + + // Register the custom filter callback. This is optional. + World_SetCustomFilterCallback :: proc(worldId: WorldId, fcn: CustomFilterFcn, ctx: rawptr) --- + + // Register the pre-solve callback. This is optional. + World_SetPreSolveCallback :: proc(worldId: WorldId, fcn: PreSolveFcn, ctx: rawptr) --- + + // Set the gravity vector for the entire world. Box3D has no concept of an up direction and this + // is left as a decision for the application. Usually in m/s^2. + // @see WorldDef + World_SetGravity :: proc(worldId: WorldId, gravity: Vec3) --- + + // Get the gravity vector + World_GetGravity :: proc(worldId: WorldId) -> Vec3 --- + + // Apply a radial explosion + // @param worldId The world id + // @param explosionDef The explosion definition + World_Explode :: proc(worldId: WorldId, #by_ptr explosionDef: ExplosionDef) --- + + // Adjust contact tuning parameters + // @param worldId The world id + // @param hertz The contact stiffness (cycles per second) + // @param dampingRatio The contact bounciness with 1 being critical damping (non-dimensional) + // @param contactSpeed The maximum contact constraint push out speed (meters per second) + // @note Advanced feature + World_SetContactTuning :: proc(worldId: WorldId, hertz: f32, dampingRatio: f32, contactSpeed: f32) --- + + // Set the contact point recycling distance. Setting this to zero disables contact point recycling. + // Usually in meters. + World_SetContactRecycleDistance :: proc(worldId: WorldId, recycleDistance: f32) --- + + // Get the contact point recycling distance. Usually in meters. + World_GetContactRecycleDistance :: proc(worldId: WorldId) -> f32 --- + + // Set the maximum linear speed. Usually in m/s. + World_SetMaximumLinearSpeed :: proc(worldId: WorldId, maximumLinearSpeed: f32) --- + + // Get the maximum linear speed. Usually in m/s. + World_GetMaximumLinearSpeed :: proc(worldId: WorldId) -> f32 --- + + // Enable/disable constraint warm starting. Advanced feature for testing. Disabling + // warm starting greatly reduces stability and provides no performance gain. + World_EnableWarmStarting :: proc(worldId: WorldId, flag: bool) --- + + // Is constraint warm starting enabled? + World_IsWarmStartingEnabled :: proc(worldId: WorldId) -> bool --- + + // Get the number of awake bodies + World_GetAwakeBodyCount :: proc(worldId: WorldId) -> c.int --- + + // Get the current world performance profile + World_GetProfile :: proc(worldId: WorldId) -> Profile --- + + // Get world counters and sizes + World_GetCounters :: proc(worldId: WorldId) -> Counters --- + + // Get max capacity. This can be used with WorldDef to avoid run-time allocations and copies + World_GetMaxCapacity :: proc(worldId: WorldId) -> Capacity --- + + // Set the user data pointer. + World_SetUserData :: proc(worldId: WorldId, userData: rawptr) --- + + // Get the user data pointer. + World_GetUserData :: proc(worldId: WorldId) --- + + // Set the friction callback. Passing NULL resets to default. + World_SetFrictionCallback :: proc(worldId: WorldId, callback: FrictionCallback) --- + + // Set the restitution callback. Passing NULL resets to default. + World_SetRestitutionCallback :: proc(worldId: WorldId, callback: RestitutionCallback) --- + + // Set the worker count. Must be in the range [1, B3_MAX_WORKERS] + World_SetWorkerCount :: proc(worldId: WorldId, count: c.int) --- + + // Get the worker count. + World_GetWorkerCount :: proc(worldId: WorldId) -> c.int --- + + // Dump memory stats to log. + World_DumpMemoryStats :: proc(worldId: WorldId) --- + + // Dump shape bounds to box3d_bounds.txt + World_DumpShapeBounds :: proc(worldId: WorldId, type: BodyType) --- + + // This is for internal testing + World_RebuildStaticTree :: proc(worldId: WorldId) --- + + // This is for internal testing + World_EnableSpeculative :: proc(worldId: WorldId, flag: bool) --- + + // Dump world to a text file. Saves only awake bodies and associated static bodies. + // Meshes are saved to binary m files. + World_DumpAwake :: proc(worldId: WorldId) --- + + // Dump world to a text file. Meshes are saved to binary m files. + World_Dump :: proc(worldId: WorldId) --- + + /** + * @defgroup recording Recording + * @brief Record and replay world state for debugging. + * @{ + */ + + // Create a recording buffer with an optional initial byte capacity. + // Pass 0 to use the default (64 KiB). The buffer grows on demand. + // @return a new recording, owned by the caller + CreateRecording :: proc(byteCapacity: c.int) -> Recording --- + + // Destroy a recording and free its buffer. + // @param recording may be NULL + DestroyRecording :: proc(recording: ^Recording) --- + + // Get a pointer to the raw recording bytes. + // Valid until the recording buffer is modified or destroyed. + // @param recording the recording handle + // @return pointer to the byte buffer, or NULL if no bytes have been written + Recording_GetData :: proc(#by_ptr recording: Recording) -> [^]u8 --- + + // Get the number of bytes currently in the recording buffer. + // @param recording the recording handle + Recording_GetSize :: proc(#by_ptr recording: Recording) -> c.int --- + + // Begin recording world mutations into the provided buffer. + // The buffer is reset on each call so a single Recording can be reused for multiple sessions. + // @param worldId the world to record + // @param recording the recording handle to write into + World_StartRecording :: proc(worldId: WorldId, recording: ^Recording) --- + + // End the current recording session. Writes the trailing geometry registry and + // backpatches the header. The buffer remains valid until the recording is destroyed. + // @param worldId the world currently being recorded + World_StopRecording :: proc(worldId: WorldId) --- + + // Save the recording buffer to a file. Returns true on success. + // @param recording the recording to save + // @param path file path to write + SaveRecordingToFile :: proc(#by_ptr recording: Recording, path: cstring) -> bool --- + + // Load a recording from a file. Returns NULL on failure (file not found, wrong magic). + // The caller owns the returned recording and must destroy it with DestroyRecording. + // @param path file path to read + LoadRecordingFromFile :: proc(path: cstring) -> Recording --- + + // Replay a recording from memory and verify it reproduces the same world-state hashes. + // Stands up a fresh world, restores the seed snapshot, replays every op, and checks each embedded + // StateHash record. Returns true if replay completed without id mismatches or hash divergences. + // @param data pointer to recording bytes + // @param size byte count of the recording + // @param workerCount reserved for future multithreaded replay; pass 1 for now + ValidateReplay :: proc(data: rawptr, size: c.int, workerCount: c.int) -> bool --- + + + // Create a player over a recording. Owns a private copy of the bytes. + // @param data pointer to recording bytes + // @param size byte count of the recording + // @param workerCount worker count for the replay world; pass 1 to match a serial recording. + // Replaying at a different count re-partitions the constraint graph, so the StateHash check + // becomes a cross-thread determinism test. Adjustable later with RecPlayer_SetWorkerCount. + // @return a new player, or NULL on bad header or deserialization failure + RecPlayer_Create :: proc(data: rawptr, size: c.int, workerCount: c.int) -> RecPlayer --- + + // Destroy the player and free all memory. Restores the previous global length scale. + RecPlayer_Destroy :: proc(player: ^RecPlayer) --- + + // Advance one frame: dispatch ops until the next Step completes. + // @return true when a frame was stepped, false at end-of-recording + RecPlayer_StepFrame :: proc(player: ^RecPlayer) -> bool --- + + // Rewind to frame 0 (in-place restore so the world id stays stable). + RecPlayer_Restart :: proc(player: ^RecPlayer) --- + + // Seek to a specific frame. Forward seek steps op-by-op; backward seek restores + // the nearest keyframe then re-steps the remaining gap. + RecPlayer_SeekFrame :: proc(player: ^RecPlayer, targetFrame: c.int) --- + + // @return the world currently driven by this player + RecPlayer_GetWorldId :: proc(#by_ptr player: RecPlayer) -> WorldId --- + + // @return the last fully-stepped frame index (0 before any step) + RecPlayer_GetFrame :: proc(#by_ptr player: RecPlayer) -> c.int --- + + // @return total number of recorded frames + RecPlayer_GetFrameCount :: proc(#by_ptr player: RecPlayer) -> c.int --- + + // @return true when the op stream is exhausted + RecPlayer_IsAtEnd :: proc(#by_ptr player: RecPlayer) -> bool --- + + // @return true when any StateHash mismatch has been detected + RecPlayer_HasDiverged :: proc(#by_ptr player: RecPlayer) -> bool --- + + // @return a summary of the recording read at open: frame count, recorded tuning, and bounds + RecPlayer_GetInfo :: proc(#by_ptr player: RecPlayer) -> RecPlayerInfo --- + + // @return the first frame at which replay diverged, or -1 if it has not diverged + RecPlayer_GetDivergeFrame :: proc(#by_ptr player: RecPlayer) -> c.int --- + + // Set the worker count of the replay world. Clamped to [1, B3_MAX_WORKERS]. Applied to the live + // world at once and reused whenever the player rebuilds its world on Restart or a backward seek. + // Replaying at a different count than recorded re-partitions the constraint graph, so the StateHash + // check becomes a cross-thread determinism test. + RecPlayer_SetWorkerCount :: proc(player: ^RecPlayer, count: c.int) --- + + // Tune the keyframe ring used to speed up backward seeking. A keyframe is a periodic snapshot the + // player restores from instead of replaying from the start, trading memory for seek speed. + // @param player the recording player + // @param budgetBytes memory cap for the kept snapshots; the spacing widens to stay under it + // @param minIntervalFrames finest spacing between keyframes, in frames + // A zero budget or a non-positive interval keeps that value. Clears the existing ring, so call + // RecPlayer_Restart afterward to repopulate it under the new policy. + RecPlayer_SetKeyframePolicy :: proc(player: ^RecPlayer, budgetBytes: uint, minIntervalFrames: c.int) --- + + // @return the keyframe memory budget in bytes + RecPlayer_GetKeyframeBudget :: proc(#by_ptr player: RecPlayer) -> uint --- + + // @return the finest keyframe spacing in frames + RecPlayer_GetKeyframeMinInterval :: proc(#by_ptr player: RecPlayer) -> c.int --- + + // @return the current keyframe spacing in frames; starts at the min interval and doubles as the + // ring evicts to stay under budget, so it reflects the effective backward-seek granularity now + RecPlayer_GetKeyframeInterval :: proc(#by_ptr player: RecPlayer) -> c.int --- + + // @return the memory currently held by keyframe snapshots, in bytes + RecPlayer_GetKeyframeBytes :: proc(#by_ptr player: RecPlayer) -> uint --- + + // @return the number of bodies tracked in creation order (including holes for destroyed bodies) + RecPlayer_GetBodyCount :: proc(#by_ptr player: RecPlayer) -> c.int --- + + // Resolve a creation ordinal to the live body id at the current frame. + // @return the body id, or a null id if that ordinal is out of range or its body is destroyed + RecPlayer_GetBodyId :: proc(#by_ptr player: RecPlayer, index: c.int) -> BodyId --- + + // Wire host debug-shape callbacks into the player's replay world so a renderer can build + // per-shape draw resources (the 3D sample needs this or the replay world draws nothing). + // Rebuilds the current world under the new callbacks and rewinds to frame 0, so call it + // once right after RecPlayer_Create and re-read the world id afterward. The callbacks + // persist across Restart and backward seeks, which recreate the world internally. + // @param player the player to configure + // @param createDebugShape called when a replayed shape is added; returns a user draw handle + // @param destroyDebugShape called when a replayed shape is removed; may be NULL + // @param context user context passed to both callbacks + RecPlayer_SetDebugShapeCallbacks :: proc(player: ^RecPlayer, createDebugShape: CreateDebugShapeCallback, destroyDebugShape: DestroyDebugShapeCallback, ctx: rawptr) --- + + // Draw the spatial queries recorded during the most recently replayed frame, layered on top of the + // world. Call after World_Draw. NULL draw function pointers are skipped. + // @param player a valid player handle + // @param draw debug draw callbacks + // @param queryIndex index of the frame query to draw, or -1 to draw all of them + // @param selectedIndex index of the query to emphasize (reserved color plus a label), or -1 for none + RecPlayer_DrawFrameQueries :: proc(player: ^RecPlayer, draw: ^DebugDraw, queryIndex: c.int, selectedIndex: c.int) --- + + + // @return the number of spatial queries recorded for the most recently replayed frame + RecPlayer_GetFrameQueryCount :: proc(#by_ptr player: RecPlayer) -> c.int --- + + // Get a recorded query from the most recently replayed frame by index. + RecPlayer_GetFrameQuery :: proc(#by_ptr player: RecPlayer, index: c.int) -> RecQueryInfo --- + + // Get one result of a recorded query from the most recently replayed frame. + RecPlayer_GetFrameQueryHit :: proc(#by_ptr player: RecPlayer, queryIndex: c.int, hitIndex: c.int) -> RecQueryHit --- + + /**@}*/ // recording + + /** @} */ // world + + /** + * @defgroup body Body + * This is the body API. + * @{ + */ + + // Create a rigid body given a definition. No reference to the definition is retained. So you can create the definition + // on the stack and pass it as a pointer. + // @code{.c} + // BodyDef bodyDef = DefaultBodyDef(); + // BodyId myBodyId = CreateBody(myWorldId, &bodyDef); + // @endcode + // @warning This function is locked during callbacks. + CreateBody :: proc(worldId: WorldId, #by_ptr def: BodyDef) -> BodyId --- + + // Destroy a rigid body given an id. This destroys all shapes and joints attached to the body. + // Do not keep references to the associated shapes and joints. + DestroyBody :: proc(bodyId: BodyId) --- + + // Body identifier validation. A valid body exists in a world and is non-null. + // This can be used to detect orphaned ids. Provides validation for up to 64K allocations. + Body_IsValid :: proc(id: BodyId) -> bool --- + + // Get the body type: static, kinematic, or dynamic + Body_GetType :: proc(bodyId: BodyId) -> BodyType --- + + // Change the body type. This is an expensive operation. This automatically updates the mass + // properties regardless of the automatic mass setting. + Body_SetType :: proc(bodyId: BodyId, type: BodyType) --- + + // Set the body name. Up to B3_BODY_NAME_LENGTH characters including null termination. + Body_SetName :: proc(bodyId: BodyId, name: cstring) --- + + // Get the body name. + Body_GetName :: proc(bodyId: BodyId) -> cstring --- + + // Set the user data for a body + Body_SetUserData :: proc(bodyId: BodyId, userData: rawptr) --- + + // Get the user data stored in a body + Body_GetUserData :: proc(bodyId: BodyId) --- + + // Get the world position of a body. This is the location of the body origin. + Body_GetPosition :: proc(bodyId: BodyId) -> Pos --- + + // Get the world rotation of a body as a quaternion + Body_GetRotation :: proc(bodyId: BodyId) -> Quat --- + + // Get the world transform of a body. + Body_GetTransform :: proc(bodyId: BodyId) -> WorldTransform --- + + // Set the world transform of a body. This acts as a teleport and is fairly expensive. + // @note Generally you should create a body with the intended transform. + // @see BodyDef::position and BodyDef::rotation + Body_SetTransform :: proc(bodyId: BodyId, position: Pos, rotation: Quat) --- + + // Get a local point on a body given a world point + Body_GetLocalPoint :: proc(bodyId: BodyId, worldPoint: Pos) -> Vec3 --- + + // Get a world point on a body given a local point + Body_GetWorldPoint :: proc(bodyId: BodyId, localPoint: Vec3) -> Pos --- + + // Get a local vector on a body given a world vector + Body_GetLocalVector :: proc(bodyId: BodyId, worldVector: Vec3) -> Vec3 --- + + // Get a world vector on a body given a local vector + Body_GetWorldVector :: proc(bodyId: BodyId, localVector: Vec3) -> Vec3 --- + + // Get the linear velocity of a body's center of mass. Usually in meters per second. + Body_GetLinearVelocity :: proc(bodyId: BodyId) -> Vec3 --- + + // Get the angular velocity of a body in radians per second + Body_GetAngularVelocity :: proc(bodyId: BodyId) -> Vec3 --- + + // Set the linear velocity of a body. Usually in meters per second. + Body_SetLinearVelocity :: proc(bodyId: BodyId, linearVelocity: Vec3) --- + + // Set the angular velocity of a body in radians per second + Body_SetAngularVelocity :: proc(bodyId: BodyId, angularVelocity: Vec3) --- + + // Set the velocity to reach the given transform after a given time step. + // The result will be close but maybe not exact. This is meant for kinematic bodies. + // The target is not applied if the velocity would be below the sleep threshold. + // This will optionally wake the body if asleep, but only if the movement is significant. + Body_SetTargetTransform :: proc(bodyId: BodyId, target: WorldTransform, timeStep: f32, wake: bool) --- + + // Get the linear velocity of a local point attached to a body. Usually in meters per second. + Body_GetLocalPointVelocity :: proc(bodyId: BodyId, localPoint: Vec3) -> Vec3 --- + + // Get the linear velocity of a world point attached to a body. Usually in meters per second. + Body_GetWorldPointVelocity :: proc(bodyId: BodyId, worldPoint: Pos) -> Vec3 --- + + // Apply a force at a world point. If the force is not applied at the center of mass, + // it will generate a torque and affect the angular velocity. This optionally wakes up the body. + // The force is ignored if the body is not awake. + // @param bodyId The body id + // @param force The world force vector, usually in newtons (N) + // @param point The world position of the point of application + // @param wake Option to wake up the body + Body_ApplyForce :: proc(bodyId: BodyId, force: Vec3, point: Pos, wake: bool) --- + + // Apply a force to the center of mass. This optionally wakes up the body. + // The force is ignored if the body is not awake. + // @param bodyId The body id + // @param force the world force vector, usually in newtons (N). + // @param wake also wake up the body + Body_ApplyForceToCenter :: proc(bodyId: BodyId, force: Vec3, wake: bool) --- + + // Apply a torque. This affects the angular velocity without affecting the linear velocity. + // This optionally wakes the body. The torque is ignored if the body is not awake. + // @param bodyId The body id + // @param torque the world torque vector, usually in N*m. + // @param wake also wake up the body + Body_ApplyTorque :: proc(bodyId: BodyId, torque: Vec3, wake: bool) --- + + // Apply an impulse at a point. This immediately modifies the velocity. + // It also modifies the angular velocity if the point of application + // is not at the center of mass. This optionally wakes the body. + // The impulse is ignored if the body is not awake. + // @param bodyId The body id + // @param impulse the world impulse vector, usually in N*s or kg*m/s. + // @param point the world position of the point of application. + // @param wake also wake up the body + // @warning This should be used for one-shot impulses. If you need a steady force, + // use a force instead, which will work better with the sub-stepping solver. + Body_ApplyLinearImpulse :: proc(bodyId: BodyId, impulse: Vec3, point: Pos, wake: bool) --- + + // Apply an impulse to the center of mass. This immediately modifies the velocity. + // The impulse is ignored if the body is not awake. This optionally wakes the body. + // @param bodyId The body id + // @param impulse the world impulse vector, usually in N*s or kg*m/s. + // @param wake also wake up the body + // @warning This should be used for one-shot impulses. If you need a steady force, + // use a force instead, which will work better with the sub-stepping solver. + Body_ApplyLinearImpulseToCenter :: proc(bodyId: BodyId, impulse: Vec3, wake: bool) --- + + // Apply an angular impulse in world space. The impulse is ignored if the body is not awake. + // This optionally wakes the body. + // @param bodyId The body id + // @param impulse the world angular impulse vector, usually in units of kg*m*m/s + // @param wake also wake up the body + // @warning This should be used for one-shot impulses. If you need a steady torque, + // use a torque instead, which will work better with the sub-stepping solver. + Body_ApplyAngularImpulse :: proc(bodyId: BodyId, impulse: Vec3, wake: bool) --- + + // Get the mass of the body, usually in kilograms + Body_GetMass :: proc(bodyId: BodyId) -> f32 --- + + // Get the rotational inertia of the body in local space, usually in kg*m^2 + Body_GetLocalRotationalInertia :: proc(bodyId: BodyId) -> Matrix3 --- + + // Get the inverse mass of the body, usually in 1/kilograms + Body_GetInverseMass :: proc(bodyId: BodyId) -> f32 --- + + // Get the inverse rotational inertia of the body in world space, usually in 1/kg*m^2 + Body_GetWorldInverseRotationalInertia :: proc(bodyId: BodyId) -> Matrix3 --- + + // Get the center of mass position of the body in local space + Body_GetLocalCenterOfMass :: proc(bodyId: BodyId) -> Vec3 --- + + // Get the center of mass position of the body in world space + Body_GetWorldCenterOfMass :: proc(bodyId: BodyId) -> Pos --- + + // Override the body's mass properties. Normally this is computed automatically using the + // shape geometry and density. This information is lost if a shape is added or removed or if the + // body type changes. + Body_SetMassData :: proc(bodyId: BodyId, massData: MassData) --- + + // Get the mass data for a body + Body_GetMassData :: proc(bodyId: BodyId) -> MassData --- + + // This updates the mass properties to the sum of the mass properties of the shapes. + // This normally does not need to be called unless you called SetMassData to override + // the mass and you later want to reset the mass. + // You may also use this when automatic mass computation has been disabled. + // You should call this regardless of body type. + Body_ApplyMassFromShapes :: proc(bodyId: BodyId) --- + + // Adjust the linear damping. Normally this is set in BodyDef before creation. + Body_SetLinearDamping :: proc(bodyId: BodyId, linearDamping: f32) --- + + // Get the current linear damping. + Body_GetLinearDamping :: proc(bodyId: BodyId) -> f32 --- + + // Adjust the angular damping. Normally this is set in BodyDef before creation. + Body_SetAngularDamping :: proc(bodyId: BodyId, angularDamping: f32) --- + + // Get the current angular damping. + Body_GetAngularDamping :: proc(bodyId: BodyId) -> f32 --- + + // Adjust the gravity scale. Normally this is set in BodyDef before creation. + // @see BodyDef::gravityScale + Body_SetGravityScale :: proc(bodyId: BodyId, gravityScale: f32) --- + + // Get the current gravity scale + Body_GetGravityScale :: proc(bodyId: BodyId) -> f32 --- + + // @return true if this body is awake + Body_IsAwake :: proc(bodyId: BodyId) -> bool --- + + // Wake a body from sleep. This wakes the entire island the body is touching. + // @warning Putting a body to sleep will put the entire island of bodies touching this body to sleep, + // which can be expensive and possibly unintuitive. + Body_SetAwake :: proc(bodyId: BodyId, awake: bool) --- + + // Enable or disable sleeping for this body. If sleeping is disabled the body will wake. + Body_EnableSleep :: proc(bodyId: BodyId, enableSleep: bool) --- + + // Returns true if sleeping is enabled for this body + Body_IsSleepEnabled :: proc(bodyId: BodyId) -> bool --- + + // Set the sleep threshold, usually in meters per second + Body_SetSleepThreshold :: proc(bodyId: BodyId, sleepThreshold: f32) --- + + // Get the sleep threshold, usually in meters per second. + Body_GetSleepThreshold :: proc(bodyId: BodyId) -> f32 --- + + // Returns true if this body is enabled + Body_IsEnabled :: proc(bodyId: BodyId) -> bool --- + + // Disable a body by removing it completely from the simulation. This is expensive. + Body_Disable :: proc(bodyId: BodyId) --- + + // Enable a body by adding it to the simulation. This is expensive. + Body_Enable :: proc(bodyId: BodyId) --- + + // Set the motion locks on this body. + Body_SetMotionLocks :: proc(bodyId: BodyId, locks: MotionLocks) --- + + // Get the motion locks for this body. + Body_GetMotionLocks :: proc(bodyId: BodyId) -> MotionLocks --- + + // Set this body to be a bullet. A bullet does continuous collision detection + // against dynamic bodies (but not other bullets). + Body_SetBullet :: proc(bodyId: BodyId, flag: bool) --- + + // Is this body a bullet? + Body_IsBullet :: proc(bodyId: BodyId) -> bool --- + + // Enable or disable contact recycling for this body. Contact recycling is a performance optimization + // that reuses contact manifolds when bodies move slightly. Disabling it can avoid ghost collisions + // on characters at the cost of higher per-step work. Existing contacts retain their prior setting; + // only contacts created after this call see the new value. + // @see BodyDef::enableContactRecycling + Body_EnableContactRecycling :: proc(bodyId: BodyId, flag: bool) --- + + // Is contact recycling enabled on this body? + Body_IsContactRecyclingEnabled :: proc(bodyId: BodyId) -> bool --- + + // Enable/disable hit events on all shapes + // @see ShapeDef::enableHitEvents + Body_EnableHitEvents :: proc(bodyId: BodyId, enableHitEvents: bool) --- + + // Get the world that owns this body + Body_GetWorld :: proc(bodyId: BodyId) -> WorldId --- + + // Get the number of shapes on this body + Body_GetShapeCount :: proc(bodyId: BodyId) -> c.int --- + + // Get the shape ids for all shapes on this body, up to the provided capacity. + // @returns the number of shape ids stored in the user array + Body_GetShapes :: proc(bodyId: BodyId, shapeArray: [^]ShapeId, capacity: c.int) -> c.int --- + + // Get the number of joints on this body + Body_GetJointCount :: proc(bodyId: BodyId) -> c.int --- + + // Get the joint ids for all joints on this body, up to the provided capacity + // @returns the number of joint ids stored in the user array + Body_GetJoints :: proc(bodyId: BodyId, jointArray: [^]JointId, capacity: c.int) -> c.int --- + + // Get the maximum capacity required for retrieving all the touching contacts on a body + Body_GetContactCapacity :: proc(bodyId: BodyId) -> c.int --- + + // Get the touching contact data for a body + Body_GetContactData :: proc(bodyId: BodyId, contactData: [^]ContactData, capacity: c.int) -> c.int --- + + // Get the current world AABB that contains all the attached shapes. Note that this may not encompass the body origin. + // If there are no shapes attached then the returned AABB is empty and centered on the body origin. + Body_ComputeAABB :: proc(bodyId: BodyId) -> AABB --- + + // Get the closest point on a body to a world target. + Body_GetClosestPoint :: proc(bodyId: BodyId, result: ^Vec3, target: Vec3) -> f32 --- + + // Cast a ray at a specific body using a specified body transform. + Body_CastRay :: proc(bodyId: BodyId, origin: Pos, translation: Vec3, filter: QueryFilter, maxFraction: f32, bodyTransform: WorldTransform) -> BodyCastResult --- + + // Cast a shape at a specific body using a specified body transform. + Body_CastShape :: proc(bodyId: BodyId, origin: Pos, #by_ptr proxy: ShapeProxy, translation: Vec3, filter: QueryFilter, maxFraction: f32, canEncroach: b32, bodyTransform: WorldTransform) -> BodyCastResult --- + + // Overlap a shape with a specific body using a specified body transform. + Body_OverlapShape :: proc(bodyId: BodyId, origin: Pos, #by_ptr proxy: ShapeProxy, filter: QueryFilter, bodyTransform: WorldTransform) -> bool --- + + // Collide a character mover with a specific body using a specified body transform. + Body_CollideMover :: proc(bodyId: BodyId, bodyPlanes: [^]BodyPlaneResult, planeCapacity: c.int, origin: Pos, #by_ptr mover: Capsule, filter: QueryFilter, bodyTransform: WorldTransform) -> c.int --- + + /** @} */ // body + + /** + * @defgroup shape Shape + * Functions to create, destroy, and access. + * Shapes bind raw geometry to bodies and hold material properties including friction and restitution. + * @{ + */ + + // Create a circle shape and attach it to a body. The shape definition and geometry are fully cloned. + // Contacts are not created until the next time step. + // @return the shape id for accessing the shape + CreateSphereShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, #by_ptr sphere: Sphere) -> ShapeId --- + + // Create a capsule shape and attach it to a body. The shape definition and geometry are fully cloned. + // Contacts are not created until the next time step. + // @return the shape id for accessing the shape + CreateCapsuleShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, #by_ptr capsule: Capsule) -> ShapeId --- + + // Create a convex hull shape and attach it to a body. The shape definition is fully cloned. Contacts are not created + // until the next time step. + // @return the shape id for accessing the shape + CreateHullShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, #by_ptr hull: HullData) -> ShapeId --- + + // Create a convex hull shape and attach it to a body. The hull is cloned then transformed with scale applied first. + // Use this for non-uniform or mirrored scale or a baked local transform. The baked result is shared through the + // world hull database. The shape definition and geometry are fully cloned. Contacts are not created until the next time step. + // @return the shape id for accessing the shape + CreateTransformedHullShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, #by_ptr hull: HullData, transform: Transform, scale: Vec3) -> ShapeId --- + + // Create a mesh hull shape and attach it to a body. The shape definition is fully cloned but the mesh is not. + // Contacts are not created until the next time step. + // Mesh collision only creates contacts on static bodies. + // @warning this holds reference to the input mesh data which must remain valid for the lifetime of this shape + // @return the shape id for accessing the shape + CreateMeshShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, #by_ptr mesh: MeshData, scale: Vec3) -> ShapeId --- + + // Create a height-field shape and attach it to a body. The shape definition is fully cloned but the height field is not. + // Contacts are not created until the next time step. + // Height field is only allowed on static bodies. + // @warning this holds reference to the input height field which must remain valid for the lifetime of this shape + // @return the shape id for accessing the shape + CreateHeightFieldShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, #by_ptr heightField: HeightFieldData) -> ShapeId --- + + // Compound shapes are only allowed on static bodies. + CreateCompoundShape :: proc(bodyId: BodyId, def: ^ShapeDef, #by_ptr compound: CompoundData) -> ShapeId --- + + // Destroy a shape. You may defer the body mass update which can improve performance if several shapes on a + // body are destroyed at once. + // @see Body_ApplyMassFromShapes + DestroyShape :: proc(shapeId: ShapeId, updateBodyMass: bool) --- + + // Shape identifier validation. Provides validation for up to 64K allocations. + Shape_IsValid :: proc(id: ShapeId) -> bool --- + + // Get the type of a shape + Shape_GetType :: proc(shapeId: ShapeId) -> ShapeType --- + + // Get the id of the body that a shape is attached to + Shape_GetBody :: proc(shapeId: ShapeId) -> BodyId --- + + // Get the world that owns this shape + Shape_GetWorld :: proc(shapeId: ShapeId) -> WorldId --- + + // Returns true if the shape is a sensor + Shape_IsSensor :: proc(shapeId: ShapeId) -> bool --- + + // Set the user data for a shape + Shape_SetUserData :: proc(shapeId: ShapeId, userData: rawptr) --- + + // Get the user data for a shape. This is useful when you get a shape id + // from an event or query. + Shape_GetUserData :: proc(shapeId: ShapeId) --- + + // Set the mass density of a shape, usually in kg/m^3. + // This will optionally update the mass properties on the parent body. + // @see ShapeDef::density, Body_ApplyMassFromShapes + Shape_SetDensity :: proc(shapeId: ShapeId, density: f32, updateBodyMass: bool) --- + + // Get the density of a shape, usually in kg/m^3 + Shape_GetDensity :: proc(shapeId: ShapeId) -> f32 --- + + // Set the friction on a shape + Shape_SetFriction :: proc(shapeId: ShapeId, friction: f32) --- + + // Get the friction of a shape + Shape_GetFriction :: proc(shapeId: ShapeId) -> f32 --- + + // Set the shape restitution (bounciness) + Shape_SetRestitution :: proc(shapeId: ShapeId, restitution: f32) --- + + // Get the shape restitution + Shape_GetRestitution :: proc(shapeId: ShapeId) -> f32 --- + + // Set the shape base surface material. Does not change per triangle materials. + Shape_SetSurfaceMaterial :: proc(shapeId: ShapeId, surfaceMaterial: SurfaceMaterial) --- + + // Get the base shape surface material. + Shape_GetSurfaceMaterial :: proc(shapeId: ShapeId) -> SurfaceMaterial --- + + // Get the number of mesh surface materials. + Shape_GetMeshMaterialCount :: proc(shapeId: ShapeId) -> c.int --- + + // Set a surface material for a mesh shape. + Shape_SetMeshMaterial :: proc(shapeId: ShapeId, surfaceMaterial: SurfaceMaterial, index: c.int) --- + + // Get a surface material for a mesh shape + Shape_GetMeshSurfaceMaterial :: proc(shapeId: ShapeId, index: c.int) -> SurfaceMaterial --- + + // Get the shape filter + Shape_GetFilter :: proc(shapeId: ShapeId) -> Filter --- + + // Set the current filter. This is almost as expensive as recreating the shape. + // @see ShapeDef::filter + // @param shapeId the shape + // @param filter the new filter + // @param invokeContacts if true then the shape will have all contacts recomputed the next time step (expensive) + Shape_SetFilter :: proc(shapeId: ShapeId, filter: Filter, invokeContacts: bool) --- + + // Enable sensor events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. + // @see ShapeDef::isSensor + Shape_EnableSensorEvents :: proc(shapeId: ShapeId, flag: bool) --- + + // Returns true if sensor events are enabled + Shape_AreSensorEventsEnabled :: proc(shapeId: ShapeId) -> bool --- + + // Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. + // @see ShapeDef::enableContactEvents + Shape_EnableContactEvents :: proc(shapeId: ShapeId, flag: bool) --- + + // Returns true if contact events are enabled + Shape_AreContactEventsEnabled :: proc(shapeId: ShapeId) -> bool --- + + // Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive + // and must be carefully handled due to multithreading. Ignored for sensors. + // @see PreSolveFcn + Shape_EnablePreSolveEvents :: proc(shapeId: ShapeId, flag: bool) --- + + // Returns true if pre-solve events are enabled + Shape_ArePreSolveEventsEnabled :: proc(shapeId: ShapeId) -> bool --- + + // Enable contact hit events for this shape. Ignored for sensors. + // @see WorldDef.hitEventThreshold + Shape_EnableHitEvents :: proc(shapeId: ShapeId, flag: bool) --- + + // Returns true if hit events are enabled + Shape_AreHitEventsEnabled :: proc(shapeId: ShapeId) -> bool --- + + // Ray cast a shape directly. The ray runs from origin to origin + translation and the hit point + // comes back as a world position, so the cast stays precise far from the world origin. + Shape_RayCast :: proc(shapeId: ShapeId, origin: Pos, translation: Vec3) -> WorldCastOutput --- + + // Get a copy of the shape's sphere. Asserts the type is correct. + Shape_GetSphere :: proc(shapeId: ShapeId) -> Sphere --- + + // Get a copy of the shape's capsule. Asserts the type is correct. + Shape_GetCapsule :: proc(shapeId: ShapeId) -> Capsule --- + + // Get the shape's convex hull. Asserts the type is correct. + Shape_GetHull :: proc(shapeId: ShapeId) -> ^HullData --- + + // Get the shape's mesh. Asserts the type is correct. + Shape_GetMesh :: proc(shapeId: ShapeId) -> Mesh --- + + // Get the shape's height field. Asserts the type is correct. + Shape_GetHeightField :: proc(shapeId: ShapeId) -> ^HeightFieldData --- + + // Allows you to change a shape to be a sphere or update the current sphere. + // This does not modify the mass properties. + // @see Body_ApplyMassFromShapes + Shape_SetSphere :: proc(shapeId: ShapeId, #by_ptr sphere: Sphere) --- + + // Allows you to change a shape to be a capsule or update the current capsule. + // This does not modify the mass properties. + // @see Body_ApplyMassFromShapes + Shape_SetCapsule :: proc(shapeId: ShapeId, #by_ptr capsule: Capsule) --- + + // Allows you to change a shape to be a hull or update the current hull. + // This does not modify the mass properties. + // @see Body_ApplyMassFromShapes + Shape_SetHull :: proc(shapeId: ShapeId, #by_ptr hull: HullData) --- + + // Allows you to change a shape to be a mesh or update the current mesh. + // This does not modify the mass properties. + // @see Body_ApplyMassFromShapes + Shape_SetMesh :: proc(shapeId: ShapeId, #by_ptr meshData: MeshData, scale: Vec3) --- + + // Get the maximum capacity required for retrieving all the touching contacts on a shape + Shape_GetContactCapacity :: proc(shapeId: ShapeId) -> c.int --- + + // Get the touching contact data for a shape. The provided shapeId will be either shapeIdA or shapeIdB on the contact data. + // @note Box3D uses speculative collision so some contact points may be separated. + // @returns the number of elements filled in the provided array + // @warning do not ignore the return value, it specifies the valid number of elements + Shape_GetContactData :: proc(shapeId: ShapeId, contactData: [^]ContactData, capacity: c.int) -> c.int --- + + // Get the maximum capacity required for retrieving all the overlapped shapes on a sensor shape. + // This returns 0 if the provided shape is not a sensor. + // @param shapeId the id of a sensor shape + // @returns the required capacity to get all the overlaps in Shape_GetSensorOverlaps + Shape_GetSensorCapacity :: proc(shapeId: ShapeId) -> c.int --- + + // Get the overlap data for a sensor shape. + // @param shapeId the id of a sensor shape + // @param visitorIds a user allocated array that is filled with the overlapping shapes (visitors) + // @param capacity the capacity of overlappedShapes + // @returns the number of elements filled in the provided array + // @warning do not ignore the return value, it specifies the valid number of elements + // @warning overlaps may contain destroyed shapes so use Shape_IsValid to confirm each overlap + Shape_GetSensorData :: proc(shapeId: ShapeId, visitorIds: [^]ShapeId, capacity: c.int) -> c.int --- + + // Get the current world AABB + Shape_GetAABB :: proc(shapeId: ShapeId) -> AABB --- + + // Compute the mass data for a shape + Shape_ComputeMassData :: proc(shapeId: ShapeId) -> MassData --- + + // Get the closest point on a shape to a target point. Target and result are in world space. + Shape_GetClosestPoint :: proc(shapeId: ShapeId, target: Vec3) -> Vec3 --- + + // Apply a wind force to the body for this shape using the density of air. This considers + // the projected area of the shape in the wind direction. This also considers + // the relative velocity of the shape. + // @param shapeId the shape id + // @param wind the wind velocity in world space + // @param drag the drag coefficient, the force that opposes the relative velocity + // @param lift the lift coefficient, the force that is perpendicular to the relative velocity + // @param maxSpeed the maximum relative speed. Speed cap is necessary for stability. Typically 10m/s or less. + // @param wake should this wake the body + Shape_ApplyWind :: proc(shapeId: ShapeId, wind: Vec3 , drag, lift: f32, maxSpeed: f32, wake: bool) --- + + /** @} */ // shape + + /** + * @defgroup joint Joint + * @brief Joints allow you to connect rigid bodies together while allowing various forms of relative motions. + * @{ + */ + + // Destroy a joint + DestroyJoint :: proc(jointId: JointId, wakeAttached: bool) --- + + // Joint identifier validation. Provides validation for up to 64K allocations. + Joint_IsValid :: proc(id: JointId) -> bool --- + + // Get the joint type + Joint_GetType :: proc(jointId: JointId) -> JointType --- + + // Get body A id on a joint + Joint_GetBodyA :: proc(jointId: JointId) -> BodyId --- + + // Get body B id on a joint + Joint_GetBodyB :: proc(jointId: JointId) -> BodyId --- + + // Get the world that owns this joint + Joint_GetWorld :: proc(jointId: JointId) -> WorldId --- + + // Set the local frame on bodyA + Joint_SetLocalFrameA :: proc(jointId: JointId, localFrame: Transform) --- + + // Get the local frame on bodyA + Joint_GetLocalFrameA :: proc(jointId: JointId) -> Transform --- + + // Set the local frame on bodyB + Joint_SetLocalFrameB :: proc(jointId: JointId, localFrame: Transform) --- + + // Get the local frame on bodyB + Joint_GetLocalFrameB :: proc(jointId: JointId) -> Transform --- + + // Toggle collision between connected bodies + Joint_SetCollideConnected :: proc(jointId: JointId, shouldCollide: bool) --- + + // Is collision allowed between connected bodies? + Joint_GetCollideConnected :: proc(jointId: JointId) -> bool --- + + // Set the user data on a joint + Joint_SetUserData :: proc(jointId: JointId, userData: rawptr) --- + + // Get the user data on a joint + Joint_GetUserData :: proc(jointId: JointId) --- + + // Wake the bodies connect to this joint + Joint_WakeBodies :: proc(jointId: JointId) --- + + // Get the current constraint force for this joint + Joint_GetConstraintForce :: proc(jointId: JointId) -> Vec3 --- + + // Get the current constraint torque for this joint + Joint_GetConstraintTorque :: proc(jointId: JointId) -> Vec3 --- + + // Get the current linear separation error for this joint. Does not consider admissible movement. Usually in meters. + Joint_GetLinearSeparation :: proc(jointId: JointId) -> f32 --- + + // Get the current angular separation error for this joint. Does not consider admissible movement. Usually in radians. + Joint_GetAngularSeparation :: proc(jointId: JointId) -> f32 --- + + // Set the joint constraint tuning. Advanced feature. + // @param jointId the joint + // @param hertz the stiffness in Hertz (cycles per second) + // @param dampingRatio the non-dimensional damping ratio (one for critical damping) + Joint_SetConstraintTuning :: proc(jointId: JointId, hertz: f32, dampingRatio: f32) --- + + // Get the joint constraint tuning. Advanced feature. + Joint_GetConstraintTuning :: proc(jointId: JointId, hertz: ^f32, dampingRatio: ^f32) --- + + // Set the force threshold for joint events (Newtons) + Joint_SetForceThreshold :: proc(jointId: JointId, threshold: f32) --- + + // Get the force threshold for joint events (Newtons) + Joint_GetForceThreshold :: proc(jointId: JointId) -> f32 --- + + // Set the torque threshold for joint events (N-m) + Joint_SetTorqueThreshold :: proc(jointId: JointId, threshold: f32) --- + + // Get the torque threshold for joint events (N-m) + Joint_GetTorqueThreshold :: proc(jointId: JointId) -> f32 --- + + /** + * @defgroup parallel_joint Parallel Joint + * @brief Functions for the parallel joint. + * @{ + */ + + // Create a parallel joint + // @see ParallelJointDef for details + CreateParallelJoint :: proc(worldId: WorldId, #by_ptr def: ParallelJointDef) -> JointId --- + + // Set the spring stiffness in Hertz + ParallelJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) --- + + // Set the spring damping ratio, non-dimensional + ParallelJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- + + // Get the spring Hertz + ParallelJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 --- + + // Get the spring damping ratio + ParallelJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 --- + + // Set the maximum spring torque, usually in newton-meters + ParallelJoint_SetMaxTorque :: proc(jointId: JointId, force: f32) --- + + // Get the maximum spring torque, usually in newton-meters + ParallelJoint_GetMaxTorque :: proc(jointId: JointId) -> f32 --- + + /** @} */ // parallel_joint + + /** + * @defgroup distance_joint Distance Joint + * @brief Functions for the distance joint. + * @{ + */ + + // Create a distance joint + // @see DistanceJointDef for details + CreateDistanceJoint :: proc(worldId: WorldId, #by_ptr def: DistanceJointDef) -> JointId --- + + // Set the rest length of a distance joint + // @param jointId The id for a distance joint + // @param length The new distance joint length + DistanceJoint_SetLength :: proc(jointId: JointId, length: f32) --- + + // Get the rest length of a distance joint + DistanceJoint_GetLength :: proc(jointId: JointId) -> f32 --- + + // Enable/disable the distance joint spring. When disabled the distance joint is rigid. + DistanceJoint_EnableSpring :: proc(jointId: JointId, enableSpring: bool) --- + + // Is the distance joint spring enabled? + DistanceJoint_IsSpringEnabled :: proc(jointId: JointId) -> bool --- + + // Set the force range for the spring. + DistanceJoint_SetSpringForceRange :: proc(jointId: JointId, lowerForce, upperForce: f32) --- + + // Get the force range for the spring. + DistanceJoint_GetSpringForceRange :: proc(jointId: JointId, lowerForce, upperForce: ^f32) --- + + // Set the spring stiffness in Hertz + DistanceJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) --- + + // Set the spring damping ratio, non-dimensional + DistanceJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- + + // Get the spring Hertz + DistanceJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 --- + + // Get the spring damping ratio + DistanceJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 --- + + // Enable joint limit. The limit only works if the joint spring is enabled. Otherwise the joint is rigid + // and the limit has no effect. + DistanceJoint_EnableLimit :: proc(jointId: JointId, enableLimit: bool) --- + + // Is the distance joint limit enabled? + DistanceJoint_IsLimitEnabled :: proc(jointId: JointId) -> bool --- + + // Set the minimum and maximum length parameters of a distance joint + DistanceJoint_SetLengthRange :: proc(jointId: JointId, minLength, maxLength: f32) --- + + // Get the distance joint minimum length + DistanceJoint_GetMinLength :: proc(jointId: JointId) -> f32 --- + + // Get the distance joint maximum length + DistanceJoint_GetMaxLength :: proc(jointId: JointId) -> f32 --- + + // Get the current length of a distance joint + DistanceJoint_GetCurrentLength :: proc(jointId: JointId) -> f32 --- + + // Enable/disable the distance joint motor + DistanceJoint_EnableMotor :: proc(jointId: JointId, enableMotor: bool) --- + + // Is the distance joint motor enabled? + DistanceJoint_IsMotorEnabled :: proc(jointId: JointId) -> bool --- + + // Set the distance joint motor speed, usually in meters per second + DistanceJoint_SetMotorSpeed :: proc(jointId: JointId, motorSpeed: f32) --- + + // Get the distance joint motor speed, usually in meters per second + DistanceJoint_GetMotorSpeed :: proc(jointId: JointId) -> f32 --- + + // Set the distance joint maximum motor force, usually in newtons + DistanceJoint_SetMaxMotorForce :: proc(jointId: JointId, force: f32) --- + + // Get the distance joint maximum motor force, usually in newtons + DistanceJoint_GetMaxMotorForce :: proc(jointId: JointId) -> f32 --- + + // Get the distance joint current motor force, usually in newtons + DistanceJoint_GetMotorForce :: proc(jointId: JointId) -> f32 --- + + /** @} */ // distance_joint + + /** + * @defgroup motor_joint Motor Joint + * @brief Functions for the motor joint. + * + * The motor joint is designed to control the movement of a body while still being + * responsive to collisions. A spring controls the position and rotation. A velocity motor + * can be used to control velocity and allows for friction in top-down games. Both types + * of control can be combined. For example, you can have a spring with friction. + * Position and velocity control have force and torque limits. + * @{ + */ + + // Create a motor joint + // @see MotorJointDef for details + CreateMotorJoint :: proc(worldId: WorldId, #by_ptr def: MotorJointDef) -> JointId --- + + // Set the desired relative linear velocity in meters per second + MotorJoint_SetLinearVelocity :: proc(jointId: JointId, velocity: Vec3) --- + + // Get the desired relative linear velocity in meters per second + MotorJoint_GetLinearVelocity :: proc(jointId: JointId) -> Vec3 --- + + // Set the desired relative angular velocity in radians per second + MotorJoint_SetAngularVelocity :: proc(jointId: JointId, velocity: Vec3) --- + + // Get the desired relative angular velocity in radians per second + MotorJoint_GetAngularVelocity :: proc(jointId: JointId) -> Vec3 --- + + // Set the motor joint maximum force, usually in newtons + MotorJoint_SetMaxVelocityForce :: proc(jointId: JointId, maxForce: f32) --- + + // Get the motor joint maximum force, usually in newtons + MotorJoint_GetMaxVelocityForce :: proc(jointId: JointId) -> f32 --- + + // Set the motor joint maximum torque, usually in newton-meters + MotorJoint_SetMaxVelocityTorque :: proc(jointId: JointId, maxTorque: f32) --- + + // Get the motor joint maximum torque, usually in newton-meters + MotorJoint_GetMaxVelocityTorque :: proc(jointId: JointId) -> f32 --- + + // Set the spring linear hertz stiffness + MotorJoint_SetLinearHertz :: proc(jointId: JointId, hertz: f32) --- + + // Get the spring linear hertz stiffness + MotorJoint_GetLinearHertz :: proc(jointId: JointId) -> f32 --- + + // Set the spring linear damping ratio. Use 1.0 for critical damping. + MotorJoint_SetLinearDampingRatio :: proc(jointId: JointId, damping: f32) --- + + // Get the spring linear damping ratio. + MotorJoint_GetLinearDampingRatio :: proc(jointId: JointId) -> f32 --- + + // Set the spring angular hertz stiffness + MotorJoint_SetAngularHertz :: proc(jointId: JointId, hertz: f32) --- + + // Get the spring angular hertz stiffness + MotorJoint_GetAngularHertz :: proc(jointId: JointId) -> f32 --- + + // Set the spring angular damping ratio. Use 1.0 for critical damping. + MotorJoint_SetAngularDampingRatio :: proc(jointId: JointId, damping: f32) --- + + // Get the spring angular damping ratio. + MotorJoint_GetAngularDampingRatio :: proc(jointId: JointId) -> f32 --- + + // Set the maximum spring force in newtons. + MotorJoint_SetMaxSpringForce :: proc(jointId: JointId, maxForce: f32) --- + + // Get the maximum spring force in newtons. + MotorJoint_GetMaxSpringForce :: proc(jointId: JointId) -> f32 --- + + // Set the maximum spring torque in newtons * meters + MotorJoint_SetMaxSpringTorque :: proc(jointId: JointId, maxTorque: f32) --- + + // Get the maximum spring torque in newtons * meters + MotorJoint_GetMaxSpringTorque :: proc(jointId: JointId) -> f32 --- + + /**@}*/ // motor_joint + + /** + * @defgroup filter_joint Filter Joint + * @brief Functions for the filter joint. + * + * The filter joint is used to disable collision between two bodies. As a side effect of being a joint, it also + * keeps the two bodies in the same simulation island. + * @{ + */ + + // Create a filter joint. + // @see FilterJointDef for details + CreateFilterJoint :: proc(worldId: WorldId, #by_ptr def: FilterJointDef) -> JointId --- + + /**@}*/ // filter_joint + + /** + * @defgroup prismatic_joint Prismatic Joint + * @brief A prismatic joint allows for translation along a single axis with no rotation. + * + * The prismatic joint is useful for things like pistons and moving platforms, where you want a body to translate + * along an axis and have no rotation. Also called a *slider* joint. + * @{ + */ + + // Create a prismatic (slider) joint. + // @see PrismaticJointDef for details + CreatePrismaticJoint :: proc(worldId: WorldId, #by_ptr def: PrismaticJointDef) -> JointId --- + + // Enable/disable the joint spring. + PrismaticJoint_EnableSpring :: proc(jointId: JointId, enableSpring: bool) --- + + // Is the prismatic joint spring enabled or not? + PrismaticJoint_IsSpringEnabled :: proc(jointId: JointId) -> bool --- + + // Set the prismatic joint stiffness in Hertz. + // This should usually be less than a quarter of the simulation rate. For example, if the simulation + // runs at 60Hz then the joint stiffness should be 15Hz or less. + PrismaticJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) --- + + // Get the prismatic joint stiffness in Hertz + PrismaticJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 --- + + // Set the prismatic joint damping ratio (non-dimensional) + PrismaticJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- + + // Get the prismatic spring damping ratio (non-dimensional) + PrismaticJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 --- + + // Set the prismatic joint target translation. Usually in meters. + PrismaticJoint_SetTargetTranslation :: proc(jointId: JointId, targetTranslation: f32) --- + + // Get the prismatic joint target translation. Usually in meters. + PrismaticJoint_GetTargetTranslation :: proc(jointId: JointId) -> f32 --- + + // Enable/disable a prismatic joint limit + PrismaticJoint_EnableLimit :: proc(jointId: JointId, enableLimit: bool) --- + + // Is the prismatic joint limit enabled? + PrismaticJoint_IsLimitEnabled :: proc(jointId: JointId) -> bool --- + + // Get the prismatic joint lower limit + PrismaticJoint_GetLowerLimit :: proc(jointId: JointId) -> f32 --- + + // Get the prismatic joint upper limit + PrismaticJoint_GetUpperLimit :: proc(jointId: JointId) -> f32 --- + + // Set the prismatic joint limits + PrismaticJoint_SetLimits :: proc(jointId: JointId, lower, upper: f32) --- + + // Enable/disable a prismatic joint motor + PrismaticJoint_EnableMotor :: proc(jointId: JointId, enableMotor: bool) --- + + // Is the prismatic joint motor enabled? + PrismaticJoint_IsMotorEnabled :: proc(jointId: JointId) -> bool --- + + // Set the prismatic joint motor speed, usually in meters per second + PrismaticJoint_SetMotorSpeed :: proc(jointId: JointId, motorSpeed: f32) --- + + // Get the prismatic joint motor speed, usually in meters per second + PrismaticJoint_GetMotorSpeed :: proc(jointId: JointId) -> f32 --- + + // Set the prismatic joint maximum motor force, usually in newtons + PrismaticJoint_SetMaxMotorForce :: proc(jointId: JointId, force: f32) --- + + // Get the prismatic joint maximum motor force, usually in newtons + PrismaticJoint_GetMaxMotorForce :: proc(jointId: JointId) -> f32 --- + + // Get the prismatic joint current motor force, usually in newtons + PrismaticJoint_GetMotorForce :: proc(jointId: JointId) -> f32 --- + + // Get the current joint translation, usually in meters. + PrismaticJoint_GetTranslation :: proc(jointId: JointId) -> f32 --- + + // Get the current joint translation speed, usually in meters per second. + PrismaticJoint_GetSpeed :: proc(jointId: JointId) -> f32 --- + + /**@}*/ // prismatic_joint + + /** + * @defgroup revolute_joint Revolute Joint + * @brief A revolute joint allows for relative rotation about a single axis with no relative translation. + * + * Also called a *hinge* or *pin* joint. + * @{ + */ + + // Create a revolute joint + // @see RevoluteJointDef for details + CreateRevoluteJoint :: proc(worldId: WorldId, #by_ptr def: RevoluteJointDef) -> JointId --- + + // Enable/disable the revolute joint spring + RevoluteJoint_EnableSpring :: proc(jointId: JointId, enableSpring: bool) --- + + // Is the revolute angular spring enabled? + RevoluteJoint_IsSpringEnabled :: proc(jointId: JointId) -> bool --- + + // Set the revolute joint spring stiffness in Hertz + RevoluteJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) --- + + // Get the revolute joint spring stiffness in Hertz + RevoluteJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 --- + + // Set the revolute joint spring damping ratio, non-dimensional + RevoluteJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- + + // Get the revolute joint spring damping ratio, non-dimensional + RevoluteJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 --- + + // Set the revolute joint target angle in radians + RevoluteJoint_SetTargetAngle :: proc(jointId: JointId, targetRadians: f32) --- + + // Get the revolute joint target angle in radians + RevoluteJoint_GetTargetAngle :: proc(jointId: JointId) -> f32 --- + + // Get the revolute joint current angle in radians relative to the reference angle + // @see RevoluteJointDef::referenceAngle + RevoluteJoint_GetAngle :: proc(jointId: JointId) -> f32 --- + + // Enable/disable the revolute joint limit + RevoluteJoint_EnableLimit :: proc(jointId: JointId, enableLimit: bool) --- + + // Is the revolute joint limit enabled? + RevoluteJoint_IsLimitEnabled :: proc(jointId: JointId) -> bool --- + + // Get the revolute joint lower limit in radians + RevoluteJoint_GetLowerLimit :: proc(jointId: JointId) -> f32 --- + + // Get the revolute joint upper limit in radians + RevoluteJoint_GetUpperLimit :: proc(jointId: JointId) -> f32 --- + + // Set the revolute joint limits in radians + RevoluteJoint_SetLimits :: proc(jointId: JointId, lowerLimitRadians, upperLimitRadians: f32) --- + + // Enable/disable a revolute joint motor + RevoluteJoint_EnableMotor :: proc(jointId: JointId, enableMotor: bool) --- + + // Is the revolute joint motor enabled? + RevoluteJoint_IsMotorEnabled :: proc(jointId: JointId) -> bool --- + + // Set the revolute joint motor speed in radians per second + RevoluteJoint_SetMotorSpeed :: proc(jointId: JointId, motorSpeed: f32) --- + + // Get the revolute joint motor speed in radians per second + RevoluteJoint_GetMotorSpeed :: proc(jointId: JointId) -> f32 --- + + // Get the revolute joint current motor torque, usually in newton-meters + RevoluteJoint_GetMotorTorque :: proc(jointId: JointId) -> f32 --- + + // Set the revolute joint maximum motor torque, usually in newton-meters + RevoluteJoint_SetMaxMotorTorque :: proc(jointId: JointId, torque: f32) --- + + // Get the revolute joint maximum motor torque, usually in newton-meters + RevoluteJoint_GetMaxMotorTorque :: proc(jointId: JointId) -> f32 --- + + /**@}*/ // revolute_joint + + /** + * @defgroup spherical_joint Spherical Joint + * @brief A spherical joint allows for relative rotation in the 3D space with no relative translation. + * + * Also called a *ball-in-socket* or *point-to-point* joint. + * @{ + */ + + // Create a spherical joint + // @see SphericalJointDef for details + CreateSphericalJoint :: proc(worldId: WorldId, #by_ptr def: SphericalJointDef) -> JointId --- + + // Enable/disable the spherical joint cone limit + SphericalJoint_EnableConeLimit :: proc(jointId: JointId, enableLimit: bool) --- + + // Is the spherical joint cone limit enabled? + SphericalJoint_IsConeLimitEnabled :: proc(jointId: JointId) -> bool --- + + // Get the spherical joint cone limit in radians + SphericalJoint_GetConeLimit :: proc(jointId: JointId) -> f32 --- + + // Set the spherical joint limits in radians + SphericalJoint_SetConeLimit :: proc(jointId: JointId, angleRadians: f32) --- + + // Get the spherical joint current cone angle in radians. + SphericalJoint_GetConeAngle :: proc(jointId: JointId) -> f32 --- + + // Enable/disable the spherical joint limit + SphericalJoint_EnableTwistLimit :: proc(jointId: JointId, enableLimit: bool) --- + + // Is the spherical joint limit enabled? + SphericalJoint_IsTwistLimitEnabled :: proc(jointId: JointId) -> bool --- + + // Get the spherical joint lower limit in radians + SphericalJoint_GetLowerTwistLimit :: proc(jointId: JointId) -> f32 --- + + // Get the spherical joint upper limit in radians + SphericalJoint_GetUpperTwistLimit :: proc(jointId: JointId) -> f32 --- + + // Set the spherical joint limits in radians + SphericalJoint_SetTwistLimits :: proc(jointId: JointId, lowerLimitRadians, upperLimitRadians: f32) --- + + // Get the spherical joint current twist angle in radians. + SphericalJoint_GetTwistAngle :: proc(jointId: JointId) -> f32 --- + + // Enable/disable the spherical joint spring + SphericalJoint_EnableSpring :: proc(jointId: JointId, enableSpring: bool) --- + + // Is the spherical angular spring enabled? + SphericalJoint_IsSpringEnabled :: proc(jointId: JointId) -> bool --- + + // Set the spherical joint spring stiffness in Hertz + SphericalJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) --- + + // Get the spherical joint spring stiffness in Hertz + SphericalJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 --- + + // Set the spherical joint spring damping ratio, non-dimensional + SphericalJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- + + // Get the spherical joint spring damping ratio, non-dimensional + SphericalJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 --- + + // Set the spherical joint spring target rotation + SphericalJoint_SetTargetRotation :: proc(jointId: JointId, targetRotation: Quat) --- + + // Get the spherical joint spring target rotation + SphericalJoint_GetTargetRotation :: proc(jointId: JointId) -> Quat --- + + // Enable/disable a spherical joint motor + SphericalJoint_EnableMotor :: proc(jointId: JointId, enableMotor: bool) --- + + // Is the spherical joint motor enabled? + SphericalJoint_IsMotorEnabled :: proc(jointId: JointId) -> bool --- + + // Set the spherical joint motor velocity in radians per second + SphericalJoint_SetMotorVelocity :: proc(jointId: JointId, motorVelocity: Vec3) --- + + // Get the spherical joint motor velocity in radians per second + SphericalJoint_GetMotorVelocity :: proc(jointId: JointId) -> Vec3 --- + + // Get the spherical joint current motor torque, usually in newton-meters + SphericalJoint_GetMotorTorque :: proc(jointId: JointId) -> Vec3 --- + + // Set the spherical joint maximum motor torque, usually in newton-meters + SphericalJoint_SetMaxMotorTorque :: proc(jointId: JointId, torque: f32) --- + + // Get the spherical joint maximum motor torque, usually in newton-meters + SphericalJoint_GetMaxMotorTorque :: proc(jointId: JointId) -> f32 --- + + /**@}*/ // spherical_joint + + /** + * @defgroup weld_joint Weld Joint + * @brief A weld joint fully constrains the relative transform between two bodies while allowing for springiness + * + * A weld joint constrains the relative rotation and translation between two bodies. Both rotation and translation + * can have damped springs. + * + * @note The accuracy of weld joint is limited by the accuracy of the solver. Long chains of weld joints may flex. + * @{ + */ + + // Create a weld joint + // @see WeldJointDef for details + CreateWeldJoint :: proc(worldId: WorldId, #by_ptr def: WeldJointDef) -> JointId --- + + // Set the weld joint linear stiffness in Hertz. 0 is rigid. + WeldJoint_SetLinearHertz :: proc(jointId: JointId, hertz: f32) --- + + // Get the weld joint linear stiffness in Hertz + WeldJoint_GetLinearHertz :: proc(jointId: JointId) -> f32 --- + + // Set the weld joint linear damping ratio (non-dimensional) + WeldJoint_SetLinearDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- + + // Get the weld joint linear damping ratio (non-dimensional) + WeldJoint_GetLinearDampingRatio :: proc(jointId: JointId) -> f32 --- + + // Set the weld joint angular stiffness in Hertz. 0 is rigid. + WeldJoint_SetAngularHertz :: proc(jointId: JointId, hertz: f32) --- + + // Get the weld joint angular stiffness in Hertz + WeldJoint_GetAngularHertz :: proc(jointId: JointId) -> f32 --- + + // Set weld joint angular damping ratio, non-dimensional + WeldJoint_SetAngularDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- + + // Get the weld joint angular damping ratio, non-dimensional + WeldJoint_GetAngularDampingRatio :: proc(jointId: JointId) -> f32 --- + + /**@}*/ // weld_joint + + /** + * @defgroup wheel_joint Wheel Joint + * The wheel joint can be used to simulate wheels on vehicles. + * + * The wheel joint restricts body B to move along a local axis in body A. Body B is free to + * rotate. Supports a linear spring, linear limits, and a rotational motor. + * + * @{ + */ + + // Create a wheel joint. + // @see WheelJointDef for details. + CreateWheelJoint :: proc(worldId: WorldId, #by_ptr def: WheelJointDef) -> JointId --- + + // Enable/disable the wheel joint spring. + WheelJoint_EnableSuspension :: proc(jointId: JointId, flag: bool) --- + + // Is the wheel joint spring enabled? + WheelJoint_IsSuspensionEnabled :: proc(jointId: JointId) -> bool --- + + // Set the wheel joint stiffness in Hertz. + WheelJoint_SetSuspensionHertz :: proc(jointId: JointId, hertz: f32) --- + + // Get the wheel joint stiffness in Hertz. + WheelJoint_GetSuspensionHertz :: proc(jointId: JointId) -> f32 --- + + // Set the wheel joint damping ratio, non-dimensional. + WheelJoint_SetSuspensionDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- + + // Get the wheel joint damping ratio, non-dimensional. + WheelJoint_GetSuspensionDampingRatio :: proc(jointId: JointId) -> f32 --- + + // Enable/disable the wheel joint limit. + WheelJoint_EnableSuspensionLimit :: proc(jointId: JointId, flag: bool) --- + + // Is the wheel joint limit enabled? + WheelJoint_IsSuspensionLimitEnabled :: proc(jointId: JointId) -> bool --- + + // Get the wheel joint lower limit. + WheelJoint_GetLowerSuspensionLimit :: proc(jointId: JointId) -> f32 --- + + // Get the wheel joint upper limit. + WheelJoint_GetUpperSuspensionLimit :: proc(jointId: JointId) -> f32 --- + + // Set the wheel joint limits. + WheelJoint_SetSuspensionLimits :: proc(jointId: JointId, lower, upper: f32) --- + + // Enable/disable the wheel joint motor. + WheelJoint_EnableSpinMotor :: proc(jointId: JointId, flag: bool) --- + + // Is the wheel joint motor enabled? + WheelJoint_IsSpinMotorEnabled :: proc(jointId: JointId) -> bool --- + + // Set the wheel joint motor speed in radians per second. + WheelJoint_SetSpinMotorSpeed :: proc(jointId: JointId, speed: f32) --- + + // Get the wheel joint motor speed in radians per second. + WheelJoint_GetSpinMotorSpeed :: proc(jointId: JointId) -> f32 --- + + // Set the wheel joint maximum motor torque, usually in newton-meters. + WheelJoint_SetMaxSpinTorque :: proc(jointId: JointId, torque: f32) --- + + // Get the wheel joint maximum motor torque, usually in newton-meters. + WheelJoint_GetMaxSpinTorque :: proc(jointId: JointId) -> f32 --- + + // Get the current spin speed in radians per second. + WheelJoint_GetSpinSpeed :: proc(jointId: JointId) -> f32 --- + + // Get the wheel joint current motor torque, usually in newton-meters. + WheelJoint_GetSpinTorque :: proc(jointId: JointId) -> f32 --- + + // Enable/disable wheel steering. Steering allows the wheel to rotate about the suspension axis. + WheelJoint_EnableSteering :: proc(jointId: JointId, flag: bool) --- + + // Can the wheel steer? + WheelJoint_IsSteeringEnabled :: proc(jointId: JointId) -> bool --- + + // Set the wheel joint steering stiffness in Hertz. + WheelJoint_SetSteeringHertz :: proc(jointId: JointId, hertz: f32) --- + + // Get the wheel joint steering stiffness in Hertz. + WheelJoint_GetSteeringHertz :: proc(jointId: JointId) -> f32 --- + + // Set the wheel joint steering damping ratio, non-dimensional. + WheelJoint_SetSteeringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- + + // Get the wheel joint steering damping ratio, non-dimensional. + WheelJoint_GetSteeringDampingRatio :: proc(jointId: JointId) -> f32 --- + + // Set the wheel joint maximum steering torque in N*m. + WheelJoint_SetMaxSteeringTorque :: proc(jointId: JointId, torque: f32) --- + + // Get the wheel joint maximum steering torque in N*m. + WheelJoint_GetMaxSteeringTorque :: proc(jointId: JointId) -> f32 --- + + // Enable/disable the wheel joint steering limit. + WheelJoint_EnableSteeringLimit :: proc(jointId: JointId, flag: bool) --- + + // Is the wheel joint steering limit enabled? + WheelJoint_IsSteeringLimitEnabled :: proc(jointId: JointId) -> bool --- + + // Get the wheel joint lower steering limit in radians. + WheelJoint_GetLowerSteeringLimit :: proc(jointId: JointId) -> f32 --- + + // Get the wheel joint upper steering limit in radians. + WheelJoint_GetUpperSteeringLimit :: proc(jointId: JointId) -> f32 --- + + // Set the wheel joint steering limits in radians. + WheelJoint_SetSteeringLimits :: proc(jointId: JointId, lowerRadians, upperRadians: f32) --- + + // Set the wheel joint target steering angle in radians. + WheelJoint_SetTargetSteeringAngle :: proc(jointId: JointId, radians: f32) --- + + // Get the wheel joint target steering angle in radians. + WheelJoint_GetTargetSteeringAngle :: proc(jointId: JointId) -> f32 --- + + // Get the current steering angle in radians. + WheelJoint_GetSteeringAngle :: proc(jointId: JointId) -> f32 --- + + // Get the current steering torque in N*m. + WheelJoint_GetSteeringTorque :: proc(jointId: JointId) -> f32 --- + + /**@}*/ // wheel_joint + + /**@}*/ // joint + + /** + * @defgroup contact Contact + * Access to contacts + * @{ + */ + + // Contact identifier validation. Provides validation for up to 2^32 allocations. + Contact_IsValid :: proc(id: ContactId) -> bool --- + + // Get the manifolds for a contact. The manifold may have no points if the contact is not touching. + Contact_GetData :: proc(contactId: ContactId) -> ContactData --- + + /**@}*/ // contact +} \ No newline at end of file diff --git a/vendor/box3d/box3d_collision.odin b/vendor/box3d/box3d_collision.odin new file mode 100644 index 000000000..26435fe6f --- /dev/null +++ b/vendor/box3d/box3d_collision.odin @@ -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)) +} diff --git a/vendor/box3d/box3d_constants.odin b/vendor/box3d/box3d_constants.odin new file mode 100644 index 000000000..3cc8233ab --- /dev/null +++ b/vendor/box3d/box3d_constants.odin @@ -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 \ No newline at end of file diff --git a/vendor/box3d/box3d_id.odin b/vendor/box3d/box3d_id.odin new file mode 100644 index 000000000..69453c4ba --- /dev/null +++ b/vendor/box3d/box3d_id.odin @@ -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 +} \ No newline at end of file diff --git a/vendor/box3d/box3d_math.odin b/vendor/box3d/box3d_math.odin new file mode 100644 index 000000000..ee7ac4528 --- /dev/null +++ b/vendor/box3d/box3d_math.odin @@ -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.. 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) +} diff --git a/vendor/box3d/box3d_types.odin b/vendor/box3d/box3d_types.odin new file mode 100644 index 000000000..895261696 --- /dev/null +++ b/vendor/box3d/box3d_types.odin @@ -0,0 +1,2923 @@ +package vendor_box3d + +import "core:c" + +DEFAULT_CATEGORY_BITS :: max(u64) +DEFAULT_MASK_BITS :: max(u64) + + +// Task interface +// This is the prototype for a Box3D task. Your task system is expected to run this callback on a worker thread, +// exactly once per enqueue, passing back the same taskContext pointer supplied to EnqueueTaskCallback. +// @ingroup world +TaskCallback :: proc "c" (taskContext: rawptr) + +// These functions can be provided to Box3D to invoke a task system. +// Returns a pointer to the user's task object. May be nullptr. A nullptr indicates to Box3D that the work was executed +// serially within the callback and there is no need to call b3FinishTaskCallback. Otherwise the returned +// value must be non-null will be passed to b3FinishTaskCallback as the userTask. +// @param task the Box3D task to be called by the scheduler +// @param taskContext the Box3D context object that the scheduler must pass to the task +// @param userContext the scheduler context object that is opaque to Box3D +// @param taskName the Box3D task name that the scheduler can use for diagnostics +// @ingroup world +EnqueueTaskCallback :: proc "c" (task: rawptr, taskContext: rawptr, userContext: rawptr, taskName: cstring) -> rawptr + +// Finishes a user task object that wraps a Box3D task. This must block until the task has completed. +// The step blocks here on the tasks it spawned, so b3World_Step holds its stack across every +// fork/join. Drive it from a thread you can dedicate to the step, or from a fiber this callback can +// park to free the underlying thread. In a job system that cannot park a job's stack, do not call +// b3World_Step from inside a job: a job that blocks on its own sub-jobs without yielding its thread +// can deadlock. The in-tree scheduler instead runs other pending tasks on the waiting thread. +// @ingroup world +FinishTaskCallback :: proc "c" (userTask: rawptr, userContext: rawptr) + + +// The user needs to be able to create debug draw shapes for multi-pass rendering to work efficiently. +// These user shapes are created and destroyed via callback so they can be bound to shape lifetime and scaling updates. +// @ingroup debug_draw +CreateDebugShapeCallback :: proc "c" (debugShape: ^DebugShape, userContext: rawptr) -> rawptr +DestroyDebugShapeCallback :: proc "c" (userShape: rawptr, userContext: rawptr) + +// Optional friction mixing callback. This intentionally provides no context objects because this is called +// from a worker thread. +// @warning This function should not attempt to modify Box3D state or user application state. +// @ingroup world +FrictionCallback :: proc "c" (frictionA: f32, userMaterialIdA: u64, frictionB: f32, userMaterialIdB: u64) -> f32 + +// Optional restitution mixing callback. This intentionally provides no context objects because this is called +// from a worker thread. +// @warning This function should not attempt to modify Box3D state or user application state. +// @ingroup world +RestitutionCallback :: proc "c" (restitutionA: f32, userMaterialIdA: u64, restitutionB: f32, userMaterialIdB: u64) -> f32 + +// Prototype for a contact filter callback. +// This is called when a contact pair is considered for collision. This allows you to +// perform custom logic to prevent collision between shapes. This is only called if +// one of the two shapes has custom filtering enabled. @see b3ShapeDef. +// Notes: +// - this function must be thread-safe +// - this is only called if one of the two shapes has enabled custom filtering +// - this is called only for awake dynamic bodies +// Return false if you want to disable the collision +// @warning Do not attempt to modify the world inside this callback +// @ingroup world +CustomFilterFcn :: proc "c" (shapeIdA: ShapeId, shapeIdB: ShapeId, ctx: rawptr) -> bool + +// Prototype for a pre-solve callback. +// This is called after a contact is updated. This allows you to inspect a +// collision before it goes to the solver. +// Notes: +// - this function must be thread-safe +// - this is only called if the shape has enabled pre-solve events +// - this may be called for awake dynamic bodies and sensors +// - this is not called for sensors +// Return false if you want to disable the contact this step +// This has limited information because it is used during CCD which does not have the +// full contact manifold. +// @warning Do not attempt to modify the world inside this callback +// @ingroup world +PreSolveFcn :: proc "c" (shapeIdA: ShapeId, shapeIdB: ShapeId, point: Pos, normal: Vec3, ctx: rawptr) -> bool + +// Prototype callback for overlap queries. +// Called for each shape found in the query. +// @see b3World_OverlapAABB +// @return false to terminate the query. +// @ingroup world +OverlapResultFcn :: proc "c" (shapeId: ShapeId, ctx: rawptr) -> bool + +// Prototype callback for ray casts. +// Called for each shape found in the query. You control how the ray cast +// proceeds by returning a float: +// return -1: ignore this shape and continue +// return 0: terminate the ray cast +// return fraction: clip the ray to this point +// return 1: don't clip the ray and continue +// @param shapeId the shape hit by the ray +// @param point the point of initial intersection +// @param normal the normal vector at the point of intersection +// @param fraction the fraction along the ray at the point of intersection +// @param userMaterialId the shape or triangle surface type +// @param triangleIndex the triangle index for mesh or height field shapes or -1 for other shape types +// @param childIndex the child shape index for compound shapes +// @param context the user context +// @return -1 to filter, 0 to terminate, fraction to clip the ray for closest hit, 1 to continue +// @see b3World_CastRay +// @ingroup world +CastResultFcn :: proc "c" (shapeId: ShapeId, point: Pos, normal: Vec3, fraction: f32, userMateriald: u64, triangleIndex: c.int, childIndex: c.int, ctx: rawptr) -> f32 + +@(link_prefix="b3", default_calling_convention="c", require_results) +foreign lib { + // Use this to initialize your world definition + // @ingroup world + DefaultWorldDef :: proc() -> WorldDef --- + + // Use this to initialize your body definition + // @ingroup body + DefaultBodyDef :: proc() -> BodyDef --- + + // Use this to initialize your filter + // @ingroup shape + DefaultFilter :: proc() -> Filter --- + + // Use this to initialize your surface material + // @ingroup shape + DefaultSurfaceMaterial :: proc() -> SurfaceMaterial --- + + // Use this to initialize your shape definition + // @ingroup shape + DefaultShapeDef :: proc() -> ShapeDef --- + + // Use this to initialize your joint definition + // @ingroup distance_joint + DefaultDistanceJointDef :: proc() -> DistanceJointDef --- + + // Use this to initialize your joint definition + // @ingroup motor_joint + DefaultMotorJointDef :: proc() -> MotorJointDef --- + + // Use this to initialize your joint definition + // @ingroup filter_joint + DefaultFilterJointDef :: proc() -> FilterJointDef --- + + + // Use this to initialize your joint definition + // @ingroup parallel_joint + DefaultParallelJointDef :: proc() -> ParallelJointDef --- + + + // Use this to initialize your joint definition + // @ingroup prismatic_joint + DefaultPrismaticJointDef :: proc() -> PrismaticJointDef --- + + + // Use this to initialize your joint definition. + // @ingroup revolute_joint + DefaultRevoluteJointDef :: proc() -> RevoluteJointDef --- + + // Use this to initialize your joint definition. + // @ingroup spherical_joint + DefaultSphericalJointDef :: proc() -> SphericalJointDef --- + + + // Use this to initialize your joint definition + // @ingroup weld_joint + DefaultWeldJointDef :: proc() -> WeldJointDef --- + + // Use this to initialize your joint definition + // @ingroup wheel_joint + DefaultWheelJointDef :: proc() -> WheelJointDef --- + + // Use this to initialize your explosion definition + // @ingroup world + DefaultExplosionDef :: proc() -> ExplosionDef --- + + // Use this to initialize your query filter + DefaultQueryFilter :: proc() -> QueryFilter --- + + // Get the visualization color assigned to a constraint graph color slot. The last index + // (B3_GRAPH_COLOR_COUNT - 1) is the overflow color. + GetGraphColor :: proc(index: c.int) -> HexColor --- + + // Create a debug draw struct with default values. + DefaultDebugDraw :: proc() -> DebugDraw --- +} + +// Pack an RGB color with a material preset for debug draw. The preset rides in +// the high byte where the color converters ignore it. +@(require_results) +MakeDebugColor :: #force_inline proc "c" (rgb: HexColor, material: DebugMaterial) -> u32 { + return (u32(rgb)&0x00FFFFFF) | (u32(material)<<24) +} + + + + +// Optional world capacities that can be use to avoid run-time allocations +// @ingroup world +Capacity :: struct { + // Number of expected static shapes. + staticShapeCount: c.int, + + // Number of expected dynamic and kinematic shapes. + dynamicShapeCount: c.int, + + // Number of expected static bodies. + staticBodyCount: c.int, + + // Number of expected dynamic and kinematic bodies. + dynamicBodyCount: c.int, + + // Number of expected contacts. + contactCount: c.int, +} + + +// World definition used to create a simulation world. Must be initialized using b3DefaultWorldDef. +// @ingroup world +WorldDef :: struct { + // Gravity vector. Box3D has no up-vector defined. + gravity: Vec3, + + // Restitution speed threshold, usually in m/s. Collisions above this + // speed have restitution applied (will bounce). + restitutionThreshold: f32, + + // Hit event speed threshold, usually in m/s. Collisions above this + // speed can generate hit events if the shape also enables hit events. + hitEventThreshold: f32, + + // Contact stiffness. Cycles per second. Increasing this increases the speed of overlap recovery, but can introduce jitter. + contactHertz: f32, + + // Contact bounciness. Non-dimensional. You can speed up overlap recovery by decreasing this with + // the trade-off that overlap resolution becomes more energetic. + contactDampingRatio: f32, + + // This parameter controls how fast overlap is resolved and usually has units of meters per second. This only + // puts a cap on the resolution speed. The resolution speed is increased by increasing the hertz and/or + // decreasing the damping ratio. + contactSpeed: f32, + + // Maximum linear speed. Usually meters per second. + maximumLinearSpeed: f32, + + // Optional mixing callback for friction. The default uses sqrt(frictionA * frictionB). + frictionCallback: FrictionCallback, + + // Optional mixing callback for restitution. The default uses max(restitutionA, restitutionB). + restitutionCallback: RestitutionCallback, + + // Can bodies go to sleep to improve performance + enableSleep: bool, + + // Enable continuous collision + enableContinuous: bool, + + // Number of workers to use with the provided task system. Box3D performs best when using only + // performance cores and accessing a single L2 cache. Efficiency cores and hyper-threading provide + // little benefit and may even harm performance. + // This is clamped to the range [1, B3_MAX_WORKERS]. Using a value above 1 will turn on multithreading. + // If task callbacks are provided then Box3D will use the user provided task system. Otherwise Box3D + // will create threads and use an internal scheduler. + workerCount: u32, + + // function to spawn task + enqueueTask: EnqueueTaskCallback, + + // function to finish a task + finishTask: FinishTaskCallback, + + // User context that is provided to enqueueTask and finishTask + userTaskContext: rawptr, + + // User data associated with a world + userData: rawptr, + + // Used to create debug draw shapes. This is called when a shape is + // first drawn using b3DebugDraw. + createDebugShape: CreateDebugShapeCallback, + + // Used to destroy debug draw shapes. This is called when a shape is modified or destroyed. + destroyDebugShape: DestroyDebugShapeCallback, + + // This is passed to the debug shape callbacks to provide a user context. + userDebugShapeContext: rawptr, + + // Optional initial capacities + capacity: Capacity, + + // Used internally to detect a valid definition. DO NOT SET. + internalValue: c.int, +} + + +// The body simulation type. +// Each body is one of these three types. The type determines how the body behaves in the simulation. +// @ingroup body +BodyType :: enum c.int { + // zero mass, zero velocity, may be manually moved + staticBody = 0, + + // zero mass, velocity set by user, moved by solver + kinematicBody = 1, + + // positive mass, velocity determined by forces, moved by solver + dynamicBody = 2, +} + +// Motion locks to restrict the body movement +// @ingroup body +MotionLocks :: struct { + // Prevent translation along the x-axis + linearX: bool, + + // Prevent translation along the y-axis + linearY: bool, + + // Prevent translation along the z-axis + linearZ: bool, + + // Prevent rotation around the x-axis + angularX: bool, + + // Prevent rotation around the y-axis + angularY: bool, + + // Prevent rotation around the z-axis + angularZ: bool, +} + +// A body definition holds all the data needed to construct a rigid body. +// You can safely re-use body definitions. Shapes are added to a body after construction. +// Body definitions are temporary objects used to bundle creation parameters. +// Must be initialized using b3DefaultBodyDef(). +// @ingroup body +BodyDef :: struct{ + // The body type: static, kinematic, or dynamic. + type: BodyType, + + // The initial world position of the body. Bodies should be created with the desired position. + // @note Creating bodies at the origin and then moving them nearly doubles the cost of body creation, especially + // if the body is moved after shapes have been added. + position: Pos, + + // The initial world rotation of the body. + rotation: Quat, + + // The initial linear velocity of the body's origin. Usually in meters per second. + linearVelocity: Vec3, + + // The initial angular velocity of the body. Radians per second. + angularVelocity: Vec3, + + // Linear damping is used to reduce the linear velocity. The damping parameter + // can be larger than 1 but the damping effect becomes sensitive to the + // time step when the damping parameter is large. + // Generally linear damping is undesirable because it makes objects move slowly + // as if they are floating. + linearDamping: f32, + + // Angular damping is used to reduce the angular velocity. The damping parameter + // can be larger than 1.0f but the damping effect becomes sensitive to the + // time step when the damping parameter is large. + // Angular damping can be used to slow down rotating bodies. + angularDamping: f32, + + // Scale the gravity applied to this body. Non-dimensional. + gravityScale: f32, + + // Sleep speed threshold, default is 0.05 meters per second + sleepThreshold: f32, + + // Optional body name for debugging. Up to B3_BODY_NAME_LENGTH characters (including null termination) + name: cstring, + + // Use this to store application specific body data. + userData: rawptr, + + // Motions locks to restrict linear and angular movement + motionLocks: MotionLocks, + + // Set this flag to false if this body should never fall asleep. + enableSleep: bool, + + // Is this body initially awake or sleeping? + isAwake: bool, + + // Treat this body as a high speed object that performs continuous collision detection + // against dynamic and kinematic bodies, but not other bullet bodies. + // @warning Bullets should be used sparingly. They are not a solution for general dynamic-versus-dynamic + // continuous collision. They do not guarantee accurate collision if both bodies are fast moving because + // the bullet does a continuous check after all non-bullet bodies have moved. You could get unlucky and have + // the bullet body end a time step very close to a non-bullet body and the non-bullet body then moves over + // the bullet body. In continuous collision, initial overlap is ignored to avoid freezing bodies in place. + // I do not recommend using them for game projectiles if precise collision timing is needed. Instead consider + // using a ray or shape cast. You can use a marching ray or shape cast for projectile that moves over time. + // If you want a fast moving projectile to collide with a fast moving target, you need to consider the relative + // movement in your ray or shape cast. This is out of the scope of Box3D. + // So what are good use cases for bullets? Pinball games or games with dynamic containers that hold other objects. + // It should be a use case where it doesn't break the game if there is a collision missed, but having them + // captured improves the quality of the game. + isBullet: bool, + + // Used to disable a body. A disabled body does not move or collide. + isEnabled: bool, + + // This allows this body to bypass rotational speed limits. Should only be used + // for circular objects, like wheels. + allowFastRotation: bool, + + // Enable contact recycling. True by default. Leaving this enabled improves performance + // but may lead to ghost collision that should be avoided on characters. + enableContactRecycling: bool, + + // Used internally to detect a valid definition. DO NOT SET. + internalValue: c.int, +} + + +// This is used to filter collision on shapes. It affects shape-vs-shape collision +// and shape-versus-query collision (such as b3World_CastRay). +// @ingroup shape +Filter :: struct { + // The collision category bits. Normally you would just set one bit. The category bits should + // represent your application object types. For example: + // @code{.odin} + // MyCategories :: bit_set[MyCategory; u64] + // MyCategory :: enum { + // Static = 0, + // Dynamic = 1, + // Debris = 2, + // Player = 3, + // // etc + // }; + // @endcode + categoryBits: u64, + + // The collision mask bits. This states the categories that this + // shape would accept for collision. + // For example, you may want your player to only collide with static objects + // and other players. + // @code{.odin} + // maskBits = {/Static, .Player}; + // @endcode + maskBits: u64, + + // Collision groups allow a certain group of objects to never collide (negative) + // or always collide (positive). A group index of zero has no effect. Non-zero group filtering + // always wins against the mask bits. + // For example, you may want ragdolls to collide with other ragdolls but you don't want + // ragdoll self-collision. In this case you would give each ragdoll a unique negative group index + // and apply that group index to all shapes on the ragdoll. + groupIndex: c.int, +} + + +// Material properties supported per triangle on meshes and height fields +// @ingroup shape +SurfaceMaterial :: struct { + // The Coulomb (dry) friction coefficient, usually in the range [0,1]. + friction: f32, + + // The coefficient of restitution (bounce) usually in the range [0,1]. + // https://en.wikipedia.org/wiki/Coefficient_of_restitution + restitution: f32, + + // The rolling resistance usually in the range [0,1]. This is only used for spheres and capsules. + rollingResistance: f32, + + // The tangent velocity for conveyor belts. This is local to the shape and will be projected + // onto the contact surface. + tangentVelocity: Vec3, + + // User material identifier. This is passed with query results and to friction and restitution + // combining functions. It is not used internally. + userMaterialId: u64, + + // Custom debug draw color. Ignored if 0. The low 24 bits are RGB. The high byte may + // carry a b3DebugMaterial preset, see b3MakeDebugColor. + // @see b3HexColor + customColor: u32, +} + + +// Shape type +// @ingroup shape +ShapeType :: enum c.int { + // A capsule is an extruded sphere + capsuleShape, + + // A compound shape composed of up to 64K spheres, capsules, hulls, and meshes + compoundShape, + + // A height field useful for terrain + heightShape, + + // A convex hull + hullShape, + + // A triangle soup + meshShape, + + // A sphere with an offset + sphereShape, +} + +// Used to create a shape +// @ingroup shape +ShapeDef :: struct { + // Use this to store application specific shape data. + userData: rawptr, + + // Surface material used on mesh shapes per triangle. Ignored for convex shapes. Ignored for compound shapes. + materials: [^]SurfaceMaterial `fmt:"v,materialCount"`, + + // Surface material count. + materialCount: c.int, + + // The base surface material. Ignored for compound shapes. + baseMaterial: SurfaceMaterial, + + // The density, usually in kg/m^3. + density: f32, + + // Explosion scale for b3World_Explode. non-dimensional + explosionScale: f32, + + // Contact filtering data. + filter: Filter, + + // Enable custom filtering. Only one of the two shapes needs to enable custom filtering. See b3WorldDef. + enableCustomFiltering: bool, + + // A sensor shape generates overlap events but never generates a collision response. + // Sensors do not have continuous collision. Instead, use a ray or shape cast for those scenarios. + // Sensors still contribute to the body mass if they have non-zero density. + // @note Sensor events are disabled by default. + // @see enableSensorEvents + isSensor: bool, + + // Enable sensor events for this shape. This applies to sensors and non-sensors. False by default, even for sensors. + enableSensorEvents: bool, + + // Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default. + enableContactEvents: bool, + + // Enable hit events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default. + enableHitEvents: bool, + + // Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive + // and must be carefully handled due to multithreading. Ignored for sensors. + enablePreSolveEvents: bool, + + // When shapes are created they will scan the environment for collision the next time step. This can significantly slow down + // static body creation when there are many static shapes. + // This is flag is ignored for dynamic and kinematic shapes which always invoke contact creation. + invokeContactCreation: bool, + + // Should the body update the mass properties when this shape is created. Default is true. + updateBodyMass: bool, + + // Used internally to detect a valid definition. DO NOT SET. + internalValue: c.int, +} + +//! @cond +// Profiling data. Times are in milliseconds. +// @ingroup world +Profile :: struct { + step: f32, + pairs: f32, + collide: f32, + solve: f32, + solverSetup: f32, + constraints: f32, + prepareConstraints: f32, + integrateVelocities: f32, + warmStart: f32, + solveImpulses: f32, + integratePositions: f32, + relaxImpulses: f32, + applyRestitution: f32, + storeImpulses: f32, + splitIslands: f32, + transforms: f32, + sensorHits: f32, + jointEvents: f32, + hitEvents: f32, + refit: f32, + bullets: f32, + sleepIslands: f32, + sensors: f32, +} + +// Counters that give details of the simulation size. +// @ingroup world +Counters :: struct { + bodyCount: c.int, + shapeCount: c.int, + contactCount: c.int, + jointCount: c.int, + islandCount: c.int, + stackUsed: c.int, + arenaCapacity: c.int, + staticTreeHeight: c.int, + treeHeight: c.int, + satCallCount: c.int, + satCacheHitCount: c.int, + byteCount: c.int, + taskCount: c.int, + colorCounts: [24]c.int, + manifoldCounts: [CONTACT_MANIFOLD_COUNT_BUCKETS]c.int, + + // Number of contacts touched by the collide pass + // graph contacts + awake-set non-touching + awakeContactCount: c.int, + + // Number of contacts recycled in the most recent step. + recycledContactCount: c.int, + + // Maximum number of time of impact iterations + distanceIterations: c.int, + pushBackIterations: c.int, + rootIterations: c.int, +} +//! @endcond + +// Joint type enumeration. This is useful because all joint types use b3JointId and sometimes you +// want to get the type of a joint. +// @ingroup joint +JointType :: enum c.int { + parallelJoint, + distanceJoint, + filterJoint, + motorJoint, + prismaticJoint, + revoluteJoint, + sphericalJoint, + weldJoint, + wheelJoint, +} + +// Base joint definition used by all joint types. The local frames are measured from the +// body's origin rather than the center of mass because: +// 1. You might not know where the center of mass will be. +// 2. If you add/remove shapes from a body and recompute the mass, the joints will be broken. +// @ingroup joint +JointDef :: struct { + // User data pointer + userData: rawptr, + + // The first attached body + bodyIdA: BodyId, + + // The second attached body + bodyIdB: BodyId, + + // The first local joint frame + localFrameA: Transform, + + // The second local joint frame + localFrameB: Transform, + + // Force threshold for joint events + forceThreshold: f32, + + // Torque threshold for joint events + torqueThreshold: f32, + + // Constraint hertz (advanced feature) + constraintHertz: f32, + + // Constraint damping ratio (advanced feature) + constraintDampingRatio: f32, + + // Debug draw scale + drawScale: f32, + + // Set this flag to true if the attached bodies should collide + collideConnected: bool, + + // Used internally to detect a valid definition. DO NOT SET. + internalValue: c.int, +} + +// Distance joint definition. +// Connects a point on body A with a point on body B by a segment. +// Useful for ropes and springs. +// @ingroup distance_joint +DistanceJointDef :: struct { + // Base joint definition + using base: JointDef, + + // The rest length of this joint. Clamped to a stable minimum value. + length: f32, + + // Enable the distance constraint to behave like a spring. If false + // then the distance joint will be rigid, overriding the limit and motor. + enableSpring: bool, + + // The lower spring force controls how much tension it can sustain + lowerSpringForce: f32, + + // The upper spring force controls how much compression it can sustain + upperSpringForce: f32, + + // The spring linear stiffness Hertz, cycles per second + hertz: f32, + + // The spring linear damping ratio, non-dimensional + dampingRatio: f32, + + // Enable/disable the joint limit + enableLimit: bool, + + // Minimum length. Clamped to a stable minimum value. + minLength: f32, + + // Maximum length. Must be greater than or equal to the minimum length. + maxLength: f32, + + // Enable/disable the joint motor + enableMotor: bool, + + // The maximum motor force, usually in newtons + maxMotorForce: f32, + + // The desired motor speed, usually in meters per second + motorSpeed: f32, +} + + +// A motor joint is used to control the relative position and velocity between two bodies. +// @ingroup motor_joint +MotorJointDef :: struct { + // Base joint definition + using base: JointDef, + + // The desired linear velocity + linearVelocity: Vec3, + + // The maximum motor force in newtons + maxVelocityForce: f32, + + // The desired angular velocity + angularVelocity: Vec3, + + // The maximum motor torque in newton-meters + maxVelocityTorque: f32, + + // Linear spring hertz for position control + linearHertz: f32, + + // Linear spring damping ratio + linearDampingRatio: f32, + + // Maximum spring force in newtons + maxSpringForce: f32, + + // Angular spring hertz for position control + angularHertz: f32, + + // Angular spring damping ratio + angularDampingRatio: f32, + + // Maximum spring torque in newton-meters + maxSpringTorque: f32, +} + +// A filter joint is used to disable collision between two specific bodies. +// @ingroup filter_joint +FilterJointDef :: struct { + // Base joint definition + using base: JointDef, +} + + +// Parallel joint definition. Constrains the angle between axis z in body A and axis z in body B +// using a spring. Useful to keep a body upright. +// @ingroup parallel_joint +ParallelJointDef :: struct { + // Base joint definition + using base: JointDef, + + // The spring stiffness Hertz, cycles per second + hertz: f32, + + // The spring damping ratio, non-dimensional + dampingRatio: f32, + + // The maximum spring torque, typically in newton-meters. + maxTorque: f32, +} + +// Prismatic joint definition. Body B may slide along the x-axis in local frame A. +// Body B cannot rotate relative to body A. The joint translation is zero when the +// local frame origins coincide in world space. +// @ingroup prismatic_joint +PrismaticJointDef :: struct { + // Base joint definition + using base: JointDef, + + // Enable a linear spring along the prismatic joint axis + enableSpring: bool, + + // The spring stiffness Hertz, cycles per second + hertz: f32, + + // The spring damping ratio, non-dimensional + dampingRatio: f32, + + // The target translation for the joint in meters. The spring-damper will drive + // to this translation. + targetTranslation: f32, + + // Enable/disable the joint limit + enableLimit: bool, + + // The lower translation limit + lowerTranslation: f32, + + // The upper translation limit + upperTranslation: f32, + + // Enable/disable the joint motor + enableMotor: bool, + + // The maximum motor force, typically in newtons + maxMotorForce: f32, + + // The desired motor speed, typically in meters per second + motorSpeed: f32, +} + +// Revolute joint definition. A point on body B is fixed to a point on body A. +// Allows relative rotation about the z-axis. +// @ingroup revolute_joint +RevoluteJointDef :: struct { + // Base joint definition. + using base: JointDef, + + // The bodyB angle minus bodyA angle in the reference state (radians). + // This defines the zero angle for the joint limit. + targetAngle: f32, + + // Enable a rotational spring on the revolute hinge axis. + enableSpring: bool, + + // The spring stiffness Hertz, cycles per second. + hertz: f32, + + // The spring damping ratio, non-dimensional. + dampingRatio: f32, + + // A flag to enable joint limits. + enableLimit: bool, + + // The lower angle for the joint limit in radians. Minimum of -0.99*pi radians. + lowerAngle: f32, + + // The upper angle for the joint limit in radians. Maximum of 0.99*pi radians. + upperAngle: f32, + + // A flag to enable the joint motor. + enableMotor: bool, + + // The maximum motor torque, typically in newton-meters. + maxMotorTorque: f32, + + // The desired motor speed in radians per second. + motorSpeed: f32, +} + +// Spherical joint definition. A point on body B is fixed to a point on body A. +// Allows rotation about the shared point. +// @ingroup spherical_joint +SphericalJointDef :: struct { + // Base joint definition + using base: JointDef, + + // Enable a rotational spring that attempts to align the two joint frames. + enableSpring: bool, + + // The spring stiffness Hertz, cycles per second. This may be clamped internally + // according to the time step to maintain stability. Non-negative number. + hertz: f32, + + // The spring damping ratio, non-dimensional. Non-negative number. + dampingRatio: f32, + + // Target spring rotation, joint frame B relative to joint frame A. + targetRotation: Quat, + + // A flag to enable the cone limit. The cone is centered on the frameA z-axis. + enableConeLimit: bool, + + // The angle for the cone limit in radians. Valid range is [0, pi] + coneAngle: f32, + + // A flag to enable the twist limit. The twist is centered on the frameB z-axis. + enableTwistLimit: bool, + + // The angle for the lower twist limit in radians. Minimum of -0.99*pi radians. + lowerTwistAngle: f32, + + // The angle for the upper twist limit in radians. Maximum of 0.99*pi radians. + upperTwistAngle: f32, + + // A flag to enable the joint motor + enableMotor: bool, + + // The maximum motor torque, typically in newton-meters. Non-negative number. + maxMotorTorque: f32, + + // The desired motor angular velocity in radians per second. + motorVelocity: Vec3, +} + + +// Weld joint definition +// Connects two bodies together rigidly. This constraint provides springs to mimic +// soft-body simulation. +// @note The approximate solver in Box3D cannot hold many bodies together rigidly +// @ingroup weld_joint +WeldJointDef :: struct { + // Base joint definition + using base: JointDef, + + // Linear stiffness expressed as Hertz (cycles per second). Use zero for maximum stiffness. + linearHertz: f32, + + // Angular stiffness as Hertz (cycles per second). Use zero for maximum stiffness. + angularHertz: f32, + + // Linear damping ratio, non-dimensional. Use 1 for critical damping. + linearDampingRatio: f32, + + // Linear damping ratio, non-dimensional. Use 1 for critical damping. + angularDampingRatio: f32, +} + +// Wheel joint definition +// Body A is the chassis and body B is the wheel. +// The wheel rotates around the local z-axis in frame B. +// The wheel translates along the local x-axis in frame A. +// The wheel can optionally steer along the x-axis in frame A. +// @ingroup wheel_joint +WheelJointDef :: struct { + // Base joint definition + using base: JointDef, + + // Enable a linear spring along the local axis + enableSuspensionSpring: bool, + + // Spring stiffness in Hertz + suspensionHertz: f32, + + // Spring damping ratio, non-dimensional + suspensionDampingRatio: f32, + + // Enable/disable the joint linear limit + enableSuspensionLimit: bool, + + // The lower suspension translation limit + lowerSuspensionLimit: f32, + + // The upper translation limit + upperSuspensionLimit: f32, + + // Enable/disable the joint rotational motor + enableSpinMotor: bool, + + // The maximum motor torque, typically in newton-meters + maxSpinTorque: f32, + + // The desired motor speed in radians per second + spinSpeed: f32, + + // Enable steering, otherwise the steering is fixed forward + enableSteering: bool, + + // Steering stiffness in Hertz + steeringHertz: f32, + + // Spring damping ratio, non-dimensional + steeringDampingRatio: f32, + + // The target steering angle in radians + targetSteeringAngle: f32, + + // The maximum steering torque in N*m + maxSteeringTorque: f32, + + // Enable/disable the steering angular limit + enableSteeringLimit: bool, + + // The lower steering angle in radians + lowerSteeringLimit: f32, + + // The upper steering angle in radians + upperSteeringLimit: f32, +} + + +// The explosion definition is used to configure options for explosions. Explosions +// consider shape geometry when computing the impulse. +// @ingroup world +ExplosionDef :: struct { + // Mask bits to filter shapes + maskBits: u64, + + // The center of the explosion in world space + position: Pos, + + // The radius of the explosion + radius: f32, + + // The falloff distance beyond the radius. Impulse is reduced to zero at this distance. + falloff: f32, + + // Impulse per unit area. This applies an impulse according to the shape area that + // is facing the explosion. Explosions only apply to spheres, capsules, and hulls. This + // may be negative for implosions. + impulsePerArea: f32, +} + +/** + * @defgroup event Events + * World event types. + * + * Events are used to collect events that occur during the world time step. These events + * are then available to query after the time step is complete. This is preferable to callbacks + * because Box3D uses multithreaded simulation. + * + * Also when events occur in the simulation step it may be problematic to modify the world, which is + * often what applications want to do when events occur. + * + * With event arrays, you can scan the events in a loop and modify the world. However, you need to be careful + * that some event data may become invalid. There are several samples that show how to do this safely. + * + * @{ + */ + +// A begin-touch event is generated when a shape starts to overlap a sensor shape. +SensorBeginTouchEvent :: struct { + // The id of the sensor shape + sensorShapeId: ShapeId, + + // The id of the shape that began touching the sensor shape + visitorShapeId: ShapeId, +} + +// An end touch event is generated when a shape stops overlapping a sensor shape. +// These include things like setting the transform, destroying a body or shape, or changing +// a filter. You will also get an end event if the sensor or visitor are destroyed. +// Therefore you should always confirm the shape id is valid using b3Shape_IsValid. +SensorEndTouchEvent :: struct { + // The id of the sensor shape + // @warning this shape may have been destroyed + // @see b3Shape_IsValid + sensorShapeId: ShapeId, + + // The id of the shape that stopped touching the sensor shape + // @warning this shape may have been destroyed + // @see b3Shape_IsValid + visitorShapeId: ShapeId, +} + +// Sensor events are buffered in the world and are available +// as begin/end overlap event arrays after the time step is complete. +// Note: these may become invalid if bodies and/or shapes are destroyed +SensorEvents :: struct { + // Array of sensor begin touch events + beginEvents: [^]SensorBeginTouchEvent `fmt:"v,beginCount"`, + + // Array of sensor end touch events + endEvents: [^]SensorEndTouchEvent `fmt:"v,endCount"`, + + // The number of begin touch events + beginCount: c.int, + + // The number of end touch events + endCount: c.int, +} + +// A begin-touch event is generated when two shapes begin touching. +ContactBeginTouchEvent :: struct { + // Id of the first shape + shapeIdA: ShapeId, + + // Id of the second shape + shapeIdB: ShapeId, + + // The transient contact id. This contact may be destroyed automatically when the world is modified or simulated. + // Use b3Contact_IsValid before using this id. + contactId: ContactId, +} + +// An end touch event is generated when two shapes stop touching. +// You will get an end event if you do anything that destroys contacts previous to the last +// world step. These include things like setting the transform, destroying a body +// or shape, or changing a filter or body type. +ContactEndTouchEvent :: struct { + // Id of the first shape + // @warning this shape may have been destroyed + // @see b3Shape_IsValid + shapeIdA: ShapeId, + + // Id of the first shape + // @warning this shape may have been destroyed + // @see b3Shape_IsValid + shapeIdB: ShapeId, + + // Id of the contact. + // @warning this contact may have been destroyed + // @see b3Contact_IsValid + contactId: ContactId, +} + +// A hit touch event is generated when two shapes collide with a speed faster than the hit speed threshold. +// This may be reported for speculative contacts that have a confirmed impulse. +ContactHitEvent :: struct { + // Id of the first shape + shapeIdA: ShapeId, + + // Id of the second shape + shapeIdB: ShapeId, + + // Id of the contact. + // @warning this contact may have been destroyed + // @see b3Contact_IsValid + contactId: ContactId, + + // Point where the shapes hit at the beginning of the time step. + // This is a mid-point between the two surfaces. It could be at speculative + // point where the two shapes were not touching at the beginning of the time step. + point: Pos, + + // Normal vector pointing from shape A to shape B + normal: Vec3, + + // The speed the shapes are approaching. Always positive. Typically in meters per second. + approachSpeed: f32, + + // User material on shape A + userMaterialIdA: u64, + + // User material on shape B + userMaterialIdB: u64, +} + +// Contact events are buffered in the world and are available +// as event arrays after the time step is complete. +// Note: these may become invalid if bodies and/or shapes are destroyed +ContactEvents :: struct { + // Array of begin touch events + beginEvents: [^]ContactBeginTouchEvent `fmt:"v,beginCount"`, + + // Array of end touch events + endEvents: [^]ContactEndTouchEvent `fmt:"v,endCount"`, + + // Array of hit events + hitEvents: [^]ContactHitEvent `fmt:"v,hitCount"`, + + // Number of begin touch events + beginCount: c.int, + + // Number of end touch events + endCount: c.int, + + // Number of hit events + hitCount: c.int, +} + +// Body move events triggered when a body moves. +// Triggered when a body moves due to simulation. Not reported for bodies moved by the user. +// This also has a flag to indicate that the body went to sleep so the application can also +// sleep that actor/entity/object associated with the body. +// On the other hand if the flag does not indicate the body went to sleep then the application +// can treat the actor/entity/object associated with the body as awake. +// This is an efficient way for an application to update game object transforms rather than +// calling functions such as b3Body_GetTransform() because this data is delivered as a contiguous array +// and it is only populated with bodies that have moved. +// @note If sleeping is disabled all dynamic and kinematic bodies will trigger move events. +BodyMoveEvent :: struct { + // The body user data. + userData: rawptr, + + // The body transform. + transform: WorldTransform, + + // The body id. + bodyId: BodyId, + + // Did the body fall asleep this time step? + fellAsleep: bool, +} + +// Body events are buffered in the world and are available +// as event arrays after the time step is complete. +// Note: this data becomes invalid if bodies are destroyed +BodyEvents :: struct { + // Array of move events + moveEvents: [^]BodyMoveEvent `fmt:"v,moveCount"`, + + // Number of move events + moveCount: c.int, +} + +// Joint events report joints that are awake and have a force and/or torque exceeding the threshold +// The observed forces and torques are not returned for efficiency reasons. +JointEvent :: struct { + // The joint id + jointId: JointId, + + // The user data from the joint for convenience + userData: rawptr, +} + +// Joint events are buffered in the world and are available +// as event arrays after the time step is complete. +// Note: this data becomes invalid if joints are destroyed +JointEvents :: struct { + // Array of events + jointEvents: [^]JointEvent `fmt:"v,count"`, + + // Number of events + count: c.int, +} + +// The contact data for two shapes. By convention the manifold normal points +// from shape A to shape B. +// @see b3Shape_GetContactData() and b3Body_GetContactData() +ContactData :: struct { + // The contact id. You may hold onto this to track a contact across time steps. + // This id may become orphaned. Use b3Contact_IsValid before using it for other functions. + contactId: ContactId, + + // The first shape id. + shapeIdA: ShapeId, + + // The second shape id. + shapeIdB: ShapeId, + + // The contact manifold. This points to internal data and may become invalid. Do not store + // this pointer. + manifolds: [^]Manifold `fmt:"v,manifoldCount"`, + + // The number of contact manifolds. For mesh and height-field collision there can be multiple manifolds. + manifoldCount: c.int, +} + +/**@}*/ // event + +/** + * @defgroup query Query + * @brief Query types and functions + * + * Queries include ray casts, shapes casts, overlap, distance, and time of impact. + * @{ + */ + +// The query filter is used to filter collisions between queries and shapes. For example, +// you may want a ray-cast representing a projectile to hit players and the static environment +// but not debris. +QueryFilter :: struct { + // The collision category bits of this query. Normally you would just set one bit. + categoryBits: u64, + + // The collision mask bits. This states the shape categories that this + // query would accept for collision. + maskBits: u64, + + // Optional id combined with @ref name to identify this query in a recording, e.g. an entity id. + // Need not be unique on its own. 0 with a null name means untagged. Ignored when not recording. + id: u64, + + // Optional label combined with @ref id to identify this query, e.g. "bullet". Need not be unique + // on its own. The recorder hashes (id, name) into one stable key the viewer tracks the query by, + // so the same id and name pair identifies the same query across frames. NULL means none. Ignored + // when not recording. + name: cstring, +} + + +// Low level ray cast input data. +RayCastInput :: struct { + // Start point of the ray cast. + origin: Vec3, + + // Translation of the ray cast. + // end = start + translation. + translation: Vec3, + + // The maximum fraction of the translation to consider, typically 1 + maxFraction: f32, +} + +// Result from b3World_RayCastClosest. +RayResult :: struct { + // The shape hit. + shapeId: ShapeId, + + // The world point of the hit. + point: Pos, + + // The world normal of the shape surface at the hit point. + normal: Vec3, + + // The user material id at the hit point. This can be per triangle + // if the shape is a mesh, height-field, or compound with child mesh. + userMaterialId: u64, + + // The fraction of the input ray. + fraction: f32, + + // The triangle index if the shape is a mesh, height-field, or compound with + // child mesh. + triangleIndex: c.int, + + // The child index if the shape is a compound. + childIndex: c.int, + + // The number of BVH nodes visited. Diagnostic. + nodeVisits: c.int, + + // The number of BVH leaves visited. Diagnostic. + leafVisits: c.int, + + // Did the ray hit? If false, all other data is invalid. + hit: bool, +} + +// A shape proxy is used by the GJK algorithm. It can represent a convex shape. +ShapeProxy :: struct { + // The point cloud. + points: [^]Vec3 `fmt:"v,count"`, + + // The number of points. Do not exceed B3_MAX_SHAPE_CAST_POINTS. + count: c.int, + + // The external radius of the point cloud. + radius: f32, +} + +// Low level shape cast input in generic form. This allows casting an arbitrary point +// cloud wrap with a radius. For example, a sphere is a single point with a non-zero radius. +// A capsule is two points with a non-zero radius. A box is four points with a zero radius. +ShapeCastInput :: struct { + // A generic query shape. + proxy: ShapeProxy, + + // The translation of the shape cast. + translation: Vec3, + + // The maximum fraction of the translation to consider, typically 1. + maxFraction: f32, + + // Allow shape cast to encroach when initially touching. This only works if the radius is greater than zero. + canEncroach: bool, +} + +// Input for sweeping an AABB through a dynamic tree. The box is in the tree's world float frame. +// The caller folds the cast shape radius and any world origin into the box, so the tree traversal +// stays a conservative box sweep and the precise narrow phase happens per shape in the callback. +BoxCastInput :: struct { + // The AABB to cast, in the tree's frame. + box: AABB, + + // The sweep translation. + translation: Vec3, + + // The maximum fraction of the translation to consider, typically 1. + maxFraction: f32, +} + +// Low level ray cast or shape-cast output data. +CastOutput :: struct { + // The surface normal at the hit point. + normal: Vec3, + + // The surface hit point. + point: Vec3, + + // The fraction of the input translation at collision. + fraction: f32, + + // The number of iterations used. + iterations: c.int, + + // The index of the mesh or height field triangle hit. + triangleIndex: c.int, + + // The index of the compound child shape. + childIndex: c.int, + + // The material index. May be -1 for null. + materialIndex: c.int, + + // Did the cast hit? + hit: bool, +} + + +// Ray cast or shape-cast output in world space. The hit point is a world position so the result +// stays precise far from the world origin. Mirrors b3CastOutput with a double precision point. +WorldCastOutput :: struct { + // The surface normal at the hit point. + normal: Vec3, + + // The surface hit point in world space. + point: Pos, + + // The fraction of the input translation at collision. + fraction: f32, + + // The number of iterations used. + iterations: c.int, + + // The index of the mesh or height field triangle hit. + triangleIndex: c.int, + + // The index of the compound child shape. + childIndex: c.int, + + // The material index. May be -1 for null. + materialIndex: c.int, + + // Did the cast hit? + hit: bool, +} + + +// Body cast result for ray and shape casts. +BodyCastResult :: struct { + // The shape hit. + shapeId: ShapeId, + + // The world point on the shape surface. + point: Pos, + + // The world normal vector on the shape surface. + normal: Vec3, + + // The fraction along the ray hit. + // hit point = origin + fraction * translation + fraction: f32, + + // The triangle index if the shape is a mesh or height-field. + triangleIndex: c.int, + + // The user material id at the hit point. This can be per triangle + // if the shape is a mesh, height-field, or compound with child mesh. + userMaterialId: u64, + + // The number of iterations used. Diagnostic. + iterations: c.int, + + // Did the cast hit? If false, all other fields are invalid. + hit: bool, +} + +// Used to warm start the GJK simplex. If you call this function multiple times with nearby +// transforms this might improve performance. Otherwise you can zero initialize this. +// The distance cache must be initialized to zero on the first call. +// Users should generally just zero initialize this structure for each call. +SimplexCache :: struct { + // Value use to compare length, area, volume of two simplexes. + metric: f32, + + // todo use an index of 0xFF as a sentinel and remove the count + // The number of stored simplex points + count: u16, + + // The cached simplex indices on shape A + indexA: [4]u8, + + // The cached simplex indices on shape B + indexB: [4]u8, +} + +emptyDistanceCache :: SimplexCache{} + +// Input parameters for b3ShapeCast +ShapeCastPairInput :: struct { + proxyA: ShapeProxy, //< The proxy for shape A + proxyB: ShapeProxy, //< The proxy for shape B + transform: Transform, //< Transform of shape B in shape A's frame, the relative pose B in A + translationB: Vec3, //< The translation of shape B, in A's frame + maxFraction: f32, //< The fraction of the translation to consider, typically 1 + canEncroach: bool, //< Allows shapes with a radius to move slightly closer if already touching +} + +// Input for ShapeDistance +DistanceInput :: struct { + // The proxy for shape A + proxyA: ShapeProxy, + + // The proxy for shape B + proxyB: ShapeProxy, + + // Transform of shape B in shape A's frame, the relative pose B in A + // (b3InvMulWorldTransforms( worldA, worldB )). The query is origin independent and runs in frame A. + transform: Transform, + + // Should the proxy radius be considered? + useRadii: bool, +} + +// Output for b3ShapeDistance +DistanceOutput :: struct { + pointA: Vec3, //< Closest point on shapeA, in shape A's frame + pointB: Vec3, //< Closest point on shapeB, in shape A's frame + normal: Vec3, //< A to B normal in shape A's frame. Invalid if distance is zero. + distance: f32, //< The final distance, zero if overlapped + iterations: c.int, //< Number of GJK iterations used + simplexCount: c.int, //< The number of simplexes stored in the simplex array +} + +// Simplex vertex for debugging the GJK algorithm +SimplexVertex :: struct { + wA: Vec3, //< support point in proxyA + wB: Vec3, //< support point in proxyB + w: Vec3, //< wB - wA + a: f32, //< barycentric coordinates + indexA: c.int, //< wA index + indexB: c.int, //< wB index +} + +// Simplex from the GJK algorithm +Simplex :: struct { + vertices: [4]SimplexVertex `fmt:"v,count"`, //< vertices + count: c.int, //< number of valid vertices +} + +// This describes the motion of a body/shape for TOI computation. Shapes are defined with respect to the body origin, +// which may not coincide with the center of mass. However, to support dynamics we must interpolate the center of mass +// position. +Sweep :: struct { + localCenter: Vec3, //< Local center of mass position + c1: Vec3, //< Starting center of mass world position + c2: Vec3, //< Ending center of mass world position + q1: Quat, //< Starting world rotation + q2: Quat, //< Ending world rotation +} + +// Time of impact input +TOIInput :: struct { + proxyA: ShapeProxy, //< The proxy for shape A + proxyB: ShapeProxy, //< The proxy for shape B + sweepA: Sweep, //< The movement of shape A + sweepB: Sweep, //< The movement of shape B + maxFraction: f32, //< Defines the sweep interval [0, tMax] +} + +// Describes the TOI output +TOIState :: enum c.int { + Unknown, + Failed, + Overlapped, + Hit, + Separated, +} + +// Time of impact output +TOIOutput :: struct { + // The type of result + state: TOIState, + + // The hit point + point: Vec3, + + // The hit normal + normal: Vec3, + + // The sweep time of the collision + fraction: f32, + + // The final distance + distance: f32, + + // Number of outer iterations + distanceIterations: c.int, + + // Total number of push back iterations + pushBackIterations: c.int, + + // Total number of root iterations + rootIterations: c.int, + + // Indicates that the time of impact detected initial + // overlap and used a fallback sphere as a last ditch effort + // to prevent tunneling. + usedFallback: bool, +} + +/**@}*/ // query + +/** + * @defgroup tree Dynamic Tree + * The dynamic tree is a binary AABB tree to organize and query large numbers of geometric objects + * + * Box3D uses the dynamic tree internally to sort collision shapes into a binary bounding volume hierarchy. + * This data structure may have uses in games for organizing other geometry data and may be used independently + * of Box3D rigid body simulation. + * + * A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt. + * A dynamic tree arranges data in a binary tree to accelerate + * queries such as AABB queries and ray casts. Leaf nodes are proxies + * with an AABB. These are used to hold a user collision object. + * Nodes are pooled and relocatable, so I use node indices rather than pointers. + * The dynamic tree is made available for advanced users that would like to use it to organize + * spatial game data besides rigid bodies. + * @{ + */ + +// Flags for tree nodes. For internal usage. +TreeNodeFlags :: distinct bit_set[TreeNodeFlag; u16] + +TreeNodeFlag :: enum u16 { + allocatedNode = 0, + enlargedNode = 1, + leafNode = 2, +} + +// Tree node child indices. For internal usage. +TreeNodeChildren :: struct { + child1: c.int, //< child node index 1 + child2: c.int, //< child node index 2 +} + +// A node in the dynamic tree. This is private data placed here for performance reasons. +// todo test padding to 64 bytes to avoid straddling cache lines +TreeNode :: struct { + // The node bounding box + aabb: AABB, // 24 + + // Category bits for collision filtering + categoryBits: u64, // 8 + + using _: struct #raw_union { + // Children (internal node) + children: TreeNodeChildren, + + // User data (leaf node) + userData: u64, + }, // 8 + + using _: struct #raw_union { + // The node parent index (allocated node) + parent: c.int, + + // The node freelist next index (free node) + next: c.int, + }, // 4 + + // Height of the node. Leaves have a height of 0. + height: u16, // 2 + + // @see TreeNodeFlags + flags: TreeNodeFlags, // 2 +} + +// Dynamic tree version for compatibility testing. +DYNAMIC_TREE_VERSION :: 0x93EDAF889FD30B4A + +// The dynamic tree structure. This should be considered private data. +// It is placed here for performance reasons. +DynamicTree :: struct { + // The dynamic tree version. Always the first field. Useful + // if the tree is serialized. + version: u64, + + // The tree nodes + nodes: [^]TreeNode `fmt:"v,nodeCapacity"`, + + // The root index + root: c.int, + + // The number of nodes + nodeCount: c.int, + + // The allocated node space + nodeCapacity: c.int, + + // Number of proxies created + proxyCount: c.int, + + // Node free list + freeList: c.int, + + // Leaf indices for rebuild + leafIndices: [^]int, + + // Leaf bounding boxes for rebuild + leafBoxes: [^]AABB, + + // Leaf bounding box centers for rebuild + leafCenters: [^]Vec3, + + // Bins for sorting during rebuild + binIndices: [^]c.int, + + // Allocated space for rebuilding + rebuildCapacity: c.int, +} + +// These are performance results returned by dynamic tree queries. +TreeStats :: struct { + // Number of internal nodes visited during the query + nodeVisits: c.int, + + // Number of leaf nodes visited during the query + leafVisits: c.int, +} + +// This function receives proxies found in the AABB query. +// @return true if the query should continue +TreeQueryCallbackFcn :: proc "c" (proxyId: c.int, userData: u64, ctx: rawptr) -> bool + +// This function receives the minimum distance squared so far and proxy to check in the closest query. +// @return minimum distance squared to user objects in the proxy +TreeQueryClosestCallbackFcn :: proc "c" (distanceSqrMin: f32, proxyId: c.int, userData: u64, ctx: rawptr) -> f32 + +// This function receives clipped AABB cast input for a proxy. The function returns the new cast +// fraction. +// - return a value of 0 to terminate the cast +// - return a value less than input->maxFraction to clip the cast +// - return a value of input->maxFraction to continue the cast without clipping +TreeBoxCastCallbackFcn :: proc "c" (#by_ptr input: BoxCastInput, proxyId: c.int, userData: u64, ctx: rawptr) -> f32 + +// This function receives clipped ray cast input for a proxy. The function +// returns the new ray fraction. +// - return a value of 0 to terminate the ray cast +// - return a value less than input->maxFraction to clip the ray +// - return a value of input->maxFraction to continue the ray cast without clipping +TreeRayCastCallbackFcn :: proc "c" (#by_ptr input: RayCastInput, proxyId: c.int, userData: u64, ctx: rawptr) -> f32 + +/**@}*/ // tree + +/** + * @defgroup character Character Mover + * Character movement solver + * @{ + */ + +// The plane between a character mover and a shape +PlaneResult :: struct { + // Outward pointing plane. + plane: Plane, + + // Closest point on the shape. May not be unique. + point: Vec3, +} + +// These are collision planes that can be fed to b3SolvePlanes. Normally +// this is assembled by the user from plane results in b3PlaneResult. +CollisionPlane :: struct { + // The collision plane between the mover and some shape. + plane: Plane, + + // Setting this to FLT_MAX makes the plane as rigid as possible. Lower values can + // make the plane collision soft. Usually in meters. + pushLimit: f32, + + // The push on the mover determined by b3SolvePlanes. Usually in meters. + push: f32, + + // Indicates if b3ClipVector should clip against this plane. Should be false for soft collision. + clipVelocity: bool, +} + +// Result returned by b3SolvePlanes. +PlaneSolverResult :: struct { + // The final relative translation. + delta: Vec3, + + // The number of iterations used by the plane solver. For diagnostics. + iterationCount: c.int, +} + +// Body plane result for movers. +BodyPlaneResult :: struct { + // The shape id on the body. + shapeId: ShapeId, + + // The plane result. + result: PlaneResult, +} + +// Used to collect collision planes for character movers. +// Return true to continue gathering planes. +PlaneResultFcn :: proc "c" (shapeId: ShapeId, plane: [^]PlaneResult, planeCount: c.int, ctx: rawptr) -> bool + +// Used to filter shapes for shape casting character movers. +// Return true to accept the collision +MoverFilterFcn :: proc "c" (shapeId: ShapeId, ctx: rawptr) -> bool + +/**@}*/ // mover + +/** + * @defgroup geometry Geometry + * @brief Geometry types and algorithms + * + * Definitions of spheres, capsules, hulls, meshes, height fields, and compounds. + * @{ + */ + +// This holds the mass data computed for a shape. +MassData :: struct { + // The shape mass + mass: f32, + + // The local center of mass position. + center: Vec3, + + // The inertia tensor about the shape center of mass. + inertia: Matrix3, +} + +/** + * @defgroup sphere Sphere + * @brief Sphere primitive + * @{ + */ + +// A solid sphere +Sphere :: struct { + // The local center + center: Vec3, + + // The radius + radius: f32, +} + +/**@}*/ // sphere + +/** + * @defgroup capsule Capsule + * @brief Capsule primitive + * @{ + */ + +// A solid capsule can be viewed as two hemispheres connected +// by a rectangle. +Capsule :: struct { + // Local center of the first hemisphere + center1: Vec3, + + // Local center of the second hemisphere + center2: Vec3, + + // The radius of the hemispheres + radius: f32, +} + +/**@}*/ // capsule + +/** + * @defgroup hull Convex Hull + * @brief Convex hull primitive + * @{ + */ + +// A hull vertex. Identified by a half-edge with this +// vertex as its tail. +HullVertex :: struct { + // A half-edge that has this vertex as the origin + // Can be used along with edge twins and winding order + // to traverse all the edges connected to this vertex. + edge: u8, +} + +// Half-edge for hull data structure +HullHalfEdge :: struct { + // Next edge index CCW + next: u8, + + // Twin edge index + twin: u8, + + // index of origin vertex and point + origin: u8, + + // Face to the left of this edge + face: u8, +} + +// A hull face. Hulls use a half-edge data structure, so a face +// can be determined from a single half-edge index. +HullFace :: struct { + // An arbitrary half-edge on this face + edge: u8, +} + +// 64-bit hull version. Useful for validating serialized data. +HULL_VERSION :: 0x9D4716CE3793900E + +// A convex hull. +// @note This data structure has data hanging off the end and cannot be directly copied. +HullData :: struct { + // Version must be first and match B3_HULL_VERSION + version: u64, + + // The total number of bytes for this hull. + byteCount: c.int, + + // Hash of this hull (this field is zero when the hash is computed). + hash: u32, + + // Axis-aligned box in local space. + aabb: AABB, + + // Surface area, typically in squared meters. + surfaceArea: f32, + + // Volume, typically in m^3. + volume: f32, + + // The radius of the largest sphere at the center. + innerRadius: f32, + + // The local centroid + center: Vec3, + + // The inertia tensor about the centroid. + centralInertia: Matrix3, + + // The vertex count. + vertexCount: c.int, + + // Offset of the vertex array in bytes from the struct address. + vertexOffset: c.int, + + // Offset of the point array in bytes from the struct address. + pointOffset: c.int, + + // This is the half-edge count (double the edge count) + edgeCount: c.int, + + // Offset of the edge array in bytes from the struct address. + edgeOffset: c.int, + + // The face count. Hulls faces are convex polygons. + faceCount: c.int, + + // Offset of the face array in bytes from the struct address. + faceOffset: c.int, + + // Offset of the face plane array in bytes from the struct address. + planeOffset: c.int, + + // Explicit padding. Hull identity is a content hash and memcmp over raw bytes, + // so there must be no unnamed padding for struct copies to scramble. + padding: c.int, +} + +// Efficient box hull +BoxHull :: struct { + // The embedded hull. So the offsets index into the arrays that follow. + base: HullData, + boxVertices: [8]HullVertex, //< Box vertices. + boxPoints: [8]Vec3, //< Box points. + boxEdges: [24]HullHalfEdge, //< Box half-edges. + boxFaces: [6]HullFace, //< Box faces. + padding: [2]u8, //< Explicit padding, see b3HullData::padding. + boxPlanes: [6]Plane, //< Box face planes. +} + +/**@}*/ // hull + +/** + * @defgroup mesh Triangle Mesh + * @brief Triangle mesh collision shape + * @{ + */ + +// This is used to create a re-usable collision mesh +MeshDef :: struct { + // Triangle vertices + vertices: [^]Vec3 `fmt:"v,vertexCount"`, + + // Triangle vertex indices. 3 for each triangle. + indices: [^]i32, + + // Triangle material index. 1 per triangle. Indexes into b3ShapeDef::materials. + // This allows different run-time material data to be associated with different + // instances of this mesh. + materialIndices: [^]u8, + + // Tolerance for vertex welding in length units. + weldTolerance: f32, + + // The vertex count. Must be 3 or more. + vertexCount: c.int, + + // The triangle count. Must be 1 or more. + triangleCount: c.int, + + // Optionally weld nearby vertices. + weldVertices: bool, + + // Use the median split instead of SAH to speed up mesh creation. Good + // for meshes that are structured like a grid. + useMedianSplit: bool, + + // Compute triangle adjacency information using shared edges + identifyEdges: bool, +} + +// 64-bit mesh version. Useful for validating serialized data. +MESH_VERSION :: 0xABD11AB62A6E886D + +// Triangle mesh edge flags. + +MeshEdgeFlags :: distinct bit_set[MeshEdgeFlag; c.uint] +MeshEdgeFlag :: enum c.uint { + concaveEdge1 = 0, + concaveEdge2 = 1, + concaveEdge3 = 2, + + inverseConcaveEdge1 = 4, + inverseConcaveEdge2 = 5, + inverseConcaveEdge3 = 6, +} + +concaveEdge1 :: MeshEdgeFlags{.concaveEdge1} +concaveEdge2 :: MeshEdgeFlags{.concaveEdge2} +concaveEdge3 :: MeshEdgeFlags{.concaveEdge3} +inverseConcaveEdge1 :: MeshEdgeFlags{.inverseConcaveEdge1} +inverseConcaveEdge2 :: MeshEdgeFlags{.inverseConcaveEdge2} +inverseConcaveEdge3 :: MeshEdgeFlags{.inverseConcaveEdge3} +allConcaveEdges :: concaveEdge1 + concaveEdge2 + concaveEdge3 +flatEdge1 :: concaveEdge1 + inverseConcaveEdge1 +flatEdge2 :: concaveEdge2 + inverseConcaveEdge2 +flatEdge3 :: concaveEdge3 + inverseConcaveEdge3 +allFlatEdges :: flatEdge1 + flatEdge2 + flatEdge3 + +// A mesh triangle. +MeshTriangle :: struct { + index1: i32, //< Index of vertex 1. + index2: i32, //< Index of vertex 2. + index3: i32, //< Index of vertex 3. +} + +// A mesh BVH node. +MeshNode :: struct { + // The lower bound of the node AABB. Strategic placement for SIMD. + lowerBound: Vec3, + + // Anonymous union. + data: struct #raw_union { + // Internal node + asNode: bit_field u32 { + // Split axis. 0, 1, or 2. + axis: u32 | 2, + // Offset of the second child node. + childOffset: u32 | 30, + }, + + // Leaf node + asLeaf: bit_field u32 { + // Aligned with axis above and has value of 3 if this is a leaf. + type: u32 | 2, + + // The number of triangles for this leaf node. + triangleCount: u32 | 30, + }, + }, + + // The upper bound of the node AABB. Strategic placement for SIMD. + upperBound: Vec3, + + // The index of the leaf triangles. + triangleOffset: u32, +} + +// This is a sorted triangle collision bounding volume hierarchy. +// @note This struct has data hanging off the end and cannot be directly copied. +MeshData :: struct { + // Version must be first. + version: u64, + + // The total number of bytes for this mesh. + byteCount: c.int, + + // Hash of this mesh (this field is zero when the hash is computed) + hash: u32, + + // Local axis-aligned box. + bounds: AABB, + + // Combined surface area of all triangles. Single-sided. + surfaceArea: f32, + + // The height of the bounding volume hierarchy. + treeHeight: c.int, + + // The number of degenerate triangles. Diagnostic. + degenerateCount: c.int, + + // Offset of the node array in bytes from the struct address. + nodeOffset: c.int, + + // The number of BVH nodes. + nodeCount: c.int, + + // Offset of the vertex array in bytes from the struct address. + vertexOffset: c.int, + + // The number of vertices. + vertexCount: c.int, + + // Offset of the triangle array in bytes from the struct address. + triangleOffset: c.int, + + // The number of triangles. + triangleCount: c.int, + + // Offset of the material array in bytes from the struct address. + materialOffset: c.int, + + // The number of materials. + materialCount: c.int, + + // Offset of the triangle flag array in bytes from the struct address. + flagsOffset: c.int, +} + +// This allows mesh data to be re-used with different scales. +Mesh :: struct { + // Immutable pointer to the mesh data. + data: ^MeshData, + + // This scale may be non-uniform and have negative components. However, + // no component may be very small in magnitude. + scale: Vec3, +} + +/**@}*/ // mesh + +/** + * @defgroup height_field Height Field + * @brief Height field collision shape + * @{ + */ + +// Data used to create a height field +HeightFieldDef :: struct { + // Grid point heights + // count = countX * countZ + heights: [^]f32, + + // Grid cell material + // A value of 0xFF is reserved for holes + // count = (countX - 1) * (countZ - 1) + materialIndices: [^]u8, + + // The height field scale. All components must be positive values. + scale: Vec3, + + // The number of grid lines along the x-axis. + countX: c.int, + + // The number of grid lines along the z-axis. + countZ: c.int, + + // Global minimum and maximum heights used for quantization. This is important + // if you want height fields to be placed next to each other and line up exactly. + // In that case, both height fields should use the same minimum and maximum heights. + // All height values are clamped to this range. + // These values are in unscaled space. + globalMinimumHeight: f32, + + // The maximum. + globalMaximumHeight: f32, + + // Use clock-wise winding. This effectively inverts the height-field along the y-axis. + clockwiseWinding: bool, +} + +// This material index is used to designate holes in a height field. +HEIGHT_FIELD_HOLE :: 0xFF + +// 64-bit height-field version. Useful for validating serialized data. +HEIGHT_FIELD_VERSION :: 0x8B18CBD138A6BC84 + +// A height field with compressed storage. +// @note This data structure has data hanging off the end and cannot be directly copied. +HeightFieldData :: struct { + // Version must be first and match B3_HEIGHT_FIELD_VERSION + version: i64, + + // The total number of bytes for this height field. + byteCount: c.int, + + // Hash of this height field (this field is zero when the hash is computed). + hash: u32, + + // The local axis-aligned bounding box. + aabb: AABB, + + // The minimum y value. + minHeight: f32, + + // The maximum y value + maxHeight: f32, + + // The quantization scale. + heightScale: f32, + + // The overall scale. + scale: Vec3, + + // The number of grid columns along the local x-axis. + columnCount: c.int, + + // The number of grid rows along the local z-axis. + rowCount: c.int, + + // Offset of the compressed height array in bytes from the struct address. + // uint16_t, one per grid point. + heightsOffset: c.int, + + // Offset of the material index array in bytes from the struct address. + // uint8_t, one per cell. + materialOffset: c.int, + + // Offset of the flag array in bytes from the struct address. + // uint8_t, one per triangle. + flagsOffset: c.int, + + // Triangle winding. + clockwise: bool, + + // Explicit padding. Identity is a content hash over raw bytes, so there must + // be no unnamed padding for struct copies to scramble. + padding: [3]u8, +} + +/**@}*/ // height_field + +/** + * @defgroup compound Compound + * @brief Compound collision shape + * @{ + */ + +// Definition for a capsule in a compound shape. +CompoundCapsuleDef :: struct { + // Local capsule. + capsule: Capsule, + + // Material properties. + material: SurfaceMaterial, +} + +// Definition for a convex hull in a compound shape. +CompoundHullDef :: struct { + // Shared hull. + hull: ^HullData, + + // Transform of the shared hull into compound local space. + transform: Transform, + + // Material properties. + material: SurfaceMaterial, +} + +// Definition for a triangle mesh in a compound shape. +CompoundMeshDef :: struct { + // Shared mesh. + meshData: ^MeshData, + + // Transform of the shared mesh into compound local space. + transform: Transform, + + // Local space non-uniform mesh scale. May have negative components. + scale: Vec3, + + // Material properties. + // This array must line up with the material indices on the triangles. + materials: [^]SurfaceMaterial `fmt:"v,materialCount"`, + + // Number of materials. + materialCount: c.int, +} + +// Definition for a sphere in a compound shape. +CompoundSphereDef :: struct { + // Local sphere. + sphere: Sphere, + + // Material properties. + material: SurfaceMaterial, +} + +// Definition for creating a compound shape. All this data is fully cloned +// into the run-time compound shape. +CompoundDef :: struct { + // Capsule instances. + capsules: [^]CompoundCapsuleDef `fmt:"v,capsuleCount"`, + + // Number of capsules. + capsuleCount: int, + + // Hulls instances. + hulls: [^]CompoundHullDef `fmt:"v,hullCount"`, + + // Number of hull instances. + hullCount: int, + + // Mesh instances. + meshes: [^]CompoundMeshDef `fmt:"v,meshCount"`, + + // Number of mesh instances. + meshCount: int, + + // Sphere instances. + spheres: [^]CompoundSphereDef `fmt:"v,sphereCount"`, + + // Number of spheres. + sphereCount: int, +} + +// The compound version depends on the tree, mesh, and hull versions. +COMPOUND_VERSION :: 0x830778DB07086EB4 ~ DYNAMIC_TREE_VERSION ~ MESH_VERSION ~ HULL_VERSION + +// Meshes used in compounds have limited space for materials. If you have +// a mesh with many materials, you can use it outside of the compound. +MAX_COMPOUND_MESH_MATERIALS :: 4 + +// The runtime data for a compound shape. This is a potentially large yet highly optimized +// data structure. It can contain thousands of child shapes, yet at runtime it populates +// into the world as a single shape in the runtime broad-phase. +// This data structure has data living off the end and must be accessed using offsets. +// Accessors are provided for user relevant data. +CompoundData :: struct { + // The compound version is always first. + version: u64, + + // The total number of bytes for this compound. + byteCount: c.int, + + // Offset of the tree node array in bytes from the struct address. + nodeOffset: c.int, + + // Immutable dynamic tree. The tree node pointer must be fixed up using the node offset + tree: DynamicTree, + + // Offset of the material array in bytes from the struct address. + materialOffset: c.int, + + // The number of materials. + materialCount: c.int, + + // Offset of the capsule array in bytes from the struct address. + capsuleOffset: c.int, + + // The number of capsules. + capsuleCount: c.int, + + // Offset of the hull instance array in bytes from the struct address. + hullOffset: c.int, + + // The number of hull instances. + hullCount: c.int, + + // The number of unique hulls. Diagnostic. + sharedHullCount: c.int, + + // Offset of the mesh instance array in bytes from the struct address. + meshOffset: c.int, + + // The number of mesh instances. + meshCount: c.int, + + // The number of unique meshes. Diagnostic. + sharedMeshCount: c.int, + + // Offset of the sphere array in bytes from the struct address. + sphereOffset: c.int, + + // The number of spheres. + sphereCount: c.int, +} + +// A capsule that lives in a compound. +CompoundCapsule :: struct { + // Local capsule. + capsule: Capsule, + + // Index to a shared material. + materialIndex: c.int, +} + +// A hull that lives in a compound. +CompoundHull :: struct { + // Pointer to the unique shared hull. + hull: ^HullData, + + // The transform of this hull instance. + transform: Transform, + + // Index to a shared material. + materialIndex: c.int, +} + +// A mesh with non-uniform scale that lives in a compound. +CompoundMesh :: struct { + // Pointer to the unique shared mesh. + meshData: [^]MeshData, + + // The transform of this mesh instance. + transform: Transform, + + // Non-uniform scale of this mesh instance. + scale: Vec3, + + // This is used to access the surface material from b3GetCompoundMaterials. + // Requires an extra level of indirection. The triangle material index + // is clamped to B3_MAX_COMPOUND_MESH_MATERIALS. + // materialIndex = materialIndices[triangle->materialIndex] + materialIndices: [MAX_COMPOUND_MESH_MATERIALS]c.int, +} + +// A sphere that lives in a compound. +CompoundSphere :: struct { + // Local sphere. + sphere: Sphere, + + // Index to a shared material. + materialIndex: c.int, +} + +// Child shape of a compound +ChildShape :: struct { + // Tagged union. + using _: struct #raw_union { + capsule: Capsule, //< Capsule. + hull: ^HullData, //< Hull. + mesh: Mesh, //< Mesh. + sphere: Sphere, //< Sphere. + }, + + // Transform of the shape into compound local space. + transform: Transform, + + // Material indices. Index 0 is used for convex shapes. + // todo limit to 64K? + materialIndices: [MAX_COMPOUND_MESH_MATERIALS]c.int, + + // The shape type (union tag). + type: ShapeType, +} + +// Callback for compound overlap queries. +CompoundQueryFcn :: proc "c" (#by_ptr compound: CompoundData, childIndex: c.int, ctx: rawptr) -> bool + +/**@}*/ // compound + +/**@}*/ // geometry + +/** + * @defgroup collision Shape Collision + * Collide pairs of shapes. + * @{ + */ + +// A manifold point is a contact point belonging to a contact manifold. +// It holds details related to the geometry and dynamics of the contact points. +// Box3D uses speculative collision so some contact points may be separated. +// You may use the maxNormalImpulse to determine if there was an interaction during +// the time step. +ManifoldPoint :: struct { + // Location of the contact point relative to the bodyA center of mass in world space. + anchorA: Vec3, + + // Location of the contact point relative to the bodyB center of mass in world space. + anchorB: Vec3, + + // The separation of the contact point, negative if penetrating + separation: f32, + + // Cached separation used for contact recycling + baseSeparation: f32, + + // The impulse along the manifold normal vector. Since Box3D uses sub-stepping, this is + // result from the final sub-step. + normalImpulse: f32, + + // The total normal impulse applied during sub-stepping. This is important + // to identify speculative contact points that had an interaction in the time step. + totalNormalImpulse: f32, + + // Relative normal velocity pre-solve. Used for hit events. If the normal impulse is + // zero then there was no hit. Negative means shapes are approaching. + normalVelocity: f32, + + // Local point for matching + // Uniquely identifies a contact point between two shapes + featureId: u32, + + // Triangle index if one of the shapes is a mesh or height field + triangleIndex: c.int, + + // Did this contact point exist in the previous step? + persisted: bool, +} + +// A contact manifold describes the contact points between colliding shapes. +// @note Box3D uses speculative collision so some contact points may be separated. +Manifold :: struct { + // The manifold points. There may be 1 to 4 valid points. + points: [MAX_MANIFOLD_POINTS]ManifoldPoint, + + // The unit normal vector in world space, points from shape A to shape B + normal: Vec3, + + // Central friction angular impulse (applied about the normal) + twistImpulse: f32, + + // Central friction linear impulse + frictionImpulse: Vec3, + + // Rolling resistance angular impulse + rollingImpulse: Vec3, + + // The number of contact points, will be 0 to 4 + pointCount: c.int, +} + +// Cached separating axis feature. +SeparatingFeature :: enum c.int { + invalidAxis = 0, + backsideAxis, + faceAxisA, + faceAxisB, + edgePairAxis, + closestPointsAxis, + + // These are for testing + manualFaceAxisA, + manualFaceAxisB, + manualEdgePairAxis, +} + +// Cached triangle feature. +TriangleFeature :: enum c.int { + featureNone = 0, + featureTriangleFace, + featureHullFace, + // v1-v2 + featureEdge1, + // v2-v3 + featureEdge2, + // v3-v1 + featureEdge3, + featureVertex1, + featureVertex2, + featureVertex3, +} + +// Separating axis test cache. Provides temporal acceleration of collision routines. +SATCache :: struct { + // The separation when the cache is populated. Negative for overlap. + separation: f32, + + // b3SeparatingFeature. + type: u8, + + // Index of the feature on shape A. + indexA: u8, + + // Index of the feature on shape B. + indexB: u8, + + // Was the cache re-used? + hit: u8, +} + +// Contact points are always the result of two edges intersecting. +// It can be two edges of the same shape, which is just a shape vertex. +// Or a contact point can be the result of two edges crossing from different shapes. +// This is designed to support hull versus hull, but it is adapted to work +// with all shape types. The feature pair is used to identify contact points +// for temporal coherence and warm starting. +FeaturePair :: struct { + // Incoming type (either edge on shape A or shape B) + owner1: u8, + // Incoming edge index (into associated shape array) + index1: u8, + // Outgoing type (either edge on shape A or shape B) + owner2: u8, + // Outgoing edge index (into associated shape array) + index2: u8, +} + +// A local manifold point and normal in frame A. +LocalManifoldPoint :: struct { + // Local point in frame A. + point: Vec3, + + // The contact point separation. Negative for overlap. + separation: f32, + + // The feature pair for this point. + pair: FeaturePair, + + // The triangle index when collide with a mesh or height-field. + triangleIndex: c.int, +} + +// A local manifold with no dynamic information. Used by b3Collide functions. +LocalManifold :: struct { + // Local normal in frame A. + normal: Vec3, + + // The triangle normal. + triangleNormal: Vec3, + + // The manifold points. From a point buffer. + points: LocalManifoldPoint, + + // The number of manifold points. Only bounded by the buffer capacity. + pointCount: c.int, + + // The index of the triangle. + triangleIndex: c.int, + + i1: c.int, //< Vertex 1 index. + i2: c.int, //< Vertex 2 index. + i3: c.int, //< Vertex 3 index. + + // The squared distance of a sphere from a triangle. For ghost collision reduction. + squaredDistance: f32, + + // The triangle feature involved. + feature: TriangleFeature, + + // MeshEdgeFlags. + triangleFlags: MeshEdgeFlags, +} + +/**@}*/ // collision + +/** + * @defgroup debug_draw Debug Draw + * @{ + */ + +// These colors are used for debug draw and mostly match the named SVG colors. +// See https://www.rapidtables.com/web/color/index.html +// https://johndecember.com/html/spec/colorsvg.html +// https://upload.wikimedia.org/wikipedia/commons/2/2b/SVG_Recognized_color_keyword_names.svg +HexColor :: enum c.uint { + AliceBlue = 0xF0F8FF, + AntiqueWhite = 0xFAEBD7, + Aqua = 0x00FFFF, + Aquamarine = 0x7FFFD4, + Azure = 0xF0FFFF, + Beige = 0xF5F5DC, + Bisque = 0xFFE4C4, + Black = 0x000000, + BlanchedAlmond = 0xFFEBCD, + Blue = 0x0000FF, + BlueViolet = 0x8A2BE2, + Brown = 0xA52A2A, + Burlywood = 0xDEB887, + CadetBlue = 0x5F9EA0, + Chartreuse = 0x7FFF00, + Chocolate = 0xD2691E, + Coral = 0xFF7F50, + CornflowerBlue = 0x6495ED, + Cornsilk = 0xFFF8DC, + Crimson = 0xDC143C, + Cyan = 0x00FFFF, + DarkBlue = 0x00008B, + DarkCyan = 0x008B8B, + DarkGoldenRod = 0xB8860B, + DarkGray = 0xA9A9A9, + DarkGreen = 0x006400, + DarkKhaki = 0xBDB76B, + DarkMagenta = 0x8B008B, + DarkOliveGreen = 0x556B2F, + DarkOrange = 0xFF8C00, + DarkOrchid = 0x9932CC, + DarkRed = 0x8B0000, + DarkSalmon = 0xE9967A, + DarkSeaGreen = 0x8FBC8F, + DarkSlateBlue = 0x483D8B, + DarkSlateGray = 0x2F4F4F, + DarkTurquoise = 0x00CED1, + DarkViolet = 0x9400D3, + DeepPink = 0xFF1493, + DeepSkyBlue = 0x00BFFF, + DimGray = 0x696969, + DodgerBlue = 0x1E90FF, + FireBrick = 0xB22222, + FloralWhite = 0xFFFAF0, + ForestGreen = 0x228B22, + Fuchsia = 0xFF00FF, + Gainsboro = 0xDCDCDC, + GhostWhite = 0xF8F8FF, + Gold = 0xFFD700, + GoldenRod = 0xDAA520, + Gray = 0x808080, + Green = 0x008000, + GreenYellow = 0xADFF2F, + HoneyDew = 0xF0FFF0, + HotPink = 0xFF69B4, + IndianRed = 0xCD5C5C, + Indigo = 0x4B0082, + Ivory = 0xFFFFF0, + Khaki = 0xF0E68C, + Lavender = 0xE6E6FA, + LavenderBlush = 0xFFF0F5, + LawnGreen = 0x7CFC00, + LemonChiffon = 0xFFFACD, + LightBlue = 0xADD8E6, + LightCoral = 0xF08080, + LightCyan = 0xE0FFFF, + LightGoldenRodYellow = 0xFAFAD2, + LightGray = 0xD3D3D3, + LightGreen = 0x90EE90, + LightPink = 0xFFB6C1, + LightSalmon = 0xFFA07A, + LightSeaGreen = 0x20B2AA, + LightSkyBlue = 0x87CEFA, + LightSlateGray = 0x778899, + LightSteelBlue = 0xB0C4DE, + LightYellow = 0xFFFFE0, + Lime = 0x00FF00, + LimeGreen = 0x32CD32, + Linen = 0xFAF0E6, + Magenta = 0xFF00FF, + Maroon = 0x800000, + MediumAquaMarine = 0x66CDAA, + MediumBlue = 0x0000CD, + MediumOrchid = 0xBA55D3, + MediumPurple = 0x9370DB, + MediumSeaGreen = 0x3CB371, + MediumSlateBlue = 0x7B68EE, + MediumSpringGreen = 0x00FA9A, + MediumTurquoise = 0x48D1CC, + MediumVioletRed = 0xC71585, + MidnightBlue = 0x191970, + MintCream = 0xF5FFFA, + MistyRose = 0xFFE4E1, + Moccasin = 0xFFE4B5, + NavajoWhite = 0xFFDEAD, + Navy = 0x000080, + OldLace = 0xFDF5E6, + Olive = 0x808000, + OliveDrab = 0x6B8E23, + Orange = 0xFFA500, + OrangeRed = 0xFF4500, + Orchid = 0xDA70D6, + PaleGoldenRod = 0xEEE8AA, + PaleGreen = 0x98FB98, + PaleTurquoise = 0xAFEEEE, + PaleVioletRed = 0xDB7093, + PapayaWhip = 0xFFEFD5, + PeachPuff = 0xFFDAB9, + Peru = 0xCD853F, + Pink = 0xFFC0CB, + Plum = 0xDDA0DD, + PowderBlue = 0xB0E0E6, + Purple = 0x800080, + RebeccaPurple = 0x663399, + Red = 0xFF0000, + RosyBrown = 0xBC8F8F, + RoyalBlue = 0x4169E1, + SaddleBrown = 0x8B4513, + Salmon = 0xFA8072, + SandyBrown = 0xF4A460, + SeaGreen = 0x2E8B57, + SeaShell = 0xFFF5EE, + Sienna = 0xA0522D, + Silver = 0xC0C0C0, + SkyBlue = 0x87CEEB, + SlateBlue = 0x6A5ACD, + SlateGray = 0x708090, + Snow = 0xFFFAFA, + SpringGreen = 0x00FF7F, + SteelBlue = 0x4682B4, + Tan = 0xD2B48C, + Teal = 0x008080, + Thistle = 0xD8BFD8, + Tomato = 0xFF6347, + Turquoise = 0x40E0D0, + Violet = 0xEE82EE, + Wheat = 0xF5DEB3, + White = 0xFFFFFF, + WhiteSmoke = 0xF5F5F5, + Yellow = 0xFFFF00, + YellowGreen = 0x9ACD32, + + Box2DRed = 0xDC3132, + Box2DBlue = 0x30AEBF, + Box2DGreen = 0x8CC924, + Box2DYellow = 0xFFEE8C, +} + +// Debug draw material preset. Optionally packed into the unused high byte of a +// b3HexColor (or b3SurfaceMaterial::customColor) to drive the renderer's PBR +// roughness and metalness. The low 24 bits stay RGB, so a plain 0xRRGGBB color +// reads as b3_debugMaterialDefault and keeps the renderer's per-body-type look. +DebugMaterial :: enum c.uint { + Default = 0, + Matte, + Soft, + Dead, + Glossy, + Metallic, +} + + +// This is sent to the user for debug shape creation. The user should know the type in case they have +// custom sphere or capsule rendering. +DebugShape :: struct { + // Shape id. + shapeId: ShapeId, + + // Shape type. + type: ShapeType, + + // Tagged union. + using _: struct #raw_union { + capsule: ^Capsule `raw_union_tag:"type=.capsuleShape"`, //< Capsule shape. + compound: ^CompoundData `raw_union_tag:"type=.compoundShape"`, //< Compound shape. + heightField: ^HeightFieldData `raw_union_tag:"type=.heightShape"`, //< Height-field shape. + hull: ^HullData `raw_union_tag:"type=.hullShape"`, //< Convex hull shape. + mesh: ^Mesh `raw_union_tag:"type=.meshShape"`, //< Mesh shape with scale. + sphere: ^Sphere `raw_union_tag:"type=.sphereShape"`, //< Sphere shape. + }, +} + +// This struct is passed to b3World_Draw to draw a debug view of the simulation world. +// Callbacks receive world coordinates. In large world mode the translation is double precision so +// it stays accurate far from the origin. Shift into your own camera frame inside the callbacks. +DebugDraw :: struct { + // Draws a shape and returns true if drawing should continue + DrawShapeFcn: proc "c" (userShape: rawptr, transform: WorldTransform, color: HexColor, ctx: rawptr) -> bool, + + // Draw a line segment. + DrawSegmentFcn: proc "c" (p1, p2: Pos, color: HexColor, ctx: rawptr), + + // Draw a transform. Choose your own length scale. + DrawTransformFcn: proc "c" (transform: WorldTransform, ctx: rawptr), + + // Draw a point. + DrawPointFcn: proc "c" (p: Pos, size: f32, color: HexColor, ctx: rawptr), + + // Draw a sphere. + DrawSphereFcn: proc "c" (p: Pos, radius: f32, color: HexColor, alpha: f32, ctx: rawptr), + + // Draw a capsule. + DrawCapsuleFcn: proc "c" (p1, p2: Pos, radius: f32, color: HexColor, alpha: f32, ctx: rawptr), + + // Draw a bounding box. + DrawBoundsFcn: proc "c" (aabb: AABB, color: HexColor, ctx: rawptr), + + // Draw an oriented box. + DrawBoxFcn: proc "c" (extents: Vec3, transform: WorldTransform, color: HexColor, ctx: rawptr), + + // Draw a string in world space + DrawStringFcn: proc "c" (p: Pos, s: cstring, color: HexColor, ctx: rawptr), + + // World bounds to use for debug draw + drawingBounds: AABB, + + // Scale to use when drawing forces + forceScale: f32, + + // Global scaling for joint drawing + jointScale: f32, + + // Option to draw shapes + drawShapes: bool, + + // Option to draw joints + drawJoints: bool, + + // Option to draw additional information for joints + drawJointExtras: bool, + + // Option to draw the bounding boxes for shapes + drawBounds: bool, + + // Option to draw the mass and center of mass of dynamic bodies + drawMass: bool, + + // Option to draw body names + drawBodyNames: bool, + + // Option to draw contact points + drawContacts: bool, + + // Draw contact anchor A or B + drawAnchorA: c.int, + + // Option to visualize the graph coloring used for contacts and joints + drawGraphColors: bool, + + // Option to draw contact features + drawContactFeatures: bool, + + // Option to draw contact normals + drawContactNormals: bool, + + // Option to draw contact normal forces + drawContactForces: bool, + + // Option to draw contact friction forces + drawFrictionForces: bool, + + // Option to draw islands as bounding boxes + drawIslands: bool, + + // User context that is passed as an argument to drawing callback functions + ctx: rawptr, +} diff --git a/vendor/box3d/lib/box3d.lib b/vendor/box3d/lib/box3d.lib new file mode 100644 index 000000000..4bc6c5b21 Binary files /dev/null and b/vendor/box3d/lib/box3d.lib differ diff --git a/vendor/box3d/src/include/box3d/base.h b/vendor/box3d/src/include/box3d/base.h new file mode 100644 index 000000000..2c0bb93f3 --- /dev/null +++ b/vendor/box3d/src/include/box3d/base.h @@ -0,0 +1,193 @@ +// SPDX-FileCopyrightText: 2025 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +// 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 +#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 diff --git a/vendor/box3d/src/include/box3d/box3d.h b/vendor/box3d/src/include/box3d/box3d.h new file mode 100644 index 000000000..22756636a --- /dev/null +++ b/vendor/box3d/src/include/box3d/box3d.h @@ -0,0 +1,1730 @@ +// SPDX-FileCopyrightText: 2025 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" +#include "collision.h" +#include "id.h" +#include "math_functions.h" +#include "types.h" + +#include + +/** + * @defgroup world World + * These functions allow you to create a simulation world. + * + * You can add rigid bodies and joint constraints to the world and run the simulation. You can get contact + * information to get contact points and normals as well as events. You can query the world, checking for overlaps and casting + * rays or shapes. There is also debugging information such as debug draw, timing information, and counters. You can find + * documentation here: https://box2d.org/ + * @{ + */ + +#if defined( BOX3D_DOUBLE_PRECISION ) +// Force a link error if the application and library disagree on precision. A float app linking +// a double precision library, or the reverse, gets one unresolved external on the first call +// every program makes. CMake consumers inherit the define and cannot mismatch. +#define b3CreateWorld b3CreateWorldDoublePrecision +#endif + +/// Create a world for rigid body simulation. A world contains bodies, shapes, and constraints. You may create +/// up to 128 worlds. Each world is completely independent and may be simulated in parallel. +/// @return the world id. +B3_API b3WorldId b3CreateWorld( const b3WorldDef* def ); + +/// Destroy a world +B3_API void b3DestroyWorld( b3WorldId worldId ); + +/// Get the current number of worlds +B3_API int b3GetWorldCount( void ); + +/// Get the maximum number of simultaneous worlds that have been created +B3_API int b3GetMaxWorldCount( void ); + +/// World id validation. Provides validation for up to 64K allocations. +B3_API bool b3World_IsValid( b3WorldId id ); + +/// Simulate a world for one time step. This performs collision detection, integration, and constraint solution. +/// @param worldId The world to simulate +/// @param timeStep The amount of time to simulate, this should be a fixed number. Usually 1/60. +/// @param subStepCount The number of sub-steps, increasing the sub-step count can increase accuracy. Usually 4. +B3_API void b3World_Step( b3WorldId worldId, float timeStep, int subStepCount ); + +/// Call this to draw shapes and other debug draw data +B3_API void b3World_Draw( b3WorldId worldId, b3DebugDraw* draw, uint64_t maskBits ); + +/// Get the world's bounds. This is the bounding box that covers the current simulation. May have a small +/// amount of padding. +B3_API b3AABB b3World_GetBounds( b3WorldId worldId ); + +/// Get the body events for the current time step. The event data is transient. Do not store a reference to this data. +B3_API b3BodyEvents b3World_GetBodyEvents( b3WorldId worldId ); + +/// Get sensor events for the current time step. The event data is transient. Do not store a reference to this data. +B3_API b3SensorEvents b3World_GetSensorEvents( b3WorldId worldId ); + +/// Get contact events for this current time step. The event data is transient. Do not store a reference to this data. +B3_API b3ContactEvents b3World_GetContactEvents( b3WorldId worldId ); + +/// Get the joint events for the current time step. The event data is transient. Do not store a reference to this data. +B3_API b3JointEvents b3World_GetJointEvents( b3WorldId worldId ); + +/// Overlap test for all shapes that *potentially* overlap the provided AABB +B3_API b3TreeStats b3World_OverlapAABB( b3WorldId worldId, b3AABB aabb, b3QueryFilter filter, b3OverlapResultFcn* fcn, + void* context ); + +/// Overlap test for all shapes that overlap the provided shape proxy. The proxy points are relative +/// to the world origin, which lets the query stay precise far from the world origin. +B3_API b3TreeStats b3World_OverlapShape( b3WorldId worldId, b3Pos origin, const b3ShapeProxy* proxy, b3QueryFilter filter, + b3OverlapResultFcn* fcn, void* context ); + +/// Cast a ray into the world to collect shapes in the path of the ray. +/// Your callback function controls whether you get the closest point, any point, or n-points. +/// @note The callback function may receive shapes in any order +/// @param worldId The world to cast the ray against +/// @param origin The start point of the ray +/// @param translation The translation of the ray from the start point to the end point +/// @param filter Contains bit flags to filter unwanted shapes from the results +/// @param fcn A user implemented callback function +/// @param context A user context that is passed along to the callback function +/// @return traversal performance counters +B3_API b3TreeStats b3World_CastRay( b3WorldId worldId, b3Pos origin, b3Vec3 translation, b3QueryFilter filter, + b3CastResultFcn* fcn, void* context ); + +/// Cast a ray into the world to collect the closest hit. This is a convenience function. Ignores initial overlap. +/// This is less general than b3World_CastRay() and does not allow for custom filtering. +B3_API b3RayResult b3World_CastRayClosest( b3WorldId worldId, b3Pos origin, b3Vec3 translation, b3QueryFilter filter ); + +/// Cast a shape through the world. Similar to a cast ray except that a shape is cast instead of a point. +/// The proxy points are relative to the origin and the hit points come back as world positions, so the +/// cast stays precise far from the world origin. +/// @see b3World_CastRay +B3_API b3TreeStats b3World_CastShape( b3WorldId worldId, b3Pos origin, const b3ShapeProxy* proxy, b3Vec3 translation, + b3QueryFilter filter, b3CastResultFcn* fcn, void* context ); + +/// Cast a capsule mover through the world. This is a special shape cast that handles sliding along other shapes while reducing +/// clipping. This is not a good source of information about what the mover is touching. Instead use the planes returned by +/// b3World_CollideMover. +/// @param worldId World to cast the mover against +/// @param origin World position the mover capsule is relative to +/// @param mover Capsule mover, relative to the origin +/// @param translation Desired mover translation +/// @param filter Contains bit flags to filter unwanted shapes from the results +/// @param fcn Optional callback for custom shape filtering +/// @param context A user context that is passed along to the callback function +/// @return the translation fraction +B3_API float b3World_CastMover( b3WorldId worldId, b3Pos origin, const b3Capsule* mover, b3Vec3 translation, b3QueryFilter filter, + b3MoverFilterFcn* fcn, void* context ); + +/// Collide a capsule mover with the world, gathering collision planes that can be fed to b3SolvePlanes. Useful for +/// kinematic character movement. The mover and the returned planes are relative to the origin. +B3_API void b3World_CollideMover( b3WorldId worldId, b3Pos origin, const b3Capsule* mover, b3QueryFilter filter, + b3PlaneResultFcn* fcn, void* context ); + +/// Enable/disable sleep. If your application does not need sleeping, you can gain some performance +/// by disabling sleep completely at the world level. +/// @see b3WorldDef +B3_API void b3World_EnableSleeping( b3WorldId worldId, bool flag ); + +/// Is body sleeping enabled? +B3_API bool b3World_IsSleepingEnabled( b3WorldId worldId ); + +/// Enable/disable continuous collision between dynamic and static bodies. Generally you should keep continuous +/// collision enabled to prevent fast moving objects from going through static objects. The performance gain from +/// disabling continuous collision is minor. +/// @see b3WorldDef +B3_API void b3World_EnableContinuous( b3WorldId worldId, bool flag ); + +/// Is continuous collision enabled? +B3_API bool b3World_IsContinuousEnabled( b3WorldId worldId ); + +/// Adjust the restitution threshold. It is recommended not to make this value very small +/// because it will prevent bodies from sleeping. Usually in meters per second. +/// @see b3WorldDef +B3_API void b3World_SetRestitutionThreshold( b3WorldId worldId, float value ); + +/// Get the restitution speed threshold. Usually in meters per second. +B3_API float b3World_GetRestitutionThreshold( b3WorldId worldId ); + +/// Adjust the hit event threshold. This controls the collision speed needed to generate a b3ContactHitEvent. +/// Usually in meters per second. +/// @see b3WorldDef::hitEventThreshold +B3_API void b3World_SetHitEventThreshold( b3WorldId worldId, float value ); + +/// Get the hit event speed threshold. Usually in meters per second. +B3_API float b3World_GetHitEventThreshold( b3WorldId worldId ); + +/// Register the custom filter callback. This is optional. +B3_API void b3World_SetCustomFilterCallback( b3WorldId worldId, b3CustomFilterFcn* fcn, void* context ); + +/// Register the pre-solve callback. This is optional. +B3_API void b3World_SetPreSolveCallback( b3WorldId worldId, b3PreSolveFcn* fcn, void* context ); + +/// Set the gravity vector for the entire world. Box3D has no concept of an up direction and this +/// is left as a decision for the application. Usually in m/s^2. +/// @see b3WorldDef +B3_API void b3World_SetGravity( b3WorldId worldId, b3Vec3 gravity ); + +/// Get the gravity vector +B3_API b3Vec3 b3World_GetGravity( b3WorldId worldId ); + +/// Apply a radial explosion +/// @param worldId The world id +/// @param explosionDef The explosion definition +B3_API void b3World_Explode( b3WorldId worldId, const b3ExplosionDef* explosionDef ); + +/// Adjust contact tuning parameters +/// @param worldId The world id +/// @param hertz The contact stiffness (cycles per second) +/// @param dampingRatio The contact bounciness with 1 being critical damping (non-dimensional) +/// @param contactSpeed The maximum contact constraint push out speed (meters per second) +/// @note Advanced feature +B3_API void b3World_SetContactTuning( b3WorldId worldId, float hertz, float dampingRatio, float contactSpeed ); + +/// Set the contact point recycling distance. Setting this to zero disables contact point recycling. +/// Usually in meters. +B3_API void b3World_SetContactRecycleDistance( b3WorldId worldId, float recycleDistance ); + +/// Get the contact point recycling distance. Usually in meters. +B3_API float b3World_GetContactRecycleDistance( b3WorldId worldId ); + +/// Set the maximum linear speed. Usually in m/s. +B3_API void b3World_SetMaximumLinearSpeed( b3WorldId worldId, float maximumLinearSpeed ); + +/// Get the maximum linear speed. Usually in m/s. +B3_API float b3World_GetMaximumLinearSpeed( b3WorldId worldId ); + +/// Enable/disable constraint warm starting. Advanced feature for testing. Disabling +/// warm starting greatly reduces stability and provides no performance gain. +B3_API void b3World_EnableWarmStarting( b3WorldId worldId, bool flag ); + +/// Is constraint warm starting enabled? +B3_API bool b3World_IsWarmStartingEnabled( b3WorldId worldId ); + +/// Get the number of awake bodies +B3_API int b3World_GetAwakeBodyCount( b3WorldId worldId ); + +/// Get the current world performance profile +B3_API b3Profile b3World_GetProfile( b3WorldId worldId ); + +/// Get world counters and sizes +B3_API b3Counters b3World_GetCounters( b3WorldId worldId ); + +/// Get max capacity. This can be used with b3WorldDef to avoid run-time allocations and copies +B3_API b3Capacity b3World_GetMaxCapacity( b3WorldId worldId ); + +/// Set the user data pointer. +B3_API void b3World_SetUserData( b3WorldId worldId, void* userData ); + +/// Get the user data pointer. +B3_API void* b3World_GetUserData( b3WorldId worldId ); + +/// Set the friction callback. Passing NULL resets to default. +B3_API void b3World_SetFrictionCallback( b3WorldId worldId, b3FrictionCallback* callback ); + +/// Set the restitution callback. Passing NULL resets to default. +B3_API void b3World_SetRestitutionCallback( b3WorldId worldId, b3RestitutionCallback* callback ); + +/// Set the worker count. Must be in the range [1, B3_MAX_WORKERS] +B3_API void b3World_SetWorkerCount( b3WorldId worldId, int count ); + +/// Get the worker count. +B3_API int b3World_GetWorkerCount( b3WorldId worldId ); + +/// Dump memory stats to log. +B3_API void b3World_DumpMemoryStats( b3WorldId worldId ); + +/// Dump shape bounds to box3d_bounds.txt +B3_API void b3World_DumpShapeBounds( b3WorldId worldId, b3BodyType type ); + +/// This is for internal testing +B3_API void b3World_RebuildStaticTree( b3WorldId worldId ); + +/// This is for internal testing +B3_API void b3World_EnableSpeculative( b3WorldId worldId, bool flag ); + +/// Dump world to a text file. Saves only awake bodies and associated static bodies. +/// Meshes are saved to binary b3m files. +B3_API void b3World_DumpAwake( b3WorldId worldId ); + +/// Dump world to a text file. Meshes are saved to binary b3m files. +B3_API void b3World_Dump( b3WorldId worldId ); + +/** + * @defgroup recording Recording + * @brief Record and replay world state for debugging. + * @{ + */ + +/// Opaque recording handle. Create with b3CreateRecording, destroy with b3DestroyRecording. +typedef struct b3Recording b3Recording; + +/// Create a recording buffer with an optional initial byte capacity. +/// Pass 0 to use the default (64 KiB). The buffer grows on demand. +/// @return a new recording, owned by the caller +B3_API b3Recording* b3CreateRecording( int byteCapacity ); + +/// Destroy a recording and free its buffer. +/// @param recording may be NULL +B3_API void b3DestroyRecording( b3Recording* recording ); + +/// Get a pointer to the raw recording bytes. +/// Valid until the recording buffer is modified or destroyed. +/// @param recording the recording handle +/// @return pointer to the byte buffer, or NULL if no bytes have been written +B3_API const uint8_t* b3Recording_GetData( const b3Recording* recording ); + +/// Get the number of bytes currently in the recording buffer. +/// @param recording the recording handle +B3_API int b3Recording_GetSize( const b3Recording* recording ); + +/// Begin recording world mutations into the provided buffer. +/// The buffer is reset on each call so a single b3Recording can be reused for multiple sessions. +/// @param worldId the world to record +/// @param recording the recording handle to write into +B3_API void b3World_StartRecording( b3WorldId worldId, b3Recording* recording ); + +/// End the current recording session. Writes the trailing geometry registry and +/// backpatches the header. The buffer remains valid until the recording is destroyed. +/// @param worldId the world currently being recorded +B3_API void b3World_StopRecording( b3WorldId worldId ); + +/// Save the recording buffer to a file. Returns true on success. +/// @param recording the recording to save +/// @param path file path to write +B3_API bool b3SaveRecordingToFile( const b3Recording* recording, const char* path ); + +/// Load a recording from a file. Returns NULL on failure (file not found, wrong magic). +/// The caller owns the returned recording and must destroy it with b3DestroyRecording. +/// @param path file path to read +B3_API b3Recording* b3LoadRecordingFromFile( const char* path ); + +/// Replay a recording from memory and verify it reproduces the same world-state hashes. +/// Stands up a fresh world, restores the seed snapshot, replays every op, and checks each embedded +/// StateHash record. Returns true if replay completed without id mismatches or hash divergences. +/// @param data pointer to recording bytes +/// @param size byte count of the recording +/// @param workerCount reserved for future multithreaded replay; pass 1 for now +B3_API bool b3ValidateReplay( const void* data, int size, int workerCount ); + +/// Opaque incremental replay player with a keyframe ring for O(interval) backward seek. +typedef struct b3RecPlayer b3RecPlayer; + +/// Summary of a recording, read once at open so a viewer can frame and label it. +typedef struct b3RecPlayerInfo +{ + int frameCount; // total recorded steps + int workerCount; // worker count requested for the replay world + float timeStep; // dt of the recorded steps + int subStepCount; // recorded sub-steps + float lengthScale; // length units per meter in effect when recorded + b3AABB bounds; // accumulated world bounds over the recording, zero-extent if unavailable +} b3RecPlayerInfo; + +/// Create a player over a recording. Owns a private copy of the bytes. +/// @param data pointer to recording bytes +/// @param size byte count of the recording +/// @param workerCount worker count for the replay world; pass 1 to match a serial recording. +/// Replaying at a different count re-partitions the constraint graph, so the StateHash check +/// becomes a cross-thread determinism test. Adjustable later with b3RecPlayer_SetWorkerCount. +/// @return a new player, or NULL on bad header or deserialization failure +B3_API b3RecPlayer* b3RecPlayer_Create( const void* data, int size, int workerCount ); + +/// Destroy the player and free all memory. Restores the previous global length scale. +B3_API void b3RecPlayer_Destroy( b3RecPlayer* player ); + +/// Advance one frame: dispatch ops until the next Step completes. +/// @return true when a frame was stepped, false at end-of-recording +B3_API bool b3RecPlayer_StepFrame( b3RecPlayer* player ); + +/// Rewind to frame 0 (in-place restore so the world id stays stable). +B3_API void b3RecPlayer_Restart( b3RecPlayer* player ); + +/// Seek to a specific frame. Forward seek steps op-by-op; backward seek restores +/// the nearest keyframe then re-steps the remaining gap. +B3_API void b3RecPlayer_SeekFrame( b3RecPlayer* player, int targetFrame ); + +/// @return the world currently driven by this player +B3_API b3WorldId b3RecPlayer_GetWorldId( const b3RecPlayer* player ); + +/// @return the last fully-stepped frame index (0 before any step) +B3_API int b3RecPlayer_GetFrame( const b3RecPlayer* player ); + +/// @return total number of recorded frames +B3_API int b3RecPlayer_GetFrameCount( const b3RecPlayer* player ); + +/// @return true when the op stream is exhausted +B3_API bool b3RecPlayer_IsAtEnd( const b3RecPlayer* player ); + +/// @return true when any StateHash mismatch has been detected +B3_API bool b3RecPlayer_HasDiverged( const b3RecPlayer* player ); + +/// @return a summary of the recording read at open: frame count, recorded tuning, and bounds +B3_API b3RecPlayerInfo b3RecPlayer_GetInfo( const b3RecPlayer* player ); + +/// @return the first frame at which replay diverged, or -1 if it has not diverged +B3_API int b3RecPlayer_GetDivergeFrame( const b3RecPlayer* player ); + +/// Set the worker count of the replay world. Clamped to [1, B3_MAX_WORKERS]. Applied to the live +/// world at once and reused whenever the player rebuilds its world on Restart or a backward seek. +/// Replaying at a different count than recorded re-partitions the constraint graph, so the StateHash +/// check becomes a cross-thread determinism test. +B3_API void b3RecPlayer_SetWorkerCount( b3RecPlayer* player, int count ); + +/// Tune the keyframe ring used to speed up backward seeking. A keyframe is a periodic snapshot the +/// player restores from instead of replaying from the start, trading memory for seek speed. +/// @param player the recording player +/// @param budgetBytes memory cap for the kept snapshots; the spacing widens to stay under it +/// @param minIntervalFrames finest spacing between keyframes, in frames +/// A zero budget or a non-positive interval keeps that value. Clears the existing ring, so call +/// b3RecPlayer_Restart afterward to repopulate it under the new policy. +B3_API void b3RecPlayer_SetKeyframePolicy( b3RecPlayer* player, size_t budgetBytes, int minIntervalFrames ); + +/// @return the keyframe memory budget in bytes +B3_API size_t b3RecPlayer_GetKeyframeBudget( const b3RecPlayer* player ); + +/// @return the finest keyframe spacing in frames +B3_API int b3RecPlayer_GetKeyframeMinInterval( const b3RecPlayer* player ); + +/// @return the current keyframe spacing in frames; starts at the min interval and doubles as the +/// ring evicts to stay under budget, so it reflects the effective backward-seek granularity now +B3_API int b3RecPlayer_GetKeyframeInterval( const b3RecPlayer* player ); + +/// @return the memory currently held by keyframe snapshots, in bytes +B3_API size_t b3RecPlayer_GetKeyframeBytes( const b3RecPlayer* player ); + +/// @return the number of bodies tracked in creation order (including holes for destroyed bodies) +B3_API int b3RecPlayer_GetBodyCount( const b3RecPlayer* player ); + +/// Resolve a creation ordinal to the live body id at the current frame. +/// @return the body id, or a null id if that ordinal is out of range or its body is destroyed +B3_API b3BodyId b3RecPlayer_GetBodyId( const b3RecPlayer* player, int index ); + +/// Wire host debug-shape callbacks into the player's replay world so a renderer can build +/// per-shape draw resources (the 3D sample needs this or the replay world draws nothing). +/// Rebuilds the current world under the new callbacks and rewinds to frame 0, so call it +/// once right after b3RecPlayer_Create and re-read the world id afterward. The callbacks +/// persist across Restart and backward seeks, which recreate the world internally. +/// @param player the player to configure +/// @param createDebugShape called when a replayed shape is added; returns a user draw handle +/// @param destroyDebugShape called when a replayed shape is removed; may be NULL +/// @param context user context passed to both callbacks +B3_API void b3RecPlayer_SetDebugShapeCallbacks( b3RecPlayer* player, b3CreateDebugShapeCallback* createDebugShape, + b3DestroyDebugShapeCallback* destroyDebugShape, void* context ); + +/// Draw the spatial queries recorded during the most recently replayed frame, layered on top of the +/// world. Call after b3World_Draw. NULL draw function pointers are skipped. +/// @param player a valid player handle +/// @param draw debug draw callbacks +/// @param queryIndex index of the frame query to draw, or -1 to draw all of them +/// @param selectedIndex index of the query to emphasize (reserved color plus a label), or -1 for none +B3_API void b3RecPlayer_DrawFrameQueries( b3RecPlayer* player, b3DebugDraw* draw, int queryIndex, int selectedIndex ); + +/// The kind of a recorded spatial query, matching the public query and cast functions. +typedef enum b3RecQueryType +{ + b3_recQueryOverlapAABB, + b3_recQueryOverlapShape, + b3_recQueryCastRay, + b3_recQueryCastShape, + b3_recQueryCastRayClosest, + b3_recQueryCastMover, + b3_recQueryCollideMover, +} b3RecQueryType; + +/// A spatial query recorded during a replayed frame, exposed for inspection. +typedef struct b3RecQueryInfo +{ + b3RecQueryType type; + b3QueryFilter filter; + b3AABB aabb; // world-space bounds of the query, swept for casts + b3Pos origin; // query origin (zero for overlap AABB) + b3Vec3 translation; // ray and cast translation + int hitCount; // number of recorded results + uint64_t key; // identity key, the hash of (id, name), 0 if untagged + uint64_t id; // query id, 0 if none + const char* name; // query label, NULL if none +} b3RecQueryInfo; + +/// One result of a recorded spatial query. +typedef struct b3RecQueryHit +{ + b3ShapeId shape; + b3Pos point; + b3Vec3 normal; + float fraction; +} b3RecQueryHit; + +/// @return the number of spatial queries recorded for the most recently replayed frame +B3_API int b3RecPlayer_GetFrameQueryCount( const b3RecPlayer* player ); + +/// Get a recorded query from the most recently replayed frame by index. +B3_API b3RecQueryInfo b3RecPlayer_GetFrameQuery( const b3RecPlayer* player, int index ); + +/// Get one result of a recorded query from the most recently replayed frame. +B3_API b3RecQueryHit b3RecPlayer_GetFrameQueryHit( const b3RecPlayer* player, int queryIndex, int hitIndex ); + +/**@}*/ // recording + +/** @} */ // world + +/** + * @defgroup body Body + * This is the body API. + * @{ + */ + +/// Create a rigid body given a definition. No reference to the definition is retained. So you can create the definition +/// on the stack and pass it as a pointer. +/// @code{.c} +/// b3BodyDef bodyDef = b3DefaultBodyDef(); +/// b3BodyId myBodyId = b3CreateBody(myWorldId, &bodyDef); +/// @endcode +/// @warning This function is locked during callbacks. +B3_API b3BodyId b3CreateBody( b3WorldId worldId, const b3BodyDef* def ); + +/// Destroy a rigid body given an id. This destroys all shapes and joints attached to the body. +/// Do not keep references to the associated shapes and joints. +B3_API void b3DestroyBody( b3BodyId bodyId ); + +/// Body identifier validation. A valid body exists in a world and is non-null. +/// This can be used to detect orphaned ids. Provides validation for up to 64K allocations. +B3_API bool b3Body_IsValid( b3BodyId id ); + +/// Get the body type: static, kinematic, or dynamic +B3_API b3BodyType b3Body_GetType( b3BodyId bodyId ); + +/// Change the body type. This is an expensive operation. This automatically updates the mass +/// properties regardless of the automatic mass setting. +B3_API void b3Body_SetType( b3BodyId bodyId, b3BodyType type ); + +/// Set the body name. Up to B3_BODY_NAME_LENGTH characters including null termination. +B3_API void b3Body_SetName( b3BodyId bodyId, const char* name ); + +/// Get the body name. +B3_API const char* b3Body_GetName( b3BodyId bodyId ); + +/// Set the user data for a body +B3_API void b3Body_SetUserData( b3BodyId bodyId, void* userData ); + +/// Get the user data stored in a body +B3_API void* b3Body_GetUserData( b3BodyId bodyId ); + +/// Get the world position of a body. This is the location of the body origin. +B3_API b3Pos b3Body_GetPosition( b3BodyId bodyId ); + +/// Get the world rotation of a body as a quaternion +B3_API b3Quat b3Body_GetRotation( b3BodyId bodyId ); + +/// Get the world transform of a body. +B3_API b3WorldTransform b3Body_GetTransform( b3BodyId bodyId ); + +/// Set the world transform of a body. This acts as a teleport and is fairly expensive. +/// @note Generally you should create a body with the intended transform. +/// @see b3BodyDef::position and b3BodyDef::rotation +B3_API void b3Body_SetTransform( b3BodyId bodyId, b3Pos position, b3Quat rotation ); + +/// Get a local point on a body given a world point +B3_API b3Vec3 b3Body_GetLocalPoint( b3BodyId bodyId, b3Pos worldPoint ); + +/// Get a world point on a body given a local point +B3_API b3Pos b3Body_GetWorldPoint( b3BodyId bodyId, b3Vec3 localPoint ); + +/// Get a local vector on a body given a world vector +B3_API b3Vec3 b3Body_GetLocalVector( b3BodyId bodyId, b3Vec3 worldVector ); + +/// Get a world vector on a body given a local vector +B3_API b3Vec3 b3Body_GetWorldVector( b3BodyId bodyId, b3Vec3 localVector ); + +/// Get the linear velocity of a body's center of mass. Usually in meters per second. +B3_API b3Vec3 b3Body_GetLinearVelocity( b3BodyId bodyId ); + +/// Get the angular velocity of a body in radians per second +B3_API b3Vec3 b3Body_GetAngularVelocity( b3BodyId bodyId ); + +/// Set the linear velocity of a body. Usually in meters per second. +B3_API void b3Body_SetLinearVelocity( b3BodyId bodyId, b3Vec3 linearVelocity ); + +/// Set the angular velocity of a body in radians per second +B3_API void b3Body_SetAngularVelocity( b3BodyId bodyId, b3Vec3 angularVelocity ); + +/// Set the velocity to reach the given transform after a given time step. +/// The result will be close but maybe not exact. This is meant for kinematic bodies. +/// The target is not applied if the velocity would be below the sleep threshold. +/// This will optionally wake the body if asleep, but only if the movement is significant. +B3_API void b3Body_SetTargetTransform( b3BodyId bodyId, b3WorldTransform target, float timeStep, bool wake ); + +/// Get the linear velocity of a local point attached to a body. Usually in meters per second. +B3_API b3Vec3 b3Body_GetLocalPointVelocity( b3BodyId bodyId, b3Vec3 localPoint ); + +/// Get the linear velocity of a world point attached to a body. Usually in meters per second. +B3_API b3Vec3 b3Body_GetWorldPointVelocity( b3BodyId bodyId, b3Pos worldPoint ); + +/// Apply a force at a world point. If the force is not applied at the center of mass, +/// it will generate a torque and affect the angular velocity. This optionally wakes up the body. +/// The force is ignored if the body is not awake. +/// @param bodyId The body id +/// @param force The world force vector, usually in newtons (N) +/// @param point The world position of the point of application +/// @param wake Option to wake up the body +B3_API void b3Body_ApplyForce( b3BodyId bodyId, b3Vec3 force, b3Pos point, bool wake ); + +/// Apply a force to the center of mass. This optionally wakes up the body. +/// The force is ignored if the body is not awake. +/// @param bodyId The body id +/// @param force the world force vector, usually in newtons (N). +/// @param wake also wake up the body +B3_API void b3Body_ApplyForceToCenter( b3BodyId bodyId, b3Vec3 force, bool wake ); + +/// Apply a torque. This affects the angular velocity without affecting the linear velocity. +/// This optionally wakes the body. The torque is ignored if the body is not awake. +/// @param bodyId The body id +/// @param torque the world torque vector, usually in N*m. +/// @param wake also wake up the body +B3_API void b3Body_ApplyTorque( b3BodyId bodyId, b3Vec3 torque, bool wake ); + +/// Apply an impulse at a point. This immediately modifies the velocity. +/// It also modifies the angular velocity if the point of application +/// is not at the center of mass. This optionally wakes the body. +/// The impulse is ignored if the body is not awake. +/// @param bodyId The body id +/// @param impulse the world impulse vector, usually in N*s or kg*m/s. +/// @param point the world position of the point of application. +/// @param wake also wake up the body +/// @warning This should be used for one-shot impulses. If you need a steady force, +/// use a force instead, which will work better with the sub-stepping solver. +B3_API void b3Body_ApplyLinearImpulse( b3BodyId bodyId, b3Vec3 impulse, b3Pos point, bool wake ); + +/// Apply an impulse to the center of mass. This immediately modifies the velocity. +/// The impulse is ignored if the body is not awake. This optionally wakes the body. +/// @param bodyId The body id +/// @param impulse the world impulse vector, usually in N*s or kg*m/s. +/// @param wake also wake up the body +/// @warning This should be used for one-shot impulses. If you need a steady force, +/// use a force instead, which will work better with the sub-stepping solver. +B3_API void b3Body_ApplyLinearImpulseToCenter( b3BodyId bodyId, b3Vec3 impulse, bool wake ); + +/// Apply an angular impulse in world space. The impulse is ignored if the body is not awake. +/// This optionally wakes the body. +/// @param bodyId The body id +/// @param impulse the world angular impulse vector, usually in units of kg*m*m/s +/// @param wake also wake up the body +/// @warning This should be used for one-shot impulses. If you need a steady torque, +/// use a torque instead, which will work better with the sub-stepping solver. +B3_API void b3Body_ApplyAngularImpulse( b3BodyId bodyId, b3Vec3 impulse, bool wake ); + +/// Get the mass of the body, usually in kilograms +B3_API float b3Body_GetMass( b3BodyId bodyId ); + +/// Get the rotational inertia of the body in local space, usually in kg*m^2 +B3_API b3Matrix3 b3Body_GetLocalRotationalInertia( b3BodyId bodyId ); + +/// Get the inverse mass of the body, usually in 1/kilograms +B3_API float b3Body_GetInverseMass( b3BodyId bodyId ); + +/// Get the inverse rotational inertia of the body in world space, usually in 1/kg*m^2 +B3_API b3Matrix3 b3Body_GetWorldInverseRotationalInertia( b3BodyId bodyId ); + +/// Get the center of mass position of the body in local space +B3_API b3Vec3 b3Body_GetLocalCenterOfMass( b3BodyId bodyId ); + +/// Get the center of mass position of the body in world space +B3_API b3Pos b3Body_GetWorldCenterOfMass( b3BodyId bodyId ); + +/// Override the body's mass properties. Normally this is computed automatically using the +/// shape geometry and density. This information is lost if a shape is added or removed or if the +/// body type changes. +B3_API void b3Body_SetMassData( b3BodyId bodyId, b3MassData massData ); + +/// Get the mass data for a body +B3_API b3MassData b3Body_GetMassData( b3BodyId bodyId ); + +/// This updates the mass properties to the sum of the mass properties of the shapes. +/// This normally does not need to be called unless you called SetMassData to override +/// the mass and you later want to reset the mass. +/// You may also use this when automatic mass computation has been disabled. +/// You should call this regardless of body type. +B3_API void b3Body_ApplyMassFromShapes( b3BodyId bodyId ); + +/// Adjust the linear damping. Normally this is set in b3BodyDef before creation. +B3_API void b3Body_SetLinearDamping( b3BodyId bodyId, float linearDamping ); + +/// Get the current linear damping. +B3_API float b3Body_GetLinearDamping( b3BodyId bodyId ); + +/// Adjust the angular damping. Normally this is set in b3BodyDef before creation. +B3_API void b3Body_SetAngularDamping( b3BodyId bodyId, float angularDamping ); + +/// Get the current angular damping. +B3_API float b3Body_GetAngularDamping( b3BodyId bodyId ); + +/// Adjust the gravity scale. Normally this is set in b3BodyDef before creation. +/// @see b3BodyDef::gravityScale +B3_API void b3Body_SetGravityScale( b3BodyId bodyId, float gravityScale ); + +/// Get the current gravity scale +B3_API float b3Body_GetGravityScale( b3BodyId bodyId ); + +/// @return true if this body is awake +B3_API bool b3Body_IsAwake( b3BodyId bodyId ); + +/// Wake a body from sleep. This wakes the entire island the body is touching. +/// @warning Putting a body to sleep will put the entire island of bodies touching this body to sleep, +/// which can be expensive and possibly unintuitive. +B3_API void b3Body_SetAwake( b3BodyId bodyId, bool awake ); + +/// Enable or disable sleeping for this body. If sleeping is disabled the body will wake. +B3_API void b3Body_EnableSleep( b3BodyId bodyId, bool enableSleep ); + +/// Returns true if sleeping is enabled for this body +B3_API bool b3Body_IsSleepEnabled( b3BodyId bodyId ); + +/// Set the sleep threshold, usually in meters per second +B3_API void b3Body_SetSleepThreshold( b3BodyId bodyId, float sleepThreshold ); + +/// Get the sleep threshold, usually in meters per second. +B3_API float b3Body_GetSleepThreshold( b3BodyId bodyId ); + +/// Returns true if this body is enabled +B3_API bool b3Body_IsEnabled( b3BodyId bodyId ); + +/// Disable a body by removing it completely from the simulation. This is expensive. +B3_API void b3Body_Disable( b3BodyId bodyId ); + +/// Enable a body by adding it to the simulation. This is expensive. +B3_API void b3Body_Enable( b3BodyId bodyId ); + +/// Set the motion locks on this body. +B3_API void b3Body_SetMotionLocks( b3BodyId bodyId, b3MotionLocks locks ); + +/// Get the motion locks for this body. +B3_API b3MotionLocks b3Body_GetMotionLocks( b3BodyId bodyId ); + +/// Set this body to be a bullet. A bullet does continuous collision detection +/// against dynamic bodies (but not other bullets). +B3_API void b3Body_SetBullet( b3BodyId bodyId, bool flag ); + +/// Is this body a bullet? +B3_API bool b3Body_IsBullet( b3BodyId bodyId ); + +/// Enable or disable contact recycling for this body. Contact recycling is a performance optimization +/// that reuses contact manifolds when bodies move slightly. Disabling it can avoid ghost collisions +/// on characters at the cost of higher per-step work. Existing contacts retain their prior setting; +/// only contacts created after this call see the new value. +/// @see b3BodyDef::enableContactRecycling +B3_API void b3Body_EnableContactRecycling( b3BodyId bodyId, bool flag ); + +/// Is contact recycling enabled on this body? +B3_API bool b3Body_IsContactRecyclingEnabled( b3BodyId bodyId ); + +/// Enable/disable hit events on all shapes +/// @see b3ShapeDef::enableHitEvents +B3_API void b3Body_EnableHitEvents( b3BodyId bodyId, bool enableHitEvents ); + +/// Get the world that owns this body +B3_API b3WorldId b3Body_GetWorld( b3BodyId bodyId ); + +/// Get the number of shapes on this body +B3_API int b3Body_GetShapeCount( b3BodyId bodyId ); + +/// Get the shape ids for all shapes on this body, up to the provided capacity. +/// @returns the number of shape ids stored in the user array +B3_API int b3Body_GetShapes( b3BodyId bodyId, b3ShapeId* shapeArray, int capacity ); + +/// Get the number of joints on this body +B3_API int b3Body_GetJointCount( b3BodyId bodyId ); + +/// Get the joint ids for all joints on this body, up to the provided capacity +/// @returns the number of joint ids stored in the user array +B3_API int b3Body_GetJoints( b3BodyId bodyId, b3JointId* jointArray, int capacity ); + +/// Get the maximum capacity required for retrieving all the touching contacts on a body +B3_API int b3Body_GetContactCapacity( b3BodyId bodyId ); + +/// Get the touching contact data for a body +B3_API int b3Body_GetContactData( b3BodyId bodyId, b3ContactData* contactData, int capacity ); + +/// Get the current world AABB that contains all the attached shapes. Note that this may not encompass the body origin. +/// If there are no shapes attached then the returned AABB is empty and centered on the body origin. +B3_API b3AABB b3Body_ComputeAABB( b3BodyId bodyId ); + +/// Get the closest point on a body to a world target. +B3_API float b3Body_GetClosestPoint( b3BodyId bodyId, b3Vec3* result, b3Vec3 target ); + +/// Cast a ray at a specific body using a specified body transform. +B3_API b3BodyCastResult b3Body_CastRay( b3BodyId bodyId, b3Pos origin, b3Vec3 translation, b3QueryFilter filter, + float maxFraction, b3WorldTransform bodyTransform ); + +/// Cast a shape at a specific body using a specified body transform. +B3_API b3BodyCastResult b3Body_CastShape( b3BodyId bodyId, b3Pos origin, const b3ShapeProxy* proxy, b3Vec3 translation, + b3QueryFilter filter, float maxFraction, bool canEncroach, + b3WorldTransform bodyTransform ); + +/// Overlap a shape with a specific body using a specified body transform. +B3_API bool b3Body_OverlapShape( b3BodyId bodyId, b3Pos origin, const b3ShapeProxy* proxy, b3QueryFilter filter, + b3WorldTransform bodyTransform ); + +/// Collide a character mover with a specific body using a specified body transform. +B3_API int b3Body_CollideMover( b3BodyId bodyId, b3BodyPlaneResult* bodyPlanes, int planeCapacity, b3Pos origin, const b3Capsule* mover, + b3QueryFilter filter, b3WorldTransform bodyTransform ); + +/** @} */ // body + +/** + * @defgroup shape Shape + * Functions to create, destroy, and access. + * Shapes bind raw geometry to bodies and hold material properties including friction and restitution. + * @{ + */ + +/// Create a circle shape and attach it to a body. The shape definition and geometry are fully cloned. +/// Contacts are not created until the next time step. +/// @return the shape id for accessing the shape +B3_API b3ShapeId b3CreateSphereShape( b3BodyId bodyId, const b3ShapeDef* def, const b3Sphere* sphere ); + +/// Create a capsule shape and attach it to a body. The shape definition and geometry are fully cloned. +/// Contacts are not created until the next time step. +/// @return the shape id for accessing the shape +B3_API b3ShapeId b3CreateCapsuleShape( b3BodyId bodyId, const b3ShapeDef* def, const b3Capsule* capsule ); + +/// Create a convex hull shape and attach it to a body. The shape definition is fully cloned. Contacts are not created +/// until the next time step. +/// @return the shape id for accessing the shape +B3_API b3ShapeId b3CreateHullShape( b3BodyId bodyId, const b3ShapeDef* def, const b3HullData* hull ); + +/// Create a convex hull shape and attach it to a body. The hull is cloned then transformed with scale applied first. +/// Use this for non-uniform or mirrored scale or a baked local transform. The baked result is shared through the +/// world hull database. The shape definition and geometry are fully cloned. Contacts are not created until the next time step. +/// @return the shape id for accessing the shape +B3_API b3ShapeId b3CreateTransformedHullShape( b3BodyId bodyId, const b3ShapeDef* def, const b3HullData* hull, + b3Transform transform, b3Vec3 scale ); + +/// Create a mesh hull shape and attach it to a body. The shape definition is fully cloned but the mesh is not. +/// Contacts are not created until the next time step. +/// Mesh collision only creates contacts on static bodies. +/// @warning this holds reference to the input mesh data which must remain valid for the lifetime of this shape +/// @return the shape id for accessing the shape +B3_API b3ShapeId b3CreateMeshShape( b3BodyId bodyId, const b3ShapeDef* def, const b3MeshData* mesh, b3Vec3 scale ); + +/// Create a height-field shape and attach it to a body. The shape definition is fully cloned but the height field is not. +/// Contacts are not created until the next time step. +/// Height field is only allowed on static bodies. +/// @warning this holds reference to the input height field which must remain valid for the lifetime of this shape +/// @return the shape id for accessing the shape +B3_API b3ShapeId b3CreateHeightFieldShape( b3BodyId bodyId, const b3ShapeDef* def, const b3HeightFieldData* heightField ); + +/// Compound shapes are only allowed on static bodies. +B3_API b3ShapeId b3CreateCompoundShape( b3BodyId bodyId, b3ShapeDef* def, const b3CompoundData* compound ); + +/// Destroy a shape. You may defer the body mass update which can improve performance if several shapes on a +/// body are destroyed at once. +/// @see b3Body_ApplyMassFromShapes +B3_API void b3DestroyShape( b3ShapeId shapeId, bool updateBodyMass ); + +/// Shape identifier validation. Provides validation for up to 64K allocations. +B3_API bool b3Shape_IsValid( b3ShapeId id ); + +/// Get the type of a shape +B3_API b3ShapeType b3Shape_GetType( b3ShapeId shapeId ); + +/// Get the id of the body that a shape is attached to +B3_API b3BodyId b3Shape_GetBody( b3ShapeId shapeId ); + +/// Get the world that owns this shape +B3_API b3WorldId b3Shape_GetWorld( b3ShapeId shapeId ); + +/// Returns true if the shape is a sensor +B3_API bool b3Shape_IsSensor( b3ShapeId shapeId ); + +/// Set the user data for a shape +B3_API void b3Shape_SetUserData( b3ShapeId shapeId, void* userData ); + +/// Get the user data for a shape. This is useful when you get a shape id +/// from an event or query. +B3_API void* b3Shape_GetUserData( b3ShapeId shapeId ); + +/// Set the mass density of a shape, usually in kg/m^3. +/// This will optionally update the mass properties on the parent body. +/// @see b3ShapeDef::density, b3Body_ApplyMassFromShapes +B3_API void b3Shape_SetDensity( b3ShapeId shapeId, float density, bool updateBodyMass ); + +/// Get the density of a shape, usually in kg/m^3 +B3_API float b3Shape_GetDensity( b3ShapeId shapeId ); + +/// Set the friction on a shape +B3_API void b3Shape_SetFriction( b3ShapeId shapeId, float friction ); + +/// Get the friction of a shape +B3_API float b3Shape_GetFriction( b3ShapeId shapeId ); + +/// Set the shape restitution (bounciness) +B3_API void b3Shape_SetRestitution( b3ShapeId shapeId, float restitution ); + +/// Get the shape restitution +B3_API float b3Shape_GetRestitution( b3ShapeId shapeId ); + +/// Set the shape base surface material. Does not change per triangle materials. +B3_API void b3Shape_SetSurfaceMaterial( b3ShapeId shapeId, b3SurfaceMaterial surfaceMaterial ); + +/// Get the base shape surface material. +B3_API b3SurfaceMaterial b3Shape_GetSurfaceMaterial( b3ShapeId shapeId ); + +/// Get the number of mesh surface materials. +B3_API int b3Shape_GetMeshMaterialCount( b3ShapeId shapeId ); + +/// Set a surface material for a mesh shape. +B3_API void b3Shape_SetMeshMaterial( b3ShapeId shapeId, b3SurfaceMaterial surfaceMaterial, int index ); + +/// Get a surface material for a mesh shape +B3_API b3SurfaceMaterial b3Shape_GetMeshSurfaceMaterial( b3ShapeId shapeId, int index ); + +/// Get the shape filter +B3_API b3Filter b3Shape_GetFilter( b3ShapeId shapeId ); + +/// Set the current filter. This is almost as expensive as recreating the shape. +/// @see b3ShapeDef::filter +/// @param shapeId the shape +/// @param filter the new filter +/// @param invokeContacts if true then the shape will have all contacts recomputed the next time step (expensive) +B3_API void b3Shape_SetFilter( b3ShapeId shapeId, b3Filter filter, bool invokeContacts ); + +/// Enable sensor events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. +/// @see b3ShapeDef::isSensor +B3_API void b3Shape_EnableSensorEvents( b3ShapeId shapeId, bool flag ); + +/// Returns true if sensor events are enabled +B3_API bool b3Shape_AreSensorEventsEnabled( b3ShapeId shapeId ); + +/// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. +/// @see b3ShapeDef::enableContactEvents +B3_API void b3Shape_EnableContactEvents( b3ShapeId shapeId, bool flag ); + +/// Returns true if contact events are enabled +B3_API bool b3Shape_AreContactEventsEnabled( b3ShapeId shapeId ); + +/// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive +/// and must be carefully handled due to multithreading. Ignored for sensors. +/// @see b3PreSolveFcn +B3_API void b3Shape_EnablePreSolveEvents( b3ShapeId shapeId, bool flag ); + +/// Returns true if pre-solve events are enabled +B3_API bool b3Shape_ArePreSolveEventsEnabled( b3ShapeId shapeId ); + +/// Enable contact hit events for this shape. Ignored for sensors. +/// @see b3WorldDef.hitEventThreshold +B3_API void b3Shape_EnableHitEvents( b3ShapeId shapeId, bool flag ); + +/// Returns true if hit events are enabled +B3_API bool b3Shape_AreHitEventsEnabled( b3ShapeId shapeId ); + +/// Ray cast a shape directly. The ray runs from origin to origin + translation and the hit point +/// comes back as a world position, so the cast stays precise far from the world origin. +B3_API b3WorldCastOutput b3Shape_RayCast( b3ShapeId shapeId, b3Pos origin, b3Vec3 translation ); + +/// Get a copy of the shape's sphere. Asserts the type is correct. +B3_API b3Sphere b3Shape_GetSphere( b3ShapeId shapeId ); + +/// Get a copy of the shape's capsule. Asserts the type is correct. +B3_API b3Capsule b3Shape_GetCapsule( b3ShapeId shapeId ); + +/// Get the shape's convex hull. Asserts the type is correct. +B3_API const b3HullData* b3Shape_GetHull( b3ShapeId shapeId ); + +/// Get the shape's mesh. Asserts the type is correct. +B3_API b3Mesh b3Shape_GetMesh( b3ShapeId shapeId ); + +/// Get the shape's height field. Asserts the type is correct. +B3_API const b3HeightFieldData* b3Shape_GetHeightField( b3ShapeId shapeId ); + +/// Allows you to change a shape to be a sphere or update the current sphere. +/// This does not modify the mass properties. +/// @see b3Body_ApplyMassFromShapes +B3_API void b3Shape_SetSphere( b3ShapeId shapeId, const b3Sphere* sphere ); + +/// Allows you to change a shape to be a capsule or update the current capsule. +/// This does not modify the mass properties. +/// @see b3Body_ApplyMassFromShapes +B3_API void b3Shape_SetCapsule( b3ShapeId shapeId, const b3Capsule* capsule ); + +/// Allows you to change a shape to be a hull or update the current hull. +/// This does not modify the mass properties. +/// @see b3Body_ApplyMassFromShapes +B3_API void b3Shape_SetHull( b3ShapeId shapeId, const b3HullData* hull ); + +/// Allows you to change a shape to be a mesh or update the current mesh. +/// This does not modify the mass properties. +/// @see b3Body_ApplyMassFromShapes +B3_API void b3Shape_SetMesh( b3ShapeId shapeId, const b3MeshData* meshData, b3Vec3 scale ); + +/// Get the maximum capacity required for retrieving all the touching contacts on a shape +B3_API int b3Shape_GetContactCapacity( b3ShapeId shapeId ); + +/// Get the touching contact data for a shape. The provided shapeId will be either shapeIdA or shapeIdB on the contact data. +/// @note Box3D uses speculative collision so some contact points may be separated. +/// @returns the number of elements filled in the provided array +/// @warning do not ignore the return value, it specifies the valid number of elements +B3_API int b3Shape_GetContactData( b3ShapeId shapeId, b3ContactData* contactData, int capacity ); + +/// Get the maximum capacity required for retrieving all the overlapped shapes on a sensor shape. +/// This returns 0 if the provided shape is not a sensor. +/// @param shapeId the id of a sensor shape +/// @returns the required capacity to get all the overlaps in b3Shape_GetSensorOverlaps +B3_API int b3Shape_GetSensorCapacity( b3ShapeId shapeId ); + +/// Get the overlap data for a sensor shape. +/// @param shapeId the id of a sensor shape +/// @param visitorIds a user allocated array that is filled with the overlapping shapes (visitors) +/// @param capacity the capacity of overlappedShapes +/// @returns the number of elements filled in the provided array +/// @warning do not ignore the return value, it specifies the valid number of elements +/// @warning overlaps may contain destroyed shapes so use b3Shape_IsValid to confirm each overlap +B3_API int b3Shape_GetSensorData( b3ShapeId shapeId, b3ShapeId* visitorIds, int capacity ); + +/// Get the current world AABB +B3_API b3AABB b3Shape_GetAABB( b3ShapeId shapeId ); + +/// Compute the mass data for a shape +B3_API b3MassData b3Shape_ComputeMassData( b3ShapeId shapeId ); + +/// Get the closest point on a shape to a target point. Target and result are in world space. +B3_API b3Vec3 b3Shape_GetClosestPoint( b3ShapeId shapeId, b3Vec3 target ); + +/// Apply a wind force to the body for this shape using the density of air. This considers +/// the projected area of the shape in the wind direction. This also considers +/// the relative velocity of the shape. +/// @param shapeId the shape id +/// @param wind the wind velocity in world space +/// @param drag the drag coefficient, the force that opposes the relative velocity +/// @param lift the lift coefficient, the force that is perpendicular to the relative velocity +/// @param maxSpeed the maximum relative speed. Speed cap is necessary for stability. Typically 10m/s or less. +/// @param wake should this wake the body +B3_API void b3Shape_ApplyWind( b3ShapeId shapeId, b3Vec3 wind, float drag, float lift, float maxSpeed, bool wake ); + +/** @} */ // shape + +/** + * @defgroup joint Joint + * @brief Joints allow you to connect rigid bodies together while allowing various forms of relative motions. + * @{ + */ + +/// Destroy a joint +B3_API void b3DestroyJoint( b3JointId jointId, bool wakeAttached ); + +/// Joint identifier validation. Provides validation for up to 64K allocations. +B3_API bool b3Joint_IsValid( b3JointId id ); + +/// Get the joint type +B3_API b3JointType b3Joint_GetType( b3JointId jointId ); + +/// Get body A id on a joint +B3_API b3BodyId b3Joint_GetBodyA( b3JointId jointId ); + +/// Get body B id on a joint +B3_API b3BodyId b3Joint_GetBodyB( b3JointId jointId ); + +/// Get the world that owns this joint +B3_API b3WorldId b3Joint_GetWorld( b3JointId jointId ); + +/// Set the local frame on bodyA +B3_API void b3Joint_SetLocalFrameA( b3JointId jointId, b3Transform localFrame ); + +/// Get the local frame on bodyA +B3_API b3Transform b3Joint_GetLocalFrameA( b3JointId jointId ); + +/// Set the local frame on bodyB +B3_API void b3Joint_SetLocalFrameB( b3JointId jointId, b3Transform localFrame ); + +/// Get the local frame on bodyB +B3_API b3Transform b3Joint_GetLocalFrameB( b3JointId jointId ); + +/// Toggle collision between connected bodies +B3_API void b3Joint_SetCollideConnected( b3JointId jointId, bool shouldCollide ); + +/// Is collision allowed between connected bodies? +B3_API bool b3Joint_GetCollideConnected( b3JointId jointId ); + +/// Set the user data on a joint +B3_API void b3Joint_SetUserData( b3JointId jointId, void* userData ); + +/// Get the user data on a joint +B3_API void* b3Joint_GetUserData( b3JointId jointId ); + +/// Wake the bodies connect to this joint +B3_API void b3Joint_WakeBodies( b3JointId jointId ); + +/// Get the current constraint force for this joint +B3_API b3Vec3 b3Joint_GetConstraintForce( b3JointId jointId ); + +/// Get the current constraint torque for this joint +B3_API b3Vec3 b3Joint_GetConstraintTorque( b3JointId jointId ); + +/// Get the current linear separation error for this joint. Does not consider admissible movement. Usually in meters. +B3_API float b3Joint_GetLinearSeparation( b3JointId jointId ); + +/// Get the current angular separation error for this joint. Does not consider admissible movement. Usually in radians. +B3_API float b3Joint_GetAngularSeparation( b3JointId jointId ); + +/// Set the joint constraint tuning. Advanced feature. +/// @param jointId the joint +/// @param hertz the stiffness in Hertz (cycles per second) +/// @param dampingRatio the non-dimensional damping ratio (one for critical damping) +B3_API void b3Joint_SetConstraintTuning( b3JointId jointId, float hertz, float dampingRatio ); + +/// Get the joint constraint tuning. Advanced feature. +B3_API void b3Joint_GetConstraintTuning( b3JointId jointId, float* hertz, float* dampingRatio ); + +/// Set the force threshold for joint events (Newtons) +B3_API void b3Joint_SetForceThreshold( b3JointId jointId, float threshold ); + +/// Get the force threshold for joint events (Newtons) +B3_API float b3Joint_GetForceThreshold( b3JointId jointId ); + +/// Set the torque threshold for joint events (N-m) +B3_API void b3Joint_SetTorqueThreshold( b3JointId jointId, float threshold ); + +/// Get the torque threshold for joint events (N-m) +B3_API float b3Joint_GetTorqueThreshold( b3JointId jointId ); + +/** + * @defgroup parallel_joint Parallel Joint + * @brief Functions for the parallel joint. + * @{ + */ + +/// Create a parallel joint +/// @see b3ParallelJointDef for details +B3_API b3JointId b3CreateParallelJoint( b3WorldId worldId, const b3ParallelJointDef* def ); + +/// Set the spring stiffness in Hertz +B3_API void b3ParallelJoint_SetSpringHertz( b3JointId jointId, float hertz ); + +/// Set the spring damping ratio, non-dimensional +B3_API void b3ParallelJoint_SetSpringDampingRatio( b3JointId jointId, float dampingRatio ); + +/// Get the spring Hertz +B3_API float b3ParallelJoint_GetSpringHertz( b3JointId jointId ); + +/// Get the spring damping ratio +B3_API float b3ParallelJoint_GetSpringDampingRatio( b3JointId jointId ); + +/// Set the maximum spring torque, usually in newton-meters +B3_API void b3ParallelJoint_SetMaxTorque( b3JointId jointId, float force ); + +/// Get the maximum spring torque, usually in newton-meters +B3_API float b3ParallelJoint_GetMaxTorque( b3JointId jointId ); + +/** @} */ // parallel_joint + +/** + * @defgroup distance_joint Distance Joint + * @brief Functions for the distance joint. + * @{ + */ + +/// Create a distance joint +/// @see b3DistanceJointDef for details +B3_API b3JointId b3CreateDistanceJoint( b3WorldId worldId, const b3DistanceJointDef* def ); + +/// Set the rest length of a distance joint +/// @param jointId The id for a distance joint +/// @param length The new distance joint length +B3_API void b3DistanceJoint_SetLength( b3JointId jointId, float length ); + +/// Get the rest length of a distance joint +B3_API float b3DistanceJoint_GetLength( b3JointId jointId ); + +/// Enable/disable the distance joint spring. When disabled the distance joint is rigid. +B3_API void b3DistanceJoint_EnableSpring( b3JointId jointId, bool enableSpring ); + +/// Is the distance joint spring enabled? +B3_API bool b3DistanceJoint_IsSpringEnabled( b3JointId jointId ); + +/// Set the force range for the spring. +B3_API void b3DistanceJoint_SetSpringForceRange( b3JointId jointId, float lowerForce, float upperForce ); + +/// Get the force range for the spring. +B3_API void b3DistanceJoint_GetSpringForceRange( b3JointId jointId, float* lowerForce, float* upperForce ); + +/// Set the spring stiffness in Hertz +B3_API void b3DistanceJoint_SetSpringHertz( b3JointId jointId, float hertz ); + +/// Set the spring damping ratio, non-dimensional +B3_API void b3DistanceJoint_SetSpringDampingRatio( b3JointId jointId, float dampingRatio ); + +/// Get the spring Hertz +B3_API float b3DistanceJoint_GetSpringHertz( b3JointId jointId ); + +/// Get the spring damping ratio +B3_API float b3DistanceJoint_GetSpringDampingRatio( b3JointId jointId ); + +/// Enable joint limit. The limit only works if the joint spring is enabled. Otherwise the joint is rigid +/// and the limit has no effect. +B3_API void b3DistanceJoint_EnableLimit( b3JointId jointId, bool enableLimit ); + +/// Is the distance joint limit enabled? +B3_API bool b3DistanceJoint_IsLimitEnabled( b3JointId jointId ); + +/// Set the minimum and maximum length parameters of a distance joint +B3_API void b3DistanceJoint_SetLengthRange( b3JointId jointId, float minLength, float maxLength ); + +/// Get the distance joint minimum length +B3_API float b3DistanceJoint_GetMinLength( b3JointId jointId ); + +/// Get the distance joint maximum length +B3_API float b3DistanceJoint_GetMaxLength( b3JointId jointId ); + +/// Get the current length of a distance joint +B3_API float b3DistanceJoint_GetCurrentLength( b3JointId jointId ); + +/// Enable/disable the distance joint motor +B3_API void b3DistanceJoint_EnableMotor( b3JointId jointId, bool enableMotor ); + +/// Is the distance joint motor enabled? +B3_API bool b3DistanceJoint_IsMotorEnabled( b3JointId jointId ); + +/// Set the distance joint motor speed, usually in meters per second +B3_API void b3DistanceJoint_SetMotorSpeed( b3JointId jointId, float motorSpeed ); + +/// Get the distance joint motor speed, usually in meters per second +B3_API float b3DistanceJoint_GetMotorSpeed( b3JointId jointId ); + +/// Set the distance joint maximum motor force, usually in newtons +B3_API void b3DistanceJoint_SetMaxMotorForce( b3JointId jointId, float force ); + +/// Get the distance joint maximum motor force, usually in newtons +B3_API float b3DistanceJoint_GetMaxMotorForce( b3JointId jointId ); + +/// Get the distance joint current motor force, usually in newtons +B3_API float b3DistanceJoint_GetMotorForce( b3JointId jointId ); + +/** @} */ // distance_joint + +/** + * @defgroup motor_joint Motor Joint + * @brief Functions for the motor joint. + * + * The motor joint is designed to control the movement of a body while still being + * responsive to collisions. A spring controls the position and rotation. A velocity motor + * can be used to control velocity and allows for friction in top-down games. Both types + * of control can be combined. For example, you can have a spring with friction. + * Position and velocity control have force and torque limits. + * @{ + */ + +/// Create a motor joint +/// @see b3MotorJointDef for details +B3_API b3JointId b3CreateMotorJoint( b3WorldId worldId, const b3MotorJointDef* def ); + +/// Set the desired relative linear velocity in meters per second +B3_API void b3MotorJoint_SetLinearVelocity( b3JointId jointId, b3Vec3 velocity ); + +/// Get the desired relative linear velocity in meters per second +B3_API b3Vec3 b3MotorJoint_GetLinearVelocity( b3JointId jointId ); + +/// Set the desired relative angular velocity in radians per second +B3_API void b3MotorJoint_SetAngularVelocity( b3JointId jointId, b3Vec3 velocity ); + +/// Get the desired relative angular velocity in radians per second +B3_API b3Vec3 b3MotorJoint_GetAngularVelocity( b3JointId jointId ); + +/// Set the motor joint maximum force, usually in newtons +B3_API void b3MotorJoint_SetMaxVelocityForce( b3JointId jointId, float maxForce ); + +/// Get the motor joint maximum force, usually in newtons +B3_API float b3MotorJoint_GetMaxVelocityForce( b3JointId jointId ); + +/// Set the motor joint maximum torque, usually in newton-meters +B3_API void b3MotorJoint_SetMaxVelocityTorque( b3JointId jointId, float maxTorque ); + +/// Get the motor joint maximum torque, usually in newton-meters +B3_API float b3MotorJoint_GetMaxVelocityTorque( b3JointId jointId ); + +/// Set the spring linear hertz stiffness +B3_API void b3MotorJoint_SetLinearHertz( b3JointId jointId, float hertz ); + +/// Get the spring linear hertz stiffness +B3_API float b3MotorJoint_GetLinearHertz( b3JointId jointId ); + +/// Set the spring linear damping ratio. Use 1.0 for critical damping. +B3_API void b3MotorJoint_SetLinearDampingRatio( b3JointId jointId, float damping ); + +/// Get the spring linear damping ratio. +B3_API float b3MotorJoint_GetLinearDampingRatio( b3JointId jointId ); + +/// Set the spring angular hertz stiffness +B3_API void b3MotorJoint_SetAngularHertz( b3JointId jointId, float hertz ); + +/// Get the spring angular hertz stiffness +B3_API float b3MotorJoint_GetAngularHertz( b3JointId jointId ); + +/// Set the spring angular damping ratio. Use 1.0 for critical damping. +B3_API void b3MotorJoint_SetAngularDampingRatio( b3JointId jointId, float damping ); + +/// Get the spring angular damping ratio. +B3_API float b3MotorJoint_GetAngularDampingRatio( b3JointId jointId ); + +/// Set the maximum spring force in newtons. +B3_API void b3MotorJoint_SetMaxSpringForce( b3JointId jointId, float maxForce ); + +/// Get the maximum spring force in newtons. +B3_API float b3MotorJoint_GetMaxSpringForce( b3JointId jointId ); + +/// Set the maximum spring torque in newtons * meters +B3_API void b3MotorJoint_SetMaxSpringTorque( b3JointId jointId, float maxTorque ); + +/// Get the maximum spring torque in newtons * meters +B3_API float b3MotorJoint_GetMaxSpringTorque( b3JointId jointId ); + +/**@}*/ // motor_joint + +/** + * @defgroup filter_joint Filter Joint + * @brief Functions for the filter joint. + * + * The filter joint is used to disable collision between two bodies. As a side effect of being a joint, it also + * keeps the two bodies in the same simulation island. + * @{ + */ + +/// Create a filter joint. +/// @see b3FilterJointDef for details +B3_API b3JointId b3CreateFilterJoint( b3WorldId worldId, const b3FilterJointDef* def ); + +/**@}*/ // filter_joint + +/** + * @defgroup prismatic_joint Prismatic Joint + * @brief A prismatic joint allows for translation along a single axis with no rotation. + * + * The prismatic joint is useful for things like pistons and moving platforms, where you want a body to translate + * along an axis and have no rotation. Also called a *slider* joint. + * @{ + */ + +/// Create a prismatic (slider) joint. +/// @see b3PrismaticJointDef for details +B3_API b3JointId b3CreatePrismaticJoint( b3WorldId worldId, const b3PrismaticJointDef* def ); + +/// Enable/disable the joint spring. +B3_API void b3PrismaticJoint_EnableSpring( b3JointId jointId, bool enableSpring ); + +/// Is the prismatic joint spring enabled or not? +B3_API bool b3PrismaticJoint_IsSpringEnabled( b3JointId jointId ); + +/// Set the prismatic joint stiffness in Hertz. +/// This should usually be less than a quarter of the simulation rate. For example, if the simulation +/// runs at 60Hz then the joint stiffness should be 15Hz or less. +B3_API void b3PrismaticJoint_SetSpringHertz( b3JointId jointId, float hertz ); + +/// Get the prismatic joint stiffness in Hertz +B3_API float b3PrismaticJoint_GetSpringHertz( b3JointId jointId ); + +/// Set the prismatic joint damping ratio (non-dimensional) +B3_API void b3PrismaticJoint_SetSpringDampingRatio( b3JointId jointId, float dampingRatio ); + +/// Get the prismatic spring damping ratio (non-dimensional) +B3_API float b3PrismaticJoint_GetSpringDampingRatio( b3JointId jointId ); + +/// Set the prismatic joint target translation. Usually in meters. +B3_API void b3PrismaticJoint_SetTargetTranslation( b3JointId jointId, float targetTranslation ); + +/// Get the prismatic joint target translation. Usually in meters. +B3_API float b3PrismaticJoint_GetTargetTranslation( b3JointId jointId ); + +/// Enable/disable a prismatic joint limit +B3_API void b3PrismaticJoint_EnableLimit( b3JointId jointId, bool enableLimit ); + +/// Is the prismatic joint limit enabled? +B3_API bool b3PrismaticJoint_IsLimitEnabled( b3JointId jointId ); + +/// Get the prismatic joint lower limit +B3_API float b3PrismaticJoint_GetLowerLimit( b3JointId jointId ); + +/// Get the prismatic joint upper limit +B3_API float b3PrismaticJoint_GetUpperLimit( b3JointId jointId ); + +/// Set the prismatic joint limits +B3_API void b3PrismaticJoint_SetLimits( b3JointId jointId, float lower, float upper ); + +/// Enable/disable a prismatic joint motor +B3_API void b3PrismaticJoint_EnableMotor( b3JointId jointId, bool enableMotor ); + +/// Is the prismatic joint motor enabled? +B3_API bool b3PrismaticJoint_IsMotorEnabled( b3JointId jointId ); + +/// Set the prismatic joint motor speed, usually in meters per second +B3_API void b3PrismaticJoint_SetMotorSpeed( b3JointId jointId, float motorSpeed ); + +/// Get the prismatic joint motor speed, usually in meters per second +B3_API float b3PrismaticJoint_GetMotorSpeed( b3JointId jointId ); + +/// Set the prismatic joint maximum motor force, usually in newtons +B3_API void b3PrismaticJoint_SetMaxMotorForce( b3JointId jointId, float force ); + +/// Get the prismatic joint maximum motor force, usually in newtons +B3_API float b3PrismaticJoint_GetMaxMotorForce( b3JointId jointId ); + +/// Get the prismatic joint current motor force, usually in newtons +B3_API float b3PrismaticJoint_GetMotorForce( b3JointId jointId ); + +/// Get the current joint translation, usually in meters. +B3_API float b3PrismaticJoint_GetTranslation( b3JointId jointId ); + +/// Get the current joint translation speed, usually in meters per second. +B3_API float b3PrismaticJoint_GetSpeed( b3JointId jointId ); + +/**@}*/ // prismatic_joint + +/** + * @defgroup revolute_joint Revolute Joint + * @brief A revolute joint allows for relative rotation about a single axis with no relative translation. + * + * Also called a *hinge* or *pin* joint. + * @{ + */ + +/// Create a revolute joint +/// @see b3RevoluteJointDef for details +B3_API b3JointId b3CreateRevoluteJoint( b3WorldId worldId, const b3RevoluteJointDef* def ); + +/// Enable/disable the revolute joint spring +B3_API void b3RevoluteJoint_EnableSpring( b3JointId jointId, bool enableSpring ); + +/// Is the revolute angular spring enabled? +B3_API bool b3RevoluteJoint_IsSpringEnabled( b3JointId jointId ); + +/// Set the revolute joint spring stiffness in Hertz +B3_API void b3RevoluteJoint_SetSpringHertz( b3JointId jointId, float hertz ); + +/// Get the revolute joint spring stiffness in Hertz +B3_API float b3RevoluteJoint_GetSpringHertz( b3JointId jointId ); + +/// Set the revolute joint spring damping ratio, non-dimensional +B3_API void b3RevoluteJoint_SetSpringDampingRatio( b3JointId jointId, float dampingRatio ); + +/// Get the revolute joint spring damping ratio, non-dimensional +B3_API float b3RevoluteJoint_GetSpringDampingRatio( b3JointId jointId ); + +/// Set the revolute joint target angle in radians +B3_API void b3RevoluteJoint_SetTargetAngle( b3JointId jointId, float targetRadians ); + +/// Get the revolute joint target angle in radians +B3_API float b3RevoluteJoint_GetTargetAngle( b3JointId jointId ); + +/// Get the revolute joint current angle in radians relative to the reference angle +/// @see b3RevoluteJointDef::referenceAngle +B3_API float b3RevoluteJoint_GetAngle( b3JointId jointId ); + +/// Enable/disable the revolute joint limit +B3_API void b3RevoluteJoint_EnableLimit( b3JointId jointId, bool enableLimit ); + +/// Is the revolute joint limit enabled? +B3_API bool b3RevoluteJoint_IsLimitEnabled( b3JointId jointId ); + +/// Get the revolute joint lower limit in radians +B3_API float b3RevoluteJoint_GetLowerLimit( b3JointId jointId ); + +/// Get the revolute joint upper limit in radians +B3_API float b3RevoluteJoint_GetUpperLimit( b3JointId jointId ); + +/// Set the revolute joint limits in radians +B3_API void b3RevoluteJoint_SetLimits( b3JointId jointId, float lowerLimitRadians, float upperLimitRadians ); + +/// Enable/disable a revolute joint motor +B3_API void b3RevoluteJoint_EnableMotor( b3JointId jointId, bool enableMotor ); + +/// Is the revolute joint motor enabled? +B3_API bool b3RevoluteJoint_IsMotorEnabled( b3JointId jointId ); + +/// Set the revolute joint motor speed in radians per second +B3_API void b3RevoluteJoint_SetMotorSpeed( b3JointId jointId, float motorSpeed ); + +/// Get the revolute joint motor speed in radians per second +B3_API float b3RevoluteJoint_GetMotorSpeed( b3JointId jointId ); + +/// Get the revolute joint current motor torque, usually in newton-meters +B3_API float b3RevoluteJoint_GetMotorTorque( b3JointId jointId ); + +/// Set the revolute joint maximum motor torque, usually in newton-meters +B3_API void b3RevoluteJoint_SetMaxMotorTorque( b3JointId jointId, float torque ); + +/// Get the revolute joint maximum motor torque, usually in newton-meters +B3_API float b3RevoluteJoint_GetMaxMotorTorque( b3JointId jointId ); + +/**@}*/ // revolute_joint + +/** + * @defgroup spherical_joint Spherical Joint + * @brief A spherical joint allows for relative rotation in the 3D space with no relative translation. + * + * Also called a *ball-in-socket* or *point-to-point* joint. + * @{ + */ + +/// Create a spherical joint +/// @see b3SphericalJointDef for details +B3_API b3JointId b3CreateSphericalJoint( b3WorldId worldId, const b3SphericalJointDef* def ); + +/// Enable/disable the spherical joint cone limit +B3_API void b3SphericalJoint_EnableConeLimit( b3JointId jointId, bool enableLimit ); + +/// Is the spherical joint cone limit enabled? +B3_API bool b3SphericalJoint_IsConeLimitEnabled( b3JointId jointId ); + +/// Get the spherical joint cone limit in radians +B3_API float b3SphericalJoint_GetConeLimit( b3JointId jointId ); + +/// Set the spherical joint limits in radians +B3_API void b3SphericalJoint_SetConeLimit( b3JointId jointId, float angleRadians ); + +/// Get the spherical joint current cone angle in radians. +B3_API float b3SphericalJoint_GetConeAngle( b3JointId jointId ); + +/// Enable/disable the spherical joint limit +B3_API void b3SphericalJoint_EnableTwistLimit( b3JointId jointId, bool enableLimit ); + +/// Is the spherical joint limit enabled? +B3_API bool b3SphericalJoint_IsTwistLimitEnabled( b3JointId jointId ); + +/// Get the spherical joint lower limit in radians +B3_API float b3SphericalJoint_GetLowerTwistLimit( b3JointId jointId ); + +/// Get the spherical joint upper limit in radians +B3_API float b3SphericalJoint_GetUpperTwistLimit( b3JointId jointId ); + +/// Set the spherical joint limits in radians +B3_API void b3SphericalJoint_SetTwistLimits( b3JointId jointId, float lowerLimitRadians, float upperLimitRadians ); + +/// Get the spherical joint current twist angle in radians. +B3_API float b3SphericalJoint_GetTwistAngle( b3JointId jointId ); + +/// Enable/disable the spherical joint spring +B3_API void b3SphericalJoint_EnableSpring( b3JointId jointId, bool enableSpring ); + +/// Is the spherical angular spring enabled? +B3_API bool b3SphericalJoint_IsSpringEnabled( b3JointId jointId ); + +/// Set the spherical joint spring stiffness in Hertz +B3_API void b3SphericalJoint_SetSpringHertz( b3JointId jointId, float hertz ); + +/// Get the spherical joint spring stiffness in Hertz +B3_API float b3SphericalJoint_GetSpringHertz( b3JointId jointId ); + +/// Set the spherical joint spring damping ratio, non-dimensional +B3_API void b3SphericalJoint_SetSpringDampingRatio( b3JointId jointId, float dampingRatio ); + +/// Get the spherical joint spring damping ratio, non-dimensional +B3_API float b3SphericalJoint_GetSpringDampingRatio( b3JointId jointId ); + +/// Set the spherical joint spring target rotation +B3_API void b3SphericalJoint_SetTargetRotation( b3JointId jointId, b3Quat targetRotation ); + +/// Get the spherical joint spring target rotation +B3_API b3Quat b3SphericalJoint_GetTargetRotation( b3JointId jointId ); + +/// Enable/disable a spherical joint motor +B3_API void b3SphericalJoint_EnableMotor( b3JointId jointId, bool enableMotor ); + +/// Is the spherical joint motor enabled? +B3_API bool b3SphericalJoint_IsMotorEnabled( b3JointId jointId ); + +/// Set the spherical joint motor velocity in radians per second +B3_API void b3SphericalJoint_SetMotorVelocity( b3JointId jointId, b3Vec3 motorVelocity ); + +/// Get the spherical joint motor velocity in radians per second +B3_API b3Vec3 b3SphericalJoint_GetMotorVelocity( b3JointId jointId ); + +/// Get the spherical joint current motor torque, usually in newton-meters +B3_API b3Vec3 b3SphericalJoint_GetMotorTorque( b3JointId jointId ); + +/// Set the spherical joint maximum motor torque, usually in newton-meters +B3_API void b3SphericalJoint_SetMaxMotorTorque( b3JointId jointId, float torque ); + +/// Get the spherical joint maximum motor torque, usually in newton-meters +B3_API float b3SphericalJoint_GetMaxMotorTorque( b3JointId jointId ); + +/**@}*/ // spherical_joint + +/** + * @defgroup weld_joint Weld Joint + * @brief A weld joint fully constrains the relative transform between two bodies while allowing for springiness + * + * A weld joint constrains the relative rotation and translation between two bodies. Both rotation and translation + * can have damped springs. + * + * @note The accuracy of weld joint is limited by the accuracy of the solver. Long chains of weld joints may flex. + * @{ + */ + +/// Create a weld joint +/// @see b3WeldJointDef for details +B3_API b3JointId b3CreateWeldJoint( b3WorldId worldId, const b3WeldJointDef* def ); + +/// Set the weld joint linear stiffness in Hertz. 0 is rigid. +B3_API void b3WeldJoint_SetLinearHertz( b3JointId jointId, float hertz ); + +/// Get the weld joint linear stiffness in Hertz +B3_API float b3WeldJoint_GetLinearHertz( b3JointId jointId ); + +/// Set the weld joint linear damping ratio (non-dimensional) +B3_API void b3WeldJoint_SetLinearDampingRatio( b3JointId jointId, float dampingRatio ); + +/// Get the weld joint linear damping ratio (non-dimensional) +B3_API float b3WeldJoint_GetLinearDampingRatio( b3JointId jointId ); + +/// Set the weld joint angular stiffness in Hertz. 0 is rigid. +B3_API void b3WeldJoint_SetAngularHertz( b3JointId jointId, float hertz ); + +/// Get the weld joint angular stiffness in Hertz +B3_API float b3WeldJoint_GetAngularHertz( b3JointId jointId ); + +/// Set weld joint angular damping ratio, non-dimensional +B3_API void b3WeldJoint_SetAngularDampingRatio( b3JointId jointId, float dampingRatio ); + +/// Get the weld joint angular damping ratio, non-dimensional +B3_API float b3WeldJoint_GetAngularDampingRatio( b3JointId jointId ); + +/**@}*/ // weld_joint + +/** + * @defgroup wheel_joint Wheel Joint + * The wheel joint can be used to simulate wheels on vehicles. + * + * The wheel joint restricts body B to move along a local axis in body A. Body B is free to + * rotate. Supports a linear spring, linear limits, and a rotational motor. + * + * @{ + */ + +/// Create a wheel joint. +/// @see b3WheelJointDef for details. +B3_API b3JointId b3CreateWheelJoint( b3WorldId worldId, const b3WheelJointDef* def ); + +/// Enable/disable the wheel joint spring. +B3_API void b3WheelJoint_EnableSuspension( b3JointId jointId, bool flag ); + +/// Is the wheel joint spring enabled? +B3_API bool b3WheelJoint_IsSuspensionEnabled( b3JointId jointId ); + +/// Set the wheel joint stiffness in Hertz. +B3_API void b3WheelJoint_SetSuspensionHertz( b3JointId jointId, float hertz ); + +/// Get the wheel joint stiffness in Hertz. +B3_API float b3WheelJoint_GetSuspensionHertz( b3JointId jointId ); + +/// Set the wheel joint damping ratio, non-dimensional. +B3_API void b3WheelJoint_SetSuspensionDampingRatio( b3JointId jointId, float dampingRatio ); + +/// Get the wheel joint damping ratio, non-dimensional. +B3_API float b3WheelJoint_GetSuspensionDampingRatio( b3JointId jointId ); + +/// Enable/disable the wheel joint limit. +B3_API void b3WheelJoint_EnableSuspensionLimit( b3JointId jointId, bool flag ); + +/// Is the wheel joint limit enabled? +B3_API bool b3WheelJoint_IsSuspensionLimitEnabled( b3JointId jointId ); + +/// Get the wheel joint lower limit. +B3_API float b3WheelJoint_GetLowerSuspensionLimit( b3JointId jointId ); + +/// Get the wheel joint upper limit. +B3_API float b3WheelJoint_GetUpperSuspensionLimit( b3JointId jointId ); + +/// Set the wheel joint limits. +B3_API void b3WheelJoint_SetSuspensionLimits( b3JointId jointId, float lower, float upper ); + +/// Enable/disable the wheel joint motor. +B3_API void b3WheelJoint_EnableSpinMotor( b3JointId jointId, bool flag ); + +/// Is the wheel joint motor enabled? +B3_API bool b3WheelJoint_IsSpinMotorEnabled( b3JointId jointId ); + +/// Set the wheel joint motor speed in radians per second. +B3_API void b3WheelJoint_SetSpinMotorSpeed( b3JointId jointId, float speed ); + +/// Get the wheel joint motor speed in radians per second. +B3_API float b3WheelJoint_GetSpinMotorSpeed( b3JointId jointId ); + +/// Set the wheel joint maximum motor torque, usually in newton-meters. +B3_API void b3WheelJoint_SetMaxSpinTorque( b3JointId jointId, float torque ); + +/// Get the wheel joint maximum motor torque, usually in newton-meters. +B3_API float b3WheelJoint_GetMaxSpinTorque( b3JointId jointId ); + +/// Get the current spin speed in radians per second. +B3_API float b3WheelJoint_GetSpinSpeed( b3JointId jointId ); + +/// Get the wheel joint current motor torque, usually in newton-meters. +B3_API float b3WheelJoint_GetSpinTorque( b3JointId jointId ); + +/// Enable/disable wheel steering. Steering allows the wheel to rotate about the suspension axis. +B3_API void b3WheelJoint_EnableSteering( b3JointId jointId, bool flag ); + +/// Can the wheel steer? +B3_API bool b3WheelJoint_IsSteeringEnabled( b3JointId jointId ); + +/// Set the wheel joint steering stiffness in Hertz. +B3_API void b3WheelJoint_SetSteeringHertz( b3JointId jointId, float hertz ); + +/// Get the wheel joint steering stiffness in Hertz. +B3_API float b3WheelJoint_GetSteeringHertz( b3JointId jointId ); + +/// Set the wheel joint steering damping ratio, non-dimensional. +B3_API void b3WheelJoint_SetSteeringDampingRatio( b3JointId jointId, float dampingRatio ); + +/// Get the wheel joint steering damping ratio, non-dimensional. +B3_API float b3WheelJoint_GetSteeringDampingRatio( b3JointId jointId ); + +/// Set the wheel joint maximum steering torque in N*m. +B3_API void b3WheelJoint_SetMaxSteeringTorque( b3JointId jointId, float torque ); + +/// Get the wheel joint maximum steering torque in N*m. +B3_API float b3WheelJoint_GetMaxSteeringTorque( b3JointId jointId ); + +/// Enable/disable the wheel joint steering limit. +B3_API void b3WheelJoint_EnableSteeringLimit( b3JointId jointId, bool flag ); + +/// Is the wheel joint steering limit enabled? +B3_API bool b3WheelJoint_IsSteeringLimitEnabled( b3JointId jointId ); + +/// Get the wheel joint lower steering limit in radians. +B3_API float b3WheelJoint_GetLowerSteeringLimit( b3JointId jointId ); + +/// Get the wheel joint upper steering limit in radians. +B3_API float b3WheelJoint_GetUpperSteeringLimit( b3JointId jointId ); + +/// Set the wheel joint steering limits in radians. +B3_API void b3WheelJoint_SetSteeringLimits( b3JointId jointId, float lowerRadians, float upperRadians ); + +/// Set the wheel joint target steering angle in radians. +B3_API void b3WheelJoint_SetTargetSteeringAngle( b3JointId jointId, float radians ); + +/// Get the wheel joint target steering angle in radians. +B3_API float b3WheelJoint_GetTargetSteeringAngle( b3JointId jointId ); + +/// Get the current steering angle in radians. +B3_API float b3WheelJoint_GetSteeringAngle( b3JointId jointId ); + +/// Get the current steering torque in N*m. +B3_API float b3WheelJoint_GetSteeringTorque( b3JointId jointId ); + +/**@}*/ // wheel_joint + +/**@}*/ // joint + +/** + * @defgroup contact Contact + * Access to contacts + * @{ + */ + +/// Contact identifier validation. Provides validation for up to 2^32 allocations. +B3_API bool b3Contact_IsValid( b3ContactId id ); + +/// Get the manifolds for a contact. The manifold may have no points if the contact is not touching. +B3_API b3ContactData b3Contact_GetData( b3ContactId contactId ); + +/**@}*/ // contact diff --git a/vendor/box3d/src/include/box3d/collision.h b/vendor/box3d/src/include/box3d/collision.h new file mode 100644 index 000000000..30cc9579f --- /dev/null +++ b/vendor/box3d/src/include/box3d/collision.h @@ -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 +#include + +/** + * @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 diff --git a/vendor/box3d/src/include/box3d/config.h b/vendor/box3d/src/include/box3d/config.h new file mode 100644 index 000000000..2790fc0b8 --- /dev/null +++ b/vendor/box3d/src/include/box3d/config.h @@ -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 + diff --git a/vendor/box3d/src/include/box3d/constants.h b/vendor/box3d/src/include/box3d/constants.h new file mode 100644 index 000000000..e32b73706 --- /dev/null +++ b/vendor/box3d/src/include/box3d/constants.h @@ -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 ) diff --git a/vendor/box3d/src/include/box3d/id.h b/vendor/box3d/src/include/box3d/id.h new file mode 100644 index 000000000..447fd34ed --- /dev/null +++ b/vendor/box3d/src/include/box3d/id.h @@ -0,0 +1,178 @@ +// SPDX-FileCopyrightText: 2026 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +// 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; +} + +/**@}*/ diff --git a/vendor/box3d/src/include/box3d/math_functions.h b/vendor/box3d/src/include/box3d/math_functions.h new file mode 100644 index 000000000..7ef7354f2 --- /dev/null +++ b/vendor/box3d/src/include/box3d/math_functions.h @@ -0,0 +1,1208 @@ +// SPDX-FileCopyrightText: 2025 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" + +#include + +// for sqrtf and remainderf +#include +#include + +/** + * @defgroup math Math + * @brief Vector math types and functions + * @{ + */ + +/// https://en.wikipedia.org/wiki/Pi +#define B3_PI 3.14159265359f + +/// Convenience macro to convert from degrees to radians. +#define B3_DEG_TO_RAD 0.01745329251f + +/// Convenience macro to convert from radians to degrees. +#define B3_RAD_TO_DEG 57.2957795131f + +/// Minimum scale used for scaling collision meshes, etc. +#define B3_MIN_SCALE 0.01f + +/// A 2D vector. +typedef struct b3Vec2 +{ + float x; + float y; +} b3Vec2; + +/// A 3D vector. +typedef struct b3Vec3 +{ + float x; + float y; + float z; +} b3Vec3; + +/// Cosine and sine pair. +/// This uses a custom implementation designed for cross-platform determinism. +typedef struct b3CosSin +{ + /// cosine and sine + float cosine; + float sine; +} b3CosSin; + +/// A quaternion. +typedef struct b3Quat +{ + b3Vec3 v; + float s; +} b3Quat; + +/// A rigid transform. +typedef struct b3Transform +{ + b3Vec3 p; + b3Quat q; +} b3Transform; + +#if defined( BOX3D_DOUBLE_PRECISION ) + +/// A world position. Double precision in large world mode so coordinates stay accurate far +/// from the origin. +typedef struct b3Pos +{ + double x, y, z; +} b3Pos; + +/// A world transform with double precision translation and float quaternion rotation. Rotation +/// is frame local and never needs the extra range, the same split as Jolt's DMat44. +typedef struct b3WorldTransform +{ + b3Pos p; + b3Quat q; +} b3WorldTransform; + +#else + +/// In single precision mode these types are the same. +typedef b3Vec3 b3Pos; + +/// In single precision mode these types are the same. +typedef b3Transform b3WorldTransform; + +#endif + +/// A 3x3 matrix. +typedef struct b3Matrix3 +{ + b3Vec3 cx, cy, cz; +} b3Matrix3; + +/// Axis aligned bounding box. +typedef struct b3AABB +{ + b3Vec3 lowerBound; + b3Vec3 upperBound; +} b3AABB; + +/// A plane. +/// separation = dot(normal, point) - offset +typedef struct b3Plane +{ + b3Vec3 normal; + float offset; +} b3Plane; + +static const b3Vec3 b3Vec3_zero = { 0.0f, 0.0f, 0.0f }; +static const b3Vec3 b3Vec3_one = { 1.0f, 1.0f, 1.0f }; +static const b3Vec3 b3Vec3_axisX = { 1.0f, 0.0f, 0.0f }; +static const b3Vec3 b3Vec3_axisY = { 0.0f, 1.0f, 0.0f }; +static const b3Vec3 b3Vec3_axisZ = { 0.0f, 0.0f, 1.0f }; +static const b3Quat b3Quat_identity = { { 0.0f, 0.0f, 0.0f }, 1.0f }; +static const b3Transform b3Transform_identity = { { 0.0f, 0.0f, 0.0f }, { { 0.0f, 0.0f, 0.0f }, 1.0f } }; +static const b3Matrix3 b3Mat3_zero = { + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f }, +}; +static const b3Matrix3 b3Mat3_identity = { + { 1.0f, 0.0f, 0.0f }, + { 0.0f, 1.0f, 0.0f }, + { 0.0f, 0.0f, 1.0f }, +}; + +// Valid in both modes: 0.0f promotes to double, the identity rotation stays float +static const b3Pos b3Pos_zero = { 0.0f, 0.0f, 0.0f }; +static const b3WorldTransform b3WorldTransform_identity = { { 0.0f, 0.0f, 0.0f }, { { 0.0f, 0.0f, 0.0f }, 1.0f } }; + +/// @return the minimum of two integers. +B3_INLINE int b3MinInt( int a, int b ) +{ + return a < b ? a : b; +} + +/// @return the maximum of two integers. +B3_INLINE int b3MaxInt( int a, int b ) +{ + return a > b ? a : b; +} + +/// @return an integer clamped between a lower and upper bound. +B3_INLINE int b3ClampInt( int a, int lower, int upper ) +{ + return a < lower ? lower : ( upper < a ? upper : a ); +} + +/// @return is this float valid (finite and not NaN). +B3_API bool b3IsValidFloat( float a ); + +/// @return the absolute value of a float. +B3_INLINE float b3AbsFloat( float a ) +{ + return a < 0 ? -a : a; +} + +/// @return the minimum of two floats. +B3_INLINE float b3MinFloat( float a, float b ) +{ + return a < b ? a : b; +} + +/// @return the maximum of two floats. +B3_INLINE float b3MaxFloat( float a, float b ) +{ + return a > b ? a : b; +} + +/// @return a float clamped between a lower and upper bound. +B3_INLINE float b3ClampFloat( float a, float lower, float upper ) +{ + return a < lower ? lower : ( upper < a ? upper : a ); +} + +/// Interpolate a scalar. +B3_INLINE float b3LerpFloat( float a, float b, float alpha ) +{ + return ( 1.0f - alpha ) * a + alpha * b; +} + +/// 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. +B3_API float b3Atan2( float y, float x ); + +/// Compute the cosine and sine of an angle in radians. Implemented +/// for cross-platform determinism. +B3_API b3CosSin b3ComputeCosSin( float radians ); + +/// @deprecated +B3_INLINE float b3Sin( float radians ) +{ + b3CosSin cs = b3ComputeCosSin( radians ); + return cs.sine; +} + +/// @deprecated +B3_INLINE float b3Cos( float radians ) +{ + b3CosSin cs = b3ComputeCosSin( radians ); + return cs.cosine; +} + +/// Convert any angle into the range [-pi, pi]. +B3_INLINE float b3UnwindAngle( float radians ) +{ + // Assuming this is deterministic + return remainderf( radians, 2.0f * B3_PI ); +} + +/// Vector addition. +B3_INLINE b3Vec3 b3Add( b3Vec3 a, b3Vec3 b ) +{ + return B3_LITERAL( b3Vec3 ){ a.x + b.x, a.y + b.y, a.z + b.z }; +} + +/// Vector subtraction. +B3_INLINE b3Vec3 b3Sub( b3Vec3 a, b3Vec3 b ) +{ + return B3_LITERAL( b3Vec3 ){ a.x - b.x, a.y - b.y, a.z - b.z }; +} + +/// Vector component-wise multiplication. +B3_INLINE b3Vec3 b3Mul( b3Vec3 a, b3Vec3 b ) +{ + return B3_LITERAL( b3Vec3 ){ a.x * b.x, a.y * b.y, a.z * b.z }; +} + +/// Vector negation. +B3_INLINE b3Vec3 b3Neg( b3Vec3 a ) +{ + return B3_LITERAL( b3Vec3 ){ -a.x, -a.y, -a.z }; +} + +/// Vector dot product. +B3_INLINE float b3Dot( b3Vec3 a, b3Vec3 b ) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +/// Vector length. +B3_INLINE float b3Length( b3Vec3 v ) +{ + return sqrtf( b3Dot( v, v ) ); +} + +/// Vector length squared. +B3_INLINE float b3LengthSquared( b3Vec3 a ) +{ + return a.x * a.x + a.y * a.y + a.z * a.z; +} + +/// Distance between two points. +B3_INLINE float b3Distance( b3Vec3 a, b3Vec3 b ) +{ + b3Vec3 dv = { b.x - a.x, b.y - a.y, b.z - a.z }; + return b3Length( dv ); +} + +/// Squared distance between two points. +B3_INLINE float b3DistanceSquared( b3Vec3 a, b3Vec3 b ) +{ + b3Vec3 dv = { b.x - a.x, b.y - a.y, b.z - a.z }; + return dv.x * dv.x + dv.y * dv.y + dv.z * dv.z; +} + +/// Normalize a vector. Returns a zero vector if the input vector is very small. +B3_INLINE b3Vec3 b3Normalize( b3Vec3 a ) +{ + float lengthSquared = a.x * a.x + a.y * a.y + a.z * a.z; + + if ( lengthSquared > 1000.0f * FLT_MIN ) + { + float s = 1.0f / sqrtf( lengthSquared ); + b3Vec3 u = { s * a.x, s * a.y, s * a.z }; + return u; + } + + return B3_LITERAL( b3Vec3 ){ 0.0f, 0.0f, 0.0f }; +} + +/// Normalize a vector and return the length. Returns a zero vector +/// if the input is very small. +B3_INLINE b3Vec3 b3GetLengthAndNormalize( float* length, b3Vec3 a ) +{ + *length = b3Length( a ); + if ( *length < FLT_EPSILON ) + { + return b3Vec3_zero; + } + + float invLength = 1.0f / *length; + b3Vec3 n = { invLength * a.x, invLength * a.y, invLength * a.z }; + return n; +} + +/// Get a unit vector that is perpendicular to the supplied vector. +B3_INLINE b3Vec3 b3Perp( b3Vec3 a ) +{ + // 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. + b3Vec3 p; + if ( a.x < -0.5f || 0.5f < a.x ) + { + p = B3_LITERAL( b3Vec3 ){ a.y, -a.x, 0.0f }; + } + else + { + p = B3_LITERAL( b3Vec3 ){ 0.0f, a.z, -a.y }; + } + + return b3Normalize( p ); +} + +/// Is a vector normalized? In other words, does it have unit length? +B3_INLINE bool b3IsNormalized( b3Vec3 a ) +{ + float aa = b3Dot( a, a ); + return b3AbsFloat( 1.0f - aa ) < 100.0f * FLT_EPSILON; +} + +/// a + s * b +B3_INLINE b3Vec3 b3MulAdd( b3Vec3 a, float s, b3Vec3 b ) +{ + return B3_LITERAL( b3Vec3 ){ a.x + s * b.x, a.y + s * b.y, a.z + s * b.z }; +} + +/// a - s * b +B3_INLINE b3Vec3 b3MulSub( b3Vec3 a, float s, b3Vec3 b ) +{ + return B3_LITERAL( b3Vec3 ){ a.x - s * b.x, a.y - s * b.y, a.z - s * b.z }; +} + +/// s * a +B3_INLINE b3Vec3 b3MulSV( float s, b3Vec3 a ) +{ + return B3_LITERAL( b3Vec3 ){ s * a.x, s * a.y, s * a.z }; +} + +/// https://en.wikipedia.org/wiki/Cross_product +B3_INLINE b3Vec3 b3Cross( b3Vec3 a, b3Vec3 b ) +{ + b3Vec3 c; + c.x = a.y * b.z - a.z * b.y; + c.y = a.z * b.x - a.x * b.z; + c.z = a.x * b.y - a.y * b.x; + return c; +} + +/// Linearly interpolate between two vectors. +B3_INLINE b3Vec3 b3Lerp( b3Vec3 a, b3Vec3 b, float alpha ) +{ + B3_ASSERT( 0.0f <= alpha && alpha <= 1.0f ); + + b3Vec3 c = { + ( 1.0f - alpha ) * a.x + alpha * b.x, + ( 1.0f - alpha ) * a.y + alpha * b.y, + ( 1.0f - alpha ) * a.z + alpha * b.z, + }; + return c; +} + +/// Blend two vectors: s * a + t * b +B3_INLINE b3Vec3 b3Blend2( float s, b3Vec3 a, float t, b3Vec3 b ) +{ + b3Vec3 d = { + s * a.x + t * b.x, + s * a.y + t * b.y, + s * a.z + t * b.z, + }; + return d; +} + +/// Component-wise absolute value. +B3_INLINE b3Vec3 b3Abs( b3Vec3 a ) +{ + return B3_LITERAL( b3Vec3 ){ + b3AbsFloat( a.x ), + b3AbsFloat( a.y ), + b3AbsFloat( a.z ), + }; +} + +/// Component-wise -1 or 1 (1 if zero). +B3_INLINE b3Vec3 b3Sign( b3Vec3 a ) +{ + return B3_LITERAL( b3Vec3 ){ + a.x >= 0.0f ? 1.0f : -1.0f, + a.y >= 0.0f ? 1.0f : -1.0f, + a.z >= 0.0f ? 1.0f : -1.0f, + }; +} + +/// Component-wise minimum value. +B3_INLINE b3Vec3 b3Min( b3Vec3 a, b3Vec3 b ) +{ + return B3_LITERAL( b3Vec3 ){ + b3MinFloat( a.x, b.x ), + b3MinFloat( a.y, b.y ), + b3MinFloat( a.z, b.z ), + }; +} + +/// Component-wise maximum value. +B3_INLINE b3Vec3 b3Max( b3Vec3 a, b3Vec3 b ) +{ + return B3_LITERAL( b3Vec3 ){ + b3MaxFloat( a.x, b.x ), + b3MaxFloat( a.y, b.y ), + b3MaxFloat( a.z, b.z ), + }; +} + +/// Component-wise clamped value. +B3_INLINE b3Vec3 b3Clamp( b3Vec3 a, b3Vec3 lower, b3Vec3 upper ) +{ + b3Vec3 b; + b.x = b3ClampFloat( a.x, lower.x, upper.x ); + b.y = b3ClampFloat( a.y, lower.y, upper.y ); + b.z = b3ClampFloat( a.z, lower.z, upper.z ); + return b; +} + +/// Create a safe scaling value for scaling collision. This allows +/// negative scale, but keeps scale sufficiently far from zero. +B3_INLINE b3Vec3 b3SafeScale( b3Vec3 a ) +{ + b3Vec3 absScale = b3Abs( a ); + b3Vec3 minScale = { B3_MIN_SCALE, B3_MIN_SCALE, B3_MIN_SCALE }; + b3Vec3 safeScale = b3Mul( b3Sign( a ), b3Max( absScale, minScale ) ); + return safeScale; +} + +/// Does the supplied quaternion have unit length? +B3_INLINE bool b3IsNormalizedQuat( b3Quat q ) +{ + float qq = q.v.x * q.v.x + q.v.y * q.v.y + q.v.z * q.v.z + q.s * q.s; + return 1.0f - 20.0f * FLT_EPSILON < qq && qq < 1.0f + 20.0f * FLT_EPSILON; +} + +/// Rotate a vector. +B3_INLINE b3Vec3 b3RotateVector( b3Quat q, b3Vec3 v ) +{ + // v + 2 * cross(q.v, cross(q.v, v) + q.s * v) + // B3_ASSERT( b3IsNormalizedQuat( q ) ); + b3Vec3 t1 = b3Cross( q.v, v ); + b3Vec3 t2 = b3MulAdd( t1, q.s, v ); + b3Vec3 t3 = b3Cross( q.v, t2 ); + return b3MulAdd( v, 2.0f, t3 ); +} + +/// Inverse rotate a vector. +B3_INLINE b3Vec3 b3InvRotateVector( b3Quat q, b3Vec3 v ) +{ + // v + 2 * cross(q.v, cross(q.v, v) - q.s * v) + // B3_ASSERT( b3IsNormalizedQuat( q ) ); + b3Vec3 t1 = b3Cross( q.v, v ); + b3Vec3 t2 = b3MulSub( t1, q.s, v ); + b3Vec3 t3 = b3Cross( q.v, t2 ); + return b3MulAdd( v, 2.0f, t3 ); +} + +/// Compute dot product of two quaternions. Useful for polarity tests. +B3_INLINE float b3DotQuat( b3Quat a, b3Quat b ) +{ + return a.v.x * b.v.x + a.v.y * b.v.y + a.v.z * b.v.z + a.s * b.s; +} + +/// Multiply two quaternions. +B3_INLINE b3Quat b3MulQuat( b3Quat q1, b3Quat q2 ) +{ + b3Vec3 t1 = b3Cross( q1.v, q2.v ); + b3Vec3 t2 = b3MulAdd( t1, q1.s, q2.v ); + b3Vec3 t3 = b3MulAdd( t2, q2.s, q1.v ); + b3Quat q = { t3, q1.s * q2.s - b3Dot( q1.v, q2.v ) }; + return q; +} + +/// Compute a relative quaternion. +/// inv(q1) * q2 +B3_INLINE b3Quat b3InvMulQuat( b3Quat q1, b3Quat q2 ) +{ + b3Vec3 t1 = b3Cross( q2.v, q1.v ); + b3Vec3 t2 = b3MulAdd( t1, q1.s, q2.v ); + b3Vec3 t3 = b3MulSub( t2, q2.s, q1.v ); + b3Quat q = { t3, q1.s * q2.s + b3Dot( q1.v, q2.v ) }; + return q; +} + +/// Quaternion conjugate (cheap inverse). +B3_INLINE b3Quat b3Conjugate( b3Quat q ) +{ + return B3_LITERAL( b3Quat ){ { -q.v.x, -q.v.y, -q.v.z }, q.s }; +} + +/// Component-wise quaternion negation. +B3_INLINE b3Quat b3NegateQuat( b3Quat q ) +{ + return B3_LITERAL( b3Quat ){ { -q.v.x, -q.v.y, -q.v.z }, -q.s }; +} + +/// Normalize a quaternion. +B3_INLINE b3Quat b3NormalizeQuat( b3Quat q ) +{ + float lengthSq = b3DotQuat( q, q ); + if ( lengthSq > 1000.0f * FLT_MIN ) + { + float s = 1.0f / sqrtf( lengthSq ); + b3Quat qn = { { s * q.v.x, s * q.v.y, s * q.v.z }, s * q.s }; + return qn; + } + + return b3Quat_identity; +} + +/// Make a quaternion that is equivalent to rotating around an axis by a specified angle. +B3_INLINE b3Quat b3MakeQuatFromAxisAngle( b3Vec3 axis, float radians ) +{ + B3_ASSERT( b3IsNormalized( axis ) ); + b3CosSin cs = b3ComputeCosSin( 0.5f * radians ); + b3Quat q = { { cs.sine * axis.x, cs.sine * axis.y, cs.sine * axis.z }, cs.cosine }; + return q; +} + +/// Get the axis and angle from a quaternion. Assumes the quaternion is normalized. +B3_INLINE b3Vec3 b3GetAxisAngle( float* radians, b3Quat q ) +{ + float length = sqrtf( q.v.x * q.v.x + q.v.y * q.v.y + q.v.z * q.v.z ); + *radians = 2.0f * b3Atan2( length, q.s ); + if ( length > 0.0f ) + { + float invLength = 1.0f / length; + b3Vec3 axis = { invLength * q.v.x, invLength * q.v.y, invLength * q.v.z }; + return axis; + } + + return b3Vec3_zero; +} + +/// Get the angle for a quaternion in radians +B3_INLINE float b3GetQuatAngle( b3Quat q ) +{ + float length = sqrtf( q.v.x * q.v.x + q.v.y * q.v.y + q.v.z * q.v.z ); + return 2.0f * b3Atan2( length, q.s ); +} + +/// Extract a quaternion from a rotation matrix. +B3_API b3Quat b3MakeQuatFromMatrix( const b3Matrix3* m ); + +/// Find a quaternion that rotates one vector to another. +B3_API b3Quat b3ComputeQuatBetweenUnitVectors( b3Vec3 v1, b3Vec3 v2 ); + +/// Twist angle around the z-axis, used for twist limit and revolute angle limit +B3_INLINE float b3GetTwistAngle( b3Quat q ) +{ + // Account for polarity to keep the twist angle in range. + // This is simpler than asking the user to check polarity or unwinding. + float twist = q.s < 0.0f ? b3Atan2( -q.v.z, -q.s ) : b3Atan2( q.v.z, q.s ); + twist *= 2.0f; + B3_ASSERT( -B3_PI <= twist && twist <= B3_PI ); + return twist; +} + +/// Swing angle used for cone limit +B3_INLINE float b3GetSwingAngle( b3Quat q ) +{ + // Polarity should not matter because all terms are squared. + float x = sqrtf( q.v.z * q.v.z + q.s * q.s ); + float y = sqrtf( q.v.x * q.v.x + q.v.y * q.v.y ); + float swing = 2.0f * b3Atan2( y, x ); + B3_ASSERT( 0.0f <= swing && swing <= B3_PI ); + return swing; +} + +/// Linearly interpolate and normalize between two quaternions +B3_INLINE b3Quat b3NLerp( b3Quat q1, b3Quat q2, float alpha ) +{ + B3_VALIDATE( 0.0f <= alpha && alpha <= 1.0f ); + if ( b3DotQuat( q1, q2 ) < 0.0f ) + { + q1 = B3_LITERAL( b3Quat ){ { -q1.v.x, -q1.v.y, -q1.v.z }, -q1.s }; + } + + b3Quat q; + q.v = b3Lerp( q1.v, q2.v, alpha ); + q.s = ( 1.0f - alpha ) * q1.s + alpha * q2.s; + + return b3NormalizeQuat( 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. +B3_INLINE b3Transform b3MulTransforms( b3Transform a, b3Transform b ) +{ + b3Transform out; + out.p = b3Add( b3RotateVector( a.q, b.p ), a.p ); + out.q = b3MulQuat( a.q, b.q ); + return out; +} + +/// 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. +B3_FORCE_INLINE b3Transform b3InvMulTransforms( b3Transform a, b3Transform b ) +{ + b3Transform out; + out.p = b3InvRotateVector( a.q, b3Sub( b.p, a.p ) ); + out.q = b3InvMulQuat( a.q, b.q ); + return out; +} + +/// Get the inverse of a transform. +B3_INLINE b3Transform b3InvertTransform( b3Transform t ) +{ + b3Transform out; + out.p = b3InvRotateVector( t.q, b3Neg( t.p ) ); + out.q = b3Conjugate( t.q ); + return out; +} + +/// Transform a point. +B3_INLINE b3Vec3 b3TransformPoint( b3Transform t, b3Vec3 v ) +{ + b3Vec3 rv = b3RotateVector( t.q, v ); + return b3Add( rv, t.p ); +} + +/// Inverse transform a point. +B3_INLINE b3Vec3 b3InvTransformPoint( b3Transform t, b3Vec3 v ) +{ + return b3InvRotateVector( t.q, b3Sub( 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. +B3_INLINE b3Pos b3ToPos( b3Vec3 v ) +{ + return B3_LITERAL( b3Pos ){ v.x, v.y, v.z }; +} + +/// Lossy conversion of a world position to a float vector. +B3_INLINE b3Vec3 b3ToVec3( b3Pos p ) +{ + return B3_LITERAL( b3Vec3 ){ (float)p.x, (float)p.y, (float)p.z }; +} + +/// Narrow a world coordinate to float, rounding toward negative infinity. Use with +/// b3RoundUpFloat 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. +B3_INLINE float b3RoundDownFloat( double x ) +{ +#if defined( BOX3D_DOUBLE_PRECISION ) + float f = (float)x; + return (double)f > x ? nextafterf( f, -FLT_MAX ) : f; +#else + return (float)x; +#endif +} + +/// Narrow a world coordinate to float, rounding toward positive infinity. +B3_INLINE float b3RoundUpFloat( double x ) +{ +#if defined( BOX3D_DOUBLE_PRECISION ) + float f = (float)x; + return (double)f < x ? nextafterf( f, FLT_MAX ) : f; +#else + return (float)x; +#endif +} + +/// a - b, demoted to float. The primary precision boundary operation. +B3_INLINE b3Vec3 b3SubPos( b3Pos a, b3Pos b ) +{ + return B3_LITERAL( b3Vec3 ){ (float)( a.x - b.x ), (float)( a.y - b.y ), (float)( a.z - b.z ) }; +} + +/// p + d +B3_INLINE b3Pos b3OffsetPos( b3Pos p, b3Vec3 d ) +{ + return B3_LITERAL( b3Pos ){ p.x + d.x, p.y + d.y, p.z + d.z }; +} + +/// World position interpolation for sweeps and sampling. +B3_INLINE b3Pos b3LerpPosition( b3Pos a, b3Pos b, float t ) +{ + return B3_LITERAL( b3Pos ){ + ( 1.0f - t ) * a.x + t * b.x, + ( 1.0f - t ) * a.y + t * b.y, + ( 1.0f - t ) * a.z + t * b.z, + }; +} + +/// Transform a local point to a world position. Rotation in float, translation in double. +B3_INLINE b3Pos b3TransformWorldPoint( b3WorldTransform t, b3Vec3 p ) +{ + b3Vec3 r = b3RotateVector( t.q, p ); + return B3_LITERAL( b3Pos ){ t.p.x + r.x, t.p.y + r.y, t.p.z + r.z }; +} + +/// Transform a world position to a local point. One double subtraction, then float. +B3_INLINE b3Vec3 b3InvTransformWorldPoint( b3WorldTransform t, b3Pos p ) +{ + b3Vec3 d = { (float)( p.x - t.p.x ), (float)( p.y - t.p.y ), (float)( p.z - t.p.z ) }; + return b3InvRotateVector( t.q, d ); +} + +/// Relative transform of frame B in frame A. The narrow phase boundary. +B3_INLINE b3Transform b3InvMulWorldTransforms( b3WorldTransform A, b3WorldTransform B ) +{ + b3Transform C; + C.q = b3InvMulQuat( A.q, B.q ); + b3Vec3 d = { (float)( B.p.x - A.p.x ), (float)( B.p.y - A.p.y ), (float)( B.p.z - A.p.z ) }; + C.p = b3InvRotateVector( A.q, d ); + return C; +} + +/// Compose a world transform with a local transform. +B3_INLINE b3WorldTransform b3MulWorldTransforms( b3WorldTransform A, b3Transform B ) +{ + b3WorldTransform C; + C.q = b3MulQuat( A.q, B.q ); + b3Vec3 r = b3RotateVector( A.q, B.p ); + C.p = B3_LITERAL( b3Pos ){ A.p.x + r.x, A.p.y + r.y, A.p.z + r.z }; + return C; +} + +/// Shift a world transform into the frame of a base position. +B3_INLINE b3Transform b3ToRelativeTransform( b3WorldTransform t, b3Pos base ) +{ + b3Transform r; + r.q = t.q; + r.p = B3_LITERAL( b3Vec3 ){ (float)( t.p.x - base.x ), (float)( t.p.y - base.y ), (float)( t.p.z - base.z ) }; + return r; +} + +/// Promote a float transform to a world transform. Lossless. +B3_INLINE b3WorldTransform b3MakeWorldTransform( b3Transform t ) +{ + b3WorldTransform w; + w.p = b3ToPos( t.p ); + w.q = t.q; + return w; +} + +/// 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. +B3_INLINE b3AABB b3OffsetAABB( b3AABB localBox, b3Pos origin ) +{ + b3AABB out; + out.lowerBound.x = b3RoundDownFloat( origin.x + localBox.lowerBound.x ); + out.lowerBound.y = b3RoundDownFloat( origin.y + localBox.lowerBound.y ); + out.lowerBound.z = b3RoundDownFloat( origin.z + localBox.lowerBound.z ); + out.upperBound.x = b3RoundUpFloat( origin.x + localBox.upperBound.x ); + out.upperBound.y = b3RoundUpFloat( origin.y + localBox.upperBound.y ); + out.upperBound.z = b3RoundUpFloat( origin.z + localBox.upperBound.z ); + return out; +} + +/// Compute the determinant of a 3-by-3 matrix. +B3_INLINE float b3Det( b3Matrix3 m ) +{ + return b3Dot( m.cx, b3Cross( m.cy, m.cz ) ); +} + +/// Multiply a matrix times a column vector. +B3_INLINE b3Vec3 b3MulMV( b3Matrix3 m, b3Vec3 a ) +{ + b3Vec3 b = { + m.cx.x * a.x + m.cy.x * a.y + m.cz.x * a.z, + m.cx.y * a.x + m.cy.y * a.y + m.cz.y * a.z, + m.cx.z * a.x + m.cy.z * a.y + m.cz.z * a.z, + }; + return b; +} + +/// Negate a matrix. +B3_INLINE b3Matrix3 b3NegateMat3( b3Matrix3 a ) +{ + return B3_LITERAL( b3Matrix3 ){ + { -a.cx.x, -a.cx.y, -a.cx.z }, + { -a.cy.x, -a.cy.y, -a.cy.z }, + { -a.cz.x, -a.cz.y, -a.cz.z }, + }; +} + +/// Matrix addition. +/// @return a + b +B3_INLINE b3Matrix3 b3AddMM( b3Matrix3 a, b3Matrix3 b ) +{ + return B3_LITERAL( b3Matrix3 ){ + { a.cx.x + b.cx.x, a.cx.y + b.cx.y, a.cx.z + b.cx.z }, + { a.cy.x + b.cy.x, a.cy.y + b.cy.y, a.cy.z + b.cy.z }, + { a.cz.x + b.cz.x, a.cz.y + b.cz.y, a.cz.z + b.cz.z }, + }; +} + +/// Matrix subtraction. +/// @return a - b +B3_INLINE b3Matrix3 b3SubMM( b3Matrix3 a, b3Matrix3 b ) +{ + return B3_LITERAL( b3Matrix3 ){ + { a.cx.x - b.cx.x, a.cx.y - b.cx.y, a.cx.z - b.cx.z }, + { a.cy.x - b.cy.x, a.cy.y - b.cy.y, a.cy.z - b.cy.z }, + { a.cz.x - b.cz.x, a.cz.y - b.cz.y, a.cz.z - b.cz.z }, + }; +} + +/// Multiply a matrix by a scalar, component-wise. +B3_INLINE b3Matrix3 b3MulSM( float s, b3Matrix3 a ) +{ + return B3_LITERAL( b3Matrix3 ){ + { s * a.cx.x, s * a.cx.y, s * a.cx.z }, + { s * a.cy.x, s * a.cy.y, s * a.cy.z }, + { s * a.cz.x, s * a.cz.y, s * a.cz.z }, + }; +} + +/// Matrix multiplication. +/// @return a * b +B3_INLINE b3Matrix3 b3MulMM( b3Matrix3 a, b3Matrix3 b ) +{ + b3Matrix3 out; + out.cx = b3MulMV( a, b.cx ); + out.cy = b3MulMV( a, b.cy ); + out.cz = b3MulMV( a, b.cz ); + return out; +} + +/// Matrix transpose. +B3_INLINE b3Matrix3 b3Transpose( b3Matrix3 m ) +{ + b3Matrix3 out; + out.cx = B3_LITERAL( b3Vec3 ){ m.cx.x, m.cy.x, m.cz.x }; + out.cy = B3_LITERAL( b3Vec3 ){ m.cx.y, m.cy.y, m.cz.y }; + out.cz = B3_LITERAL( b3Vec3 ){ m.cx.z, m.cy.z, m.cz.z }; + + return out; +} + +/// General matrix inverse. +B3_INLINE b3Matrix3 b3InvertMatrix( b3Matrix3 m ) +{ + float det = b3Det( m ); + if ( b3AbsFloat( det ) > 1000.0f * FLT_MIN ) + { + float invDet = 1.0f / det; + b3Matrix3 out; + out.cx = b3MulSV( invDet, b3Cross( m.cy, m.cz ) ); + out.cy = b3MulSV( invDet, b3Cross( m.cz, m.cx ) ); + out.cz = b3MulSV( invDet, b3Cross( m.cx, m.cy ) ); + + return b3Transpose( out ); + } + + return b3Mat3_zero; +} + +/// Solve a matrix equation. +/// @return inv(m) * a +B3_INLINE b3Vec3 b3Solve3( b3Matrix3 m, b3Vec3 a ) +{ + float det = b3Det( m ); + if ( b3AbsFloat( det ) > 1000.0f * FLT_MIN ) + { + float invDet = 1.0f / det; + b3Matrix3 s; + s.cx = b3Cross( m.cy, m.cz ); + s.cy = b3Cross( m.cz, m.cx ); + s.cz = b3Cross( m.cx, m.cy ); + + b3Vec3 b = { + invDet * b3Dot( s.cx, a ), + invDet * b3Dot( s.cy, a ), + invDet * b3Dot( s.cz, a ), + }; + + return b; + } + + return b3Vec3_zero; +} + +/// Invert a matrix. +B3_INLINE b3Matrix3 b3InvertT( b3Matrix3 m ) +{ + float det = b3Det( m ); + if ( b3AbsFloat( det ) > 1000.0f * FLT_MIN ) + { + float invDet = 1.0f / det; + b3Matrix3 out; + out.cx = b3MulSV( invDet, b3Cross( m.cy, m.cz ) ); + out.cy = b3MulSV( invDet, b3Cross( m.cz, m.cx ) ); + out.cz = b3MulSV( invDet, b3Cross( m.cx, m.cy ) ); + return out; + } + + return b3Mat3_zero; +} + +/// Get the component-wise absolute value of a matrix. +B3_INLINE b3Matrix3 b3AbsMatrix3( b3Matrix3 m ) +{ + b3Matrix3 out; + out.cx = b3Abs( m.cx ); + out.cy = b3Abs( m.cy ); + out.cz = b3Abs( m.cz ); + + return out; +} + +/// Make a matrix from a quaternion. This is useful if you need to +/// rotate many vectors. +/// The force inline improves the performance of b3ShapeDistance. +B3_FORCE_INLINE b3Matrix3 b3MakeMatrixFromQuat( b3Quat q ) +{ + float xx = q.v.x * q.v.x; + float yy = q.v.y * q.v.y; + float zz = q.v.z * q.v.z; + float xy = q.v.x * q.v.y; + float xz = q.v.x * q.v.z; + float xw = q.v.x * q.s; + float yz = q.v.y * q.v.z; + float yw = q.v.y * q.s; + float zw = q.v.z * q.s; + + return B3_LITERAL( b3Matrix3 ){ + { 1.0f - 2.0f * ( yy + zz ), 2.0f * ( xy + zw ), 2.0f * ( xz - yw ) }, + { 2.0f * ( xy - zw ), 1.0f - 2.0f * ( xx + zz ), 2.0f * ( yz + xw ) }, + { 2.0f * ( xz + yw ), 2.0f * ( yz - xw ), 1.0f - 2.0f * ( xx + yy ) }, + }; +} + +/// Get the inertia tensor of an offset point. +/// https://en.wikipedia.org/wiki/Parallel_axis_theorem +B3_API b3Matrix3 b3Steiner( float mass, b3Vec3 origin ); + +/// Get the AABB of a point cloud. +B3_INLINE b3AABB b3MakeAABB( const b3Vec3* points, int count, float radius ) +{ + B3_ASSERT( count > 0 ); + b3AABB a = { points[0], points[0] }; + for ( int i = 1; i < count; ++i ) + { + a.lowerBound = b3Min( a.lowerBound, points[i] ); + a.upperBound = b3Max( a.upperBound, points[i] ); + } + + b3Vec3 r = { radius, radius, radius }; + a.lowerBound = b3Sub( a.lowerBound, r ); + a.upperBound = b3Add( a.upperBound, r ); + + return a; +} + +/// Does a fully contain b? +B3_INLINE bool b3AABB_Contains( b3AABB a, b3AABB b ) +{ + if ( a.lowerBound.x > b.lowerBound.x || b.upperBound.x > a.upperBound.x ) + return false; + if ( a.lowerBound.y > b.lowerBound.y || b.upperBound.y > a.upperBound.y ) + return false; + if ( 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. +B3_INLINE float b3AABB_Area( b3AABB a ) +{ + b3Vec3 delta = b3Sub( a.upperBound, a.lowerBound ); + return 2.0f * ( delta.x * delta.y + delta.y * delta.z + delta.z * delta.x ); +} + +/// Get the center of an axis-aligned bounding box. +B3_INLINE b3Vec3 b3AABB_Center( b3AABB a ) +{ + return b3MulSV( 0.5f, b3Add( a.upperBound, a.lowerBound ) ); +} + +/// Get the extents (half-widths) of an axis-aligned bounding box. +B3_INLINE b3Vec3 b3AABB_Extents( b3AABB a ) +{ + return b3MulSV( 0.5f, b3Sub( a.upperBound, a.lowerBound ) ); +} + +/// Get the union of two axis-aligned bounding boxes. +B3_INLINE b3AABB b3AABB_Union( b3AABB a, b3AABB b ) +{ + b3AABB out; + out.lowerBound = b3Min( a.lowerBound, b.lowerBound ); + out.upperBound = b3Max( a.upperBound, b.upperBound ); + return out; +} + +/// Add uniform padding to an axis-aligned bounding box. +B3_INLINE b3AABB b3AABB_Inflate( b3AABB a, float extension ) +{ + b3Vec3 radius = { extension, extension, extension }; + + b3AABB out; + out.lowerBound = b3Sub( a.lowerBound, radius ); + out.upperBound = b3Add( a.upperBound, radius ); + return out; +} + +/// Do two axis-aligned boxes overlap? +B3_INLINE bool b3AABB_Overlaps( b3AABB a, b3AABB b ) +{ + // No intersection if separated along one axis + if ( a.upperBound.x < b.lowerBound.x || a.lowerBound.x > b.upperBound.x ) + return false; + if ( a.upperBound.y < b.lowerBound.y || a.lowerBound.y > b.upperBound.y ) + return false; + if ( 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. +B3_INLINE b3AABB b3AABB_Transform( b3Transform transform, b3AABB a ) +{ + b3Vec3 center = b3TransformPoint( transform, b3AABB_Center( a ) ); + b3Matrix3 m = b3MakeMatrixFromQuat( transform.q ); + b3Vec3 extent = b3MulMV( b3AbsMatrix3( m ), b3AABB_Extents( a ) ); + b3AABB out = { b3Sub( center, extent ), b3Add( center, extent ) }; + return out; +} + +/// Get the closest point on an axis-aligned bounding box. +B3_INLINE b3Vec3 b3ClosestPointToAABB( b3Vec3 point, b3AABB a ) +{ + return b3Clamp( point, a.lowerBound, a.upperBound ); +} + +/// The closest points between to segments or infinite lines. +typedef struct b3SegmentDistanceResult +{ + b3Vec3 point1; + float fraction1; + b3Vec3 point2; + float fraction2; +} b3SegmentDistanceResult; + +/// Compute the closest point on the segment a-b to the target q. +B3_API b3Vec3 b3PointToSegmentDistance( b3Vec3 a, b3Vec3 b, b3Vec3 q ); + +/// Compute the closest points on two infinite lines. +B3_API b3SegmentDistanceResult b3LineDistance( b3Vec3 p1, b3Vec3 d1, b3Vec3 p2, b3Vec3 d2 ); + +/// Compute the closest points on two line segments. +B3_API b3SegmentDistanceResult b3SegmentDistance( b3Vec3 p1, b3Vec3 q1, b3Vec3 p2, b3Vec3 q2 ); + +/// Is this a valid number? Not NaN or infinity. +B3_API bool b3IsValidFloat( float a ); + +/// Is this a valid vector? Not NaN or infinity. +B3_API bool b3IsValidVec3( b3Vec3 a ); + +/// Is this a valid quaternion? Not NaN or infinity. Is normalized. +B3_API bool b3IsValidQuat( b3Quat q ); + +/// Is this a valid transform? Not NaN or infinity. Is normalized. +B3_API bool b3IsValidTransform( b3Transform a ); + +/// Is this a valid matrix? Not NaN or infinity. +B3_API bool b3IsValidMatrix3( b3Matrix3 a ); + +/// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound. +B3_API bool b3IsValidAABB( b3AABB a ); + +/// Is this AABB reasonably close to the origin? See B3_HUGE. +B3_API bool b3IsBoundedAABB( b3AABB a ); + +/// Is this AABB valid and reasonable? +B3_API bool b3IsSaneAABB( b3AABB a ); + +/// Is this a valid plane? Normal is a unit vector. Not Nan or infinity. +B3_API bool b3IsValidPlane( b3Plane a ); + +/// Is this a valid world position? Not NaN or infinity. +B3_API bool b3IsValidPosition( b3Pos p ); + +/// Is this a valid world transform? Not NaN or infinity. Rotation is normalized. +B3_API bool b3IsValidWorldTransform( b3WorldTransform t ); + +/**@}*/ // math + +/** + * @defgroup math_cpp C++ Math + * @brief Math operator overloads for C++ + * Some of the simpler ones are expanded to improve debug performance. + * See math_functions.h for details. + * @{ + */ + +#ifdef __cplusplus + +/// Vector addition. +B3_FORCE_INLINE b3Vec3& operator+=( b3Vec3& a, b3Vec3 b ) +{ + a.x += b.x; + a.y += b.y; + a.z += b.z; + return a; +} + +/// Vector subtraction. +B3_FORCE_INLINE b3Vec3& operator-=( b3Vec3& a, b3Vec3 b ) +{ + a.x -= b.x; + a.y -= b.y; + a.z -= b.z; + return a; +} + +/// Vector scaling. +B3_FORCE_INLINE b3Vec3& operator*=( b3Vec3& a, float s ) +{ + a.x *= s; + a.y *= s; + a.z *= s; + return a; +} + +/// Vector negation. +B3_FORCE_INLINE b3Vec3 operator-( b3Vec3 a ) +{ + return { -a.x, -a.y, -a.z }; +} + +/// Vector scaling. +B3_FORCE_INLINE b3Vec3 operator*( float s, b3Vec3 a ) +{ + return { s * a.x, s * a.y, s * a.z }; +} + +/// Vector scaling. +B3_FORCE_INLINE b3Vec3 operator*( b3Vec3 a, float s ) +{ + return { s * a.x, s * a.y, s * a.z }; +} + +/// Component-wise vector multiplication. +B3_FORCE_INLINE b3Vec3 operator*( b3Vec3 a, b3Vec3 b ) +{ + return { a.x * b.x, a.y * b.y, a.z * b.z }; +} + +/// Vector addition. +B3_FORCE_INLINE b3Vec3 operator+( b3Vec3 a, b3Vec3 b ) +{ + return { a.x + b.x, a.y + b.y, a.z + b.z }; +} + +/// Vector subtraction. +B3_FORCE_INLINE b3Vec3 operator-( b3Vec3 a, b3Vec3 b ) +{ + return { a.x - b.x, a.y - b.y, a.z - b.z }; +} + +#if defined( BOX3D_DOUBLE_PRECISION ) + +/// Offset a world position by a vector. +B3_FORCE_INLINE b3Pos operator+( b3Pos a, b3Vec3 b ) +{ + return { a.x + b.x, a.y + b.y, a.z + b.z }; +} + +/// Offset a world position by a vector. +B3_FORCE_INLINE b3Pos operator-( b3Pos a, b3Vec3 b ) +{ + return { a.x - b.x, a.y - b.y, a.z - b.z }; +} + +/// Delta between two world positions, demoted to float. +B3_FORCE_INLINE b3Vec3 operator-( b3Pos a, b3Pos b ) +{ + return { (float)( a.x - b.x ), (float)( a.y - b.y ), (float)( a.z - b.z ) }; +} + +#endif + +#endif + +/**@}*/ // math_cpp diff --git a/vendor/box3d/src/include/box3d/types.h b/vendor/box3d/src/include/box3d/types.h new file mode 100644 index 000000000..2c75977f0 --- /dev/null +++ b/vendor/box3d/src/include/box3d/types.h @@ -0,0 +1,3034 @@ +// SPDX-FileCopyrightText: 2025 Erin Catto +// SPDX-License-Identifier: MIT + +#pragma once + +#include "base.h" +#include "constants.h" +#include "id.h" +#include "math_functions.h" + +#include + +#define B3_DEFAULT_CATEGORY_BITS UINT64_MAX +#define B3_DEFAULT_MASK_BITS UINT64_MAX + +/// Task interface +/// This is the prototype for a Box3D task. Your task system is expected to run this callback on a worker thread, +/// exactly once per enqueue, passing back the same taskContext pointer supplied to b3EnqueueTaskCallback. +/// @ingroup world +typedef void b3TaskCallback( void* taskContext ); + +/// These functions can be provided to Box3D to invoke a task system. +/// Returns a pointer to the user's task object. May be nullptr. A nullptr indicates to Box3D that the work was executed +/// serially within the callback and there is no need to call b3FinishTaskCallback. Otherwise the returned +/// value must be non-null will be passed to b3FinishTaskCallback as the userTask. +/// @param task the Box3D task to be called by the scheduler +/// @param taskContext the Box3D context object that the scheduler must pass to the task +/// @param userContext the scheduler context object that is opaque to Box3D +/// @param taskName the Box3D task name that the scheduler can use for diagnostics +/// @ingroup world +typedef void* b3EnqueueTaskCallback( b3TaskCallback* task, void* taskContext, void* userContext, const char* taskName ); + +/// Finishes a user task object that wraps a Box3D task. This must block until the task has completed. +/// The step blocks here on the tasks it spawned, so b3World_Step holds its stack across every +/// fork/join. Drive it from a thread you can dedicate to the step, or from a fiber this callback can +/// park to free the underlying thread. In a job system that cannot park a job's stack, do not call +/// b3World_Step from inside a job: a job that blocks on its own sub-jobs without yielding its thread +/// can deadlock. The in-tree scheduler instead runs other pending tasks on the waiting thread. +/// @ingroup world +typedef void b3FinishTaskCallback( void* userTask, void* userContext ); + +typedef struct b3DebugShape b3DebugShape; + +/// The user needs to be able to create debug draw shapes for multi-pass rendering to work efficiently. +/// These user shapes are created and destroyed via callback so they can be bound to shape lifetime and scaling updates. +/// @ingroup debug_draw +typedef void* b3CreateDebugShapeCallback( const b3DebugShape* debugShape, void* userContext ); +typedef void b3DestroyDebugShapeCallback( void* userShape, void* userContext ); + +/// Optional friction mixing callback. This intentionally provides no context objects because this is called +/// from a worker thread. +/// @warning This function should not attempt to modify Box3D state or user application state. +/// @ingroup world +typedef float b3FrictionCallback( float frictionA, uint64_t userMaterialIdA, float frictionB, uint64_t userMaterialIdB ); + +/// Optional restitution mixing callback. This intentionally provides no context objects because this is called +/// from a worker thread. +/// @warning This function should not attempt to modify Box3D state or user application state. +/// @ingroup world +typedef float b3RestitutionCallback( float restitutionA, uint64_t userMaterialIdA, float restitutionB, uint64_t userMaterialIdB ); + +/// Prototype for a contact filter callback. +/// This is called when a contact pair is considered for collision. This allows you to +/// perform custom logic to prevent collision between shapes. This is only called if +/// one of the two shapes has custom filtering enabled. @see b3ShapeDef. +/// Notes: +/// - this function must be thread-safe +/// - this is only called if one of the two shapes has enabled custom filtering +/// - this is called only for awake dynamic bodies +/// Return false if you want to disable the collision +/// @warning Do not attempt to modify the world inside this callback +/// @ingroup world +typedef bool b3CustomFilterFcn( b3ShapeId shapeIdA, b3ShapeId shapeIdB, void* context ); + +/// Prototype for a pre-solve callback. +/// This is called after a contact is updated. This allows you to inspect a +/// collision before it goes to the solver. +/// Notes: +/// - this function must be thread-safe +/// - this is only called if the shape has enabled pre-solve events +/// - this may be called for awake dynamic bodies and sensors +/// - this is not called for sensors +/// Return false if you want to disable the contact this step +/// This has limited information because it is used during CCD which does not have the +/// full contact manifold. +/// @warning Do not attempt to modify the world inside this callback +/// @ingroup world +typedef bool b3PreSolveFcn( b3ShapeId shapeIdA, b3ShapeId shapeIdB, b3Pos point, b3Vec3 normal, void* context ); + +/// Prototype callback for overlap queries. +/// Called for each shape found in the query. +/// @see b3World_OverlapAABB +/// @return false to terminate the query. +/// @ingroup world +typedef bool b3OverlapResultFcn( b3ShapeId shapeId, void* context ); + +/// Prototype callback for ray casts. +/// Called for each shape found in the query. You control how the ray cast +/// proceeds by returning a float: +/// return -1: ignore this shape and continue +/// return 0: terminate the ray cast +/// return fraction: clip the ray to this point +/// return 1: don't clip the ray and continue +/// @param shapeId the shape hit by the ray +/// @param point the point of initial intersection +/// @param normal the normal vector at the point of intersection +/// @param fraction the fraction along the ray at the point of intersection +/// @param userMaterialId the shape or triangle surface type +/// @param triangleIndex the triangle index for mesh or height field shapes or -1 for other shape types +/// @param childIndex the child shape index for compound shapes +/// @param context the user context +/// @return -1 to filter, 0 to terminate, fraction to clip the ray for closest hit, 1 to continue +/// @see b3World_CastRay +/// @ingroup world +typedef float b3CastResultFcn( b3ShapeId shapeId, b3Pos point, b3Vec3 normal, float fraction, uint64_t userMaterialId, + int triangleIndex, int childIndex, void* context ); + +/// Optional world capacities that can be use to avoid run-time allocations +/// @ingroup world +typedef struct b3Capacity +{ + /// Number of expected static shapes. + int staticShapeCount; + + /// Number of expected dynamic and kinematic shapes. + int dynamicShapeCount; + + /// Number of expected static bodies. + int staticBodyCount; + + /// Number of expected dynamic and kinematic bodies. + int dynamicBodyCount; + + /// Number of expected contacts. + int contactCount; +} b3Capacity; + +/// World definition used to create a simulation world. Must be initialized using b3DefaultWorldDef. +/// @ingroup world +typedef struct b3WorldDef +{ + /// Gravity vector. Box3D has no up-vector defined. + b3Vec3 gravity; + + /// Restitution speed threshold, usually in m/s. Collisions above this + /// speed have restitution applied (will bounce). + float restitutionThreshold; + + /// Hit event speed threshold, usually in m/s. Collisions above this + /// speed can generate hit events if the shape also enables hit events. + float hitEventThreshold; + + /// Contact stiffness. Cycles per second. Increasing this increases the speed of overlap recovery, but can introduce jitter. + float contactHertz; + + /// Contact bounciness. Non-dimensional. You can speed up overlap recovery by decreasing this with + /// the trade-off that overlap resolution becomes more energetic. + float contactDampingRatio; + + /// This parameter controls how fast overlap is resolved and usually has units of meters per second. This only + /// puts a cap on the resolution speed. The resolution speed is increased by increasing the hertz and/or + /// decreasing the damping ratio. + float contactSpeed; + + /// Maximum linear speed. Usually meters per second. + float maximumLinearSpeed; + + /// Optional mixing callback for friction. The default uses sqrt(frictionA * frictionB). + b3FrictionCallback* frictionCallback; + + /// Optional mixing callback for restitution. The default uses max(restitutionA, restitutionB). + b3RestitutionCallback* restitutionCallback; + + /// Can bodies go to sleep to improve performance + bool enableSleep; + + /// Enable continuous collision + bool enableContinuous; + + /// Number of workers to use with the provided task system. Box3D performs best when using only + /// performance cores and accessing a single L2 cache. Efficiency cores and hyper-threading provide + /// little benefit and may even harm performance. + /// This is clamped to the range [1, B3_MAX_WORKERS]. Using a value above 1 will turn on multithreading. + /// If task callbacks are provided then Box3D will use the user provided task system. Otherwise Box3D + /// will create threads and use an internal scheduler. + uint32_t workerCount; + + /// function to spawn task + b3EnqueueTaskCallback* enqueueTask; + + /// function to finish a task + b3FinishTaskCallback* finishTask; + + /// User context that is provided to enqueueTask and finishTask + void* userTaskContext; + + /// User data associated with a world + void* userData; + + /// Used to create debug draw shapes. This is called when a shape is + /// first drawn using b3DebugDraw. + b3CreateDebugShapeCallback* createDebugShape; + + /// Used to destroy debug draw shapes. This is called when a shape is modified or destroyed. + b3DestroyDebugShapeCallback* destroyDebugShape; + + /// This is passed to the debug shape callbacks to provide a user context. + void* userDebugShapeContext; + + /// Optional initial capacities + b3Capacity capacity; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b3WorldDef; + +/// Use this to initialize your world definition +/// @ingroup world +B3_API b3WorldDef b3DefaultWorldDef( void ); + +/// The body simulation type. +/// Each body is one of these three types. The type determines how the body behaves in the simulation. +/// @ingroup body +typedef enum b3BodyType +{ + /// zero mass, zero velocity, may be manually moved + b3_staticBody = 0, + + /// zero mass, velocity set by user, moved by solver + b3_kinematicBody = 1, + + /// positive mass, velocity determined by forces, moved by solver + b3_dynamicBody = 2, + + /// number of body types + b3_bodyTypeCount, +} b3BodyType; + +/// Motion locks to restrict the body movement +/// @ingroup body +typedef struct b3MotionLocks +{ + /// Prevent translation along the x-axis + bool linearX; + + /// Prevent translation along the y-axis + bool linearY; + + /// Prevent translation along the z-axis + bool linearZ; + + /// Prevent rotation around the x-axis + bool angularX; + + /// Prevent rotation around the y-axis + bool angularY; + + /// Prevent rotation around the z-axis + bool angularZ; +} b3MotionLocks; + +/// A body definition holds all the data needed to construct a rigid body. +/// You can safely re-use body definitions. Shapes are added to a body after construction. +/// Body definitions are temporary objects used to bundle creation parameters. +/// Must be initialized using b3DefaultBodyDef(). +/// @ingroup body +typedef struct b3BodyDef +{ + /// The body type: static, kinematic, or dynamic. + b3BodyType type; + + /// The initial world position of the body. Bodies should be created with the desired position. + /// @note Creating bodies at the origin and then moving them nearly doubles the cost of body creation, especially + /// if the body is moved after shapes have been added. + b3Pos position; + + /// The initial world rotation of the body. + b3Quat rotation; + + /// The initial linear velocity of the body's origin. Usually in meters per second. + b3Vec3 linearVelocity; + + /// The initial angular velocity of the body. Radians per second. + b3Vec3 angularVelocity; + + /// Linear damping is used to reduce the linear velocity. The damping parameter + /// can be larger than 1 but the damping effect becomes sensitive to the + /// time step when the damping parameter is large. + /// Generally linear damping is undesirable because it makes objects move slowly + /// as if they are floating. + float linearDamping; + + /// Angular damping is used to reduce the angular velocity. The damping parameter + /// can be larger than 1.0f but the damping effect becomes sensitive to the + /// time step when the damping parameter is large. + /// Angular damping can be used to slow down rotating bodies. + float angularDamping; + + /// Scale the gravity applied to this body. Non-dimensional. + float gravityScale; + + /// Sleep speed threshold, default is 0.05 meters per second + float sleepThreshold; + + /// Optional body name for debugging. Up to B3_BODY_NAME_LENGTH characters (including null termination) + const char* name; + + /// Use this to store application specific body data. + void* userData; + + /// Motions locks to restrict linear and angular movement + b3MotionLocks motionLocks; + + /// Set this flag to false if this body should never fall asleep. + bool enableSleep; + + /// Is this body initially awake or sleeping? + bool isAwake; + + /// Treat this body as a high speed object that performs continuous collision detection + /// against dynamic and kinematic bodies, but not other bullet bodies. + /// @warning Bullets should be used sparingly. They are not a solution for general dynamic-versus-dynamic + /// continuous collision. They do not guarantee accurate collision if both bodies are fast moving because + /// the bullet does a continuous check after all non-bullet bodies have moved. You could get unlucky and have + /// the bullet body end a time step very close to a non-bullet body and the non-bullet body then moves over + /// the bullet body. In continuous collision, initial overlap is ignored to avoid freezing bodies in place. + /// I do not recommend using them for game projectiles if precise collision timing is needed. Instead consider + /// using a ray or shape cast. You can use a marching ray or shape cast for projectile that moves over time. + /// If you want a fast moving projectile to collide with a fast moving target, you need to consider the relative + /// movement in your ray or shape cast. This is out of the scope of Box3D. + /// So what are good use cases for bullets? Pinball games or games with dynamic containers that hold other objects. + /// It should be a use case where it doesn't break the game if there is a collision missed, but having them + /// captured improves the quality of the game. + bool isBullet; + + /// Used to disable a body. A disabled body does not move or collide. + bool isEnabled; + + /// This allows this body to bypass rotational speed limits. Should only be used + /// for circular objects, like wheels. + bool allowFastRotation; + + /// Enable contact recycling. True by default. Leaving this enabled improves performance + /// but may lead to ghost collision that should be avoided on characters. + bool enableContactRecycling; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b3BodyDef; + +/// Use this to initialize your body definition +/// @ingroup body +B3_API b3BodyDef b3DefaultBodyDef( void ); + +/// This is used to filter collision on shapes. It affects shape-vs-shape collision +/// and shape-versus-query collision (such as b3World_CastRay). +/// @ingroup shape +typedef struct b3Filter +{ + /// The collision category bits. Normally you would just set one bit. The category bits should + /// represent your application object types. For example: + /// @code{.cpp} + /// enum MyCategories + /// { + /// Static = 0x00000001, + /// Dynamic = 0x00000002, + /// Debris = 0x00000004, + /// Player = 0x00000008, + /// // etc + /// }; + /// @endcode + uint64_t categoryBits; + + /// The collision mask bits. This states the categories that this + /// shape would accept for collision. + /// For example, you may want your player to only collide with static objects + /// and other players. + /// @code{.c} + /// maskBits = Static | Player; + /// @endcode + uint64_t maskBits; + + /// Collision groups allow a certain group of objects to never collide (negative) + /// or always collide (positive). A group index of zero has no effect. Non-zero group filtering + /// always wins against the mask bits. + /// For example, you may want ragdolls to collide with other ragdolls but you don't want + /// ragdoll self-collision. In this case you would give each ragdoll a unique negative group index + /// and apply that group index to all shapes on the ragdoll. + int groupIndex; +} b3Filter; + +/// Use this to initialize your filter +/// @ingroup shape +B3_API b3Filter b3DefaultFilter( void ); + +/// Material properties supported per triangle on meshes and height fields +/// @ingroup shape +typedef struct b3SurfaceMaterial +{ + /// The Coulomb (dry) friction coefficient, usually in the range [0,1]. + float friction; + + /// The coefficient of restitution (bounce) usually in the range [0,1]. + /// https://en.wikipedia.org/wiki/Coefficient_of_restitution + float restitution; + + /// The rolling resistance usually in the range [0,1]. This is only used for spheres and capsules. + float rollingResistance; + + /// The tangent velocity for conveyor belts. This is local to the shape and will be projected + /// onto the contact surface. + b3Vec3 tangentVelocity; + + /// User material identifier. This is passed with query results and to friction and restitution + /// combining functions. It is not used internally. + uint64_t userMaterialId; + + /// Custom debug draw color. Ignored if 0. The low 24 bits are RGB. The high byte may + /// carry a b3DebugMaterial preset, see b3MakeDebugColor. + /// @see b3HexColor + uint32_t customColor; +} b3SurfaceMaterial; + +/// Use this to initialize your surface material +/// @ingroup shape +B3_API b3SurfaceMaterial b3DefaultSurfaceMaterial( void ); + +/// Shape type +/// @ingroup shape +typedef enum b3ShapeType +{ + /// A capsule is an extruded sphere + b3_capsuleShape, + + /// A compound shape composed of up to 64K spheres, capsules, hulls, and meshes + b3_compoundShape, + + /// A height field useful for terrain + b3_heightShape, + + /// A convex hull + b3_hullShape, + + /// A triangle soup + b3_meshShape, + + /// A sphere with an offset + b3_sphereShape, + + /// The number of shape types + b3_shapeTypeCount +} b3ShapeType; + +/// Used to create a shape +/// @ingroup shape +typedef struct b3ShapeDef +{ + /// Use this to store application specific shape data. + void* userData; + + /// Surface material used on mesh shapes per triangle. Ignored for convex shapes. Ignored for compound shapes. + b3SurfaceMaterial* materials; + + /// Surface material count. + int materialCount; + + /// The base surface material. Ignored for compound shapes. + b3SurfaceMaterial baseMaterial; + + /// The density, usually in kg/m^3. + float density; + + /// Explosion scale for b3World_Explode. non-dimensional + float explosionScale; + + /// Contact filtering data. + b3Filter filter; + + /// Enable custom filtering. Only one of the two shapes needs to enable custom filtering. See b3WorldDef. + bool enableCustomFiltering; + + /// A sensor shape generates overlap events but never generates a collision response. + /// Sensors do not have continuous collision. Instead, use a ray or shape cast for those scenarios. + /// Sensors still contribute to the body mass if they have non-zero density. + /// @note Sensor events are disabled by default. + /// @see enableSensorEvents + bool isSensor; + + /// Enable sensor events for this shape. This applies to sensors and non-sensors. False by default, even for sensors. + bool enableSensorEvents; + + /// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default. + bool enableContactEvents; + + /// Enable hit events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default. + bool enableHitEvents; + + /// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive + /// and must be carefully handled due to multithreading. Ignored for sensors. + bool enablePreSolveEvents; + + /// When shapes are created they will scan the environment for collision the next time step. This can significantly slow down + /// static body creation when there are many static shapes. + /// This is flag is ignored for dynamic and kinematic shapes which always invoke contact creation. + bool invokeContactCreation; + + /// Should the body update the mass properties when this shape is created. Default is true. + bool updateBodyMass; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; + +} b3ShapeDef; + +/// Use this to initialize your shape definition +/// @ingroup shape +B3_API b3ShapeDef b3DefaultShapeDef( void ); + +//! @cond +/// Profiling data. Times are in milliseconds. +/// @ingroup world +typedef struct b3Profile +{ + float step; + float pairs; + float collide; + float solve; + float solverSetup; + float constraints; + float prepareConstraints; + float integrateVelocities; + float warmStart; + float solveImpulses; + float integratePositions; + float relaxImpulses; + float applyRestitution; + float storeImpulses; + float splitIslands; + float transforms; + float sensorHits; + float jointEvents; + float hitEvents; + float refit; + float bullets; + float sleepIslands; + float sensors; +} b3Profile; + +/// Counters that give details of the simulation size. +/// @ingroup world +typedef struct b3Counters +{ + int bodyCount; + int shapeCount; + int contactCount; + int jointCount; + int islandCount; + int stackUsed; + int arenaCapacity; + int staticTreeHeight; + int treeHeight; + int satCallCount; + int satCacheHitCount; + int byteCount; + int taskCount; + int colorCounts[24]; + int manifoldCounts[B3_CONTACT_MANIFOLD_COUNT_BUCKETS]; + + /// Number of contacts touched by the collide pass + /// graph contacts + awake-set non-touching + int awakeContactCount; + + /// Number of contacts recycled in the most recent step. + int recycledContactCount; + + /// Maximum number of time of impact iterations + int distanceIterations; + int pushBackIterations; + int rootIterations; +} b3Counters; +//! @endcond + +/// Joint type enumeration. This is useful because all joint types use b3JointId and sometimes you +/// want to get the type of a joint. +/// @ingroup joint +typedef enum b3JointType +{ + b3_parallelJoint, + b3_distanceJoint, + b3_filterJoint, + b3_motorJoint, + b3_prismaticJoint, + b3_revoluteJoint, + b3_sphericalJoint, + b3_weldJoint, + b3_wheelJoint, +} b3JointType; + +/// Base joint definition used by all joint types. The local frames are measured from the +/// body's origin rather than the center of mass because: +/// 1. You might not know where the center of mass will be. +/// 2. If you add/remove shapes from a body and recompute the mass, the joints will be broken. +/// @ingroup joint +typedef struct b3JointDef +{ + /// User data pointer + void* userData; + + /// The first attached body + b3BodyId bodyIdA; + + /// The second attached body + b3BodyId bodyIdB; + + /// The first local joint frame + b3Transform localFrameA; + + /// The second local joint frame + b3Transform localFrameB; + + /// Force threshold for joint events + float forceThreshold; + + /// Torque threshold for joint events + float torqueThreshold; + + /// Constraint hertz (advanced feature) + float constraintHertz; + + /// Constraint damping ratio (advanced feature) + float constraintDampingRatio; + + /// Debug draw scale + float drawScale; + + /// Set this flag to true if the attached bodies should collide + bool collideConnected; + + /// Used internally to detect a valid definition. DO NOT SET. + int internalValue; +} b3JointDef; + +/// Distance joint definition. +/// Connects a point on body A with a point on body B by a segment. +/// Useful for ropes and springs. +/// @ingroup distance_joint +typedef struct b3DistanceJointDef +{ + /// Base joint definition + b3JointDef base; + + /// The rest length of this joint. Clamped to a stable minimum value. + float length; + + /// Enable the distance constraint to behave like a spring. If false + /// then the distance joint will be rigid, overriding the limit and motor. + bool enableSpring; + + /// The lower spring force controls how much tension it can sustain + float lowerSpringForce; + + /// The upper spring force controls how much compression it can sustain + float upperSpringForce; + + /// The spring linear stiffness Hertz, cycles per second + float hertz; + + /// The spring linear damping ratio, non-dimensional + float dampingRatio; + + /// Enable/disable the joint limit + bool enableLimit; + + /// Minimum length. Clamped to a stable minimum value. + float minLength; + + /// Maximum length. Must be greater than or equal to the minimum length. + float maxLength; + + /// Enable/disable the joint motor + bool enableMotor; + + /// The maximum motor force, usually in newtons + float maxMotorForce; + + /// The desired motor speed, usually in meters per second + float motorSpeed; +} b3DistanceJointDef; + +/// Use this to initialize your joint definition +/// @ingroup distance_joint +B3_API b3DistanceJointDef b3DefaultDistanceJointDef( void ); + +/// A motor joint is used to control the relative position and velocity between two bodies. +/// @ingroup motor_joint +typedef struct b3MotorJointDef +{ + /// Base joint definition + b3JointDef base; + + /// The desired linear velocity + b3Vec3 linearVelocity; + + /// The maximum motor force in newtons + float maxVelocityForce; + + /// The desired angular velocity + b3Vec3 angularVelocity; + + /// The maximum motor torque in newton-meters + float maxVelocityTorque; + + /// Linear spring hertz for position control + float linearHertz; + + /// Linear spring damping ratio + float linearDampingRatio; + + /// Maximum spring force in newtons + float maxSpringForce; + + /// Angular spring hertz for position control + float angularHertz; + + /// Angular spring damping ratio + float angularDampingRatio; + + /// Maximum spring torque in newton-meters + float maxSpringTorque; +} b3MotorJointDef; + +/// Use this to initialize your joint definition +/// @ingroup motor_joint +B3_API b3MotorJointDef b3DefaultMotorJointDef( void ); + +/// A filter joint is used to disable collision between two specific bodies. +/// @ingroup filter_joint +typedef struct b3FilterJointDef +{ + /// Base joint definition + b3JointDef base; +} b3FilterJointDef; + +/// Use this to initialize your joint definition +/// @ingroup filter_joint +B3_API b3FilterJointDef b3DefaultFilterJointDef( void ); + +/// Parallel joint definition. Constrains the angle between axis z in body A and axis z in body B +/// using a spring. Useful to keep a body upright. +/// @ingroup parallel_joint +typedef struct b3ParallelJointDef +{ + /// Base joint definition + b3JointDef base; + + /// The spring stiffness Hertz, cycles per second + float hertz; + + /// The spring damping ratio, non-dimensional + float dampingRatio; + + /// The maximum spring torque, typically in newton-meters. + float maxTorque; + +} b3ParallelJointDef; + +/// Use this to initialize your joint definition +/// @ingroup parallel_joint +B3_API b3ParallelJointDef b3DefaultParallelJointDef( void ); + +/// Prismatic joint definition. Body B may slide along the x-axis in local frame A. +/// Body B cannot rotate relative to body A. The joint translation is zero when the +/// local frame origins coincide in world space. +/// @ingroup prismatic_joint +typedef struct b3PrismaticJointDef +{ + /// Base joint definition + b3JointDef base; + + /// Enable a linear spring along the prismatic joint axis + bool enableSpring; + + /// The spring stiffness Hertz, cycles per second + float hertz; + + /// The spring damping ratio, non-dimensional + float dampingRatio; + + /// The target translation for the joint in meters. The spring-damper will drive + /// to this translation. + float targetTranslation; + + /// Enable/disable the joint limit + bool enableLimit; + + /// The lower translation limit + float lowerTranslation; + + /// The upper translation limit + float upperTranslation; + + /// Enable/disable the joint motor + bool enableMotor; + + /// The maximum motor force, typically in newtons + float maxMotorForce; + + /// The desired motor speed, typically in meters per second + float motorSpeed; +} b3PrismaticJointDef; + +/// Use this to initialize your joint definition +/// @ingroup prismatic_joint +B3_API b3PrismaticJointDef b3DefaultPrismaticJointDef( void ); + +/// Revolute joint definition. A point on body B is fixed to a point on body A. +/// Allows relative rotation about the z-axis. +/// @ingroup revolute_joint +typedef struct b3RevoluteJointDef +{ + /// Base joint definition. + b3JointDef base; + + /// The bodyB angle minus bodyA angle in the reference state (radians). + /// This defines the zero angle for the joint limit. + float targetAngle; + + /// Enable a rotational spring on the revolute hinge axis. + bool enableSpring; + + /// The spring stiffness Hertz, cycles per second. + float hertz; + + /// The spring damping ratio, non-dimensional. + float dampingRatio; + + /// A flag to enable joint limits. + bool enableLimit; + + /// The lower angle for the joint limit in radians. Minimum of -0.99*pi radians. + float lowerAngle; + + /// The upper angle for the joint limit in radians. Maximum of 0.99*pi radians. + float upperAngle; + + /// A flag to enable the joint motor. + bool enableMotor; + + /// The maximum motor torque, typically in newton-meters. + float maxMotorTorque; + + /// The desired motor speed in radians per second. + float motorSpeed; +} b3RevoluteJointDef; + +/// Use this to initialize your joint definition. +/// @ingroup revolute_joint +B3_API b3RevoluteJointDef b3DefaultRevoluteJointDef( void ); + +/// Spherical joint definition. A point on body B is fixed to a point on body A. +/// Allows rotation about the shared point. +/// @ingroup spherical_joint +typedef struct b3SphericalJointDef +{ + /// Base joint definition + b3JointDef base; + + /// Enable a rotational spring that attempts to align the two joint frames. + bool enableSpring; + + /// The spring stiffness Hertz, cycles per second. This may be clamped internally + /// according to the time step to maintain stability. Non-negative number. + float hertz; + + /// The spring damping ratio, non-dimensional. Non-negative number. + float dampingRatio; + + /// Target spring rotation, joint frame B relative to joint frame A. + b3Quat targetRotation; + + /// A flag to enable the cone limit. The cone is centered on the frameA z-axis. + bool enableConeLimit; + + /// The angle for the cone limit in radians. Valid range is [0, pi] + float coneAngle; + + /// A flag to enable the twist limit. The twist is centered on the frameB z-axis. + bool enableTwistLimit; + + /// The angle for the lower twist limit in radians. Minimum of -0.99*pi radians. + float lowerTwistAngle; + + /// The angle for the upper twist limit in radians. Maximum of 0.99*pi radians. + float upperTwistAngle; + + /// A flag to enable the joint motor + bool enableMotor; + + /// The maximum motor torque, typically in newton-meters. Non-negative number. + float maxMotorTorque; + + /// The desired motor angular velocity in radians per second. + b3Vec3 motorVelocity; +} b3SphericalJointDef; + +/// Use this to initialize your joint definition. +/// @ingroup spherical_joint +B3_API b3SphericalJointDef b3DefaultSphericalJointDef( void ); + +/// Weld joint definition +/// Connects two bodies together rigidly. This constraint provides springs to mimic +/// soft-body simulation. +/// @note The approximate solver in Box3D cannot hold many bodies together rigidly +/// @ingroup weld_joint +typedef struct b3WeldJointDef +{ + /// Base joint definition + b3JointDef base; + + /// Linear stiffness expressed as Hertz (cycles per second). Use zero for maximum stiffness. + float linearHertz; + + /// Angular stiffness as Hertz (cycles per second). Use zero for maximum stiffness. + float angularHertz; + + /// Linear damping ratio, non-dimensional. Use 1 for critical damping. + float linearDampingRatio; + + /// Linear damping ratio, non-dimensional. Use 1 for critical damping. + float angularDampingRatio; +} b3WeldJointDef; + +/// Use this to initialize your joint definition +/// @ingroup weld_joint +B3_API b3WeldJointDef b3DefaultWeldJointDef( void ); + +/// Wheel joint definition +/// Body A is the chassis and body B is the wheel. +/// The wheel rotates around the local z-axis in frame B. +/// The wheel translates along the local x-axis in frame A. +/// The wheel can optionally steer along the x-axis in frame A. +/// @ingroup wheel_joint +typedef struct b3WheelJointDef +{ + /// Base joint definition + b3JointDef base; + + /// Enable a linear spring along the local axis + bool enableSuspensionSpring; + + /// Spring stiffness in Hertz + float suspensionHertz; + + /// Spring damping ratio, non-dimensional + float suspensionDampingRatio; + + /// Enable/disable the joint linear limit + bool enableSuspensionLimit; + + /// The lower suspension translation limit + float lowerSuspensionLimit; + + /// The upper translation limit + float upperSuspensionLimit; + + /// Enable/disable the joint rotational motor + bool enableSpinMotor; + + /// The maximum motor torque, typically in newton-meters + float maxSpinTorque; + + /// The desired motor speed in radians per second + float spinSpeed; + + /// Enable steering, otherwise the steering is fixed forward + bool enableSteering; + + /// Steering stiffness in Hertz + float steeringHertz; + + /// Spring damping ratio, non-dimensional + float steeringDampingRatio; + + /// The target steering angle in radians + float targetSteeringAngle; + + /// The maximum steering torque in N*m + float maxSteeringTorque; + + /// Enable/disable the steering angular limit + bool enableSteeringLimit; + + /// The lower steering angle in radians + float lowerSteeringLimit; + + /// The upper steering angle in radians + float upperSteeringLimit; +} b3WheelJointDef; + +/// Use this to initialize your joint definition +/// @ingroup wheel_joint +B3_API b3WheelJointDef b3DefaultWheelJointDef( void ); + +/// The explosion definition is used to configure options for explosions. Explosions +/// consider shape geometry when computing the impulse. +/// @ingroup world +typedef struct b3ExplosionDef +{ + /// Mask bits to filter shapes + uint64_t maskBits; + + /// The center of the explosion in world space + b3Pos position; + + /// The radius of the explosion + float radius; + + /// The falloff distance beyond the radius. Impulse is reduced to zero at this distance. + float falloff; + + /// Impulse per unit area. This applies an impulse according to the shape area that + /// is facing the explosion. Explosions only apply to spheres, capsules, and hulls. This + /// may be negative for implosions. + float impulsePerArea; +} b3ExplosionDef; + +/// Use this to initialize your explosion definition +/// @ingroup world +B3_API b3ExplosionDef b3DefaultExplosionDef( void ); + +/** + * @defgroup event Events + * World event types. + * + * Events are used to collect events that occur during the world time step. These events + * are then available to query after the time step is complete. This is preferable to callbacks + * because Box3D uses multithreaded simulation. + * + * Also when events occur in the simulation step it may be problematic to modify the world, which is + * often what applications want to do when events occur. + * + * With event arrays, you can scan the events in a loop and modify the world. However, you need to be careful + * that some event data may become invalid. There are several samples that show how to do this safely. + * + * @{ + */ + +/// A begin-touch event is generated when a shape starts to overlap a sensor shape. +typedef struct b3SensorBeginTouchEvent +{ + /// The id of the sensor shape + b3ShapeId sensorShapeId; + + /// The id of the shape that began touching the sensor shape + b3ShapeId visitorShapeId; +} b3SensorBeginTouchEvent; + +/// An end touch event is generated when a shape stops overlapping a sensor shape. +/// These include things like setting the transform, destroying a body or shape, or changing +/// a filter. You will also get an end event if the sensor or visitor are destroyed. +/// Therefore you should always confirm the shape id is valid using b3Shape_IsValid. +typedef struct b3SensorEndTouchEvent +{ + /// The id of the sensor shape + /// @warning this shape may have been destroyed + /// @see b3Shape_IsValid + b3ShapeId sensorShapeId; + + /// The id of the shape that stopped touching the sensor shape + /// @warning this shape may have been destroyed + /// @see b3Shape_IsValid + b3ShapeId visitorShapeId; +} b3SensorEndTouchEvent; + +/// Sensor events are buffered in the world and are available +/// as begin/end overlap event arrays after the time step is complete. +/// Note: these may become invalid if bodies and/or shapes are destroyed +typedef struct b3SensorEvents +{ + /// Array of sensor begin touch events + b3SensorBeginTouchEvent* beginEvents; + + /// Array of sensor end touch events + b3SensorEndTouchEvent* endEvents; + + /// The number of begin touch events + int beginCount; + + /// The number of end touch events + int endCount; +} b3SensorEvents; + +/// A begin-touch event is generated when two shapes begin touching. +typedef struct b3ContactBeginTouchEvent +{ + /// Id of the first shape + b3ShapeId shapeIdA; + + /// Id of the second shape + b3ShapeId shapeIdB; + + /// The transient contact id. This contact may be destroyed automatically when the world is modified or simulated. + /// Use b3Contact_IsValid before using this id. + b3ContactId contactId; +} b3ContactBeginTouchEvent; + +/// An end touch event is generated when two shapes stop touching. +/// You will get an end event if you do anything that destroys contacts previous to the last +/// world step. These include things like setting the transform, destroying a body +/// or shape, or changing a filter or body type. +typedef struct b3ContactEndTouchEvent +{ + /// Id of the first shape + /// @warning this shape may have been destroyed + /// @see b3Shape_IsValid + b3ShapeId shapeIdA; + + /// Id of the first shape + /// @warning this shape may have been destroyed + /// @see b3Shape_IsValid + b3ShapeId shapeIdB; + + /// Id of the contact. + /// @warning this contact may have been destroyed + /// @see b3Contact_IsValid + b3ContactId contactId; +} b3ContactEndTouchEvent; + +/// A hit touch event is generated when two shapes collide with a speed faster than the hit speed threshold. +/// This may be reported for speculative contacts that have a confirmed impulse. +typedef struct b3ContactHitEvent +{ + /// Id of the first shape + b3ShapeId shapeIdA; + + /// Id of the second shape + b3ShapeId shapeIdB; + + /// Id of the contact. + /// @warning this contact may have been destroyed + /// @see b3Contact_IsValid + b3ContactId contactId; + + /// Point where the shapes hit at the beginning of the time step. + /// This is a mid-point between the two surfaces. It could be at speculative + /// point where the two shapes were not touching at the beginning of the time step. + b3Pos point; + + /// Normal vector pointing from shape A to shape B + b3Vec3 normal; + + /// The speed the shapes are approaching. Always positive. Typically in meters per second. + float approachSpeed; + + /// User material on shape A + uint64_t userMaterialIdA; + + /// User material on shape B + uint64_t userMaterialIdB; + +} b3ContactHitEvent; + +/// Contact events are buffered in the world and are available +/// as event arrays after the time step is complete. +/// Note: these may become invalid if bodies and/or shapes are destroyed +typedef struct b3ContactEvents +{ + /// Array of begin touch events + b3ContactBeginTouchEvent* beginEvents; + + /// Array of end touch events + b3ContactEndTouchEvent* endEvents; + + /// Array of hit events + b3ContactHitEvent* hitEvents; + + /// Number of begin touch events + int beginCount; + + /// Number of end touch events + int endCount; + + /// Number of hit events + int hitCount; +} b3ContactEvents; + +/// Body move events triggered when a body moves. +/// Triggered when a body moves due to simulation. Not reported for bodies moved by the user. +/// This also has a flag to indicate that the body went to sleep so the application can also +/// sleep that actor/entity/object associated with the body. +/// On the other hand if the flag does not indicate the body went to sleep then the application +/// can treat the actor/entity/object associated with the body as awake. +/// This is an efficient way for an application to update game object transforms rather than +/// calling functions such as b3Body_GetTransform() because this data is delivered as a contiguous array +/// and it is only populated with bodies that have moved. +/// @note If sleeping is disabled all dynamic and kinematic bodies will trigger move events. +typedef struct b3BodyMoveEvent +{ + /// The body user data. + void* userData; + + /// The body transform. + b3WorldTransform transform; + + /// The body id. + b3BodyId bodyId; + + /// Did the body fall asleep this time step? + bool fellAsleep; +} b3BodyMoveEvent; + +/// Body events are buffered in the world and are available +/// as event arrays after the time step is complete. +/// Note: this data becomes invalid if bodies are destroyed +typedef struct b3BodyEvents +{ + /// Array of move events + b3BodyMoveEvent* moveEvents; + + /// Number of move events + int moveCount; +} b3BodyEvents; + +/// Joint events report joints that are awake and have a force and/or torque exceeding the threshold +/// The observed forces and torques are not returned for efficiency reasons. +typedef struct b3JointEvent +{ + /// The joint id + b3JointId jointId; + + /// The user data from the joint for convenience + void* userData; +} b3JointEvent; + +/// Joint events are buffered in the world and are available +/// as event arrays after the time step is complete. +/// Note: this data becomes invalid if joints are destroyed +typedef struct b3JointEvents +{ + /// Array of events + b3JointEvent* jointEvents; + + /// Number of events + int count; +} b3JointEvents; + +/// The contact data for two shapes. By convention the manifold normal points +/// from shape A to shape B. +/// @see b3Shape_GetContactData() and b3Body_GetContactData() +typedef struct b3ContactData +{ + /// The contact id. You may hold onto this to track a contact across time steps. + /// This id may become orphaned. Use b3Contact_IsValid before using it for other functions. + b3ContactId contactId; + + /// The first shape id. + b3ShapeId shapeIdA; + + /// The second shape id. + b3ShapeId shapeIdB; + + /// The contact manifold. This points to internal data and may become invalid. Do not store + /// this pointer. + const struct b3Manifold* manifolds; + + /// The number of contact manifolds. For mesh and height-field collision there can be multiple manifolds. + int manifoldCount; +} b3ContactData; + +/**@}*/ // event + +/** + * @defgroup query Query + * @brief Query types and functions + * + * Queries include ray casts, shapes casts, overlap, distance, and time of impact. + * @{ + */ + +/// The query filter is used to filter collisions between queries and shapes. For example, +/// you may want a ray-cast representing a projectile to hit players and the static environment +/// but not debris. +typedef struct b3QueryFilter +{ + /// The collision category bits of this query. Normally you would just set one bit. + uint64_t categoryBits; + + /// The collision mask bits. This states the shape categories that this + /// query would accept for collision. + uint64_t maskBits; + + /// Optional id combined with @ref name to identify this query in a recording, e.g. an entity id. + /// Need not be unique on its own. 0 with a null name means untagged. Ignored when not recording. + uint64_t id; + + /// Optional label combined with @ref id to identify this query, e.g. "bullet". Need not be unique + /// on its own. The recorder hashes (id, name) into one stable key the viewer tracks the query by, + /// so the same id and name pair identifies the same query across frames. NULL means none. Ignored + /// when not recording. + const char* name; +} b3QueryFilter; + +/// Use this to initialize your query filter +B3_API b3QueryFilter b3DefaultQueryFilter( void ); + +/// Low level ray cast input data. +typedef struct b3RayCastInput +{ + /// Start point of the ray cast. + b3Vec3 origin; + + /// Translation of the ray cast. + /// end = start + translation. + b3Vec3 translation; + + /// The maximum fraction of the translation to consider, typically 1 + float maxFraction; +} b3RayCastInput; + +/// Result from b3World_RayCastClosest. +typedef struct b3RayResult +{ + /// The shape hit. + b3ShapeId shapeId; + + /// The world point of the hit. + b3Pos point; + + /// The world normal of the shape surface at the hit point. + b3Vec3 normal; + + /// The user material id at the hit point. This can be per triangle + /// if the shape is a mesh, height-field, or compound with child mesh. + uint64_t userMaterialId; + + /// The fraction of the input ray. + float fraction; + + /// The triangle index if the shape is a mesh, height-field, or compound with + /// child mesh. + int triangleIndex; + + /// The child index if the shape is a compound. + int childIndex; + + /// The number of BVH nodes visited. Diagnostic. + int nodeVisits; + + /// The number of BVH leaves visited. Diagnostic. + int leafVisits; + + /// Did the ray hit? If false, all other data is invalid. + bool hit; +} b3RayResult; + +/// A shape proxy is used by the GJK algorithm. It can represent a convex shape. +typedef struct b3ShapeProxy +{ + /// The point cloud. + const b3Vec3* points; + + /// The number of points. Do not exceed B3_MAX_SHAPE_CAST_POINTS. + int count; + + /// The external radius of the point cloud. + float radius; +} b3ShapeProxy; + +/// Low level shape cast input in generic form. This allows casting an arbitrary point +/// cloud wrap with a radius. For example, a sphere is a single point with a non-zero radius. +/// A capsule is two points with a non-zero radius. A box is four points with a zero radius. +typedef struct b3ShapeCastInput +{ + /// A generic query shape. + b3ShapeProxy proxy; + + /// The translation of the shape cast. + b3Vec3 translation; + + /// The maximum fraction of the translation to consider, typically 1. + float maxFraction; + + /// Allow shape cast to encroach when initially touching. This only works if the radius is greater than zero. + bool canEncroach; +} b3ShapeCastInput; + +/// Input for sweeping an AABB through a dynamic tree. The box is in the tree's world float frame. +/// The caller folds the cast shape radius and any world origin into the box, so the tree traversal +/// stays a conservative box sweep and the precise narrow phase happens per shape in the callback. +typedef struct b3BoxCastInput +{ + /// The AABB to cast, in the tree's frame. + b3AABB box; + + /// The sweep translation. + b3Vec3 translation; + + /// The maximum fraction of the translation to consider, typically 1. + float maxFraction; +} b3BoxCastInput; + +/// Low level ray cast or shape-cast output data. +typedef struct b3CastOutput +{ + /// The surface normal at the hit point. + b3Vec3 normal; + + /// The surface hit point. + b3Vec3 point; + + /// The fraction of the input translation at collision. + float fraction; + + /// The number of iterations used. + int iterations; + + /// The index of the mesh or height field triangle hit. + int triangleIndex; + + /// The index of the compound child shape. + int childIndex; + + /// The material index. May be -1 for null. + int materialIndex; + + /// Did the cast hit? + bool hit; +} b3CastOutput; + +#if defined( BOX3D_DOUBLE_PRECISION ) + +/// Ray cast or shape-cast output in world space. The hit point is a world position so the result +/// stays precise far from the world origin. Mirrors b3CastOutput with a double precision point. +typedef struct b3WorldCastOutput +{ + /// The surface normal at the hit point. + b3Vec3 normal; + + /// The surface hit point in world space. + b3Pos point; + + /// The fraction of the input translation at collision. + float fraction; + + /// The number of iterations used. + int iterations; + + /// The index of the mesh or height field triangle hit. + int triangleIndex; + + /// The index of the compound child shape. + int childIndex; + + /// The material index. May be -1 for null. + int materialIndex; + + /// Did the cast hit? + bool hit; +} b3WorldCastOutput; + +#else + +/// Same type in single precision. +typedef b3CastOutput b3WorldCastOutput; + +#endif + +/// Body cast result for ray and shape casts. +typedef struct b3BodyCastResult +{ + /// The shape hit. + b3ShapeId shapeId; + + /// The world point on the shape surface. + b3Pos point; + + /// The world normal vector on the shape surface. + b3Vec3 normal; + + /// The fraction along the ray hit. + /// hit point = origin + fraction * translation + float fraction; + + /// The triangle index if the shape is a mesh or height-field. + int triangleIndex; + + /// The user material id at the hit point. This can be per triangle + /// if the shape is a mesh, height-field, or compound with child mesh. + uint64_t userMaterialId; + + /// The number of iterations used. Diagnostic. + int iterations; + + /// Did the cast hit? If false, all other fields are invalid. + bool hit; +} b3BodyCastResult; + +/// Used to warm start the GJK simplex. If you call this function multiple times with nearby +/// transforms this might improve performance. Otherwise you can zero initialize this. +/// The distance cache must be initialized to zero on the first call. +/// Users should generally just zero initialize this structure for each call. +typedef struct b3SimplexCache +{ + /// Value use to compare length, area, volume of two simplexes. + float metric; + + // todo use an index of 0xFF as a sentinel and remove the count + /// The number of stored simplex points + uint16_t count; + + /// The cached simplex indices on shape A + uint8_t indexA[4]; + + /// The cached simplex indices on shape B + uint8_t indexB[4]; + +} b3SimplexCache; + +static const b3SimplexCache b3_emptyDistanceCache = B3_ZERO_INIT; + +/// Input parameters for b3ShapeCast +typedef struct b3ShapeCastPairInput +{ + b3ShapeProxy proxyA; ///< The proxy for shape A + b3ShapeProxy proxyB; ///< The proxy for shape B + b3Transform transform; ///< Transform of shape B in shape A's frame, the relative pose B in A + b3Vec3 translationB; ///< The translation of shape B, in A's frame + float maxFraction; ///< The fraction of the translation to consider, typically 1 + bool canEncroach; ///< Allows shapes with a radius to move slightly closer if already touching +} b3ShapeCastPairInput; + +/// Input for b3ShapeDistance +typedef struct b3DistanceInput +{ + /// The proxy for shape A + b3ShapeProxy proxyA; + + /// The proxy for shape B + b3ShapeProxy proxyB; + + /// Transform of shape B in shape A's frame, the relative pose B in A + /// (b3InvMulWorldTransforms( worldA, worldB )). The query is origin independent and runs in frame A. + b3Transform transform; + + /// Should the proxy radius be considered? + bool useRadii; +} b3DistanceInput; + +/// Output for b3ShapeDistance +typedef struct b3DistanceOutput +{ + b3Vec3 pointA; ///< Closest point on shapeA, in shape A's frame + b3Vec3 pointB; ///< Closest point on shapeB, in shape A's frame + b3Vec3 normal; ///< A to B normal in shape A's frame. Invalid if distance is zero. + float distance; ///< The final distance, zero if overlapped + int iterations; ///< Number of GJK iterations used + int simplexCount; ///< The number of simplexes stored in the simplex array +} b3DistanceOutput; + +/// Simplex vertex for debugging the GJK algorithm +typedef struct b3SimplexVertex +{ + b3Vec3 wA; ///< support point in proxyA + b3Vec3 wB; ///< support point in proxyB + b3Vec3 w; ///< wB - wA + float a; ///< barycentric coordinates + int indexA; ///< wA index + int indexB; ///< wB index +} b3SimplexVertex; + +/// Simplex from the GJK algorithm +typedef struct b3Simplex +{ + b3SimplexVertex vertices[4]; ///< vertices + int count; ///< number of valid vertices +} b3Simplex; + +/// This describes the motion of a body/shape for TOI computation. Shapes are defined with respect to the body origin, +/// which may not coincide with the center of mass. However, to support dynamics we must interpolate the center of mass +/// position. +typedef struct b3Sweep +{ + b3Vec3 localCenter; ///< Local center of mass position + b3Vec3 c1; ///< Starting center of mass world position + b3Vec3 c2; ///< Ending center of mass world position + b3Quat q1; ///< Starting world rotation + b3Quat q2; ///< Ending world rotation +} b3Sweep; + +/// Time of impact input +typedef struct b3TOIInput +{ + b3ShapeProxy proxyA; ///< The proxy for shape A + b3ShapeProxy proxyB; ///< The proxy for shape B + b3Sweep sweepA; ///< The movement of shape A + b3Sweep sweepB; ///< The movement of shape B + float maxFraction; ///< Defines the sweep interval [0, tMax] +} b3TOIInput; + +/// Describes the TOI output +typedef enum b3TOIState +{ + b3_toiStateUnknown, + b3_toiStateFailed, + b3_toiStateOverlapped, + b3_toiStateHit, + b3_toiStateSeparated +} b3TOIState; + +/// Time of impact output +typedef struct b3TOIOutput +{ + /// The type of result + b3TOIState state; + + /// The hit point + b3Vec3 point; + + /// The hit normal + b3Vec3 normal; + + /// The sweep time of the collision + float fraction; + + /// The final distance + float distance; + + /// Number of outer iterations + int distanceIterations; + + /// Total number of push back iterations + int pushBackIterations; + + /// Total number of root iterations + int rootIterations; + + /// Indicates that the time of impact detected initial + /// overlap and used a fallback sphere as a last ditch effort + /// to prevent tunneling. + bool usedFallback; +} b3TOIOutput; + +/**@}*/ // query + +/** + * @defgroup tree Dynamic Tree + * The dynamic tree is a binary AABB tree to organize and query large numbers of geometric objects + * + * Box3D uses the dynamic tree internally to sort collision shapes into a binary bounding volume hierarchy. + * This data structure may have uses in games for organizing other geometry data and may be used independently + * of Box3D rigid body simulation. + * + * A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt. + * A dynamic tree arranges data in a binary tree to accelerate + * queries such as AABB queries and ray casts. Leaf nodes are proxies + * with an AABB. These are used to hold a user collision object. + * Nodes are pooled and relocatable, so I use node indices rather than pointers. + * The dynamic tree is made available for advanced users that would like to use it to organize + * spatial game data besides rigid bodies. + * @{ + */ + +/// Flags for tree nodes. For internal usage. +typedef enum b3TreeNodeFlags +{ + b3_allocatedNode = 0x0001, + b3_enlargedNode = 0x0002, + b3_leafNode = 0x0004, +} b3TreeNodeFlags; + +/// Tree node child indices. For internal usage. +typedef struct b3TreeNodeChildren +{ + int child1; ///< child node index 1 + int child2; ///< child node index 2 +} b3TreeNodeChildren; + +/// A node in the dynamic tree. This is private data placed here for performance reasons. +/// todo test padding to 64 bytes to avoid straddling cache lines +typedef struct b3TreeNode +{ + /// The node bounding box + b3AABB aabb; // 24 + + /// Category bits for collision filtering + uint64_t categoryBits; // 8 + + union + { + /// Children (internal node) + b3TreeNodeChildren children; + + /// User data (leaf node) + uint64_t userData; + }; // 8 + + union + { + /// The node parent index (allocated node) + int parent; + + /// The node freelist next index (free node) + int next; + }; // 4 + + /// Height of the node. Leaves have a height of 0. + uint16_t height; // 2 + + /// @see b3TreeNodeFlags + uint16_t flags; // 2 +} b3TreeNode; + +/// Dynamic tree version for compatibility testing. +#define B3_DYNAMIC_TREE_VERSION 0x93EDAF889FD30B4Aull + +/// The dynamic tree structure. This should be considered private data. +/// It is placed here for performance reasons. +typedef struct b3DynamicTree +{ + /// The dynamic tree version. Always the first field. Useful + /// if the tree is serialized. + uint64_t version; + + /// The tree nodes + b3TreeNode* nodes; + + /// The root index + int root; + + /// The number of nodes + int nodeCount; + + /// The allocated node space + int nodeCapacity; + + /// Number of proxies created + int proxyCount; + + /// Node free list + int freeList; + + /// Leaf indices for rebuild + int* leafIndices; + + /// Leaf bounding boxes for rebuild + b3AABB* leafBoxes; + + /// Leaf bounding box centers for rebuild + b3Vec3* leafCenters; + + /// Bins for sorting during rebuild + int* binIndices; + + /// Allocated space for rebuilding + int rebuildCapacity; +} b3DynamicTree; + +/// These are performance results returned by dynamic tree queries. +typedef struct b3TreeStats +{ + /// Number of internal nodes visited during the query + int nodeVisits; + + /// Number of leaf nodes visited during the query + int leafVisits; +} b3TreeStats; + +/// This function receives proxies found in the AABB query. +/// @return true if the query should continue +typedef bool b3TreeQueryCallbackFcn( int proxyId, uint64_t userData, void* context ); + +/// This function receives the minimum distance squared so far and proxy to check in the closest query. +/// @return minimum distance squared to user objects in the proxy +typedef float b3TreeQueryClosestCallbackFcn( float distanceSqrMin, int proxyId, uint64_t userData, void* context ); + +/// This function receives clipped AABB cast input for a proxy. The function returns the new cast +/// fraction. +/// - return a value of 0 to terminate the cast +/// - return a value less than input->maxFraction to clip the cast +/// - return a value of input->maxFraction to continue the cast without clipping +typedef float b3TreeBoxCastCallbackFcn( const b3BoxCastInput* input, int proxyId, uint64_t userData, void* context ); + +/// This function receives clipped ray cast input for a proxy. The function +/// returns the new ray fraction. +/// - return a value of 0 to terminate the ray cast +/// - return a value less than input->maxFraction to clip the ray +/// - return a value of input->maxFraction to continue the ray cast without clipping +typedef float b3TreeRayCastCallbackFcn( const b3RayCastInput* input, int proxyId, uint64_t userData, void* context ); + +/**@}*/ // tree + +/** + * @defgroup character Character Mover + * Character movement solver + * @{ + */ + +/// The plane between a character mover and a shape +typedef struct b3PlaneResult +{ + /// Outward pointing plane. + b3Plane plane; + + /// Closest point on the shape. May not be unique. + b3Vec3 point; + +} b3PlaneResult; + +/// These are collision planes that can be fed to b3SolvePlanes. Normally +/// this is assembled by the user from plane results in b3PlaneResult. +typedef struct b3CollisionPlane +{ + /// The collision plane between the mover and some shape. + b3Plane plane; + + /// Setting this to FLT_MAX makes the plane as rigid as possible. Lower values can + /// make the plane collision soft. Usually in meters. + float pushLimit; + + /// The push on the mover determined by b3SolvePlanes. Usually in meters. + float push; + + /// Indicates if b3ClipVector should clip against this plane. Should be false for soft collision. + bool clipVelocity; +} b3CollisionPlane; + +/// Result returned by b3SolvePlanes. +typedef struct b3PlaneSolverResult +{ + /// The final relative translation. + b3Vec3 delta; + + /// The number of iterations used by the plane solver. For diagnostics. + int iterationCount; +} b3PlaneSolverResult; + +/// Body plane result for movers. +typedef struct b3BodyPlaneResult +{ + /// The shape id on the body. + b3ShapeId shapeId; + + /// The plane result. + b3PlaneResult result; +} b3BodyPlaneResult; + +/// Used to collect collision planes for character movers. +/// Return true to continue gathering planes. +typedef bool b3PlaneResultFcn( b3ShapeId shapeId, const b3PlaneResult* plane, int planeCount, void* context ); + +/// Used to filter shapes for shape casting character movers. +/// Return true to accept the collision +typedef bool b3MoverFilterFcn( b3ShapeId shapeId, void* context ); + +/**@}*/ // mover + +/** + * @defgroup geometry Geometry + * @brief Geometry types and algorithms + * + * Definitions of spheres, capsules, hulls, meshes, height fields, and compounds. + * @{ + */ + +/// This holds the mass data computed for a shape. +typedef struct b3MassData +{ + /// The shape mass + float mass; + + /// The local center of mass position. + b3Vec3 center; + + /// The inertia tensor about the shape center of mass. + b3Matrix3 inertia; +} b3MassData; + +/** + * @defgroup sphere Sphere + * @brief Sphere primitive + * @{ + */ + +/// A solid sphere +typedef struct b3Sphere +{ + /// The local center + b3Vec3 center; + + /// The radius + float radius; +} b3Sphere; + +/**@}*/ // sphere + +/** + * @defgroup capsule Capsule + * @brief Capsule primitive + * @{ + */ + +/// A solid capsule can be viewed as two hemispheres connected +/// by a rectangle. +typedef struct b3Capsule +{ + /// Local center of the first hemisphere + b3Vec3 center1; + + /// Local center of the second hemisphere + b3Vec3 center2; + + /// The radius of the hemispheres + float radius; +} b3Capsule; + +/**@}*/ // capsule + +/** + * @defgroup hull Convex Hull + * @brief Convex hull primitive + * @{ + */ + +/// A hull vertex. Identified by a half-edge with this +/// vertex as its tail. +typedef struct b3HullVertex +{ + /// A half-edge that has this vertex as the origin + /// Can be used along with edge twins and winding order + /// to traverse all the edges connected to this vertex. + uint8_t edge; +} b3HullVertex; + +/// Half-edge for hull data structure +typedef struct b3HullHalfEdge +{ + /// Next edge index CCW + uint8_t next; + + /// Twin edge index + uint8_t twin; + + /// index of origin vertex and point + uint8_t origin; + + /// Face to the left of this edge + uint8_t face; +} b3HullHalfEdge; + +/// A hull face. Hulls use a half-edge data structure, so a face +/// can be determined from a single half-edge index. +typedef struct b3HullFace +{ + /// An arbitrary half-edge on this face + uint8_t edge; +} b3HullFace; + +/// 64-bit hull version. Useful for validating serialized data. +#define B3_HULL_VERSION 0x9D4716CE3793900Eull + +/// A convex hull. +/// @note This data structure has data hanging off the end and cannot be directly copied. +typedef struct b3HullData +{ + /// Version must be first and match B3_HULL_VERSION + uint64_t version; + + /// The total number of bytes for this hull. + int byteCount; + + /// Hash of this hull (this field is zero when the hash is computed). + uint32_t hash; + + /// Axis-aligned box in local space. + b3AABB aabb; + + /// Surface area, typically in squared meters. + float surfaceArea; + + /// Volume, typically in m^3. + float volume; + + /// The radius of the largest sphere at the center. + float innerRadius; + + /// The local centroid + b3Vec3 center; + + /// The inertia tensor about the centroid. + b3Matrix3 centralInertia; + + /// The vertex count. + int vertexCount; + + /// Offset of the vertex array in bytes from the struct address. + int vertexOffset; + + /// Offset of the point array in bytes from the struct address. + int pointOffset; + + /// This is the half-edge count (double the edge count) + int edgeCount; + + /// Offset of the edge array in bytes from the struct address. + int edgeOffset; + + /// The face count. Hulls faces are convex polygons. + int faceCount; + + /// Offset of the face array in bytes from the struct address. + int faceOffset; + + /// Offset of the face plane array in bytes from the struct address. + int planeOffset; + + /// Explicit padding. Hull identity is a content hash and memcmp over raw bytes, + /// so there must be no unnamed padding for struct copies to scramble. + int padding; +} b3HullData; + +/// Efficient box hull +typedef struct b3BoxHull +{ + /// The embedded hull. So the offsets index into the arrays that follow. + b3HullData base; + b3HullVertex boxVertices[8]; ///< Box vertices. + b3Vec3 boxPoints[8]; ///< Box points. + b3HullHalfEdge boxEdges[24]; ///< Box half-edges. + b3HullFace boxFaces[6]; ///< Box faces. + uint8_t padding[2]; ///< Explicit padding, see b3HullData::padding. + b3Plane boxPlanes[6]; ///< Box face planes. +} b3BoxHull; + +/**@}*/ // hull + +/** + * @defgroup mesh Triangle Mesh + * @brief Triangle mesh collision shape + * @{ + */ + +/// This is used to create a re-usable collision mesh +typedef struct b3MeshDef +{ + /// Triangle vertices + b3Vec3* vertices; + + /// Triangle vertex indices. 3 for each triangle. + int32_t* indices; + + /// Triangle material index. 1 per triangle. Indexes into b3ShapeDef::materials. + /// This allows different run-time material data to be associated with different + /// instances of this mesh. + uint8_t* materialIndices; + + /// Tolerance for vertex welding in length units. + float weldTolerance; + + /// The vertex count. Must be 3 or more. + int vertexCount; + + /// The triangle count. Must be 1 or more. + int triangleCount; + + /// Optionally weld nearby vertices. + bool weldVertices; + + /// Use the median split instead of SAH to speed up mesh creation. Good + /// for meshes that are structured like a grid. + bool useMedianSplit; + + /// Compute triangle adjacency information using shared edges + bool identifyEdges; +} b3MeshDef; + +/// 64-bit mesh version. Useful for validating serialized data. +#define B3_MESH_VERSION 0xABD11AB62A6E886Dull + +/// Triangle mesh edge flags. +typedef enum b3MeshEdgeFlags +{ + b3_concaveEdge1 = 0x01, + b3_concaveEdge2 = 0x02, + b3_concaveEdge3 = 0x04, + + b3_inverseConcaveEdge1 = 0x10, + b3_inverseConcaveEdge2 = 0x20, + b3_inverseConcaveEdge3 = 0x40, + + b3_allConcaveEdges = b3_concaveEdge1 | b3_concaveEdge2 | b3_concaveEdge3, + + b3_flatEdge1 = b3_concaveEdge1 | b3_inverseConcaveEdge1, + b3_flatEdge2 = b3_concaveEdge2 | b3_inverseConcaveEdge2, + b3_flatEdge3 = b3_concaveEdge3 | b3_inverseConcaveEdge3, + + b3_allFlatEdges = b3_flatEdge1 | b3_flatEdge2 | b3_flatEdge3, + +} b3MeshEdgeFlags; + +/// A mesh triangle. +typedef struct b3MeshTriangle +{ + int32_t index1; ///< Index of vertex 1. + int32_t index2; ///< Index of vertex 2. + int32_t index3; ///< Index of vertex 3. +} b3MeshTriangle; + +/// A mesh BVH node. +typedef struct b3MeshNode +{ + /// The lower bound of the node AABB. Strategic placement for SIMD. + b3Vec3 lowerBound; + + /// Anonymous union. + union + { + /// Internal node + struct + { + /// Split axis. 0, 1, or 2. + uint32_t axis : 2; + /// Offset of the second child node. + uint32_t childOffset : 30; + } asNode; + + /// Leaf node + struct + { + /// Aligned with axis above and has value of 3 if this is a leaf. + uint32_t type : 2; + + /// The number of triangles for this leaf node. + uint32_t triangleCount : 30; + } asLeaf; + } data; + + /// The upper bound of the node AABB. Strategic placement for SIMD. + b3Vec3 upperBound; + + /// The index of the leaf triangles. + uint32_t triangleOffset; +} b3MeshNode; + +/// This is a sorted triangle collision bounding volume hierarchy. +/// @note This struct has data hanging off the end and cannot be directly copied. +typedef struct b3MeshData +{ + /// Version must be first. + uint64_t version; + + /// The total number of bytes for this mesh. + int byteCount; + + /// Hash of this mesh (this field is zero when the hash is computed) + uint32_t hash; + + /// Local axis-aligned box. + b3AABB bounds; + + /// Combined surface area of all triangles. Single-sided. + float surfaceArea; + + /// The height of the bounding volume hierarchy. + int treeHeight; + + /// The number of degenerate triangles. Diagnostic. + int degenerateCount; + + /// Offset of the node array in bytes from the struct address. + int nodeOffset; + + /// The number of BVH nodes. + int nodeCount; + + /// Offset of the vertex array in bytes from the struct address. + int vertexOffset; + + /// The number of vertices. + int vertexCount; + + /// Offset of the triangle array in bytes from the struct address. + int triangleOffset; + + /// The number of triangles. + int triangleCount; + + /// Offset of the material array in bytes from the struct address. + int materialOffset; + + /// The number of materials. + int materialCount; + + /// Offset of the triangle flag array in bytes from the struct address. + int flagsOffset; +} b3MeshData; + +/// This allows mesh data to be re-used with different scales. +typedef struct b3Mesh +{ + /// Immutable pointer to the mesh data. + const b3MeshData* data; + + /// This scale may be non-uniform and have negative components. However, + /// no component may be very small in magnitude. + b3Vec3 scale; +} b3Mesh; + +/**@}*/ // mesh + +/** + * @defgroup height_field Height Field + * @brief Height field collision shape + * @{ + */ + +/// Data used to create a height field +typedef struct b3HeightFieldDef +{ + /// Grid point heights + /// count = countX * countZ + float* heights; + + /// Grid cell material + /// A value of 0xFF is reserved for holes + /// count = (countX - 1) * (countZ - 1) + uint8_t* materialIndices; + + /// The height field scale. All components must be positive values. + b3Vec3 scale; + + /// The number of grid lines along the x-axis. + int countX; + + /// The number of grid lines along the z-axis. + int countZ; + + /// Global minimum and maximum heights used for quantization. This is important + /// if you want height fields to be placed next to each other and line up exactly. + /// In that case, both height fields should use the same minimum and maximum heights. + /// All height values are clamped to this range. + /// These values are in unscaled space. + float globalMinimumHeight; + + /// The maximum. + float globalMaximumHeight; + + /// Use clock-wise winding. This effectively inverts the height-field along the y-axis. + bool clockwiseWinding; +} b3HeightFieldDef; + +/// This material index is used to designate holes in a height field. +#define B3_HEIGHT_FIELD_HOLE 0xFF + +/// 64-bit height-field version. Useful for validating serialized data. +#define B3_HEIGHT_FIELD_VERSION 0x8B18CBD138A6BC84ull + +/// A height field with compressed storage. +/// @note This data structure has data hanging off the end and cannot be directly copied. +typedef struct b3HeightFieldData +{ + /// Version must be first and match B3_HEIGHT_FIELD_VERSION + uint64_t version; + + /// The total number of bytes for this height field. + int byteCount; + + /// Hash of this height field (this field is zero when the hash is computed). + uint32_t hash; + + /// The local axis-aligned bounding box. + b3AABB aabb; + + /// The minimum y value. + float minHeight; + + /// The maximum y value + float maxHeight; + + /// The quantization scale. + float heightScale; + + /// The overall scale. + b3Vec3 scale; + + /// The number of grid columns along the local x-axis. + int columnCount; + + /// The number of grid rows along the local z-axis. + int rowCount; + + /// Offset of the compressed height array in bytes from the struct address. + /// uint16_t, one per grid point. + int heightsOffset; + + /// Offset of the material index array in bytes from the struct address. + /// uint8_t, one per cell. + int materialOffset; + + /// Offset of the flag array in bytes from the struct address. + /// uint8_t, one per triangle. + int flagsOffset; + + /// Triangle winding. + bool clockwise; + + /// Explicit padding. Identity is a content hash over raw bytes, so there must + /// be no unnamed padding for struct copies to scramble. + uint8_t padding[3]; +} b3HeightFieldData; + +/**@}*/ // height_field + +/** + * @defgroup compound Compound + * @brief Compound collision shape + * @{ + */ + +/// Definition for a capsule in a compound shape. +typedef struct b3CompoundCapsuleDef +{ + /// Local capsule. + b3Capsule capsule; + + /// Material properties. + b3SurfaceMaterial material; +} b3CompoundCapsuleDef; + +/// Definition for a convex hull in a compound shape. +typedef struct b3CompoundHullDef +{ + /// Shared hull. + const b3HullData* hull; + + /// Transform of the shared hull into compound local space. + b3Transform transform; + + /// Material properties. + b3SurfaceMaterial material; +} b3CompoundHullDef; + +/// Definition for a triangle mesh in a compound shape. +typedef struct b3CompoundMeshDef +{ + /// Shared mesh. + const b3MeshData* meshData; + + /// Transform of the shared mesh into compound local space. + b3Transform transform; + + /// Local space non-uniform mesh scale. May have negative components. + b3Vec3 scale; + + /// Material properties. + /// This array must line up with the material indices on the triangles. + const b3SurfaceMaterial* materials; + + /// Number of materials. + int materialCount; +} b3CompoundMeshDef; + +/// Definition for a sphere in a compound shape. +typedef struct b3CompoundSphereDef +{ + /// Local sphere. + b3Sphere sphere; + + /// Material properties. + b3SurfaceMaterial material; +} b3CompoundSphereDef; + +/// Definition for creating a compound shape. All this data is fully cloned +/// into the run-time compound shape. +typedef struct b3CompoundDef +{ + /// Capsule instances. + b3CompoundCapsuleDef* capsules; + + /// Number of capsules. + int capsuleCount; + + /// Hulls instances. + b3CompoundHullDef* hulls; + + /// Number of hull instances. + int hullCount; + + /// Mesh instances. + b3CompoundMeshDef* meshes; + + /// Number of mesh instances. + int meshCount; + + /// Sphere instances. + b3CompoundSphereDef* spheres; + + /// Number of spheres. + int sphereCount; +} b3CompoundDef; + +/// The compound version depends on the tree, mesh, and hull versions. +#define B3_COMPOUND_VERSION ( 0x830778DB07086EB4ull ^ B3_DYNAMIC_TREE_VERSION ^ B3_MESH_VERSION ^ B3_HULL_VERSION ) + +/// Meshes used in compounds have limited space for materials. If you have +/// a mesh with many materials, you can use it outside of the compound. +#define B3_MAX_COMPOUND_MESH_MATERIALS 4 + +/// The runtime data for a compound shape. This is a potentially large yet highly optimized +/// data structure. It can contain thousands of child shapes, yet at runtime it populates +/// into the world as a single shape in the runtime broad-phase. +/// This data structure has data living off the end and must be accessed using offsets. +/// Accessors are provided for user relevant data. +typedef struct b3CompoundData +{ + /// The compound version is always first. + uint64_t version; + + /// The total number of bytes for this compound. + int byteCount; + + /// Offset of the tree node array in bytes from the struct address. + int nodeOffset; + + /// Immutable dynamic tree. The tree node pointer must be fixed up using the node offset + b3DynamicTree tree; + + /// Offset of the material array in bytes from the struct address. + int materialOffset; + + /// The number of materials. + int materialCount; + + /// Offset of the capsule array in bytes from the struct address. + int capsuleOffset; + + /// The number of capsules. + int capsuleCount; + + /// Offset of the hull instance array in bytes from the struct address. + int hullOffset; + + /// The number of hull instances. + int hullCount; + + /// The number of unique hulls. Diagnostic. + int sharedHullCount; + + /// Offset of the mesh instance array in bytes from the struct address. + int meshOffset; + + /// The number of mesh instances. + int meshCount; + + /// The number of unique meshes. Diagnostic. + int sharedMeshCount; + + /// Offset of the sphere array in bytes from the struct address. + int sphereOffset; + + /// The number of spheres. + int sphereCount; +} b3CompoundData; + +/// A capsule that lives in a compound. +typedef struct b3CompoundCapsule +{ + /// Local capsule. + b3Capsule capsule; + + /// Index to a shared material. + int materialIndex; +} b3CompoundCapsule; + +/// A hull that lives in a compound. +typedef struct b3CompoundHull +{ + /// Pointer to the unique shared hull. + const b3HullData* hull; + + /// The transform of this hull instance. + b3Transform transform; + + /// Index to a shared material. + int materialIndex; +} b3CompoundHull; + +/// A mesh with non-uniform scale that lives in a compound. +typedef struct b3CompoundMesh +{ + /// Pointer to the unique shared mesh. + const b3MeshData* meshData; + + /// The transform of this mesh instance. + b3Transform transform; + + /// Non-uniform scale of this mesh instance. + b3Vec3 scale; + + /// This is used to access the surface material from b3GetCompoundMaterials. + /// Requires an extra level of indirection. The triangle material index + /// is clamped to B3_MAX_COMPOUND_MESH_MATERIALS. + /// materialIndex = materialIndices[triangle->materialIndex] + int materialIndices[B3_MAX_COMPOUND_MESH_MATERIALS]; +} b3CompoundMesh; + +/// A sphere that lives in a compound. +typedef struct b3CompoundSphere +{ + /// Local sphere. + b3Sphere sphere; + + /// Index to a shared material. + int materialIndex; +} b3CompoundSphere; + +/// Child shape of a compound +typedef struct b3ChildShape +{ + /// Tagged union. + union + { + b3Capsule capsule; ///< Capsule. + const b3HullData* hull; ///< Hull. + b3Mesh mesh; ///< Mesh. + b3Sphere sphere; ///< Sphere. + }; + + /// Transform of the shape into compound local space. + b3Transform transform; + + /// Material indices. Index 0 is used for convex shapes. + /// todo limit to 64K? + int materialIndices[B3_MAX_COMPOUND_MESH_MATERIALS]; + + /// The shape type (union tag). + b3ShapeType type; +} b3ChildShape; + +/// Callback for compound overlap queries. +typedef bool b3CompoundQueryFcn( const b3CompoundData* compound, int childIndex, void* context ); + +/**@}*/ // compound + +/**@}*/ // geometry + +/** + * @defgroup collision Shape Collision + * Collide pairs of shapes. + * @{ + */ + +/// A manifold point is a contact point belonging to a contact manifold. +/// It holds details related to the geometry and dynamics of the contact points. +/// Box3D uses speculative collision so some contact points may be separated. +/// You may use the maxNormalImpulse to determine if there was an interaction during +/// the time step. +typedef struct b3ManifoldPoint +{ + /// Location of the contact point relative to the bodyA center of mass in world space. + b3Vec3 anchorA; + + /// Location of the contact point relative to the bodyB center of mass in world space. + b3Vec3 anchorB; + + /// The separation of the contact point, negative if penetrating + float separation; + + /// Cached separation used for contact recycling + float baseSeparation; + + /// The impulse along the manifold normal vector. Since Box3D uses sub-stepping, this is + /// result from the final sub-step. + float normalImpulse; + + /// The total normal impulse applied during sub-stepping. This is important + /// to identify speculative contact points that had an interaction in the time step. + float totalNormalImpulse; + + /// Relative normal velocity pre-solve. Used for hit events. If the normal impulse is + /// zero then there was no hit. Negative means shapes are approaching. + float normalVelocity; + + /// Local point for matching + /// Uniquely identifies a contact point between two shapes + uint32_t featureId; + + /// Triangle index if one of the shapes is a mesh or height field + int triangleIndex; + + /// Did this contact point exist in the previous step? + bool persisted; +} b3ManifoldPoint; + +/// A contact manifold describes the contact points between colliding shapes. +/// @note Box3D uses speculative collision so some contact points may be separated. +typedef struct b3Manifold +{ + /// The manifold points. There may be 1 to 4 valid points. + b3ManifoldPoint points[B3_MAX_MANIFOLD_POINTS]; + + /// The unit normal vector in world space, points from shape A to shape B + b3Vec3 normal; + + /// Central friction angular impulse (applied about the normal) + float twistImpulse; + + /// Central friction linear impulse + b3Vec3 frictionImpulse; + + /// Rolling resistance angular impulse + b3Vec3 rollingImpulse; + + /// The number of contact points, will be 0 to 4 + int pointCount; + +} b3Manifold; + +/// Cached separating axis feature. +typedef enum +{ + b3_invalidAxis = 0, + b3_backsideAxis, + b3_faceAxisA, + b3_faceAxisB, + b3_edgePairAxis, + b3_closestPointsAxis, + + /// These are for testing + b3_manualFaceAxisA, + b3_manualFaceAxisB, + b3_manualEdgePairAxis, +} b3SeparatingFeature; + +/// Cached triangle feature. +typedef enum +{ + b3_featureNone = 0, + b3_featureTriangleFace, + b3_featureHullFace, + /// v1-v2 + b3_featureEdge1, + /// v2-v3 + b3_featureEdge2, + /// v3-v1 + b3_featureEdge3, + b3_featureVertex1, + b3_featureVertex2, + b3_featureVertex3 +} b3TriangleFeature; + +/// Separating axis test cache. Provides temporal acceleration of collision routines. +typedef struct +{ + /// The separation when the cache is populated. Negative for overlap. + float separation; + + /// b3SeparatingFeature. + uint8_t type; + + /// Index of the feature on shape A. + uint8_t indexA; + + /// Index of the feature on shape B. + uint8_t indexB; + + /// Was the cache re-used? + uint8_t hit; +} b3SATCache; + +/// Contact points are always the result of two edges intersecting. +/// It can be two edges of the same shape, which is just a shape vertex. +/// Or a contact point can be the result of two edges crossing from different shapes. +/// This is designed to support hull versus hull, but it is adapted to work +/// with all shape types. The feature pair is used to identify contact points +/// for temporal coherence and warm starting. +typedef struct b3FeaturePair +{ + /// Incoming type (either edge on shape A or shape B) + uint8_t owner1; + /// Incoming edge index (into associated shape array) + uint8_t index1; + /// Outgoing type (either edge on shape A or shape B) + uint8_t owner2; + /// Outgoing edge index (into associated shape array) + uint8_t index2; +} b3FeaturePair; + +/// A local manifold point and normal in frame A. +typedef struct b3LocalManifoldPoint +{ + /// Local point in frame A. + b3Vec3 point; + + /// The contact point separation. Negative for overlap. + float separation; + + /// The feature pair for this point. + b3FeaturePair pair; + + /// The triangle index when collide with a mesh or height-field. + int triangleIndex; +} b3LocalManifoldPoint; + +/// A local manifold with no dynamic information. Used by b3Collide functions. +typedef struct b3LocalManifold +{ + /// Local normal in frame A. + b3Vec3 normal; + + /// The triangle normal. + b3Vec3 triangleNormal; + + /// The manifold points. From a point buffer. + b3LocalManifoldPoint* points; + + /// The number of manifold points. Only bounded by the buffer capacity. + int pointCount; + + /// The index of the triangle. + int triangleIndex; + + int i1; ///< Vertex 1 index. + int i2; ///< Vertex 2 index. + int i3; ///< Vertex 3 index. + + /// The squared distance of a sphere from a triangle. For ghost collision reduction. + float squaredDistance; + + /// The triangle feature involved. + b3TriangleFeature feature; + + /// b3MeshEdgeFlags. + int triangleFlags; +} b3LocalManifold; + +/**@}*/ // collision + +/** + * @defgroup debug_draw Debug Draw + * @{ + */ + +/// These colors are used for debug draw and mostly match the named SVG colors. +/// See https://www.rapidtables.com/web/color/index.html +/// https://johndecember.com/html/spec/colorsvg.html +/// https://upload.wikimedia.org/wikipedia/commons/2/2b/SVG_Recognized_color_keyword_names.svg +typedef enum b3HexColor +{ + b3_colorAliceBlue = 0xF0F8FF, + b3_colorAntiqueWhite = 0xFAEBD7, + b3_colorAqua = 0x00FFFF, + b3_colorAquamarine = 0x7FFFD4, + b3_colorAzure = 0xF0FFFF, + b3_colorBeige = 0xF5F5DC, + b3_colorBisque = 0xFFE4C4, + b3_colorBlack = 0x000000, + b3_colorBlanchedAlmond = 0xFFEBCD, + b3_colorBlue = 0x0000FF, + b3_colorBlueViolet = 0x8A2BE2, + b3_colorBrown = 0xA52A2A, + b3_colorBurlywood = 0xDEB887, + b3_colorCadetBlue = 0x5F9EA0, + b3_colorChartreuse = 0x7FFF00, + b3_colorChocolate = 0xD2691E, + b3_colorCoral = 0xFF7F50, + b3_colorCornflowerBlue = 0x6495ED, + b3_colorCornsilk = 0xFFF8DC, + b3_colorCrimson = 0xDC143C, + b3_colorCyan = 0x00FFFF, + b3_colorDarkBlue = 0x00008B, + b3_colorDarkCyan = 0x008B8B, + b3_colorDarkGoldenRod = 0xB8860B, + b3_colorDarkGray = 0xA9A9A9, + b3_colorDarkGreen = 0x006400, + b3_colorDarkKhaki = 0xBDB76B, + b3_colorDarkMagenta = 0x8B008B, + b3_colorDarkOliveGreen = 0x556B2F, + b3_colorDarkOrange = 0xFF8C00, + b3_colorDarkOrchid = 0x9932CC, + b3_colorDarkRed = 0x8B0000, + b3_colorDarkSalmon = 0xE9967A, + b3_colorDarkSeaGreen = 0x8FBC8F, + b3_colorDarkSlateBlue = 0x483D8B, + b3_colorDarkSlateGray = 0x2F4F4F, + b3_colorDarkTurquoise = 0x00CED1, + b3_colorDarkViolet = 0x9400D3, + b3_colorDeepPink = 0xFF1493, + b3_colorDeepSkyBlue = 0x00BFFF, + b3_colorDimGray = 0x696969, + b3_colorDodgerBlue = 0x1E90FF, + b3_colorFireBrick = 0xB22222, + b3_colorFloralWhite = 0xFFFAF0, + b3_colorForestGreen = 0x228B22, + b3_colorFuchsia = 0xFF00FF, + b3_colorGainsboro = 0xDCDCDC, + b3_colorGhostWhite = 0xF8F8FF, + b3_colorGold = 0xFFD700, + b3_colorGoldenRod = 0xDAA520, + b3_colorGray = 0x808080, + b3_colorGreen = 0x008000, + b3_colorGreenYellow = 0xADFF2F, + b3_colorHoneyDew = 0xF0FFF0, + b3_colorHotPink = 0xFF69B4, + b3_colorIndianRed = 0xCD5C5C, + b3_colorIndigo = 0x4B0082, + b3_colorIvory = 0xFFFFF0, + b3_colorKhaki = 0xF0E68C, + b3_colorLavender = 0xE6E6FA, + b3_colorLavenderBlush = 0xFFF0F5, + b3_colorLawnGreen = 0x7CFC00, + b3_colorLemonChiffon = 0xFFFACD, + b3_colorLightBlue = 0xADD8E6, + b3_colorLightCoral = 0xF08080, + b3_colorLightCyan = 0xE0FFFF, + b3_colorLightGoldenRodYellow = 0xFAFAD2, + b3_colorLightGray = 0xD3D3D3, + b3_colorLightGreen = 0x90EE90, + b3_colorLightPink = 0xFFB6C1, + b3_colorLightSalmon = 0xFFA07A, + b3_colorLightSeaGreen = 0x20B2AA, + b3_colorLightSkyBlue = 0x87CEFA, + b3_colorLightSlateGray = 0x778899, + b3_colorLightSteelBlue = 0xB0C4DE, + b3_colorLightYellow = 0xFFFFE0, + b3_colorLime = 0x00FF00, + b3_colorLimeGreen = 0x32CD32, + b3_colorLinen = 0xFAF0E6, + b3_colorMagenta = 0xFF00FF, + b3_colorMaroon = 0x800000, + b3_colorMediumAquaMarine = 0x66CDAA, + b3_colorMediumBlue = 0x0000CD, + b3_colorMediumOrchid = 0xBA55D3, + b3_colorMediumPurple = 0x9370DB, + b3_colorMediumSeaGreen = 0x3CB371, + b3_colorMediumSlateBlue = 0x7B68EE, + b3_colorMediumSpringGreen = 0x00FA9A, + b3_colorMediumTurquoise = 0x48D1CC, + b3_colorMediumVioletRed = 0xC71585, + b3_colorMidnightBlue = 0x191970, + b3_colorMintCream = 0xF5FFFA, + b3_colorMistyRose = 0xFFE4E1, + b3_colorMoccasin = 0xFFE4B5, + b3_colorNavajoWhite = 0xFFDEAD, + b3_colorNavy = 0x000080, + b3_colorOldLace = 0xFDF5E6, + b3_colorOlive = 0x808000, + b3_colorOliveDrab = 0x6B8E23, + b3_colorOrange = 0xFFA500, + b3_colorOrangeRed = 0xFF4500, + b3_colorOrchid = 0xDA70D6, + b3_colorPaleGoldenRod = 0xEEE8AA, + b3_colorPaleGreen = 0x98FB98, + b3_colorPaleTurquoise = 0xAFEEEE, + b3_colorPaleVioletRed = 0xDB7093, + b3_colorPapayaWhip = 0xFFEFD5, + b3_colorPeachPuff = 0xFFDAB9, + b3_colorPeru = 0xCD853F, + b3_colorPink = 0xFFC0CB, + b3_colorPlum = 0xDDA0DD, + b3_colorPowderBlue = 0xB0E0E6, + b3_colorPurple = 0x800080, + b3_colorRebeccaPurple = 0x663399, + b3_colorRed = 0xFF0000, + b3_colorRosyBrown = 0xBC8F8F, + b3_colorRoyalBlue = 0x4169E1, + b3_colorSaddleBrown = 0x8B4513, + b3_colorSalmon = 0xFA8072, + b3_colorSandyBrown = 0xF4A460, + b3_colorSeaGreen = 0x2E8B57, + b3_colorSeaShell = 0xFFF5EE, + b3_colorSienna = 0xA0522D, + b3_colorSilver = 0xC0C0C0, + b3_colorSkyBlue = 0x87CEEB, + b3_colorSlateBlue = 0x6A5ACD, + b3_colorSlateGray = 0x708090, + b3_colorSnow = 0xFFFAFA, + b3_colorSpringGreen = 0x00FF7F, + b3_colorSteelBlue = 0x4682B4, + b3_colorTan = 0xD2B48C, + b3_colorTeal = 0x008080, + b3_colorThistle = 0xD8BFD8, + b3_colorTomato = 0xFF6347, + b3_colorTurquoise = 0x40E0D0, + b3_colorViolet = 0xEE82EE, + b3_colorWheat = 0xF5DEB3, + b3_colorWhite = 0xFFFFFF, + b3_colorWhiteSmoke = 0xF5F5F5, + b3_colorYellow = 0xFFFF00, + b3_colorYellowGreen = 0x9ACD32, + + b3_colorBox2DRed = 0xDC3132, + b3_colorBox2DBlue = 0x30AEBF, + b3_colorBox2DGreen = 0x8CC924, + b3_colorBox2DYellow = 0xFFEE8C +} b3HexColor; + +/// Debug draw material preset. Optionally packed into the unused high byte of a +/// b3HexColor (or b3SurfaceMaterial::customColor) to drive the renderer's PBR +/// roughness and metalness. The low 24 bits stay RGB, so a plain 0xRRGGBB color +/// reads as b3_debugMaterialDefault and keeps the renderer's per-body-type look. +typedef enum b3DebugMaterial +{ + b3_debugMaterialDefault = 0, + b3_debugMaterialMatte, + b3_debugMaterialSoft, + b3_debugMaterialDead, + b3_debugMaterialGlossy, + b3_debugMaterialMetallic +} b3DebugMaterial; + +/// Pack an RGB color with a material preset for debug draw. The preset rides in +/// the high byte where the color converters ignore it. +B3_INLINE uint32_t b3MakeDebugColor( b3HexColor rgb, b3DebugMaterial material ) +{ + return ( (uint32_t)rgb & 0x00FFFFFFu ) | ( (uint32_t)material << 24 ); +} + +/// Get the visualization color assigned to a constraint graph color slot. The last index +/// (B3_GRAPH_COLOR_COUNT - 1) is the overflow color. +B3_API b3HexColor b3GetGraphColor( int index ); + +/// This is sent to the user for debug shape creation. The user should know the type in case they have +/// custom sphere or capsule rendering. +typedef struct b3DebugShape +{ + /// Shape id. + b3ShapeId shapeId; + + /// Shape type. + b3ShapeType type; + + /// Tagged union. + union + { + const b3Capsule* capsule; ///< Capsule shape. + const b3CompoundData* compound; ///< Compound shape. + const b3HeightFieldData* heightField; ///< Height-field shape. + const b3HullData* hull; ///< Convex hull shape. + const b3Mesh* mesh; ///< Mesh shape with scale. + const b3Sphere* sphere; ///< Sphere shape. + }; +} b3DebugShape; + +/// This struct is passed to b3World_Draw to draw a debug view of the simulation world. +/// Callbacks receive world coordinates. In large world mode the translation is double precision so +/// it stays accurate far from the origin. Shift into your own camera frame inside the callbacks. +typedef struct b3DebugDraw +{ + /// Draws a shape and returns true if drawing should continue + bool ( *DrawShapeFcn )( void* userShape, b3WorldTransform transform, b3HexColor color, void* context ); + + /// Draw a line segment. + void ( *DrawSegmentFcn )( b3Pos p1, b3Pos p2, b3HexColor color, void* context ); + + /// Draw a transform. Choose your own length scale. + void ( *DrawTransformFcn )( b3WorldTransform transform, void* context ); + + /// Draw a point. + void ( *DrawPointFcn )( b3Pos p, float size, b3HexColor color, void* context ); + + /// Draw a sphere. + void ( *DrawSphereFcn )( b3Pos p, float radius, b3HexColor color, float alpha, void* context ); + + /// Draw a capsule. + void ( *DrawCapsuleFcn )( b3Pos p1, b3Pos p2, float radius, b3HexColor color, float alpha, void* context ); + + /// Draw a bounding box. + void ( *DrawBoundsFcn )( b3AABB aabb, b3HexColor color, void* context ); + + /// Draw an oriented box. + void ( *DrawBoxFcn )( b3Vec3 extents, b3WorldTransform transform, b3HexColor color, void* context ); + + /// Draw a string in world space + void ( *DrawStringFcn )( b3Pos p, const char* s, b3HexColor color, void* context ); + + /// World bounds to use for debug draw + b3AABB drawingBounds; + + /// Scale to use when drawing forces + float forceScale; + + /// Global scaling for joint drawing + float jointScale; + + /// Option to draw shapes + bool drawShapes; + + /// Option to draw joints + bool drawJoints; + + /// Option to draw additional information for joints + bool drawJointExtras; + + /// Option to draw the bounding boxes for shapes + bool drawBounds; + + /// Option to draw the mass and center of mass of dynamic bodies + bool drawMass; + + /// Option to draw body names + bool drawBodyNames; + + /// Option to draw contact points + bool drawContacts; + + /// Draw contact anchor A or B + int drawAnchorA; + + /// Option to visualize the graph coloring used for contacts and joints + bool drawGraphColors; + + /// Option to draw contact features + bool drawContactFeatures; + + /// Option to draw contact normals + bool drawContactNormals; + + /// Option to draw contact normal forces + bool drawContactForces; + + /// Option to draw contact friction forces + bool drawFrictionForces; + + /// Option to draw islands as bounding boxes + bool drawIslands; + + /// User context that is passed as an argument to drawing callback functions + void* context; +} b3DebugDraw; + +/// Create a debug draw struct with default values. +B3_API b3DebugDraw b3DefaultDebugDraw( void ); + +/**@}*/ // debug_draw