aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgingerBill <gingerBill@users.noreply.github.com>2025-04-25 08:26:43 +0100
committerGitHub <noreply@github.com>2025-04-25 08:26:43 +0100
commitb5c658a2cfe3bcbe3534a6c169ec45f5acc3adc4 (patch)
tree6ccadc96bc40490c4e30590619168ff5fd026eb2
parent9f30380712a50e963d7d318e0fcd700d61d1e2b0 (diff)
parent38f56c0edec4d3afde3a5fe164f79ea78ddc568f (diff)
Merge pull request #5069 from laytan/box2d-3.1.0
box2d: update to 3.1.0
-rw-r--r--vendor/box2d/box2d.odin627
-rwxr-xr-xvendor/box2d/build_box2d.sh6
-rw-r--r--vendor/box2d/collision.odin267
-rw-r--r--vendor/box2d/id.odin41
-rw-r--r--vendor/box2d/lib/box2d_darwin_amd64_avx2.abin355808 -> 383512 bytes
-rw-r--r--vendor/box2d/lib/box2d_darwin_amd64_sse2.abin355808 -> 383512 bytes
-rw-r--r--vendor/box2d/lib/box2d_darwin_arm64.abin290152 -> 319496 bytes
-rwxr-xr-xvendor/box2d/lib/box2d_wasm.obin433031 -> 463785 bytes
-rwxr-xr-xvendor/box2d/lib/box2d_wasm_simd.obin405135 -> 457399 bytes
-rw-r--r--vendor/box2d/lib/box2d_windows_amd64_avx2.libbin731030 -> 819724 bytes
-rw-r--r--vendor/box2d/lib/box2d_windows_amd64_sse2.libbin745400 -> 828566 bytes
-rw-r--r--vendor/box2d/math_functions.odin285
-rw-r--r--vendor/box2d/types.odin581
-rw-r--r--vendor/box2d/wasm.Makefile8
-rw-r--r--vendor/libc/include/math.h6
-rw-r--r--vendor/libc/include/sched.h23
-rw-r--r--vendor/libc/sched.odin35
17 files changed, 1191 insertions, 688 deletions
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 ---
@@ -143,6 +142,10 @@ foreign lib {
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, 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
- MakeOffsetPolygon :: proc(#by_ptr hull: Hull, radius: f32, transform: Transform) -> Polygon ---
+ 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" (points: []Vec2, radius: f32) -> ShapeProxy {
+ foreign lib {
+ b2MakeProxy :: proc "c" (points: [^]Vec2, count: i32, radius: f32) -> ShapeProxy ---
+ }
+ 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)
-MakeProxy :: proc "c" (vertices: []Vec2, radius: f32) -> DistanceProxy {
+MakeOffsetProxy :: proc "c" (points: []Vec2, radius: f32, position: Vec2, rotation: Rot) -> ShapeProxy {
foreign lib {
- b2MakeProxy :: proc "c" (vertices: [^]Vec2, count: i32, radius: f32) -> DistanceProxy ---
+ b2MakeOffsetProxy :: proc "c" (points: [^]Vec2, count: i32, radius: f32, position: Vec2, rotation: Rot) -> ShapeProxy ---
}
- return b2MakeProxy(raw_data(vertices), i32(len(vertices)), radius)
+ 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) ---
+
+ // 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.
+ // 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,15 +1434,30 @@ 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,
}
-// 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.
-ShapeCastInput :: struct {
- // A point cloud to cast
- points: [maxPolygonVertices]Vec2 `fmt:"v,count"`,
+// 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
- count: i32,
+ // The number of points. Must be greater than 0.
+ count: c.int,
- // The radius around the point cloud
- radius: f32,
+ // 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.
+ShapeCastInput :: struct {
+ // 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 unit normal vector in world space, points from shape A to bodyB
+ 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,
+ points: [2]ManifoldPoint,
- // The unit normal vector in world space, points from shape A to bodyB
- normal: Vec2,
// 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,
+ 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,
+ generation: 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,
-}
/// 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
--- a/vendor/box2d/lib/box2d_darwin_amd64_avx2.a
+++ b/vendor/box2d/lib/box2d_darwin_amd64_avx2.a
Binary files 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
--- a/vendor/box2d/lib/box2d_darwin_amd64_sse2.a
+++ b/vendor/box2d/lib/box2d_darwin_amd64_sse2.a
Binary files differ
diff --git a/vendor/box2d/lib/box2d_darwin_arm64.a b/vendor/box2d/lib/box2d_darwin_arm64.a
index 4feb4aac9..c5d04fc73 100644
--- a/vendor/box2d/lib/box2d_darwin_arm64.a
+++ b/vendor/box2d/lib/box2d_darwin_arm64.a
Binary files differ
diff --git a/vendor/box2d/lib/box2d_wasm.o b/vendor/box2d/lib/box2d_wasm.o
index 15d71c5a1..fc3ed1cbd 100755
--- a/vendor/box2d/lib/box2d_wasm.o
+++ b/vendor/box2d/lib/box2d_wasm.o
Binary files differ
diff --git a/vendor/box2d/lib/box2d_wasm_simd.o b/vendor/box2d/lib/box2d_wasm_simd.o
index 568900bd9..8b70dcedd 100755
--- a/vendor/box2d/lib/box2d_wasm_simd.o
+++ b/vendor/box2d/lib/box2d_wasm_simd.o
Binary files 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
--- a/vendor/box2d/lib/box2d_windows_amd64_avx2.lib
+++ b/vendor/box2d/lib/box2d_windows_amd64_avx2.lib
Binary files 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
--- a/vendor/box2d/lib/box2d_windows_amd64_sse2.lib
+++ b/vendor/box2d/lib/box2d_windows_amd64_sse2.lib
Binary files 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,60 +30,79 @@ 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 floats
+// @return the minimum of two integers
@(deprecated="Prefer the built-in 'min(a, b)'", require_results)
-MinFloat :: proc "c" (a, b: f32) -> f32 {
+MinInt :: proc "c" (a, b: c.int) -> c.int {
return min(a, b)
}
-// @return the maximum of two floats
+// @return the maximum of two integers
@(deprecated="Prefer the built-in 'max(a, b)'", require_results)
-MaxFloat :: proc "c" (a, b: f32) -> f32 {
+MaxInt :: proc "c" (a, b: c.int) -> c.int {
return max(a, b)
}
-// @return the absolute value of a float
+// @return the absolute value of an integer
@(deprecated="Prefer the built-in 'abs(a)'", require_results)
-AbsFloat :: proc "c" (a: f32) -> f32 {
+AbsInt :: proc "c" (a: c.int) -> c.int {
return abs(a)
}
-// @return a f32 clamped between a lower and upper bound
+// @return an integer clamped between a lower and upper bound
@(deprecated="Prefer the built-in 'clamp(a, lower, upper)'", require_results)
-ClampFloat :: proc "c" (a, lower, upper: f32) -> f32 {
+ClampInt :: proc "c" (a, lower, upper: c.int) -> c.int {
return clamp(a, lower, upper)
}
-// @return the minimum of two integers
+
+// @return the minimum of two floats
@(deprecated="Prefer the built-in 'min(a, b)'", require_results)
-MinInt :: proc "c" (a, b: c.int) -> c.int {
+MinFloat :: proc "c" (a, b: f32) -> f32 {
return min(a, b)
}
-// @return the maximum of two integers
+// @return the maximum of two floats
@(deprecated="Prefer the built-in 'max(a, b)'", require_results)
-MaxInt :: proc "c" (a, b: c.int) -> c.int {
+MaxFloat :: proc "c" (a, b: f32) -> f32 {
return max(a, b)
}
-// @return the absolute value of an integer
+// @return the absolute value of a float
@(deprecated="Prefer the built-in 'abs(a)'", require_results)
-AbsInt :: proc "c" (a: c.int) -> c.int {
+AbsFloat :: proc "c" (a: f32) -> f32 {
return abs(a)
}
-// @return an integer clamped between a lower and upper bound
+// @return a f32 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 {
+ClampFloat :: proc "c" (a, lower, upper: f32) -> f32 {
return clamp(a, lower, upper)
}
+@(require_results)
+Atan2 :: proc "c" (y, x: f32) -> f32 {
+ return math.atan2(y, x)
+}
+
+@(require_results)
+ComputeCosSin :: proc "c" (radians: f32) -> (res: CosSin) {
+ res.sine, res.cosine = math.sincos(radians)
+ return
+}
+
// Vector dot product
@(require_results)
Dot :: proc "c" (a, b: Vec2) -> f32 {
@@ -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,6 +234,64 @@ Distance :: proc "c" (a, b: Vec2) -> f32 {
return math.sqrt(dx * dx + dy * dy)
}
+@(require_results)
+Normalize :: proc "c" (v: Vec2) -> Vec2 {
+ length := Length(v)
+ if length < EPSILON {
+ return Vec2_zero
+ }
+ invLength := 1 / length
+ return invLength * v
+}
+
+@(require_results)
+IsNormalized :: proc "c" (v: Vec2) -> bool {
+ aa := Dot(v, v)
+ return abs(1. - aa) < 10. * EPSILON
+}
+
+@(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
+}
+
+// Integration rotation from angular velocity
+// @param q1 initial rotation
+// @param deltaAngle the angular displacement in radians
+@(require_results)
+IntegrateRotation :: proc "c" (q1: Rot, deltaAngle: f32) -> Rot {
+ // dc/dt = -omega * sin(t)
+ // ds/dt = omega * cos(t)
+ // c2 = c1 - omega * h * s1
+ // s2 = s1 + omega * h * c1
+ q2 := Rot{q1.c - deltaAngle * q1.s, q1.s + deltaAngle * q1.c}
+ mag := math.sqrt(q2.s * q2.s + q2.c * q2.c)
+ invMag := f32(mag > 0.0 ? 1 / mag : 0.0)
+ 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 {
@@ -222,28 +302,38 @@ DistanceSquared :: proc "c" (a, b: Vec2) -> f32 {
// 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)}
+ cs := ComputeCosSin(angle)
+ return Rot{c=cs.cosine, s=cs.sine}
}
-// Normalize rotation
+// Compute the rotation between two unit vectors
@(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}
+ComputeRotationBetweenUnitVectors :: proc(v1, v2: Vec2) -> Rot {
+ return NormalizeRot({
+ c = Dot(v1, v2),
+ s = Cross(v1, v2),
+ })
}
// Is this rotation normalized?
@(require_results)
-IsNormalized :: proc "c" (q: Rot) -> bool {
+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
@@ -253,21 +343,6 @@ NLerp :: proc "c" (q1: Rot, q2: Rot, t: f32) -> Rot {
})
}
-// Integration rotation from angular velocity
-// @param q1 initial rotation
-// @param deltaAngle the angular displacement in radians
-@(require_results)
-IntegrateRotation :: proc "c" (q1: Rot, deltaAngle: f32) -> Rot {
- // dc/dt = -omega * sin(t)
- // ds/dt = omega * cos(t)
- // c2 = c1 - omega * h * s1
- // s2 = s1 + omega * h * c1
- q2 := Rot{q1.c - deltaAngle * q1.s, q1.s + deltaAngle * q1.c}
- mag := math.sqrt(q2.s * q2.s + q2.c * q2.c)
- invMag := f32(mag > 0.0 ? 1 / mag : 0.0)
- return {q2.c * invMag, q2.s * invMag}
-}
-
// 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
- return true
+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)
-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
- return true
+PlaneSeparation :: proc "c" (plane: Plane, point: Vec2) -> f32 {
+ return Dot(plane.normal, point) - plane.offset
}
@(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)
+IsValidFloat :: proc "c" (a: f32) -> bool {
+ #partial switch math.classify(a) {
+ case .NaN, .Inf, .Neg_Inf: return false
+ case: return true
+ }
}
@(require_results)
-Normalize :: proc "c" (v: Vec2) -> Vec2 {
- length := Length(v)
- if length < 1e-23 {
- return Vec2_zero
- }
- invLength := 1 / length
- return invLength * v
+IsValidVec2 :: proc "c" (v: Vec2) -> bool {
+ IsValidFloat(v.x) or_return
+ IsValidFloat(v.y) or_return
+ return true
}
@(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
+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)
-GetLengthAndNormalize :: proc "c" (v: Vec2) -> (length: f32, vn: Vec2) {
- length = Length(v)
- if length < 1e-23 {
- return
- }
- invLength := 1 / length
- vn = invLength * v
- return
+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)
+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 <stdint.h>
+
+#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
+}