diff --git a/vendor/box2d/box2d.odin b/vendor/box2d/box2d.odin index 8abf6ce03..abff1efcf 100644 --- a/vendor/box2d/box2d.odin +++ b/vendor/box2d/box2d.odin @@ -53,26 +53,7 @@ Version :: struct { revision: i32, // Bug fixes } -when ODIN_OS == .Windows { - // Timer for profiling. This has platform specific code and may - // not work on every platform. - Timer :: struct { - start: i64, - } -} else when ODIN_OS == .Linux || ODIN_OS == .Darwin { - // Timer for profiling. This has platform specific code and may - // not work on every platform. - Timer :: struct { - start_sec: u64, - start_usec: u64, - } -} else { - // Timer for profiling. This has platform specific code and may - // not work on every platform. - Timer :: struct { - dummy: i32, - } -} +HASH_INIT :: 5381 @(link_prefix="b2", default_calling_convention="c", require_results) foreign lib { @@ -85,19 +66,33 @@ foreign lib { // @param assertFcn a non-null assert callback SetAssertFcn :: proc(assertfcn: AssertFcn) --- - - CreateTimer :: proc() -> Timer --- - GetTicks :: proc(timer: ^Timer) -> i64 --- - GetMilliseconds :: proc(#by_ptr timer: Timer) -> f32 --- - GetMillisecondsAndReset :: proc(timer: ^Timer) -> f32 --- - SleepMilliseconds :: proc(milliseconds: c.int) --- + // 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. Resets the passed in + // value to the current tick value. + GetMillisecondsAndReset :: proc(ticks: ^u64) -> f32 --- + // Yield to be used in a busy loop. Yield :: proc() --- + // Simple djb2 hash function for determinism testing. + Hash :: proc(hash: u32, data: [^]byte, count: c.int) -> u32 --- // Box2D 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 Box2D + // and only modified once. Default value is 1. + // For example, if your game uses pixels for units you can use pixels for all length values + // sent to Box2D. There should be no extra cost. However, Box2D has some internal tolerances + // and thresholds that have been tuned for meters. By calling this function, Box2D is able + // to adjust those tolerances and thresholds to improve accuracy. + // A good rule of thumb is to pass the height of your player character to this function. So + // if your player character is 32 pixels high, then pass 32 to this function. Then you may + // confidently use pixels for all the length values sent to Box2D. All length values returned + // from Box2D will also be pixels because Box2D does not do any scaling internally. + // However, you are now on the hook for coming up with good values for gravity, density, and + // forces. + // @warning This must be modified before any calls to Box2D SetLengthUnitsPerMeter :: proc(lengthUnits: f32) --- // Get the current length units per meter. @@ -122,6 +117,10 @@ foreign lib { // @ingroup shape DefaultQueryFilter :: proc() -> QueryFilter --- + // Use this to initialize your surface material + // @ingroup shape + DefaultSurfaceMaterial :: proc() -> SurfaceMaterial --- + // Use this to initialize your shape definition // @ingroup shape DefaultShapeDef :: proc() -> ShapeDef --- @@ -142,6 +141,10 @@ foreign lib { // @ingroup mouse_joint DefaultMouseJointDef :: proc() -> MouseJointDef --- + // Use this to initialize your joint definition + // @ingroup filter_joint + DefaultFilterJointDef :: proc() -> FilterJointDef --- + // Use this to initialize your joint definition // @ingroupd prismatic_joint DefaultPrismaticJointDef :: proc() -> PrismaticJointDef --- @@ -157,6 +160,14 @@ foreign lib { // 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 drawing interface. This allows you to implement a sub-set + // of the drawing functions. + DefaultDebugDraw :: proc() -> DebugDraw --- } @@ -164,85 +175,92 @@ foreign lib { @(link_prefix="b2", default_calling_convention="c", require_results) foreign lib { // Validate ray cast input data (NaN, etc) - IsValidRay :: proc(#by_ptr input: RayCastInput) -> bool --- + IsValidRay :: proc(#by_ptr input: RayCastInput) -> bool --- // Make a convex polygon from a convex hull. This will assert if the hull is not valid. // @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull - MakePolygon :: proc(#by_ptr hull: Hull, radius: f32) -> Polygon --- + MakePolygon :: proc(#by_ptr hull: Hull, radius: f32) -> Polygon --- // Make an offset convex polygon from a convex hull. This will assert if the hull is not valid. // @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull - MakeOffsetPolygon :: proc(#by_ptr hull: Hull, radius: f32, transform: Transform) -> Polygon --- + MakeOffsetPolygon :: proc(#by_ptr hull: Hull, position: Vec2, rotation: Rot) -> Polygon --- + + // Make an offset convex polygon from a convex hull. This will assert if the hull is not valid. + // @warning Do not manually fill in the hull data, it must come directly from b2ComputeHull + MakeOffsetRoundedPolygon :: proc(#by_ptr hull: Hull, position: Vec2, rotation: Rot, radius: f32) -> Polygon --- // Make a square polygon, bypassing the need for a convex hull. - MakeSquare :: proc(h: f32) -> Polygon --- + MakeSquare :: proc(halfWidth: f32) -> Polygon --- // Make a box (rectangle) polygon, bypassing the need for a convex hull. - MakeBox :: proc(hx, hy: f32) -> Polygon --- + MakeBox :: proc(halfWidth, halfHeight: f32) -> Polygon --- // Make a rounded box, bypassing the need for a convex hull. - MakeRoundedBox :: proc(hx, hy: f32, radius: f32) -> Polygon --- + MakeRoundedBox :: proc(halfWidth, halfHeight: f32, radius: f32) -> Polygon --- // Make an offset box, bypassing the need for a convex hull. - MakeOffsetBox :: proc(hx, hy: f32, center: Vec2, angle: f32) -> Polygon --- + MakeOffsetBox :: proc(halfWidth, halfHeight: f32, center: Vec2, rotation: Rot) -> Polygon --- + + // Make an offset rounded box, bypassing the need for a convex hull. + MakeOffsetRoundedBox :: proc(halfWidth, halfHeight: f32, center: Vec2, rotation: Rot, radius: f32) -> Polygon --- // Transform a polygon. This is useful for transferring a shape from one body to another. - TransformPolygon :: proc(transform: Transform, #by_ptr polygon: Polygon) -> Polygon --- + TransformPolygon :: proc(transform: Transform, #by_ptr polygon: Polygon) -> Polygon --- // Compute mass properties of a circle - ComputeCircleMass :: proc(#by_ptr shape: Circle, density: f32) -> MassData --- + ComputeCircleMass :: proc(#by_ptr shape: Circle, density: f32) -> MassData --- // Compute mass properties of a capsule - ComputeCapsuleMass :: proc(#by_ptr shape: Capsule, density: f32) -> MassData --- + ComputeCapsuleMass :: proc(#by_ptr shape: Capsule, density: f32) -> MassData --- // Compute mass properties of a polygon - ComputePolygonMass :: proc(#by_ptr shape: Polygon, density: f32) -> MassData --- + ComputePolygonMass :: proc(#by_ptr shape: Polygon, density: f32) -> MassData --- // Compute the bounding box of a transformed circle - ComputeCircleAABB :: proc(#by_ptr shape: Circle, transform: Transform) -> AABB --- + ComputeCircleAABB :: proc(#by_ptr shape: Circle, transform: Transform) -> AABB --- // Compute the bounding box of a transformed capsule - ComputeCapsuleAABB :: proc(#by_ptr shape: Capsule, transform: Transform) -> AABB --- + ComputeCapsuleAABB :: proc(#by_ptr shape: Capsule, transform: Transform) -> AABB --- // Compute the bounding box of a transformed polygon - ComputePolygonAABB :: proc(#by_ptr shape: Polygon, transform: Transform) -> AABB --- + ComputePolygonAABB :: proc(#by_ptr shape: Polygon, transform: Transform) -> AABB --- // Compute the bounding box of a transformed line segment - ComputeSegmentAABB :: proc(#by_ptr shape: Segment, transform: Transform) -> AABB --- + ComputeSegmentAABB :: proc(#by_ptr shape: Segment, transform: Transform) -> AABB --- // Test a point for overlap with a circle in local space - PointInCircle :: proc(point: Vec2, #by_ptr shape: Circle) -> bool --- + PointInCircle :: proc(point: Vec2, #by_ptr shape: Circle) -> bool --- // Test a point for overlap with a capsule in local space - PointInCapsule :: proc(point: Vec2, #by_ptr shape: Capsule) -> bool --- + PointInCapsule :: proc(point: Vec2, #by_ptr shape: Capsule) -> bool --- // Test a point for overlap with a convex polygon in local space - PointInPolygon :: proc(point: Vec2, #by_ptr shape: Polygon) -> bool --- + PointInPolygon :: proc(point: Vec2, #by_ptr shape: Polygon) -> bool --- // Ray cast versus circle in shape local space. Initial overlap is treated as a miss. - RayCastCircle :: proc(#by_ptr input: RayCastInput, #by_ptr shape: Circle) -> CastOutput --- + RayCastCircle :: proc(#by_ptr input: RayCastInput, #by_ptr shape: Circle) -> CastOutput --- // Ray cast versus capsule in shape local space. Initial overlap is treated as a miss. - RayCastCapsule :: proc(#by_ptr input: RayCastInput, #by_ptr shape: Capsule) -> CastOutput --- + RayCastCapsule :: proc(#by_ptr input: RayCastInput, #by_ptr shape: Capsule) -> CastOutput --- // Ray cast versus segment in shape local space. Optionally treat the segment as one-sided with hits from // the left side being treated as a miss. - RayCastSegment :: proc(#by_ptr input: RayCastInput, #by_ptr shape: Segment, oneSided: bool) -> CastOutput --- + RayCastSegment :: proc(#by_ptr input: RayCastInput, #by_ptr shape: Segment, oneSided: bool) -> CastOutput --- // Ray cast versus polygon in shape local space. Initial overlap is treated as a miss. - RayCastPolygon :: proc(#by_ptr input: RayCastInput, #by_ptr shape: Polygon) -> CastOutput --- + RayCastPolygon :: proc(#by_ptr input: RayCastInput, #by_ptr shape: Polygon) -> CastOutput --- // Shape cast versus a circle. Initial overlap is treated as a miss. - ShapeCastCircle :: proc(#by_ptr input: ShapeCastInput, #by_ptr shape: Circle) -> CastOutput --- + ShapeCastCircle :: proc(#by_ptr input: ShapeCastInput, #by_ptr shape: Circle) -> CastOutput --- // Shape cast versus a capsule. Initial overlap is treated as a miss. - ShapeCastCapsule :: proc(#by_ptr input: ShapeCastInput, #by_ptr shape: Capsule) -> CastOutput --- + ShapeCastCapsule :: proc(#by_ptr input: ShapeCastInput, #by_ptr shape: Capsule) -> CastOutput --- // Shape cast versus a line segment. Initial overlap is treated as a miss. - ShapeCastSegment :: proc(#by_ptr input: ShapeCastInput, #by_ptr shape: Segment) -> CastOutput --- + ShapeCastSegment :: proc(#by_ptr input: ShapeCastInput, #by_ptr shape: Segment) -> CastOutput --- // Shape cast versus a convex polygon. Initial overlap is treated as a miss. - ShapeCastPolygon :: proc(#by_ptr input: ShapeCastInput, #by_ptr shape: Polygon) -> CastOutput --- + ShapeCastPolygon :: proc(#by_ptr input: ShapeCastInput, #by_ptr shape: Polygon) -> CastOutput --- } @@ -251,7 +269,7 @@ foreign lib { // - all points very close together // - all points on a line // - less than 3 points -// - more than maxPolygonVertices points +// - more than MAX_POLYGON_VERTICES points // This welds close points and removes collinear points. // @warning Do not modify a hull once it has been computed @(require_results) @@ -279,30 +297,40 @@ foreign lib { } // Compute the closest points between two shapes represented as point clouds. -// DistanceCache cache is input/output. On the first call set DistanceCache.count to zero. +// SimplexCache cache is input/output. On the first call set SimplexCache.count to zero. // The underlying GJK algorithm may be debugged by passing in debug simplexes and capacity. You may pass in NULL and 0 for these. @(require_results) -ShapeDistance :: proc "c" (cache: ^DistanceCache, #by_ptr input: DistanceInput, simplexes: []Simplex) -> DistanceOutput { +ShapeDistance :: proc "c" (#by_ptr input: DistanceInput, cache: ^SimplexCache, simplexes: []Simplex) -> DistanceOutput { foreign lib { - b2ShapeDistance :: proc "c" (cache: ^DistanceCache, #by_ptr input: DistanceInput, simplexes: [^]Simplex, simplexCapacity: c.int) -> DistanceOutput --- + b2ShapeDistance :: proc "c" (#by_ptr input: DistanceInput, cache: ^SimplexCache, simplexes: [^]Simplex, simplexCapacity: c.int) -> DistanceOutput --- } - return b2ShapeDistance(cache, input, raw_data(simplexes), i32(len(simplexes))) + return b2ShapeDistance(input, cache, raw_data(simplexes), i32(len(simplexes))) } -// Make a proxy for use in GJK and related functions. +// Make a proxy for use in overlap, shape cast, and related functions. This is a deep copy of the points. @(require_results) -MakeProxy :: proc "c" (vertices: []Vec2, radius: f32) -> DistanceProxy { +MakeProxy :: proc "c" (points: []Vec2, radius: f32) -> ShapeProxy { foreign lib { - b2MakeProxy :: proc "c" (vertices: [^]Vec2, count: i32, radius: f32) -> DistanceProxy --- + b2MakeProxy :: proc "c" (points: [^]Vec2, count: i32, radius: f32) -> ShapeProxy --- } - return b2MakeProxy(raw_data(vertices), i32(len(vertices)), radius) + return b2MakeProxy(raw_data(points), i32(len(points)), radius) +} + +// Make a proxy with a transform. This is a deep copy of the points. +@(require_results) +MakeOffsetProxy :: proc "c" (points: []Vec2, radius: f32, position: Vec2, rotation: Rot) -> ShapeProxy { + foreign lib { + b2MakeOffsetProxy :: proc "c" (points: [^]Vec2, count: i32, radius: f32, position: Vec2, rotation: Rot) -> ShapeProxy --- + } + return b2MakeOffsetProxy(raw_data(points), i32(len(points)), radius, position, rotation) } @(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. ShapeCast :: proc(#by_ptr input: ShapeCastPairInput) -> CastOutput --- // Evaluate the transform sweep at a specific time. @@ -344,14 +372,14 @@ foreign lib { // Compute the contact manifold between an segment and a polygon CollideSegmentAndPolygon :: proc(#by_ptr segmentA: Segment, xfA: Transform, #by_ptr polygonB: Polygon, xfB: Transform) -> Manifold --- - // Compute the contact manifold between a smooth segment and a circle - CollideSmoothSegmentAndCircle :: proc(#by_ptr smoothSegmentA: SmoothSegment, xfA: Transform, #by_ptr circleB: Circle, xfB: Transform) -> Manifold --- + // Compute the contact manifold between a chain segment and a circle + CollideChainSegmentAndCircle :: proc(#by_ptr segmentA: ChainSegment, xfA: Transform, #by_ptr circleB: Circle, xfB: Transform) -> Manifold --- // Compute the contact manifold between an segment and a capsule - CollideSmoothSegmentAndCapsule :: proc(#by_ptr smoothSegmentA: SmoothSegment, xfA: Transform, #by_ptr capsuleB: Capsule, xfB: Transform, cache: ^DistanceCache) -> Manifold --- + CollideChainSegmentAndCapsule :: proc(#by_ptr segmentA: ChainSegment, xfA: Transform, #by_ptr capsuleB: Capsule, xfB: Transform, cache: ^SimplexCache) -> Manifold --- - // Compute the contact manifold between a smooth segment and a rounded polygon - CollideSmoothSegmentAndPolygon :: proc(#by_ptr smoothSegmentA: SmoothSegment, xfA: Transform, #by_ptr polygonB: Polygon, xfB: Transform, cache: ^DistanceCache) -> Manifold --- + // Compute the contact manifold between a chain segment and a rounded polygon + CollideChainSegmentAndPolygon :: proc(#by_ptr segmentA: ChainSegment, xfA: Transform, #by_ptr polygonB: Polygon, xfB: Transform, cache: ^SimplexCache) -> Manifold --- } @@ -365,7 +393,7 @@ foreign lib { DynamicTree_Destroy :: proc(tree: ^DynamicTree) --- // Create a proxy. Provide an AABB and a userData value. - DynamicTree_CreateProxy :: proc(tree: ^DynamicTree, aabb: AABB, categoryBits: u32, userData: i32) -> i32 --- + DynamicTree_CreateProxy :: proc(tree: ^DynamicTree, aabb: AABB, categoryBits: u64, userData: u64) -> i32 --- // Destroy a proxy. This asserts if the id is invalid. DynamicTree_DestroyProxy :: proc(tree: ^DynamicTree, proxyId: i32) --- @@ -376,50 +404,52 @@ foreign lib { // Enlarge a proxy and enlarge ancestors as necessary. DynamicTree_EnlargeProxy :: proc(tree: ^DynamicTree, proxyId: i32, aabb: AABB) --- - // Query an AABB for overlapping proxies. The callback class - // is called for each proxy that overlaps the supplied AABB. - DynamicTree_Query :: proc(#by_ptr tree: DynamicTree, aabb: AABB, maskBits: u32, callback: TreeQueryCallbackFcn, ctx: rawptr) --- + // Modify the category bits on a proxy. This is an expensive operation. + DynamicTree_SetCategoryBits :: proc(tree: ^DynamicTree, proxyId: i32, categoryBits: u64) --- - // Ray-cast against the proxies in the tree. This relies on the callback - // to perform a exact ray-cast in the case were the proxy contains a shape. + // Get the category bits on a proxy. + DynamicTree_GetCategoryBits :: proc(tree: ^DynamicTree, proxyId: i32) --- + + // Query an AABB for overlapping proxies. The callback class is called for each proxy that overlaps the supplied AABB. + // @return performance data + DynamicTree_Query :: proc(#by_ptr tree: DynamicTree, aabb: AABB, maskBits: u64, callback: TreeQueryCallbackFcn, ctx: rawptr) -> TreeStats --- + + // Ray cast against the proxies in the tree. This relies on the callback + // to perform a exact ray cast in the case were the proxy contains a shape. // The callback also performs the 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. - // @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 filter bits: `bool accept = (maskBits & node->categoryBits) != 0 ---` + // 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 mask bit hint: `bool accept = (maskBits & node->categoryBits) != 0;` // @param callback a callback class that is called for each proxy that is hit by the ray - // @param context user context that is passed to the callback - DynamicTree_RayCast :: proc(#by_ptr tree: DynamicTree, #by_ptr input: RayCastInput, maskBits: u32, callback: TreeRayCastCallbackFcn, ctx: rawptr) --- + // @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, callback: TreeRayCastCallbackFcn, ctx: rawptr) -> TreeStats --- - // Ray-cast against the proxies in the tree. This relies on the callback - // to perform a exact ray-cast in the case were the proxy contains a shape. + // Ray cast against the proxies in the tree. This relies on the callback + // to perform a exact ray cast in the case were the proxy contains a shape. // The callback also performs the 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. // @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 input the ray cast input data. The ray extends from p1 to p1 + maxFraction * (p2 - p1). // @param maskBits filter bits: `bool accept = (maskBits & node->categoryBits) != 0 ---` // @param callback a callback class that is called for each proxy that is hit by the shape // @param context user context that is passed to the callback - DynamicTree_ShapeCast :: proc(#by_ptr tree: DynamicTree, #by_ptr input: ShapeCastInput, maskBits: u32, callback: TreeShapeCastCallbackFcn, ctx: rawptr) --- + // @return performance data + DynamicTree_ShapeCast :: proc(#by_ptr tree: DynamicTree, #by_ptr input: ShapeCastInput, maskBits: u32, callback: TreeShapeCastCallbackFcn, ctx: rawptr) -> TreeStats --- - // Validate this tree. For testing. - DynamicTree_Validate :: proc(#by_ptr tree: DynamicTree) --- - - // Compute the height of the binary tree in O(N) time. Should not be - // called often. + // Get the height of the binary tree. DynamicTree_GetHeight :: proc(#by_ptr tree: DynamicTree) -> c.int --- - // Get the maximum balance of the tree. The balance is the difference in height of the two children of a node. - DynamicTree_GetMaxBalance :: 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 --- - // Build an optimal tree. Very expensive. For testing. - DynamicTree_RebuildBottomUp :: proc(tree: ^DynamicTree) --- + // 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 --- @@ -427,29 +457,47 @@ foreign lib { // 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 --- - // Shift the world origin. Useful for large worlds. - // The shift formula is: position -= newOrigin - // @param tree the tree to shift - // @param newOrigin the new origin with respect to the old origin - DynamicTree_ShiftOrigin :: proc(tree: ^DynamicTree, newOrigin: Vec2) --- - // Get the number of bytes used by this tree DynamicTree_GetByteCount :: proc(#by_ptr tree: DynamicTree) -> c.int --- + + // Get proxy user data + DynamicTree_GetUserData :: proc(#by_ptr tree: DynamicTree, proxyId: c.int) -> u64 --- + + // Get the AABB of a proxy + DynamicTree_GetAABB :: proc(#by_ptr tree: DynamicTree, proxyId: c.int) -> AABB --- + + // Validate this tree. For testing. + DynamicTree_Validate :: proc(#by_ptr tree: DynamicTree) --- + + // Validate this tree has no enlarged AABBs. For testing. + DynamicTree_ValidateNoEnlarged :: proc(#by_ptr tree: DynamicTree) --- } -// Get proxy user data -// @return the proxy user data or 0 if the id is invalid +/** + * @defgroup character Character mover + * Character movement solver + * @{ + */ + @(require_results) -DynamicTree_GetUserData :: #force_inline proc "contextless" (tree: DynamicTree, proxyId: i32) -> i32 { - return tree.nodes[proxyId].userData +SolvePlanes :: proc(position: Vec2, planes: []CollisionPlane) -> PlaneSolverResult { + foreign lib { + b2SolvePlanes :: proc "c" (position: Vec2, planes: [^]CollisionPlane, count: i32) -> PlaneSolverResult --- + } + + return b2SolvePlanes(position, raw_data(planes), i32(len(planes))) } -// Get the AABB of a proxy @(require_results) -DynamicTree_GetAABB :: #force_inline proc "contextless" (tree: DynamicTree, proxyId: i32) -> AABB { - return tree.nodes[proxyId].aabb +ClipVector :: proc(vector: Vec2, planes: []CollisionPlane) -> Vec2 { + foreign lib { + b2ClipVector :: proc "c" (vector: Vec2, planes: [^]CollisionPlane, count: i32) -> Vec2 --- + } + + return b2ClipVector(vector, raw_data(planes), i32(len(planes))) } +/**@}*/ @(link_prefix="b2", default_calling_convention="c", require_results) @@ -477,8 +525,8 @@ foreign lib { // 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. Typically 1/60. - // @param subStepCount The number of sub-steps, increasing the sub-step count can increase accuracy. Typically 4. + // @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 @@ -494,63 +542,73 @@ foreign lib { World_GetContactEvents :: proc(worldId: WorldId) -> ContactEvents --- // Overlap test for all shapes that *potentially* overlap the provided AABB - World_OverlapAABB :: proc(worldId: WorldId, aabb: AABB, filter: QueryFilter, fcn: OverlapResultFcn, ctx: rawptr) --- + World_OverlapAABB :: proc(worldId: WorldId, aabb: AABB, filter: QueryFilter, fcn: OverlapResultFcn, ctx: rawptr) -> TreeStats --- - // Overlap test for for all shapes that overlap the provided circle - World_OverlapCircle :: proc(worldId: WorldId, #by_ptr circle: Circle, transform: Transform, filter: QueryFilter, fcn: OverlapResultFcn, ctx: rawptr) --- - - // Overlap test for all shapes that overlap the provided capsule - World_OverlapCapsule :: proc(worldId: WorldId, #by_ptr capsule: Capsule, transform: Transform, filter: QueryFilter, fcn: OverlapResultFcn, ctx: rawptr) --- - - // Overlap test for all shapes that overlap the provided polygon - World_OverlapPolygon :: proc(worldId: WorldId, #by_ptr polygon: Polygon, transform: Transform, filter: QueryFilter, fcn: OverlapResultFcn, ctx: rawptr) --- + // Overlap test for all shapes that overlap the provided shape proxy. + World_OverlapShape :: proc(worldId: WorldId, #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. // 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 // @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 - // @note The callback function may receive shapes in any order - World_CastRay :: proc(worldId: WorldId, origin: Vec2, translation: Vec2, filter: QueryFilter, fcn: CastResultFcn, ctx: rawptr) --- + // @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: 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. // 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 --- - // Cast a circle through the world. Similar to a cast ray except that a circle is cast instead of a point. - World_CastCircle :: proc(worldId: WorldId, #by_ptr circle: Circle, originTransform: Transform, translation: Vec2, filter: QueryFilter, fcn: CastResultFcn, ctx: rawptr) --- + // Cast a shape through the world. Similar to a cast ray except that a shape is cast instead of a point. + // @see World_CastRay + World_CastShape :: proc(worldId: WorldId, #by_ptr shape: ShapeProxy, translation: Vec2, filter: QueryFilter, fcn: CastResultFcn, ctx: rawptr) -> TreeStats --- - // Cast a capsule through the world. Similar to a cast ray except that a capsule is cast instead of a point. - World_CastCapsule :: proc(worldId: WorldId, #by_ptr capsule: Capsule, originTransform: Transform, translation: Vec2, filter: QueryFilter, fcn: CastResultFcn, ctx: rawptr) --- + // Cast a capsule mover through the world. This is a special shape cast that handles sliding along other shapes while reducing + // clipping. + World_CastMover :: proc(worldId: WorldId, #by_ptr mover: Capsule, translation: Vec2, filter: QueryFilter) -> f32 --- - // Cast a polygon through the world. Similar to a cast ray except that a polygon is cast instead of a point. - World_CastPolygon :: proc(worldId: WorldId, #by_ptr polygon: Polygon, originTransform: Transform, translation: Vec2, filter: QueryFilter, fcn: CastResultFcn, ctx: rawptr) --- + // Collide a capsule mover with the world, gathering collision planes that can be fed to b2SolvePlanes. Useful for + // kinematic character movement. + World_CollideMover :: proc(worldId: WorldId, #by_ptr mover: Capsule, filter: QueryFilter, fcn: CastResultFcn, 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. Typically in meters per second. + // 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 velocity needed to generate a b2ContactHitEvent. - // Typically in meters per second. + // 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) --- @@ -558,7 +616,7 @@ foreign lib { World_SetPreSolveCallback :: proc(worldId: WorldId, fcn: PreSolveFcn, ctx: rawptr) --- // Set the gravity vector for the entire world. Box2D has no concept of an up direction and this - // is left as a decision for the application. Typically in m/s^2. + // is left as a decision for the application. Usually in m/s^2. // @see WorldDef World_SetGravity :: proc(worldId: WorldId, gravity: Vec2) --- @@ -567,31 +625,66 @@ foreign lib { // Apply a radial explosion // @param worldId The world id - // @param position The center of the explosion - // @param radius The radius of the explosion - // @param impulse The impulse of the explosion, typically in kg * m / s or N * s. - World_Explode :: proc(worldId: WorldId, position: Vec2, radius: f32, impulse: f32) --- + // @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 pushVelocity The maximum contact constraint push out velocity (meters per second) + // @param pushSpeed The maximum contact constraint push out speed (meters per second) // @note Advanced feature - World_SetContactTuning :: proc(worldId: WorldId, hertz: f32, dampingRatio: f32, pushVelocity: f32) --- + 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) --- + + // 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 - // sleeping greatly reduces stability and provides no performance gain. + // 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 --- + // Set the user data pointer. + World_SetUserData :: proc(worldId: WorldId, userData: rawptr) --- + + // Get the user data pointer. + World_GetUserData :: proc(worldId: WorldId) -> rawptr --- + + // Set the friction callback. Passing nil resets to default. + World_SetFrictionCallback :: proc(worldId: WorldId, callback: FrictionCallback) --- + + // Set the restitution callback. Passing nil resets to default. + World_SetRestitutionCallback :: proc(worldId: WorldId, callback: RestitutionCallback) --- + // Dump memory stats to box2d_memory.txt World_DumpMemoryStats :: proc(worldId: WorldId) --- + + // This is for internal testing + World_RebuildStaticTree :: proc(worldId: WorldId) --- + + // This is for internal testing + World_EnableSpeculative :: proc(worldId: WorldId, flag: bool) --- } @@ -625,6 +718,12 @@ foreign lib { // properties regardless of the automatic mass setting. Body_SetType :: proc(bodyId: BodyId, type: BodyType) --- + // Set the body name. Up to 32 characters excluding 0 termination. + Body_SetName :: proc(bodyId: BodyId, name: cstring) --- + + // Get the body name. May be nil. + Body_GetName :: proc(bodyId: BodyId) -> cstring --- + // Set the user data for a body Body_SetUserData :: proc(bodyId: BodyId, userData: rawptr) --- @@ -657,23 +756,34 @@ foreign lib { // Get a world vector on a body given a local vector Body_GetWorldVector :: proc(bodyId: BodyId, localVector: Vec2) -> Vec2 --- - // Get the linear velocity of a body's center of mass. Typically in meters per second. + // Get the linear velocity of a body's center of mass. Usually in meters per second. Body_GetLinearVelocity :: proc(bodyId: BodyId) -> Vec2 --- // Get the angular velocity of a body in radians per second Body_GetAngularVelocity :: proc(bodyId: BodyId) -> f32 --- - // Set the linear velocity of a body. Typically in meters per second. + // Set the linear velocity of a body. Usually in meters per second. Body_SetLinearVelocity :: proc(bodyId: BodyId, linearVelocity: Vec2) --- // Set the angular velocity of a body in radians per second Body_SetAngularVelocity :: proc(bodyId: BodyId, angularVelocity: f32) --- + // 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. + // This will automatically wake the body if asleep. + Body_SetTargetTransform :: proc(bodyId: BodyId, target: Transform, timeStep: f32) --- + + // Get the linear velocity of a local point attached to a body. Usually in meters per second. + Body_GetLocalPointVelocity :: proc(bodyId: BodyId, localPoint: Vec2) -> Vec2 --- + + // Get the linear velocity of a world point attached to a body. Usually in meters per second. + GetWorldPointVelocity :: proc(bodyId: BodyId, worldPoint: Vec2) -> Vec2 --- + // 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, typically in newtons (N) + // @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: Vec2, point: Vec2, wake: bool) --- @@ -688,7 +798,7 @@ foreign lib { // 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 about the z-axis (out of the screen), typically in N*m. + // @param torque about the z-axis (out of the screen), usually in N*m. // @param wake also wake up the body Body_ApplyTorque :: proc(bodyId: BodyId, torque: f32, wake: bool) --- @@ -697,7 +807,7 @@ foreign lib { // 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, typically in N*s or kg*m/s. + // @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, @@ -707,7 +817,7 @@ foreign lib { // 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, typically in N*s or kg*m/s. + // @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. @@ -716,17 +826,17 @@ foreign lib { // Apply an angular impulse. The impulse is ignored if the body is not awake. // This optionally wakes the body. // @param bodyId The body id - // @param impulse the angular impulse, typically in units of kg*m*m/s + // @param impulse the angular impulse, 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 force, // use a force instead, which will work better with the sub-stepping solver. Body_ApplyAngularImpulse :: proc(bodyId: BodyId, impulse: f32, wake: bool) --- - // Get the mass of the body, typically in kilograms + // Get the mass of the body, usually in kilograms Body_GetMass :: proc(bodyId: BodyId) -> f32 --- - // Get the inertia tensor of the body, typically in kg*m^2 - Body_GetInertiaTensor :: proc(bodyId: BodyId) -> f32 --- + // Get the rotational inertia of the body, usually in kg*m^2 + Body_GetRotationalInertia :: proc(bodyId: BodyId) -> f32 --- // Get the center of mass position of the body in local space Body_GetLocalCenterOfMass :: proc(bodyId: BodyId) -> Vec2 --- @@ -749,13 +859,6 @@ foreign lib { // You should call this regardless of body type. Body_ApplyMassFromShapes :: proc(bodyId: BodyId) --- - // Set the automatic mass setting. Normally this is set in BodyDef before creation. - // @see BodyDef::automaticMass - Body_SetAutomaticMass :: proc(bodyId: BodyId, automaticMass: bool ) --- - - // Get the automatic mass setting - Body_GetAutomaticMass :: proc(bodyId: BodyId) -> bool --- - // Adjust the linear damping. Normally this is set in BodyDef before creation. Body_SetLinearDamping :: proc(bodyId: BodyId, linearDamping: f32) --- @@ -789,10 +892,10 @@ foreign lib { // Returns true if sleeping is enabled for this body Body_IsSleepEnabled :: proc(bodyId: BodyId) -> bool --- - // Set the sleep threshold, typically in meters per second - Body_SetSleepThreshold :: proc(bodyId: BodyId, sleepVelocity: f32) --- + // Set the sleep threshold, usually in meters per second + Body_SetSleepThreshold :: proc(bodyId: BodyId, sleepThreshold: f32) --- - // Get the sleep threshold, typically in meters per second. + // Get the sleep threshold, usually in meters per second. Body_GetSleepThreshold :: proc(bodyId: BodyId) -> f32 --- // Returns true if this body is enabled @@ -817,9 +920,17 @@ foreign lib { // Is this body a bullet? Body_IsBullet :: proc(bodyId: BodyId) -> bool --- + // Enable/disable contact events on all shapes. + // @see b2ShapeDef::enableContactEvents + // @warning changing this at runtime may cause mismatched begin/end touch events + Body_EnableContactEvents :: proc(bodyId: BodyId, flag: bool) --- + // Enable/disable hit events on all shapes // @see b2ShapeDef::enableHitEvents - Body_EnableHitEvents :: proc(bodyId: BodyId, enableHitEvents: bool) --- + Body_EnableHitEvents :: proc(bodyId: BodyId, flag: 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 --- @@ -898,8 +1009,10 @@ foreign lib { // @return the shape id for accessing the shape CreatePolygonShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, #by_ptr polygon: Polygon) -> ShapeId --- - // Destroy a shape - DestroyShape :: proc(shapeId: 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 b2Body_ApplyMassFromShapes + DestroyShape :: proc(shapeId: ShapeId, updateBodyMass: bool) --- // Shape identifier validation. Provides validation for up to 64K allocations. Shape_IsValid :: proc(id: ShapeId) -> bool --- @@ -910,7 +1023,12 @@ foreign lib { // Get the id of the body that a shape is attached to Shape_GetBody :: proc(shapeId: ShapeId) -> BodyId --- - // Returns true If the shape is a sensor + // Get the world that owns this shape. + Shape_GetWorld :: proc(shapeId: ShapeId) -> WorldId --- + + // Returns true if the shape is a sensor. It is not possible to change a shape + // from sensor to solid dynamically because this breaks the contract for + // sensor events. Shape_IsSensor :: proc(shapeId: ShapeId) -> bool --- // Set the user data for a shape @@ -920,12 +1038,12 @@ foreign lib { // from an event or query. Shape_GetUserData :: proc(shapeId: ShapeId) -> rawptr --- - // Set the mass density of a shape, typically in kg/m^2. - // This will not update the mass properties on the parent body. + // Set the mass density of a shape, usually in kg/m^2. + // This will optionally update the mass properties on the parent body. // @see b2ShapeDef::density, b2Body_ApplyMassFromShapes - Shape_SetDensity :: proc(shapeId: ShapeId, density: f32) --- + Shape_SetDensity :: proc(shapeId: ShapeId, density: f32, updateBodyMass: bool) --- - // Get the density of a shape, typically in kg/m^2 + // Get the density of a shape, usually in kg/m^2 Shape_GetDensity :: proc(shapeId: ShapeId) -> f32 --- // Set the friction on a shape @@ -942,15 +1060,24 @@ foreign lib { // Get the shape restitution Shape_GetRestitution :: proc(shapeId: ShapeId) -> f32 --- + // Set the shape material identifier + // @see b2ShapeDef::material + Shape_SetMaterial :: proc(shapeId: ShapeId, material: c.int) --- + + // Get the shape material identifier + Shape_GetMaterial :: proc(shapeId: ShapeId) -> c.int --- + // Get the shape filter Shape_GetFilter :: proc(shapeId: ShapeId) -> Filter --- - // Set the current filter. This is almost as expensive as recreating the shape. - // @see b2ShapeDef::filter + // Set the current filter. This is almost as expensive as recreating the shape. This may cause + // contacts to be immediately destroyed. However contacts are not created until the next world step. + // Sensor overlap state is also not updated until the next world step. + // @see b2ShapeDef::filter Shape_SetFilter :: proc(shapeId: ShapeId, filter: Filter) --- - // Enable sensor events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. - // @see b2ShapeDef::isSensor + // Enable sensor events for this shape. + // @see b2ShapeDef::enableSensorEvents Shape_EnableSensorEvents :: proc(shapeId: ShapeId, flag: bool) --- // Returns true if sensor events are enabled @@ -958,6 +1085,7 @@ foreign lib { // Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. // @see b2ShapeDef::enableContactEvents + // @warning changing this at run-time may lead to lost begin/end events Shape_EnableContactEvents :: proc(shapeId: ShapeId, flag: bool) --- // Returns true if contact events are enabled @@ -982,7 +1110,7 @@ foreign lib { Shape_TestPoint :: proc(shapeId: ShapeId, point: Vec2) -> bool --- // Ray cast a shape directly - Shape_RayCast :: proc(shapeId: ShapeId, origin: Vec2, translation: Vec2) -> CastOutput --- + Shape_RayCast :: proc(shapeId: ShapeId, #by_ptr input: RayCastInput) -> CastOutput --- // Get a copy of the shape's circle. Asserts the type is correct. Shape_GetCircle :: proc(shapeId: ShapeId) -> Circle --- @@ -990,9 +1118,9 @@ foreign lib { // Get a copy of the shape's line segment. Asserts the type is correct. Shape_GetSegment :: proc(shapeId: ShapeId) -> Segment --- - // Get a copy of the shape's smooth line segment. These come from chain shapes. + // Get a copy of the shape's chain segment. These come from chain shapes. // Asserts the type is correct. - Shape_GetSmoothSegment :: proc(shapeId: ShapeId) -> SmoothSegment --- + Shape_GetChainSegment :: proc(shapeId: ShapeId) -> ChainSegment --- // Get a copy of the shape's capsule. Asserts the type is correct. Shape_GetCapsule :: proc(shapeId: ShapeId) -> Capsule --- @@ -1018,16 +1146,34 @@ foreign lib { // @see b2Body_ApplyMassFromShapes Shape_SetPolygon :: proc(shapeId: ShapeId, #by_ptr polygon: Polygon) --- - // Get the parent chain id if the shape type is b2_smoothSegmentShape, otherwise + // Get the parent chain id if the shape type is a chain segment, otherwise // returns b2_nullChainId. Shape_GetParentChain :: proc(shapeId: ShapeId) -> ChainId --- // Get the maximum capacity required for retrieving all the touching contacts on a shape Shape_GetContactCapacity :: proc(shapeId: ShapeId) -> 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 b2Shape_GetSensorOverlaps + Shape_GetSensorCapacity :: proc(shapeId: ShapeId) -> c.int --- + + // Get the overlapped shapes for a sensor shape. + // @param shapeId the id of a sensor shape + // @param overlaps a user allocated array that is filled with the overlapping shapes + // @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 b2Shape_IsValid to confirm each overlap + Shape_GetSensorOverlaps :: proc(shapeId: ShapeId, overlaps: [^]ShapeId, capacity: c.int) -> c.int --- + // Get the current world AABB Shape_GetAABB :: proc(shapeId: ShapeId) -> AABB --- + // Get the mass data of a shape + Shape_GetMassData :: 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: Vec2) -> Vec2 --- } @@ -1049,21 +1195,44 @@ foreign lib { // Create a chain shape // @see b2ChainDef for details - CreateChain :: proc(bodyId: BodyId, #by_ptr def: ChainDef) -> ChainId --- + CreateChain :: proc(bodyId: BodyId, #by_ptr def: ChainDef) -> ChainId --- // Destroy a chain shape - DestroyChain :: proc(chainId: ChainId) --- + DestroyChain :: proc(chainId: ChainId) --- + + // Get the world that owns this chain shape + Chain_GetWorld :: proc(chainId: ChainId) -> WorldId --- + + // Get the number of segments on this chain + Chain_GetSegmentCount :: proc(chainId: ChainId) -> c.int --- + + // Fill a user array with chain segment shape ids up to the specified capacity. Returns + // the actual number of segments returned. + Chain_GetSegments :: proc(chainId: ChainId, segmentArray: [^]ShapeId, capacity: c.int) -> c.int --- // Set the chain friction // @see b2ChainDef::friction - Chain_SetFriction :: proc(chainId: ChainId, friction: f32) --- + Chain_SetFriction :: proc(chainId: ChainId, friction: f32) --- + + // Get the chain friction + Chain_GetFriction :: proc(chainId: ChainId) -> f32 --- // Set the chain restitution (bounciness) // @see b2ChainDef::restitution - Chain_SetRestitution :: proc(chainId: ChainId, restitution: f32) --- + Chain_SetRestitution :: proc(chainId: ChainId, restitution: f32) --- + + // Get the chain restitution + Chain_GetRestitution :: proc(chainId: ChainId) -> f32 --- + + // Set the chain material + // @see b2ChainDef::material + Chain_SetMaterial :: proc(chainId: ChainId, material: c.int) --- + + // Get the chain material + Chain_GetMaterial :: proc(chainId: ChainId) -> c.int --- // Chain identifier validation. Provides validation for up to 64K allocations. - Chain_IsValid :: proc(id: ChainId) -> bool --- + Chain_IsValid :: proc(id: ChainId) -> bool --- /** * @defgroup joint Joint @@ -1085,6 +1254,9 @@ foreign lib { // 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 --- + // Get the local anchor on bodyA Joint_GetLocalAnchorA :: proc(jointId: JointId) -> Vec2 --- @@ -1106,10 +1278,10 @@ foreign lib { // Wake the bodies connect to this joint Joint_WakeBodies :: proc(jointId: JointId) --- - // Get the current constraint force for this joint + // Get the current constraint force for this joint. Usually in Newtons. Joint_GetConstraintForce :: proc(jointId: JointId) -> Vec2 --- - // Get the current constraint torque for this joint + // Get the current constraint torque for this joint. Usually in Newton * meters. Joint_GetConstraintTorque :: proc(jointId: JointId) -> f32 --- /** @@ -1142,10 +1314,10 @@ foreign lib { DistanceJoint_SetSpringDampingRatio :: proc(jointId: JointId, dampingRatio: f32) --- // Get the spring Hertz - DistanceJoint_GetHertz :: proc(jointId: JointId) -> f32 --- + DistanceJoint_GetSpringHertz :: proc(jointId: JointId) -> f32 --- // Get the spring damping ratio - DistanceJoint_GetDampingRatio :: proc(jointId: JointId) -> f32 --- + 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. @@ -1172,19 +1344,19 @@ foreign lib { // Is the distance joint motor enabled? DistanceJoint_IsMotorEnabled :: proc(jointId: JointId) -> bool --- - // Set the distance joint motor speed, typically in meters per second + // Set the distance joint motor speed, usually in meters per second DistanceJoint_SetMotorSpeed :: proc(jointId: JointId, motorSpeed: f32) --- - // Get the distance joint motor speed, typically in meters per second + // Get the distance joint motor speed, usually in meters per second DistanceJoint_GetMotorSpeed :: proc(jointId: JointId) -> f32 --- - // Set the distance joint maximum motor force, typically in newtons + // Set the distance joint maximum motor force, usually in newtons DistanceJoint_SetMaxMotorForce :: proc(jointId: JointId, force: f32) --- - // Get the distance joint maximum motor force, typically in newtons + // Get the distance joint maximum motor force, usually in newtons DistanceJoint_GetMaxMotorForce :: proc(jointId: JointId) -> f32 --- - // Get the distance joint current motor force, typically in newtons + // Get the distance joint current motor force, usually in newtons DistanceJoint_GetMotorForce :: proc(jointId: JointId) -> f32 --- /** @@ -1212,22 +1384,22 @@ foreign lib { // Get the motor joint angular offset target in radians MotorJoint_GetAngularOffset :: proc(jointId: JointId) -> f32 --- - // Set the motor joint maximum force, typically in newtons + // Set the motor joint maximum force, usually in newtons MotorJoint_SetMaxForce :: proc(jointId: JointId, maxForce: f32) --- - // Get the motor joint maximum force, typically in newtons + // Get the motor joint maximum force, usually in newtons MotorJoint_GetMaxForce :: proc(jointId: JointId) -> f32 --- - // Set the motor joint maximum torque, typically in newton-meters + // Set the motor joint maximum torque, usually in newton-meters MotorJoint_SetMaxTorque :: proc(jointId: JointId, maxTorque: f32) --- - // Get the motor joint maximum torque, typically in newton-meters + // Get the motor joint maximum torque, usually in newton-meters MotorJoint_GetMaxTorque :: proc(jointId: JointId) -> f32 --- - // Set the motor joint correction factor, typically in [0, 1] + // Set the motor joint correction factor, usually in [0, 1] MotorJoint_SetCorrectionFactor :: proc(jointId: JointId, correctionFactor: f32) --- - // Get the motor joint correction factor, typically in [0, 1] + // Get the motor joint correction factor, usually in [0, 1] MotorJoint_GetCorrectionFactor :: proc(jointId: JointId) -> f32 --- /**@}*/ @@ -1262,14 +1434,29 @@ foreign lib { // Get the mouse joint damping ratio, non-dimensional MouseJoint_GetSpringDampingRatio :: proc(jointId: JointId) -> f32 --- - // Set the mouse joint maximum force, typically in newtons + // Set the mouse joint maximum force, usually in newtons MouseJoint_SetMaxForce :: proc(jointId: JointId, maxForce: f32) --- - // Get the mouse joint maximum force, typically in newtons + // Get the mouse joint maximum force, usually in newtons MouseJoint_GetMaxForce :: proc(jointId: JointId) -> f32 --- /**@}*/ + /** + * @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 b2FilterJointDef for details + CreateFilterJoint :: proc(worldId: WorldId, #by_ptr def: FilterJointDef) -> JointId --- + + /**@}*/ + /** * @defgroup prismatic_joint Prismatic Joint * @brief A prismatic joint allows for translation along a single axis with no rotation. @@ -1323,21 +1510,27 @@ foreign lib { // Is the prismatic joint motor enabled? PrismaticJoint_IsMotorEnabled :: proc(jointId: JointId) -> bool --- - // Set the prismatic joint motor speed, typically in meters per second + // Set the prismatic joint motor speed, usually in meters per second PrismaticJoint_SetMotorSpeed :: proc(jointId: JointId, motorSpeed: f32) --- - // Get the prismatic joint motor speed, typically in meters per second + // Get the prismatic joint motor speed, usually in meters per second PrismaticJoint_GetMotorSpeed :: proc(jointId: JointId) -> f32 --- - // Set the prismatic joint maximum motor force, typically in newtons + // Set the prismatic joint maximum motor force, usually in newtons PrismaticJoint_SetMaxMotorForce :: proc(jointId: JointId, force: f32) --- - // Get the prismatic joint maximum motor force, typically in newtons + // Get the prismatic joint maximum motor force, usually in newtons PrismaticJoint_GetMaxMotorForce :: proc(jointId: JointId) -> f32 --- - // Get the prismatic joint current motor force, typically in newtons + // 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 --- + /** * @defgroup revolute_joint Revolute Joint * @brief A revolute joint allows for relative rotation in the 2D plane with no relative translation. @@ -1353,6 +1546,9 @@ foreign lib { // Enable/disable the revolute joint spring RevoluteJoint_EnableSpring :: proc(jointId: JointId, enableSpring: bool) --- + // Is the revolute spring enabled? + RevoluteJoint_IsSpringEnabled :: proc(jointId: JointId) -> bool --- + // Set the revolute joint spring stiffness in Hertz RevoluteJoint_SetSpringHertz :: proc(jointId: JointId, hertz: f32) --- @@ -1381,7 +1577,8 @@ foreign lib { // Get the revolute joint upper limit in radians RevoluteJoint_GetUpperLimit :: proc(jointId: JointId) -> f32 --- - // Set the revolute joint limits in radians + // 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. RevoluteJoint_SetLimits :: proc(jointId: JointId, lower, upper: f32) --- // Enable/disable a revolute joint motor @@ -1396,13 +1593,13 @@ foreign lib { // Get the revolute joint motor speed in radians per second RevoluteJoint_GetMotorSpeed :: proc(jointId: JointId) -> f32 --- - // Get the revolute joint current motor torque, typically in newton-meters + // Get the revolute joint current motor torque, usually in newton-meters RevoluteJoint_GetMotorTorque :: proc(jointId: JointId) -> f32 --- - // Set the revolute joint maximum motor torque, typically in newton-meters + // 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, typically in newton-meters + // Get the revolute joint maximum motor torque, usually in newton-meters RevoluteJoint_GetMaxMotorTorque :: proc(jointId: JointId) -> f32 --- /**@}*/ @@ -1421,6 +1618,12 @@ 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) --- @@ -1503,22 +1706,24 @@ foreign lib { // Get the wheel joint motor speed in radians per second WheelJoint_GetMotorSpeed :: proc(jointId: JointId) -> f32 --- - // Set the wheel joint maximum motor torque, typically in newton-meters + // Set the wheel joint maximum motor torque, usually in newton-meters WheelJoint_SetMaxMotorTorque :: proc(jointId: JointId, torque: f32) --- - // Get the wheel joint maximum motor torque, typically in newton-meters + // Get the wheel joint maximum motor torque, usually in newton-meters WheelJoint_GetMaxMotorTorque :: proc(jointId: JointId) -> f32 --- - // Get the wheel joint current motor torque, typically in newton-meters + // Get the wheel joint current motor torque, usually in newton-meters WheelJoint_GetMotorTorque :: proc(jointId: JointId) -> f32 --- } IsValid :: proc{ - Float_IsValid, - Vec2_IsValid, - Rot_IsValid, + IsValidFloat, + IsValidVec2, + IsValidRotation, + IsValidAABB, + IsValidPlane, World_IsValid, Body_IsValid, Shape_IsValid, diff --git a/vendor/box2d/build_box2d.sh b/vendor/box2d/build_box2d.sh index 74d75eb57..e9682c826 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.0.0" +VERSION="3.1.0" RELEASE="https://github.com/erincatto/box2d/archive/refs/tags/v$VERSION.tar.gz" cd "$(odin root)"/vendor/box2d @@ -75,5 +75,5 @@ if [[ $? -ne 0 ]]; then fi set -e -rm -rf v3.0.0.tar.gz -rm -rf box2d-3.0.0 +rm -rf "v$VERSION.tar.gz" +rm -rf box2d-"$VERSION" diff --git a/vendor/box2d/collision.odin b/vendor/box2d/collision.odin index 88f3b3a77..b29e2f946 100644 --- a/vendor/box2d/collision.odin +++ b/vendor/box2d/collision.odin @@ -5,9 +5,9 @@ import "core:c" // The maximum number of vertices on a convex polygon. Changing this affects performance even if you // don't use more vertices. -maxPolygonVertices :: 8 +MAX_POLYGON_VERTICES :: 8 -// Low level ray-cast input data +// Low level ray cast input data RayCastInput :: struct { // Start point of the ray cast origin: Vec2, @@ -19,27 +19,37 @@ RayCastInput :: struct { maxFraction: f32, } +// A distance proxy is used by the GJK algorithm. It encapsulates any shape. +// You can provide between 1 and MAX_POLYGON_VERTICES and a radius. +ShapeProxy :: struct { + // The point cloud + points: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`, + + // The number of points. Must be greater than 0. + count: c.int, + + // The external radius of the point cloud. May be zero. + radius: f32, +} + // Low level shape cast input in generic form. This allows casting an arbitrary point -// cloud wrap with a radius. For example, a circle 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. +// cloud wrap with a radius. For example, a circle 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 point cloud to cast - points: [maxPolygonVertices]Vec2 `fmt:"v,count"`, - - // The number of points - count: i32, - - // The radius around the point cloud - radius: f32, + // A generic shape + proxy: ShapeProxy, // The translation of the shape cast translation: Vec2, // 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, } -// Low level ray-cast or shape-cast output data +// Low level ray cast or shape-cast output data CastOutput :: struct { // The surface normal at the hit point normal: Vec2, @@ -51,7 +61,7 @@ CastOutput :: struct { fraction: f32, // The number of iterations used - iterations: i32, + iterations: c.int, // Did the cast hit? hit: bool, @@ -93,16 +103,16 @@ Capsule :: struct { // A solid convex polygon. It is assumed that the interior of the polygon is to // the left of each edge. -// Polygons have a maximum number of vertices equal to maxPolygonVertices. +// Polygons have a maximum number of vertices equal to MAX_POLYGON_VERTICES. // In most cases you should not need many vertices for a convex polygon. -// @warning DO NOT fill this out manually, instead use a helper function like -// b2MakePolygon or b2MakeBox. +// @warning DO NOT fill this out manually, instead use a helper function like +// b2MakePolygon or b2MakeBox. Polygon :: struct { // The polygon vertices - vertices: [maxPolygonVertices]Vec2 `fmt:"v,count"`, + vertices: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`, // The outward normal vectors of the polygon sides - normals: [maxPolygonVertices]Vec2 `fmt:"v,count"`, + normals: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`, // The centroid of the polygon centroid: Vec2, @@ -111,7 +121,7 @@ Polygon :: struct { radius: f32, // The number of polygon vertices - count: i32, + count: c.int, } // A line segment with two-sided collision. @@ -123,10 +133,10 @@ Segment :: struct { point2: Vec2, } -// A smooth line segment with one-sided collision. Only collides on the right side. +// A line segment with one-sided collision. Only collides on the right side. // Several of these are generated for a chain shape. // ghost1 -> point1 -> point2 -> ghost2 -SmoothSegment :: struct { +ChainSegment :: struct { // The tail ghost vertex ghost1: Vec2, @@ -137,7 +147,7 @@ SmoothSegment :: struct { ghost2: Vec2, // The owning chain shape index (internal usage only) - chainId: i32, + chainId: c.int, } @@ -145,10 +155,10 @@ SmoothSegment :: struct { // @warning Do not modify these values directly, instead use b2ComputeHull() Hull :: struct { // The final points of the hull - points: [maxPolygonVertices]Vec2 `fmt:"v,count"`, + points: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`, // The number of points - count: i32, + count: c.int, } /** @@ -178,21 +188,11 @@ SegmentDistanceResult :: struct { distanceSquared: f32, } -// A distance proxy is used by the GJK algorithm. It encapsulates any shape. -DistanceProxy :: struct { - // The point cloud - points: [maxPolygonVertices]Vec2 `fmt:"v,count"`, - - // The number of points - count: i32, - - // The external radius of the point cloud - radius: f32, -} - -// Used to warm start b2Distance. Set count to zero on first call or -// use zero initialization. -DistanceCache :: struct { +// 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 { // The number of stored simplex points count: u16, @@ -203,15 +203,15 @@ DistanceCache :: struct { indexB: [3]u8 `fmt:"v,count"`, } -emptyDistanceCache :: DistanceCache{} +emptySimplexCache :: SimplexCache{} // Input for b2ShapeDistance DistanceInput :: struct { // The proxy for shape A - proxyA: DistanceProxy, + proxyA: ShapeProxy, // The proxy for shape B - proxyB: DistanceProxy, + proxyB: ShapeProxy, // The world transform for shape A transformA: Transform, @@ -227,6 +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 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 @@ -234,28 +235,29 @@ DistanceOutput :: struct { // Simplex vertex for debugging the GJK algorithm SimplexVertex :: struct { - wA: Vec2, // support point in proxyA - wB: Vec2, // support point in proxyB - w: Vec2, // wB - wA - a: f32, // barycentric coordinate for closest point - indexA: i32, // wA index - indexB: i32, // wB index + wA: Vec2, // support point in proxyA + wB: Vec2, // support point in proxyB + w: Vec2, // wB - wA + a: f32, // barycentric coordinate for closest point + indexA: c.int, // wA index + indexB: c.int, // wB index } // Simplex from the GJK algorithm Simplex :: struct { v1, v2, v3: SimplexVertex `fmt:"v,count"`, // vertices - count: i32, // number of valid vertices + count: c.int, // number of valid vertices } // Input parameters for b2ShapeCast ShapeCastPairInput :: struct { - proxyA: DistanceProxy, // The proxy for shape A - proxyB: DistanceProxy, // The proxy for shape B + proxyA: ShapeProxy, // The proxy for shape A + proxyB: ShapeProxy, // The proxy for shape B transformA: Transform, // The world transform for shape A transformB: Transform, // The world transform for shape B translationB: Vec2, // The translation of shape B 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 } @@ -272,11 +274,11 @@ Sweep :: struct { // Input parameters for b2TimeOfImpact TOIInput :: struct { - proxyA: DistanceProxy, // The proxy for shape A - proxyB: DistanceProxy, // The proxy for shape B - sweepA: Sweep, // The movement of shape A - sweepB: Sweep, // The movement of shape B - tMax: f32, // Defines the sweep interval [0, tMax] + 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, maxFraction] } // Describes the TOI output @@ -290,8 +292,8 @@ TOIState :: enum c.int { // Output parameters for b2TimeOfImpact. TOIOutput :: struct { - state: TOIState, // The type of result - t: f32, // The time of the collision + state: TOIState, // The type of result + fraction: f32, // The sweep time of the collision } @@ -301,26 +303,30 @@ TOIOutput :: struct { * @brief Functions for colliding 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. +// 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. +// Box2D uses speculative collision so some contact points may be separated. +// You may use the totalNormalImpulse to determine if there was an interaction during +// the time step. ManifoldPoint :: struct { // Location of the contact point in world space. Subject to precision loss at large coordinates. // @note Should only be used for debugging. point: Vec2, - // Location of the contact point relative to bodyA's origin in world space - // @note When used internally to the Box2D solver, these are relative to the center of mass. + // Location of the contact point relative to shapeA's origin in world space + // @note When used internally to the Box2D solver, this is relative to the body center of mass. anchorA: Vec2, - // Location of the contact point relative to bodyB's origin in world space + // Location of the contact point relative to shapeB's origin in world space + // @note When used internally to the Box2D solver, this is relative to the body center of mass. anchorB: Vec2, // The separation of the contact point, negative if penetrating separation: f32, - // The impulse along the manifold normal vector. - normalImpulse: f32, + // The total normal impulse applied across sub-stepping and restitution. This is important + // to identify speculative contact points that had an interaction in the time step. + totalNormalImpulse: f32, // The friction impulse tangentImpulse: f32, @@ -340,16 +346,21 @@ ManifoldPoint :: struct { persisted: bool, } -// A contact manifold describes the contact points between colliding shapes +// A contact manifold describes the contact points between colliding shapes. +// @note Box2D uses speculative collision so some contact points may be separated. Manifold :: struct { - // The manifold points, up to two are possible in 2D - points: [2]ManifoldPoint, - // The unit normal vector in world space, points from shape A to bodyB - normal: Vec2, + normal: Vec2, + + // Angular impulse applied for rolling resistance. N * m * s = kg * m^2 / s + rollingImpulse: f32, + + // The manifold points, up to two are possible in 2D + points: [2]ManifoldPoint, + // The number of contacts points, will be 0, 1, or 2 - pointCount: i32, + pointCount: c.int, } @@ -364,63 +375,17 @@ Manifold :: struct { * 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, such as a reference to a b2Shape. + * 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. - * - * @note This is an advanced feature and normally not used by applications directly. */ -// The default category bit for a tree proxy. Used for collision filtering. -defaultCategoryBits :: 0x00000001 - -// Convenience mask bits to use when you don't need collision filtering and just want -// all results. -defaultMaskBits :: 0xFFFFFFFF - -// A node in the dynamic tree. This is private data placed here for performance reasons. -// 16 + 16 + 8 + pad(8) -TreeNode :: struct { - // The node bounding box - aabb: AABB, // 16 - - // Category bits for collision filtering - categoryBits: u32, // 4 - - using _: struct #raw_union { - // The node parent index - parent: i32, - - // The node freelist next index - next: i32, - }, // 4 - - // Child 1 index - child1: i32, // 4 - - // Child 2 index - child2: i32, // 4 - - // User data - // todo could be union with child index - userData: i32, // 4 - - // Leaf = 0, free node = -1 - height: i16, // 2 - - // Has the AABB been enlarged? - enlarged: bool, // 1 - - // Padding for clarity - _: [9]byte, -} - // The dynamic tree structure. This should be considered private data. // It is placed here for performance reasons. DynamicTree :: struct { // The tree nodes - nodes: [^]TreeNode `fmt"v,nodeCount"`, + nodes: rawptr, // The root index root: i32, @@ -453,16 +418,25 @@ DynamicTree :: struct { rebuildCapacity: i32, } +// 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 :: #type proc "c" (proxyId: i32, userData: i32, ctx: rawptr) -> bool +TreeQueryCallbackFcn :: #type proc "c" (proxyId: i32, userData: u64, ctx: rawptr) -> bool -// This function receives clipped ray-cast input for a proxy. The function +// 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 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 -TreeShapeCastCallbackFcn :: #type proc "c" (#by_ptr input: ShapeCastInput, proxyId: i32, userData: i32, ctx: rawptr) -> f32 +TreeShapeCastCallbackFcn :: #type proc "c" (#by_ptr input: ShapeCastInput, proxyId: i32, userData: u64, ctx: rawptr) -> f32 // This function receives clipped raycast input for a proxy. The function @@ -470,4 +444,47 @@ TreeShapeCastCallbackFcn :: #type proc "c" (#by_ptr input: ShapeCastInput, proxy // - 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 :: #type proc "c" (#by_ptr input: RayCastInput, proxyId: i32, userData: i32, ctx: rawptr) -> f32 +TreeRayCastCallbackFcn :: #type proc "c" (#by_ptr input: RayCastInput, proxyId: i32, userData: u64, ctx: rawptr) -> f32 + +/**@}*/ + +/** + * @defgroup character Character mover + * Character movement solver + * @{ + */ + +/// These are the collision planes returned from b2World_CollideMover +PlaneResult :: struct { + // The collision plane between the mover and convex shape + plane: Plane, + + // Did the collision register a hit? If not this plane should be ignored. + hit: bool, +} + +// These are collision planes that can be fed to b2SolvePlanes. Normally +// this is assembled by the user from plane results in b2PlaneResult +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 b2SolvePlanes. Usually in meters. + push: f32, + + // Indicates if b2ClipVector should clip against this plane. Should be false for soft collision. + clipVelocity: bool, +} + +// Result returned by b2SolvePlanes +PlaneSolverResult :: struct { + // The final position of the mover + position: 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 211b8b79d..cb530527a 100644 --- a/vendor/box2d/id.odin +++ b/vendor/box2d/id.odin @@ -23,45 +23,46 @@ import "base:intrinsics" /// World id references a world instance. This should be treated as an opaque handle. WorldId :: struct { - index1: u16, - revision: u16, + index1: u16, + generation: u16, } /// Body id references a body instance. This should be treated as an opaque handle. BodyId :: struct { - index1: i32, - world0: u16, - revision: u16, + 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, - revision: u16, -} - -/// Joint id references a joint instance. This should be treated as an opaque handle. -JointId :: struct { - index1: i32, - world0: u16, - revision: u16, + index1: i32, + world0: u16, + generation: u16, } /// Chain id references a chain instances. This should be treated as an opaque handle. ChainId :: struct { - index1: i32, - world0: u16, - revision: u16, + 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, +} + + /// Use these to make your identifiers null. /// You may also use zero initialization to get null. nullWorldId :: WorldId{} nullBodyId :: BodyId{} nullShapeId :: ShapeId{} -nullJointId :: JointId{} nullChainId :: ChainId{} +nullJointId :: JointId{} /// Macro to determine if any id is null. IS_NULL :: #force_inline proc "c" (id: $T) -> bool @@ -82,6 +83,6 @@ ID_EQUALS :: #force_inline proc "c" (id1, id2: $T) -> bool where intrinsics.type_is_struct(T), intrinsics.type_has_field(T, "index1"), intrinsics.type_has_field(T, "world0"), - intrinsics.type_has_field(T, "revision") { - return id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.revision == id2.revision + intrinsics.type_has_field(T, "generation") { + return id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.generation == id2.generation } diff --git a/vendor/box2d/lib/box2d_darwin_amd64_avx2.a b/vendor/box2d/lib/box2d_darwin_amd64_avx2.a index 269de02fa..f98e919a4 100644 Binary files a/vendor/box2d/lib/box2d_darwin_amd64_avx2.a and b/vendor/box2d/lib/box2d_darwin_amd64_avx2.a differ diff --git a/vendor/box2d/lib/box2d_darwin_amd64_sse2.a b/vendor/box2d/lib/box2d_darwin_amd64_sse2.a index 096db9d7a..3df91e852 100644 Binary files a/vendor/box2d/lib/box2d_darwin_amd64_sse2.a and b/vendor/box2d/lib/box2d_darwin_amd64_sse2.a differ diff --git a/vendor/box2d/lib/box2d_darwin_arm64.a b/vendor/box2d/lib/box2d_darwin_arm64.a index 4feb4aac9..c5d04fc73 100644 Binary files a/vendor/box2d/lib/box2d_darwin_arm64.a and b/vendor/box2d/lib/box2d_darwin_arm64.a differ diff --git a/vendor/box2d/lib/box2d_wasm.o b/vendor/box2d/lib/box2d_wasm.o index 15d71c5a1..fc3ed1cbd 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 568900bd9..8b70dcedd 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/lib/box2d_windows_amd64_avx2.lib b/vendor/box2d/lib/box2d_windows_amd64_avx2.lib index cea3f678d..7b8626b80 100644 Binary files a/vendor/box2d/lib/box2d_windows_amd64_avx2.lib and b/vendor/box2d/lib/box2d_windows_amd64_avx2.lib differ diff --git a/vendor/box2d/lib/box2d_windows_amd64_sse2.lib b/vendor/box2d/lib/box2d_windows_amd64_sse2.lib index 1ba62c76b..f1442615f 100644 Binary files a/vendor/box2d/lib/box2d_windows_amd64_sse2.lib and b/vendor/box2d/lib/box2d_windows_amd64_sse2.lib differ diff --git a/vendor/box2d/math_functions.odin b/vendor/box2d/math_functions.odin index 2294a515c..4394feb39 100644 --- a/vendor/box2d/math_functions.odin +++ b/vendor/box2d/math_functions.odin @@ -3,9 +3,18 @@ package vendor_box2d import "core:c" import "core:math" -pi :: 3.14159265359 +EPSILON :: 1e-23 Vec2 :: [2]f32 + +// Cosine and sine pair +// This uses a custom implementation designed for cross-platform determinism +CosSin :: struct { + // cosine and sine + cosine: f32, + sine: f32, +} + Rot :: struct { c, s: f32, // cosine and sine } @@ -21,11 +30,43 @@ AABB :: struct { upperBound: Vec2, } +// separation = dot(normal, point) - offset +Plane :: struct { + normal: Vec2, + offset: f32, +} + +PI :: math.PI + Vec2_zero :: Vec2{0, 0} Rot_identity :: Rot{1, 0} Transform_identity :: Transform{{0, 0}, {1, 0}} Mat22_zero :: Mat22{0, 0, 0, 0} +// @return the minimum of two integers +@(deprecated="Prefer the built-in 'min(a, b)'", require_results) +MinInt :: proc "c" (a, b: c.int) -> c.int { + return min(a, b) +} + +// @return the maximum of two integers +@(deprecated="Prefer the built-in 'max(a, b)'", require_results) +MaxInt :: proc "c" (a, b: c.int) -> c.int { + return max(a, b) +} + +// @return the absolute value of an integer +@(deprecated="Prefer the built-in 'abs(a)'", require_results) +AbsInt :: proc "c" (a: c.int) -> c.int { + return abs(a) +} + +// @return an integer clamped between a lower and upper bound +@(deprecated="Prefer the built-in 'clamp(a, lower, upper)'", require_results) +ClampInt :: proc "c" (a, lower, upper: c.int) -> c.int { + return clamp(a, lower, upper) +} + // @return the minimum of two floats @(deprecated="Prefer the built-in 'min(a, b)'", require_results) @@ -51,28 +92,15 @@ ClampFloat :: proc "c" (a, lower, upper: f32) -> f32 { return clamp(a, lower, upper) } -// @return the minimum of two integers -@(deprecated="Prefer the built-in 'min(a, b)'", require_results) -MinInt :: proc "c" (a, b: c.int) -> c.int { - return min(a, b) +@(require_results) +Atan2 :: proc "c" (y, x: f32) -> f32 { + return math.atan2(y, x) } -// @return the maximum of two integers -@(deprecated="Prefer the built-in 'max(a, b)'", require_results) -MaxInt :: proc "c" (a, b: c.int) -> c.int { - return max(a, b) -} - -// @return the absolute value of an integer -@(deprecated="Prefer the built-in 'abs(a)'", require_results) -AbsInt :: proc "c" (a: c.int) -> c.int { - return abs(a) -} - -// @return an integer clamped between a lower and upper bound -@(deprecated="Prefer the built-in 'clamp(a, lower, upper)'", require_results) -ClampInt :: proc "c" (a, lower, upper: c.int) -> c.int { - return clamp(a, lower, upper) +@(require_results) +ComputeCosSin :: proc "c" (radians: f32) -> (res: CosSin) { + res.sine, res.cosine = math.sincos(radians) + return } // Vector dot product @@ -198,12 +226,6 @@ Length :: proc "c" (v: Vec2) -> f32 { return math.sqrt(v.x * v.x + v.y * v.y) } -// Get the length squared of this vector -@(require_results) -LengthSquared :: proc "c" (v: Vec2) -> f32 { - return v.x * v.x + v.y * v.y -} - // Get the distance between two points @(require_results) Distance :: proc "c" (a, b: Vec2) -> f32 { @@ -212,45 +234,41 @@ Distance :: proc "c" (a, b: Vec2) -> f32 { return math.sqrt(dx * dx + dy * dy) } -// Get the distance squared between points @(require_results) -DistanceSquared :: proc "c" (a, b: Vec2) -> f32 { - c := Vec2{b.x - a.x, b.y - a.y} - return c.x * c.x + c.y * c.y +Normalize :: proc "c" (v: Vec2) -> Vec2 { + length := Length(v) + if length < EPSILON { + return Vec2_zero + } + invLength := 1 / length + return invLength * v } -// Make a rotation using an angle in radians @(require_results) -MakeRot :: proc "c" (angle: f32) -> Rot { - // todo determinism - return {math.cos(angle), math.sin(angle)} +IsNormalized :: proc "c" (v: Vec2) -> bool { + aa := Dot(v, v) + return abs(1. - aa) < 10. * EPSILON } -// Normalize rotation @(require_results) -NormalizeRot :: proc "c" (q: Rot) -> Rot { - mag := math.sqrt(q.s * q.s + q.c * q.c) - invMag := f32(mag > 0.0 ? 1.0 / mag : 0.0) - return {q.c * invMag, q.s * invMag} +NormalizeChecked :: proc "odin" (v: Vec2) -> Vec2 { + length := Length(v) + if length < 1e-23 { + panic("zero-length Vec2") + } + invLength := 1 / length + return invLength * v } -// Is this rotation normalized? @(require_results) -IsNormalized :: proc "c" (q: Rot) -> bool { - // larger tolerance due to failure on mingw 32-bit - qq := q.s * q.s + q.c * q.c - return 1.0 - 0.0006 < qq && qq < 1 + 0.0006 -} - -// Normalized linear interpolation -// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ -@(require_results) -NLerp :: proc "c" (q1: Rot, q2: Rot, t: f32) -> Rot { - omt := 1 - t - return NormalizeRot({ - omt * q1.c + t * q2.c, - omt * q1.s + t * q2.s, - }) +GetLengthAndNormalize :: proc "c" (v: Vec2) -> (length: f32, vn: Vec2) { + length = Length(v) + if length < 1e-23 { + return + } + invLength := 1 / length + vn = invLength * v + return } // Integration rotation from angular velocity @@ -268,6 +286,63 @@ IntegrateRotation :: proc "c" (q1: Rot, deltaAngle: f32) -> Rot { return {q2.c * invMag, q2.s * invMag} } +// Get the length squared of this vector +@(require_results) +LengthSquared :: proc "c" (v: Vec2) -> f32 { + return v.x * v.x + v.y * v.y +} + +// Get the distance squared between points +@(require_results) +DistanceSquared :: proc "c" (a, b: Vec2) -> f32 { + c := Vec2{b.x - a.x, b.y - a.y} + return c.x * c.x + c.y * c.y +} + +// Make a rotation using an angle in radians +@(require_results) +MakeRot :: proc "c" (angle: f32) -> Rot { + cs := ComputeCosSin(angle) + return Rot{c=cs.cosine, s=cs.sine} +} + +// Compute the rotation between two unit vectors +@(require_results) +ComputeRotationBetweenUnitVectors :: proc(v1, v2: Vec2) -> Rot { + return NormalizeRot({ + c = Dot(v1, v2), + s = Cross(v1, v2), + }) +} + +// Is this rotation normalized? +@(require_results) +IsNormalizedRot :: proc "c" (q: Rot) -> bool { + // larger tolerance due to failure on mingw 32-bit + qq := q.s * q.s + q.c * q.c + return 1.0 - 0.0006 < qq && qq < 1 + 0.0006 +} + +// Normalize rotation +@(require_results) +NormalizeRot :: proc "c" (q: Rot) -> Rot { + mag := math.sqrt(q.s * q.s + q.c * q.c) + invMag := f32(mag > 0.0 ? 1.0 / mag : 0.0) + return {q.c * invMag, q.s * invMag} +} + +// Normalized linear interpolation +// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ +// https://web.archive.org/web/20170825184056/http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/ +@(require_results) +NLerp :: proc "c" (q1: Rot, q2: Rot, t: f32) -> Rot { + omt := 1 - t + return NormalizeRot({ + omt * q1.c + t * q2.c, + omt * q1.s + t * q2.s, + }) +} + // Compute the angular velocity necessary to rotate between two rotations over a give time // @param q1 initial rotation // @param q2 final rotation @@ -291,8 +366,7 @@ ComputeAngularVelocity :: proc "c" (q1: Rot, q2: Rot, inv_h: f32) -> f32 { // Get the angle in radians in the range [-pi, pi] @(require_results) Rot_GetAngle :: proc "c" (q: Rot) -> f32 { - // todo determinism - return math.atan2(q.s, q.c) + return Atan2(q.s, q.c) } // Get the x-axis @@ -338,18 +412,34 @@ RelativeAngle :: proc "c" (b, a: Rot) -> f32 { // cos(b - a) = bc * ac + bs * as s := b.s * a.c - b.c * a.s c := b.c * a.c + b.s * a.s - return math.atan2(s, c) + return Atan2(s, c) } // Convert an angle in the range [-2*pi, 2*pi] into the range [-pi, pi] @(require_results) -UnwindAngle :: proc "c" (angle: f32) -> f32 { - if angle < -pi { - return angle + 2.0 * pi - } else if angle > pi { - return angle - 2.0 * pi +UnwindAngle :: proc "c" (radians: f32) -> f32 { + if radians < -PI { + return radians + 2.0 * PI + } else if radians > PI { + return radians - 2.0 * PI } - return angle + 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 } // Rotate a vector @@ -380,6 +470,9 @@ InvTransformPoint :: proc "c" (t: Transform, p: Vec2) -> Vec2 { return {t.q.c * vx + t.q.s * vy, -t.q.s * vx + t.q.c * vy} } +// 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. // v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p // = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p @(require_results) @@ -389,6 +482,7 @@ MulTransforms :: proc "c" (A, B: Transform) -> (C: Transform) { return } +// Creates a transform that converts a local point in frame B to a local point in frame A. // v2 = A.q' * (B.q * v1 + B.p - A.p) // = A.q' * B.q * v1 + A.q' * (B.p - A.p) @(require_results) @@ -469,54 +563,65 @@ AABB_Union :: proc "c" (a, b: AABB) -> (c: AABB) { return } +// Compute the bounding box of an array of circles @(require_results) -Float_IsValid :: proc "c" (a: f32) -> bool { - math.is_nan(a) or_return - math.is_inf(a) or_return +MakeAABB :: proc "c" (points: []Vec2, radius: f32) -> AABB { + a := AABB{points[0], points[0]} + for point in points { + a.lowerBound = Min(a.lowerBound, point) + a.upperBound = Max(a.upperBound, point) + } + + r := Vec2{radius, radius} + a.lowerBound = a.lowerBound - r + a.upperBound = a.upperBound + r + + return a +} + +// Signed separation of a point from a plane +@(require_results) +PlaneSeparation :: proc "c" (plane: Plane, point: Vec2) -> f32 { + return Dot(plane.normal, point) - plane.offset +} + +@(require_results) +IsValidFloat :: proc "c" (a: f32) -> bool { + #partial switch math.classify(a) { + case .NaN, .Inf, .Neg_Inf: return false + case: return true + } +} + +@(require_results) +IsValidVec2 :: proc "c" (v: Vec2) -> bool { + IsValidFloat(v.x) or_return + IsValidFloat(v.y) or_return return true } @(require_results) -Vec2_IsValid :: proc "c" (v: Vec2) -> bool { - (math.is_nan(v.x) || math.is_nan(v.y)) or_return - (math.is_inf(v.x) || math.is_inf(v.y)) or_return +IsValidRotation :: proc "c" (q: Rot) -> bool { + IsValidFloat(q.s) or_return + IsValidFloat(q.c) or_return + return IsNormalizedRot(q) +} + +// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound. +@(require_results) +IsValidAABB :: proc "c" (aabb: AABB) -> bool { + IsValidVec2(aabb.lowerBound) or_return + IsValidVec2(aabb.upperBound) or_return + (aabb.upperBound.x >= aabb.lowerBound.x) or_return + (aabb.upperBound.y >= aabb.lowerBound.y) or_return return true } +// Is this a valid plane? Normal is a unit vector. Not Nan or infinity. @(require_results) -Rot_IsValid :: proc "c" (q: Rot) -> bool { - (math.is_nan(q.s) || math.is_nan(q.c)) or_return - (math.is_inf(q.s) || math.is_inf(q.c)) or_return - return IsNormalized(q) -} - -@(require_results) -Normalize :: proc "c" (v: Vec2) -> Vec2 { - length := Length(v) - if length < 1e-23 { - return Vec2_zero - } - invLength := 1 / length - return invLength * v -} - -@(require_results) -NormalizeChecked :: proc "odin" (v: Vec2) -> Vec2 { - length := Length(v) - if length < 1e-23 { - panic("zero-length Vec2") - } - invLength := 1 / length - return invLength * v -} - -@(require_results) -GetLengthAndNormalize :: proc "c" (v: Vec2) -> (length: f32, vn: Vec2) { - length = Length(v) - if length < 1e-23 { - return - } - invLength := 1 / length - vn = invLength * v - return +IsValidPlane :: proc "c" (plane: Plane) -> bool { + IsValidFloat(plane.offset) or_return + IsValidVec2(plane.normal) or_return + IsNormalized(plane.normal) or_return + return true } diff --git a/vendor/box2d/types.odin b/vendor/box2d/types.odin index 0c022fbce..b12410be2 100644 --- a/vendor/box2d/types.odin +++ b/vendor/box2d/types.odin @@ -37,14 +37,28 @@ EnqueueTaskCallback :: #type proc "c" (task: TaskCallback, itemCount: i32, minRa // @ingroup world FinishTaskCallback :: #type proc "c" (userTask: 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 Box2D state or user application state. +// @ingroup world +FrictionCallback :: #type proc "c" (frictionA: f32, userMaterialIdA: i32, frictionB: f32, userMaterialIdB: i32) + +// 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 Box2D state or user application state. +// @ingroup world +RestitutionCallback :: #type proc "c" (restitutionA: f32, userMaterialIdA: i32, restitutuionB: f32, userMaterialIdB: i32) + // Result from b2World_RayCastClosest // @ingroup world RayResult :: struct { - shapeId: ShapeId, - point: Vec2, - normal: Vec2, - fraction: f32, - hit: bool, + shapeId: ShapeId, + point: Vec2, + normal: Vec2, + fraction: f32, + nodeVisits: i32, + leafVisits: i32, + hit: bool, } // World definition used to create a simulation world. @@ -54,37 +68,53 @@ WorldDef :: struct { // Gravity vector. Box2D has no up-vector defined. gravity: Vec2, - // Restitution velocity threshold, usually in m/s. Collisions above this + // Restitution speed threshold, usually in m/s. Collisions above this // speed have restitution applied (will bounce). restitutionThreshold: f32, - // This parameter controls how fast overlap is resolved and has units of meters per second - contactPushoutVelocity: f32, - - // Threshold velocity for hit events. Usually meters per second. + // Threshold speed for hit events. Usually meters per second. hitEventThreshold: f32, - // Contact stiffness. Cycles per second. + // Contact stiffness. Cycles per second. Increasing this increases the speed of overlap recovery, but can introduce jitter. contactHertz: f32, - // Contact bounciness. Non-dimensional. + // 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. + maxContactPushSpeed: f32, + // Joint stiffness. Cycles per second. jointHertz: f32, // Joint bounciness. Non-dimensional. jointDampingRatio: 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 - enableContinous: bool, + enableContinuous: bool, // Number of workers to use with the provided task system. Box2D 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. + // performance cores and accessing a single L2 cache. Efficiency cores and hyper-threading provide + // little benefit and may even harm performance. + // @note Box2D does not create threads. This is the number of threads your applications has created + // that you are allocating to b2World_Step. + // @warning Do not modify the default value unless you are also providing a task system and providing + // task callbacks (enqueueTask and finishTask). workerCount: i32, // Function to spawn tasks @@ -96,6 +126,9 @@ WorldDef :: struct { // User context that is provided to enqueueTask and finishTask userTaskContext: rawptr, + // User data + userData: rawptr, + // Used internally to detect a valid definition. DO NOT SET. internalValue: i32, } @@ -138,20 +171,20 @@ BodyDef :: struct { // The initial world rotation of the body. Use b2MakeRot() if you have an angle. rotation: Rot, - // The initial linear velocity of the body's origin. Typically in meters per second. + // The initial linear velocity of the body's origin. Usually in meters per second. linearVelocity: Vec2, // The initial angular velocity of the body. Radians per second. angularVelocity: f32, - // Linear damping is use to reduce the linear velocity. The damping parameter + // 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 f32ing. linearDamping: f32, - // Angular damping is use to reduce the angular velocity. The damping parameter + // 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 use slow down rotating bodies. @@ -160,9 +193,12 @@ BodyDef :: struct { // Scale the gravity applied to this body. Non-dimensional. gravityScale: f32, - // Sleep velocity threshold, default is 0.05 meter per second + // Sleep speed threshold, default is 0.05 meters per second sleepThreshold: f32, + // Optional body name for debugging. Up to 32 characters (excluding null termination) + name: cstring, + // Use this to store application specific body data. userData: rawptr, @@ -184,10 +220,6 @@ BodyDef :: struct { // Used to disable a body. A disabled body does not move or collide. isEnabled: bool, - // Automatically compute mass and related properties on this body from shapes. - // Triggers whenever a shape is add/removed/changed. Default is true. - automaticMass: bool, - // This allows this body to bypass rotational speed limits. Should only be used // for circular objects, like wheels. allowFastRotation: bool, @@ -204,7 +236,7 @@ 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} - // My_Categories :: enum u32 { + // My_Categories :: enum u64 { // Static = 0x00000001, // Dynamic = 0x00000002, // Debris = 0x00000004, @@ -213,16 +245,16 @@ Filter :: struct { // }; // @endcode // Or use a bit_set. - categoryBits: u32, + 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 = u32(My_Categories.Static | My_Categories.Player); + // maskBits = u64(My_Categories.Static | My_Categories.Player); // @endcode - maskBits: u32, + 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 @@ -240,11 +272,11 @@ Filter :: struct { // @ingroup shape QueryFilter :: struct { // The collision category bits of this query. Normally you would just set one bit. - categoryBits: u32, + categoryBits: u64, // The collision mask bits. This states the shape categories that this // query would accept for collision. - maskBits: u32, + maskBits: u64, } @@ -263,13 +295,37 @@ ShapeType :: enum c.int { // A convex polygon polygonShape, - // A smooth segment owned by a chain shape - smoothSegmentShape, + // A line segment owned by a chain shape + chainSegmentShape, } // The number of shape types shapeTypeCount :: len(ShapeType) +// Surface materials allow chain shapes to have per segment surface properties. +// @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]. + rollingResistance: f32, + + // The tangent speed for conveyor belts + tangentSpeed: f32, + + // User material identifier. This is passed with query results and to friction and restitution + // combining functions. It is not used internally. + userMaterialId: i32, + + // Custom debug draw color. + customColor: u32, +} + // Used to create a shape. // This is a temporary object used to bundle shape creation parameters. You may use // the same shape definition to create multiple shapes. @@ -279,58 +335,61 @@ ShapeDef :: struct { // Use this to store application specific shape data. userData: rawptr, - // The Coulomb (dry) friction coefficient, usually in the range [0,1]. - friction: f32, - - // The restitution (bounce) usually in the range [0,1]. - restitution: f32, + // The surface material for this shape. + material: SurfaceMaterial, // The density, usually in kg/m^2. + // This is not part of the surface material because this is for the interior, which may have + // other considerations, such as being hollow. For example a wood barrel may be hollow or full of water. density: f32, // Collision filtering data. filter: Filter, - // Custom debug draw color. - customColor: u32, - // 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. Only applies to kinematic and dynamic bodies. Ignored for sensors. + // 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. + // 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. + // 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 threading. Ignored for sensors. enablePreSolveEvents: bool, - // Normally shapes on static bodies don't invoke contact creation when they are added to the world. This overrides - // that behavior and causes contact creation. This significantly slows down static body creation which can be important - // when there are many static shapes. - forceContactCreation: 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: i32, } -// Used to create a chain of edges. This is designed to eliminate ghost collisions with some limitations. +// Used to create a chain of line segments. This is designed to eliminate ghost collisions with some limitations. // - chains are one-sided // - chains have no mass and should be used on static bodies -// - chains have a counter-clockwise winding order +// - chains have a counter-clockwise winding order (normal points right of segment direction) // - chains are either a loop or open // - a chain must have at least 4 points -// - the distance between any two points must be greater than b2_linearSlop +// - the distance between any two points must be greater than B2_LINEAR_SLOP // - a chain shape should not self intersect (this is not validated) // - an open chain shape has NO COLLISION on the first and final edge // - you may overlap two open chains on their first three and/or last three points to get smooth collision -// - a chain shape creates multiple smooth edges shapes on the body +// - a chain shape creates multiple line segment shapes on the body // https://en.wikipedia.org/wiki/Polygonal_chain // Must be initialized using b2DefaultChainDef(). // @warning Do not use chain shapes unless you understand the limitations. This is an advanced feature. @@ -345,11 +404,12 @@ ChainDef :: struct { // The point count, must be 4 or more. count: i32, - // The friction coefficient, usually in the range [0,1]. - friction: f32, + // Surface materials for each segment. These are cloned. + materials: [^]SurfaceMaterial `fmt:"v,materialCount"`, - // The restitution (elasticity) usually in the range [0,1]. - restitution: f32, + // The material count. Must be 1 or count. This allows you to provide one + // material for all segments or a unique material per segment. + materialCount: i32, // Contact filtering data. filter: Filter, @@ -357,6 +417,9 @@ ChainDef :: struct { // Indicates a closed chain formed by connecting the first and last points isLoop: bool, + // Enable sensors to detect this chain. False by default. + enableSensorEvents: bool, + // Used internally to detect a valid definition. DO NOT SET. internalValue: i32, } @@ -369,29 +432,28 @@ Profile :: struct { pairs: f32, collide: f32, solve: f32, - buildIslands: f32, + mergeIslands: f32, + prepareStages: f32, solveConstraints: f32, - prepareTasks: f32, - solverTasks: f32, prepareConstraints: f32, integrateVelocities: f32, warmStart: f32, - solveVelocities: f32, + solveImpulses: f32, integratePositions: f32, - relaxVelocities: f32, + relaxImpulses: f32, applyRestitution: f32, storeImpulses: f32, - finalizeBodies: f32, splitIslands: f32, - sleepIslands: f32, + transforms: f32, hitEvents: f32, - broadphase: f32, - continuous: f32, + refit: f32, + bullets: f32, + sleepIslands: f32, + sensors: f32, } // Counters that give details of the simulation size. Counters :: struct { - staticBodyCount: i32, bodyCount: i32, shapeCount: i32, contactCount: i32, @@ -413,6 +475,7 @@ Counters :: struct { // @ingroup joint JointType :: enum c.int { distanceJoint, + filterJoint, motorJoint, mouseJoint, prismaticJoint, @@ -526,7 +589,7 @@ MotorJointDef :: struct { // applying huge forces. This also applies rotation constraint heuristic to improve control. // @ingroup mouse_joint MouseJointDef :: struct { - // The first attached body. + // The first attached body. This is assumed to be static. bodyIdA: BodyId, // The second attached body. @@ -554,6 +617,22 @@ MouseJointDef :: struct { internalValue: i32, } +// A filter joint is used to disable collision between two specific bodies. +// +// @ingroup filter_joint +FilterJointDef :: struct { + /// The first attached body. + bodyIdA: BodyId, + + /// The second attached body. + bodyIdB: BodyId, + + /// User data pointer + userData: rawptr, + + /// Used internally to detect a valid definition. DO NOT SET. + internalValue: i32, +} // Prismatic joint definition // @@ -660,10 +739,10 @@ RevoluteJointDef :: struct { // A flag to enable joint limits enableLimit: bool, - // The lower angle for the joint limit in radians + // The lower angle for the joint limit in radians. Minimum of -0.95*pi radians. lowerAngle: f32, - // The upper angle for the joint limit in radians + // The upper angle for the joint limit in radians. Maximum of 0.95*pi radians. upperAngle: f32, // A flag to enable the joint motor @@ -794,6 +873,27 @@ WheelJointDef :: struct { internalValue: i32, } +// 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: Vec2, + + /// 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 length. This applies an impulse according to the shape perimeter that + /// is facing the explosion. Explosions only apply to circles, capsules, and polygons. This + /// may be negative for implosions. + impulsePerLength: f32, +} /** * @defgroup events Events @@ -822,11 +922,18 @@ SensorBeginTouchEvent :: struct { } // 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 b2Shape_IsValid. SensorEndTouchEvent :: struct { // The id of the sensor shape + // @warning this shape may have been destroyed + // @see b2Shape_IsValid sensorShapeId: ShapeId, // The id of the dynamic shape that stopped touching the sensor shape + // @warning this shape may have been destroyed + // @see b2Shape_IsValid visitorShapeId: ShapeId, } @@ -854,14 +961,25 @@ ContactBeginTouchEvent :: struct { // Id of the second shape shapeIdB: ShapeId, + + // The initial contact manifold. This is recorded before the solver is called, + // so all the impulses will be zero. + manifold: Manifold, } // 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 b2Shape_IsValid shapeIdA: ShapeId, // Id of the second shape + // @warning this shape may have been destroyed + // @see b2Shape_IsValid shapeIdB: ShapeId, } @@ -998,201 +1116,191 @@ OverlapResultFcn :: #type proc "c" (shapeId: ShapeId, ctx: rawptr) -> bool // @ingroup world CastResultFcn :: #type proc "c" (shapeId: ShapeId, point: Vec2, normal: Vec2, fraction: f32, ctx: rawptr) -> f32 -// These colors are used for debug draw. -// See https://www.rapidtables.com/web/color/index.html +// Used to collect collision planes for character movers. +// Return true to continue gathering planes. +PlaneResultFcn :: #type proc "c" (shapeId: ShapeId, plane: ^PlaneResult, ctx: rawptr) + +// 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.int { - AliceBlue = 0xf0f8ff, - AntiqueWhite = 0xfaebd7, - Aqua = 0x00ffff, - Aquamarine = 0x7fffd4, - Azure = 0xf0ffff, - Beige = 0xf5f5dc, - Bisque = 0xffe4c4, + 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, + 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, + 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 = 0xbebebe, - Gray1 = 0x1a1a1a, - Gray2 = 0x333333, - Gray3 = 0x4d4d4d, - Gray4 = 0x666666, - Gray5 = 0x7f7f7f, - Gray6 = 0x999999, - Gray7 = 0xb3b3b3, - Gray8 = 0xcccccc, - Gray9 = 0xe5e5e5, - Green = 0x00ff00, - 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, - LightGoldenrod = 0xeedd82, - LightGoldenrodYellow = 0xfafad2, - LightGray = 0xd3d3d3, - LightGreen = 0x90ee90, - LightPink = 0xffb6c1, - LightSalmon = 0xffa07a, - LightSeaGreen = 0x20b2aa, - LightSkyBlue = 0x87cefa, - LightSlateBlue = 0x8470ff, + 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 = 0xb03060, - MediumAquamarine = 0x66cdaa, - MediumBlue = 0x0000cd, - MediumOrchid = 0xba55d3, - MediumPurple = 0x9370db, - MediumSeaGreen = 0x3cb371, - MediumSlateBlue = 0x7b68ee, - MediumSpringGreen = 0x00fa9a, - MediumTurquoise = 0x48d1cc, - MediumVioletRed = 0xc71585, + 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, + MintCream = 0xF5FFFA, + MistyRose = 0xFFE4E1, + Moccasin = 0xFFE4B5, + NavajoWhite = 0xFFDEAD, Navy = 0x000080, - NavyBlue = 0x000080, - OldLace = 0xfdf5e6, + 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 = 0xa020f0, + 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, + 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, + Snow = 0xFFFAFA, + SpringGreen = 0x00FF7F, + SteelBlue = 0x4682B4, + Tan = 0xD2B48C, Teal = 0x008080, - Thistle = 0xd8bfd8, - Tomato = 0xff6347, - Turquoise = 0x40e0d0, - Violet = 0xee82ee, - VioletRed = 0xd02090, - Wheat = 0xf5deb3, - White = 0xffffff, - WhiteSmoke = 0xf5f5f5, - Yellow = 0xffff00, - YellowGreen = 0x9acd32, - Box2DRed = 0xdc3132, - Box2DBlue = 0x30aebf, - Box2DGreen = 0x8cc924, - Box2DYellow = 0xffee8c, + 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, } // This struct holds callbacks you can implement to draw a Box2D world. // @ingroup world DebugDraw :: struct { // Draw a closed polygon provided in CCW order. - DrawPolygon: proc "c" (vertices: [^]Vec2, vertexCount: c.int, color: HexColor, ctx: rawptr), + DrawPolygonFcn: proc "c" (vertices: [^]Vec2, vertexCount: c.int, color: HexColor, ctx: rawptr), // Draw a solid closed polygon provided in CCW order. - DrawSolidPolygon: proc "c" (transform: Transform, vertices: [^]Vec2, vertexCount: c.int, radius: f32, colr: HexColor, ctx: rawptr ), + DrawSolidPolygonFcn: proc "c" (transform: Transform, vertices: [^]Vec2, vertexCount: c.int, radius: f32, colr: HexColor, ctx: rawptr ), // Draw a circle. - DrawCircle: proc "c" (center: Vec2, radius: f32, color: HexColor, ctx: rawptr), + DrawCircleFcn: proc "c" (center: Vec2, radius: f32, color: HexColor, ctx: rawptr), // Draw a solid circle. - DrawSolidCircle: proc "c" (transform: Transform, radius: f32, color: HexColor, ctx: rawptr), - - // Draw a capsule. - DrawCapsule: proc "c" (p1, p2: Vec2, radius: f32, color: HexColor, ctx: rawptr), + DrawSolidCircleFcn: proc "c" (transform: Transform, radius: f32, color: HexColor, ctx: rawptr), // Draw a solid capsule. - DrawSolidCapsule: proc "c" (p1, p2: Vec2, radius: f32, color: HexColor, ctx: rawptr), + DrawSolidCapsuleFcn: proc "c" (p1, p2: Vec2, radius: f32, color: HexColor, ctx: rawptr), // Draw a line segment. - DrawSegment: proc "c" (p1, p2: Vec2, color: HexColor, ctx: rawptr), + DrawSegmentFcn: proc "c" (p1, p2: Vec2, color: HexColor, ctx: rawptr), // Draw a transform. Choose your own length scale. - DrawTransform: proc "c" (transform: Transform, ctx: rawptr), + DrawTransformFcn: proc "c" (transform: Transform, ctx: rawptr), // Draw a point. - DrawPoint: proc "c" (p: Vec2, size: f32, color: HexColor, ctx: rawptr), + DrawPointFcn: proc "c" (p: Vec2, size: f32, color: HexColor, ctx: rawptr), - // Draw a string. - DrawString: proc "c" (p: Vec2, s: cstring, ctx: rawptr), + // Draw a string in world space. + DrawStringFcn: proc "c" (p: Vec2, s: cstring, color: HexColor, ctx: rawptr), // Bounds to use if restricting drawing to a rectangular region drawingBounds: AABB, @@ -1210,11 +1318,14 @@ DebugDraw :: struct { drawJointExtras: bool, // Option to draw the bounding boxes for shapes - drawAABBs: bool, + 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, @@ -1227,9 +1338,15 @@ DebugDraw :: struct { // Option to draw contact normal impulses drawContactImpulses: bool, + // Option to draw contact feature ids + drawContactFeatures: bool, + // Option to draw contact friction impulses drawFrictionImpulses: bool, + // Option to draw islands as bounding boxes + drawIslands: bool, + // User context that is passed as an argument to drawing callback functions userContext: rawptr, -} \ No newline at end of file +} diff --git a/vendor/box2d/wasm.Makefile b/vendor/box2d/wasm.Makefile index e8ecb485e..be2ed6af9 100644 --- a/vendor/box2d/wasm.Makefile +++ b/vendor/box2d/wasm.Makefile @@ -7,20 +7,20 @@ # CC = $(shell brew --prefix llvm)/bin/clang # LD = $(shell brew --prefix llvm)/bin/wasm-ld -VERSION = 3.0.0 +VERSION = 3.1.0 SRCS = $(wildcard box2d-$(VERSION)/src/*.c) OBJS_SIMD = $(SRCS:.c=_simd.o) OBJS = $(SRCS:.c=.o) SYSROOT = $(shell odin root)/vendor/libc -CFLAGS = -Ibox2d-$(VERSION)/include -Ibox2d-$(VERSION)/extern/simde --target=wasm32 -D__EMSCRIPTEN__ -DNDEBUG -O3 --sysroot=$(SYSROOT) +CFLAGS = -Ibox2d-$(VERSION)/include --target=wasm32 -D__EMSCRIPTEN__ -DNDEBUG -O3 --sysroot=$(SYSROOT) all: lib/box2d_wasm.o lib/box2d_wasm_simd.o clean %.o: %.c - $(CC) -c $(CFLAGS) $< -o $@ + $(CC) -c $(CFLAGS) -DBOX2D_DISABLE_SIMD $< -o $@ %_simd.o: %.c - $(CC) -c $(CFLAGS) -msimd128 $< -o $@ + $(CC) -c $(CFLAGS) -DBOX2D_DISABLE_SIMD -msimd128 $< -o $@ lib/box2d_wasm.o: $(OBJS) $(LD) -r -o lib/box2d_wasm.o $(OBJS) diff --git a/vendor/libc/include/math.h b/vendor/libc/include/math.h index 9903ac3f9..fba520e4a 100644 --- a/vendor/libc/include/math.h +++ b/vendor/libc/include/math.h @@ -47,19 +47,19 @@ bool __isnanf(float); bool __isnand(double); #define isnan(x) \ ( sizeof(x) == sizeof(float) ? __isnanf((float)(x)) \ - : : __isnand((double)(x))) + : __isnand((double)(x))) bool __isinff(float); bool __isinfd(double); #define isinf(x) \ ( sizeof(x) == sizeof(float) ? __isinff((float)(x)) \ - : : __isinfd((double)(x))) + : __isinfd((double)(x))) bool __isfinitef(float); bool __isfinited(double); #define isfinite(x) \ ( sizeof(x) == sizeof(float) ? __isfinitef((float)(x)) \ - : : __isfinited((double)(x))) + : __isfinited((double)(x))) #ifdef __cplusplus } diff --git a/vendor/libc/include/sched.h b/vendor/libc/include/sched.h new file mode 100644 index 000000000..6e4397536 --- /dev/null +++ b/vendor/libc/include/sched.h @@ -0,0 +1,23 @@ +#ifdef __cplusplus +extern "C" { +#endif + +#pragma once + +#include + +#define CLOCK_MONOTONIC 1 + +struct timespec +{ + int64_t tv_sec; + int64_t tv_nsec; +}; + +int clock_gettime(int clockid, struct timespec *tp); + +int sched_yield(); + +#ifdef __cplusplus +} +#endif diff --git a/vendor/libc/sched.odin b/vendor/libc/sched.odin new file mode 100644 index 000000000..85fad3c05 --- /dev/null +++ b/vendor/libc/sched.odin @@ -0,0 +1,35 @@ +package odin_libc + +import "core:time" +import "core:thread" + +Clock :: enum i32 { + Monotonic = 1, +} + +Time_Spec :: struct { + tv_sec: i64, + tv_nsec: i64, +} + +@(require, linkage="strong", link_name="clock_gettime") +clock_gettine :: proc "c" (clockid: Clock, tp: ^Time_Spec) -> i32 { + switch clockid { + case .Monotonic: + tick := time.tick_now() + tp.tv_sec = tick._nsec/1e9 + tp.tv_nsec = tick._nsec%1e9/1000 + return 0 + + case: return -1 + } +} + +@(require, linkage="strong", link_name="sched_yield") +sched_yield :: proc "c" () -> i32 { + when thread.IS_SUPPORTED { + context = g_ctx + thread.yield() + } + return 0 +}