diff --git a/vendor/box2d/box2d.odin b/vendor/box2d/box2d.odin index ad4670110..2301ed3d0 100644 --- a/vendor/box2d/box2d.odin +++ b/vendor/box2d/box2d.odin @@ -4,6 +4,8 @@ package vendor_box2d import "base:intrinsics" import "core:c" +_ :: intrinsics + when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { @(private) VECTOR_EXT :: "_simd" when #config(VENDOR_BOX2D_ENABLE_SIMD128, intrinsics.has_target_feature("simd128")) else "" } else { @@ -331,7 +333,7 @@ MakeOffsetProxy :: proc "c" (points: []Vec2, radius: f32, position: Vec2, rotati @(link_prefix="b2", default_calling_convention="c", require_results) foreign lib { // Perform a linear shape cast of shape B moving and shape A fixed. Determines the hit point, normal, and translation fraction. - // You may optionally supply an array to hold debug data. + // Initially touching shapes are treated as a miss. ShapeCast :: proc(#by_ptr input: ShapeCastPairInput) -> CastOutput --- // Evaluate the transform sweep at a specific time. @@ -481,12 +483,12 @@ foreign lib { */ @(require_results) -SolvePlanes :: proc(position: Vec2, planes: []CollisionPlane) -> PlaneSolverResult { +SolvePlanes :: proc(targetDelta: Vec2, planes: []CollisionPlane) -> PlaneSolverResult { foreign lib { - b2SolvePlanes :: proc "c" (position: Vec2, planes: [^]CollisionPlane, count: i32) -> PlaneSolverResult --- + b2SolvePlanes :: proc "c" (targetDelta: Vec2, planes: [^]CollisionPlane, count: i32) -> PlaneSolverResult --- } - return b2SolvePlanes(position, raw_data(planes), i32(len(planes))) + return b2SolvePlanes(targetDelta, raw_data(planes), i32(len(planes))) } @(require_results) @@ -550,7 +552,6 @@ foreign lib { // 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. - // The ray-cast ignores shapes that contain the starting point. // @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 @@ -561,7 +562,7 @@ foreign lib { // @return traversal performance counters World_CastRay :: proc(worldId: WorldId, origin: Vec2, translation: Vec2, filter: QueryFilter, fcn: CastResultFcn, ctx: rawptr) -> TreeStats --- - // Cast a ray into the world to collect the closest hit. This is a convenience function. + // Cast a ray into the world to collect the closest hit. This is a convenience function. Ignores initial overlap. // This is less general than b2World_CastRay() and does not allow for custom filtering. World_CastRayClosest :: proc(worldId: WorldId, origin: Vec2, translation: Vec2, filter: QueryFilter) -> RayResult --- @@ -637,13 +638,6 @@ foreign lib { // @note Advanced feature World_SetContactTuning :: proc(worldId: WorldId, hertz: f32, dampingRatio: f32, pushSpeed: f32) --- - // Adjust joint 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) - // @note Advanced feature - World_SetJointTuning :: proc(worldId: WorldId, hertz: f32, dampingRatio: f32) --- - // Set the maximum linear speed. Usually in m/s. World_SetMaximumLinearSpeed :: proc(worldId: WorldId, maximumLinearSpeed: f32) --- @@ -771,6 +765,7 @@ foreign lib { // 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 automatically wake the body if asleep. Body_SetTargetTransform :: proc(bodyId: BodyId, target: Transform, timeStep: f32) --- @@ -1068,6 +1063,12 @@ foreign lib { // Get the shape material identifier Shape_GetMaterial :: proc(shapeId: ShapeId) -> c.int --- + // Set the shape surface material + Shape_SetSurfaceMaterial :: proc(shapeId: ShapeId, surfaceMaterial: SurfaceMaterial) --- + + // Get the shape surface material + Shape_GetSurfaceMaterial :: proc(shapeId: ShapeId) -> SurfaceMaterial --- + // Get the shape filter Shape_GetFilter :: proc(shapeId: ShapeId) -> Filter --- @@ -1258,12 +1259,30 @@ foreign lib { // Get the world that owns this joint Joint_GetWorld :: proc(jointId: JointId) -> WorldId --- + // Set the local anchor on bodyA + Joint_SetLocalAnchorA :: proc(jointId: JointId, localAnchor: Vec2) --- + // Get the local anchor on bodyA Joint_GetLocalAnchorA :: proc(jointId: JointId) -> Vec2 --- + // Set the local anchor on bodyB + Joint_SetLocalAnchorB :: proc(jointId: JointId, localAnchor: Vec2) --- + // Get the local anchor on bodyB Joint_GetLocalAnchorB :: proc(jointId: JointId) -> Vec2 --- + // Get the joint reference angle in radians (revolute, prismatic, and weld) + Joint_GetReferenceAngle :: proc(jointId: JointId) -> f32 --- + + // Set the joint reference angle in radians, must be in [-pi,pi]. (revolute, prismatic, and weld) + Joint_SetReferenceAngle :: proc(jointId: JointId, angleInRadians: f32) --- + + // Set the local axis on bodyA (prismatic and wheel) + Joint_SetLocalAxisA :: proc(jointId: JointId, localAxis: Vec2) --- + + // Get the local axis on bodyA (prismatic and wheel) + Joint_GetLocalAxisA :: proc(jointId: JointId) -> Vec2 --- + // Toggle collision between connected bodies Joint_SetCollideConnected :: proc(jointId: JointId, shouldCollide: bool) --- @@ -1285,6 +1304,21 @@ foreign lib { // Get the current constraint torque for this joint. Usually in Newton * meters. Joint_GetConstraintTorque :: proc(jointId: JointId) -> f32 --- + // 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 meters. + Joint_GetAngularSeparation :: proc(jointId: JointId) -> f32 --- + + // Get the joint constraint tuning. Advanced feature. + Joint_GetConstraintTuning :: proc(jointId: JointId, hertz: ^f32, dampingRatio: ^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) --- + /** * @defgroup distance_joint Distance Joint * @brief Functions for the distance joint. @@ -1379,7 +1413,8 @@ foreign lib { // Get the motor joint linear offset target MotorJoint_GetLinearOffset :: proc(jointId: JointId) -> Vec2 --- - // Set the motor joint angular offset target in radians + // Set the motor joint angular offset target in radians. This angle will be unwound + // so the motor will drive along the shortest arc. MotorJoint_SetAngularOffset :: proc(jointId: JointId, angularOffset: f32) --- // Get the motor joint angular offset target in radians @@ -1415,31 +1450,37 @@ foreign lib { // Create a mouse joint // @see b2MouseJointDef for details - CreateMouseJoint :: proc(worldId: WorldId, #by_ptr def: MouseJointDef) -> JointId --- + CreateMouseJoint :: proc(worldId: WorldId, #by_ptr def: MouseJointDef) -> JointId --- // Set the mouse joint target - MouseJoint_SetTarget :: proc(jointId: JointId, target: Vec2) --- + MouseJoint_SetTarget :: proc(jointId: JointId, target: Vec2) --- // Get the mouse joint target - MouseJoint_GetTarget :: proc(jointId: JointId) -> Vec2 --- + MouseJoint_GetTarget :: proc(jointId: JointId) -> Vec2 --- // Set the mouse joint spring stiffness in Hertz - MouseJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) --- + MouseJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) --- // Get the mouse joint spring stiffness in Hertz - MouseJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 --- + MouseJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 --- // Set the mouse joint spring damping ratio, non-dimensional - MouseJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- + MouseJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- // Get the mouse joint damping ratio, non-dimensional - MouseJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 --- + MouseJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 --- + + // Set the prismatic joint sprint target angle, usually in meters + PrismaticJoint_SetTargetTranslation :: proc(jointId: JointId, translation: f32) --- + + // Get the prismatic joint sprint target translation, usually in meters + PrismaticJoint_GetTargetTranslation :: proc(jointId: JointId) -> f32 --- // Set the mouse joint maximum force, usually in newtons - MouseJoint_SetMaxForce :: proc(jointId: JointId, maxForce: f32) --- + MouseJoint_SetMaxForce :: proc(jointId: JointId, maxForce: f32) --- // Get the mouse joint maximum force, usually in newtons - MouseJoint_GetMaxForce :: proc(jointId: JointId) -> f32 --- + MouseJoint_GetMaxForce :: proc(jointId: JointId) -> f32 --- /**@}*/ @@ -1585,7 +1626,7 @@ foreign lib { RevoluteJoint_GetUpperLimit :: proc(jointId: JointId) -> f32 --- // Set the revolute joint limits in radians. It is expected that lower <= upper - // and that -0.95 * B2_PI <= lower && upper <= -0.95 * B2_PI. + // and that -0.99 * B2_PI <= lower && upper <= -0.99 * B2_PI. RevoluteJoint_SetLimits :: proc(jointId: JointId, lower, upper: f32) --- // Enable/disable a revolute joint motor @@ -1625,12 +1666,6 @@ foreign lib { // @see b2WeldJointDef for details CreateWeldJoint :: proc(worldId: WorldId, #by_ptr def: WeldJointDef) -> JointId --- - // Get the weld joint reference angle in radians - WeldJoint_GetReferenceAngle :: proc(jointId: JointId) -> f32 --- - - // Set the weld joint reference angle in radians, must be in [-pi,pi]. - WeldJoint_SetReferenceAngle :: proc(jointId: JointId, angleInRadians: f32) --- - // Set the weld joint linear stiffness in Hertz. 0 is rigid. WeldJoint_SetLinearHertz :: proc(jointId: JointId, hertz: f32) --- diff --git a/vendor/box2d/build_box2d.sh b/vendor/box2d/build_box2d.sh index d6fb3888d..9c79ce009 100755 --- a/vendor/box2d/build_box2d.sh +++ b/vendor/box2d/build_box2d.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -eu -VERSION="3.1.0" +VERSION="3.1.1" RELEASE="https://github.com/erincatto/box2d/archive/refs/tags/v$VERSION.tar.gz" cd "$(dirname "$0")" diff --git a/vendor/box2d/collision.odin b/vendor/box2d/collision.odin index b29e2f946..9930e0549 100644 --- a/vendor/box2d/collision.odin +++ b/vendor/box2d/collision.odin @@ -49,7 +49,7 @@ ShapeCastInput :: struct { canEncroach: bool, } -// Low level ray cast or shape-cast output data +// Low level ray cast or shape-cast output data. Returns a zero fraction and normal in the case of initial overlap. CastOutput :: struct { // The surface normal at the hit point normal: Vec2, @@ -227,7 +227,7 @@ DistanceInput :: struct { DistanceOutput :: struct { pointA: Vec2, // Closest point on shapeA pointB: Vec2, // Closest point on shapeB - normal: Vec2, // Normal vector that points from A to B + normal: Vec2, // Normal vector that points from A to B. Invalid if distance is zero. distance: f32, // The final distance, zero if overlapped iterations: i32, // Number of GJK iterations used simplexCount: i32, // The number of simplexes stored in the simplex array @@ -459,6 +459,9 @@ PlaneResult :: struct { // The collision plane between the mover and convex shape plane: Plane, + // The collision point on the shape. + point: Vec2, + // Did the collision register a hit? If not this plane should be ignored. hit: bool, } @@ -482,8 +485,8 @@ CollisionPlane :: struct { // Result returned by b2SolvePlanes PlaneSolverResult :: struct { - // The final position of the mover - position: Vec2, + // The translation of the mover + translation: Vec2, // The number of iterations used by the plane solver. For diagnostics. iterationCount: i32, diff --git a/vendor/box2d/id.odin b/vendor/box2d/id.odin index cb530527a..d2465f7d7 100644 --- a/vendor/box2d/id.odin +++ b/vendor/box2d/id.odin @@ -86,3 +86,16 @@ ID_EQUALS :: #force_inline proc "c" (id1, id2: $T) -> bool intrinsics.type_has_field(T, "generation") { return id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.generation == id2.generation } + +// Store a world id into a u32. +StoreWorldId :: #force_inline proc "c" (id: WorldId) -> u32 { + return (u32(id.index1) << 16) | u32(id.generation) +} + +// Load a u32 into a world id. +LoadWorldId :: #force_inline proc "c" (x: u32) -> WorldId { + return { + u16(x >> 16), + u16(x), + } +} diff --git a/vendor/box2d/lib/box2d_wasm.o b/vendor/box2d/lib/box2d_wasm.o index fc3ed1cbd..493df4a1c 100755 Binary files a/vendor/box2d/lib/box2d_wasm.o and b/vendor/box2d/lib/box2d_wasm.o differ diff --git a/vendor/box2d/lib/box2d_wasm_simd.o b/vendor/box2d/lib/box2d_wasm_simd.o index 8b70dcedd..d7150082e 100755 Binary files a/vendor/box2d/lib/box2d_wasm_simd.o and b/vendor/box2d/lib/box2d_wasm_simd.o differ diff --git a/vendor/box2d/math_functions.odin b/vendor/box2d/math_functions.odin index c2d197f08..31c6d4090 100644 --- a/vendor/box2d/math_functions.odin +++ b/vendor/box2d/math_functions.odin @@ -415,31 +415,10 @@ RelativeAngle :: proc "c" (b, a: Rot) -> f32 { return Atan2(s, c) } -// Convert an angle in the range [-2*pi, 2*pi] into the range [-pi, pi] +// Convert any angle into the range [-pi, pi] @(require_results) UnwindAngle :: proc "c" (radians: f32) -> f32 { - if radians < -PI { - return radians + 2.0 * PI - } else if radians > PI { - return radians - 2.0 * PI - } - return radians -} - -// Convert any into the range [-pi, pi] (slow) -@(require_results) -UnwindLargeAngle :: proc "c" (radians: f32) -> f32 { - radians := radians - - for radians > PI { - radians -= 2. * PI - } - - for radians < -PI { - radians += 2. * PI - } - - return radians + return math.remainder(radians, 2. * PI) } // Rotate a vector @@ -563,6 +542,17 @@ AABB_Union :: proc "c" (a, b: AABB) -> (c: AABB) { return } +// Do a and b overlap +@(require_results) +AABB_Overlaps :: proc "c" (a, b: AABB) -> bool { + return !( + b.lowerBound.x > a.upperBound.x || + b.lowerBound.y > a.upperBound.y || + a.lowerBound.x > b.upperBound.x || + a.lowerBound.y > b.upperBound.y \ + ) +} + // Compute the bounding box of an array of circles @(require_results) MakeAABB :: proc "c" (points: []Vec2, radius: f32) -> AABB { @@ -625,3 +615,15 @@ IsValidPlane :: proc "c" (plane: Plane) -> bool { IsNormalized(plane.normal) or_return return true } + +// One-dimensional mass-spring-damper simulation. Returns the new velocity given the position and time step. +// You can then compute the new position using: +// position += timeStep * newVelocity +// This drives towards a zero position. By using implicit integration we get a stable solution +// that doesn't require transcendental functions. +@(require_results) +b2SpringDamper :: proc "c" (hertz, dampingRatio, position, velocity, timeStep: f32) -> f32 { + omega := 2. * PI * hertz + omegaH := omega * timeStep + return (velocity - omega * omegaH * position) / (1. + 2. * dampingRatio * omegaH + omegaH * omegaH) +} diff --git a/vendor/box2d/types.odin b/vendor/box2d/types.odin index 1ad7f379c..9e12aa1a1 100644 --- a/vendor/box2d/types.odin +++ b/vendor/box2d/types.odin @@ -50,6 +50,7 @@ FrictionCallback :: #type proc "c" (frictionA: f32, userMaterialIdA: i32, fricti RestitutionCallback :: #type proc "c" (restitutionA: f32, userMaterialIdA: i32, restitutionB: f32, userMaterialIdB: i32) -> f32 // Result from b2World_RayCastClosest +// If there is initial overlap the fraction and normal will be zero while the point is an arbitrary point in the overlap region. // @ingroup world RayResult :: struct { shapeId: ShapeId, @@ -87,12 +88,6 @@ WorldDef :: struct { // decreasing the damping ratio. maxContactPushSpeed: f32, - // Joint stiffness. Cycles per second. - jointHertz: f32, - - // Joint bounciness. Non-dimensional. - jointDampingRatio: f32, - // Maximum linear speed. Usually meters per second. maximumLinearSpeed: f32, @@ -660,6 +655,10 @@ PrismaticJointDef :: struct { // The constrained angle between the bodies: bodyB_angle - bodyA_angle referenceAngle: f32, + // The target translation for the joint in meters. The spring-damper will drive + // to this translation. + targetTranslation: f32, + // Enable a linear spring along the prismatic joint axis enableSpring: bool, @@ -743,10 +742,10 @@ RevoluteJointDef :: struct { // A flag to enable joint limits enableLimit: bool, - // The lower angle for the joint limit in radians. Minimum of -0.95*pi radians. + // 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.95*pi radians. + // The upper angle for the joint limit in radians. Maximum of 0.99*pi radians. upperAngle: f32, // A flag to enable the joint motor @@ -988,6 +987,7 @@ ContactEndTouchEvent :: struct { } // 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, @@ -995,7 +995,9 @@ ContactHitEvent :: struct { // Id of the second shape shapeIdB: ShapeId, - // Point where the shapes hit + // 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: Vec2, // Normal vector pointing from shape A to shape B @@ -1103,17 +1105,18 @@ PreSolveFcn :: #type proc "c" (shapeIdA, shapeIdB: ShapeId, manifold: ^Manifold, // @ingroup world OverlapResultFcn :: #type proc "c" (shapeId: ShapeId, ctx: rawptr) -> bool -// Prototype callback for ray casts. +// Prototype callback for ray and shape casts. // Called for each shape found in the query. You control how the ray cast // proceeds by returning a f32: // 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 +// A cast with initial overlap will return a zero fraction and a zero normal. // @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 normal the normal vector at the point of intersection, zero for a shape cast with initial overlap +// @param fraction the fraction along the ray at the point of intersection, zero for a shape cast with initial overlap // @param context the user context // @return -1 to filter, 0 to terminate, fraction to clip the ray for closest hit, 1 to continue // @see b2World_CastRay diff --git a/vendor/box2d/wasm.Makefile b/vendor/box2d/wasm.Makefile index 74f4e4eff..80dd2ba14 100644 --- a/vendor/box2d/wasm.Makefile +++ b/vendor/box2d/wasm.Makefile @@ -7,7 +7,7 @@ # CC = $(shell brew --prefix llvm)/bin/clang # LD = $(shell brew --prefix llvm)/bin/wasm-ld -VERSION = 3.1.0 +VERSION = 3.1.1 SRCS = $(wildcard box2d-$(VERSION)/src/*.c) OBJS_SIMD = $(SRCS:.c=_simd.o) OBJS = $(SRCS:.c=.o) diff --git a/vendor/libc/include/math.h b/vendor/libc/include/math.h index fba520e4a..dd9e8e9ba 100644 --- a/vendor/libc/include/math.h +++ b/vendor/libc/include/math.h @@ -43,6 +43,8 @@ double tan(double); double atan2(double, double); double modf(double, double*); +float remainderf(float x, float y); + bool __isnanf(float); bool __isnand(double); #define isnan(x) \ diff --git a/vendor/libc/math.odin b/vendor/libc/math.odin index 93df1fea6..26d8a917a 100644 --- a/vendor/libc/math.odin +++ b/vendor/libc/math.odin @@ -204,3 +204,8 @@ modf :: proc "c" (num: f64, iptr: ^f64) -> f64 { iptr^ = integral return fractional } + +@(require, linkage="strong", link_name="remainderf") +remainderf :: proc "c" (x, y: f32) -> f32 { + return math.remainder(x, y) +}