box2d: update to 3.1.0

This commit is contained in:
Laytan Laats
2025-04-22 22:38:15 +02:00
parent c7f9d2b1b7
commit e086151fcd
15 changed files with 1204 additions and 706 deletions
+416 -211
View File
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -eu set -eu
VERSION="3.0.0" VERSION="3.1.0"
RELEASE="https://github.com/erincatto/box2d/archive/refs/tags/v$VERSION.tar.gz" RELEASE="https://github.com/erincatto/box2d/archive/refs/tags/v$VERSION.tar.gz"
cd "$(odin root)"/vendor/box2d cd "$(odin root)"/vendor/box2d
@@ -75,5 +75,5 @@ if [[ $? -ne 0 ]]; then
fi fi
set -e set -e
rm -rf v3.0.0.tar.gz rm -rf "v$VERSION.tar.gz"
rm -rf box2d-3.0.0 rm -rf box2d-"$VERSION"
+143 -126
View File
@@ -5,9 +5,9 @@ import "core:c"
// The maximum number of vertices on a convex polygon. Changing this affects performance even if you // The maximum number of vertices on a convex polygon. Changing this affects performance even if you
// don't use more vertices. // 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 { RayCastInput :: struct {
// Start point of the ray cast // Start point of the ray cast
origin: Vec2, origin: Vec2,
@@ -19,27 +19,37 @@ RayCastInput :: struct {
maxFraction: f32, maxFraction: f32,
} }
// A distance proxy is used by the GJK algorithm. It encapsulates any shape.
// You can provide between 1 and MAX_POLYGON_VERTICES and a radius.
ShapeProxy :: struct {
// The point cloud
points: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`,
// The number of points. Must be greater than 0.
count: c.int,
// The external radius of the point cloud. May be zero.
radius: f32,
}
// Low level shape cast input in generic form. This allows casting an arbitrary point // 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. // 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. // A capsule is two points with a non-zero radius. A box is four points with a zero radius.
ShapeCastInput :: struct { ShapeCastInput :: struct {
// A point cloud to cast // A generic shape
points: [maxPolygonVertices]Vec2 `fmt:"v,count"`, proxy: ShapeProxy,
// The number of points
count: i32,
// The radius around the point cloud
radius: f32,
// The translation of the shape cast // The translation of the shape cast
translation: Vec2, translation: Vec2,
// The maximum fraction of the translation to consider, typically 1 // The maximum fraction of the translation to consider, typically 1
maxFraction: f32, 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 { CastOutput :: struct {
// The surface normal at the hit point // The surface normal at the hit point
normal: Vec2, normal: Vec2,
@@ -51,7 +61,7 @@ CastOutput :: struct {
fraction: f32, fraction: f32,
// The number of iterations used // The number of iterations used
iterations: i32, iterations: c.int,
// Did the cast hit? // Did the cast hit?
hit: bool, hit: bool,
@@ -93,16 +103,16 @@ Capsule :: struct {
// A solid convex polygon. It is assumed that the interior of the polygon is to // A solid convex polygon. It is assumed that the interior of the polygon is to
// the left of each edge. // 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. // 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 // @warning DO NOT fill this out manually, instead use a helper function like
// b2MakePolygon or b2MakeBox. // b2MakePolygon or b2MakeBox.
Polygon :: struct { Polygon :: struct {
// The polygon vertices // 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 // 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 // The centroid of the polygon
centroid: Vec2, centroid: Vec2,
@@ -111,7 +121,7 @@ Polygon :: struct {
radius: f32, radius: f32,
// The number of polygon vertices // The number of polygon vertices
count: i32, count: c.int,
} }
// A line segment with two-sided collision. // A line segment with two-sided collision.
@@ -123,10 +133,10 @@ Segment :: struct {
point2: Vec2, 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. // Several of these are generated for a chain shape.
// ghost1 -> point1 -> point2 -> ghost2 // ghost1 -> point1 -> point2 -> ghost2
SmoothSegment :: struct { ChainSegment :: struct {
// The tail ghost vertex // The tail ghost vertex
ghost1: Vec2, ghost1: Vec2,
@@ -137,7 +147,7 @@ SmoothSegment :: struct {
ghost2: Vec2, ghost2: Vec2,
// The owning chain shape index (internal usage only) // 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() // @warning Do not modify these values directly, instead use b2ComputeHull()
Hull :: struct { Hull :: struct {
// The final points of the hull // The final points of the hull
points: [maxPolygonVertices]Vec2 `fmt:"v,count"`, points: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`,
// The number of points // The number of points
count: i32, count: c.int,
} }
/** /**
@@ -178,21 +188,11 @@ SegmentDistanceResult :: struct {
distanceSquared: f32, distanceSquared: f32,
} }
// A distance proxy is used by the GJK algorithm. It encapsulates any shape. // Used to warm start the GJK simplex. If you call this function multiple times with nearby
DistanceProxy :: struct { // transforms this might improve performance. Otherwise you can zero initialize this.
// The point cloud // The distance cache must be initialized to zero on the first call.
points: [maxPolygonVertices]Vec2 `fmt:"v,count"`, // Users should generally just zero initialize this structure for each call.
SimplexCache :: struct {
// 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 {
// The number of stored simplex points // The number of stored simplex points
count: u16, count: u16,
@@ -203,15 +203,15 @@ DistanceCache :: struct {
indexB: [3]u8 `fmt:"v,count"`, indexB: [3]u8 `fmt:"v,count"`,
} }
emptyDistanceCache :: DistanceCache{} emptySimplexCache :: SimplexCache{}
// Input for b2ShapeDistance // Input for b2ShapeDistance
DistanceInput :: struct { DistanceInput :: struct {
// The proxy for shape A // The proxy for shape A
proxyA: DistanceProxy, proxyA: ShapeProxy,
// The proxy for shape B // The proxy for shape B
proxyB: DistanceProxy, proxyB: ShapeProxy,
// The world transform for shape A // The world transform for shape A
transformA: Transform, transformA: Transform,
@@ -227,6 +227,7 @@ DistanceInput :: struct {
DistanceOutput :: struct { DistanceOutput :: struct {
pointA: Vec2, // Closest point on shapeA pointA: Vec2, // Closest point on shapeA
pointB: Vec2, // Closest point on shapeB pointB: Vec2, // Closest point on shapeB
normal: Vec2, // Normal vector that points from A to B
distance: f32, // The final distance, zero if overlapped distance: f32, // The final distance, zero if overlapped
iterations: i32, // Number of GJK iterations used iterations: i32, // Number of GJK iterations used
simplexCount: i32, // The number of simplexes stored in the simplex array simplexCount: i32, // The number of simplexes stored in the simplex array
@@ -234,28 +235,29 @@ DistanceOutput :: struct {
// Simplex vertex for debugging the GJK algorithm // Simplex vertex for debugging the GJK algorithm
SimplexVertex :: struct { SimplexVertex :: struct {
wA: Vec2, // support point in proxyA wA: Vec2, // support point in proxyA
wB: Vec2, // support point in proxyB wB: Vec2, // support point in proxyB
w: Vec2, // wB - wA w: Vec2, // wB - wA
a: f32, // barycentric coordinate for closest point a: f32, // barycentric coordinate for closest point
indexA: i32, // wA index indexA: c.int, // wA index
indexB: i32, // wB index indexB: c.int, // wB index
} }
// Simplex from the GJK algorithm // Simplex from the GJK algorithm
Simplex :: struct { Simplex :: struct {
v1, v2, v3: SimplexVertex `fmt:"v,count"`, // vertices 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 // Input parameters for b2ShapeCast
ShapeCastPairInput :: struct { ShapeCastPairInput :: struct {
proxyA: DistanceProxy, // The proxy for shape A proxyA: ShapeProxy, // The proxy for shape A
proxyB: DistanceProxy, // The proxy for shape B proxyB: ShapeProxy, // The proxy for shape B
transformA: Transform, // The world transform for shape A transformA: Transform, // The world transform for shape A
transformB: Transform, // The world transform for shape B transformB: Transform, // The world transform for shape B
translationB: Vec2, // The translation of shape B translationB: Vec2, // The translation of shape B
maxFraction: f32, // The fraction of the translation to consider, typically 1 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 // Input parameters for b2TimeOfImpact
TOIInput :: struct { TOIInput :: struct {
proxyA: DistanceProxy, // The proxy for shape A proxyA: ShapeProxy, // The proxy for shape A
proxyB: DistanceProxy, // The proxy for shape B proxyB: ShapeProxy, // The proxy for shape B
sweepA: Sweep, // The movement of shape A sweepA: Sweep, // The movement of shape A
sweepB: Sweep, // The movement of shape B sweepB: Sweep, // The movement of shape B
tMax: f32, // Defines the sweep interval [0, tMax] maxFraction: f32, // Defines the sweep interval [0, maxFraction]
} }
// Describes the TOI output // Describes the TOI output
@@ -290,8 +292,8 @@ TOIState :: enum c.int {
// Output parameters for b2TimeOfImpact. // Output parameters for b2TimeOfImpact.
TOIOutput :: struct { TOIOutput :: struct {
state: TOIState, // The type of result state: TOIState, // The type of result
t: f32, // The time of the collision fraction: f32, // The sweep time of the collision
} }
@@ -301,26 +303,30 @@ TOIOutput :: struct {
* @brief Functions for colliding pairs of shapes * @brief Functions for colliding pairs of shapes
*/ */
// A manifold point is a contact point belonging to a contact // A manifold point is a contact point belonging to a contact manifold.
// manifold. It holds details related to the geometry and dynamics // It holds details related to the geometry and dynamics of the contact points.
// 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 { ManifoldPoint :: struct {
// Location of the contact point in world space. Subject to precision loss at large coordinates. // Location of the contact point in world space. Subject to precision loss at large coordinates.
// @note Should only be used for debugging. // @note Should only be used for debugging.
point: Vec2, point: Vec2,
// Location of the contact point relative to bodyA's origin in world space // Location of the contact point relative to shapeA's origin in world space
// @note When used internally to the Box2D solver, these are relative to the center of mass. // @note When used internally to the Box2D solver, this is relative to the body center of mass.
anchorA: Vec2, 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, anchorB: Vec2,
// The separation of the contact point, negative if penetrating // The separation of the contact point, negative if penetrating
separation: f32, separation: f32,
// The impulse along the manifold normal vector. // The total normal impulse applied across sub-stepping and restitution. This is important
normalImpulse: f32, // to identify speculative contact points that had an interaction in the time step.
totalNormalImpulse: f32,
// The friction impulse // The friction impulse
tangentImpulse: f32, tangentImpulse: f32,
@@ -340,16 +346,21 @@ ManifoldPoint :: struct {
persisted: bool, 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 { Manifold :: struct {
// The manifold points, up to two are possible in 2D
points: [2]ManifoldPoint,
// The unit normal vector in world space, points from shape A to bodyB // The unit normal vector in world space, points from shape A to bodyB
normal: Vec2, normal: Vec2,
// Angular impulse applied for rolling resistance. N * m * s = kg * m^2 / s
rollingImpulse: f32,
// The manifold points, up to two are possible in 2D
points: [2]ManifoldPoint,
// The number of contacts points, will be 0, 1, or 2 // 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 AABB tree broad-phase, inspired by Nathanael Presson's btDbvt.
* A dynamic tree arranges data in a binary tree to accelerate * A dynamic tree arranges data in a binary tree to accelerate
* queries such as AABB queries and ray casts. Leaf nodes are proxies * 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. * 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 * The dynamic tree is made available for advanced users that would like to use it to organize
* spatial game data besides rigid bodies. * 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. // The dynamic tree structure. This should be considered private data.
// It is placed here for performance reasons. // It is placed here for performance reasons.
DynamicTree :: struct { DynamicTree :: struct {
// The tree nodes // The tree nodes
nodes: [^]TreeNode `fmt"v,nodeCount"`, nodes: rawptr,
// The root index // The root index
root: i32, root: i32,
@@ -453,16 +418,25 @@ DynamicTree :: struct {
rebuildCapacity: i32, 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. // This function receives proxies found in the AABB query.
// @return true if the query should continue // @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. // 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 less than input->maxFraction to clip the ray
// - return a value of input->maxFraction to continue the ray cast without clipping // - 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 // 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 of 0 to terminate the ray cast
// - return a value less than input->maxFraction to clip the ray // - return a value less than input->maxFraction to clip the ray
// - return a value of input->maxFraction to continue the ray cast without clipping // - 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,
}
+22 -21
View File
@@ -23,45 +23,46 @@ import "base:intrinsics"
/// World id references a world instance. This should be treated as an opaque handle. /// World id references a world instance. This should be treated as an opaque handle.
WorldId :: struct { WorldId :: struct {
index1: u16, index1: u16,
revision: u16, generation: u16,
} }
/// Body id references a body instance. This should be treated as an opaque handle. /// Body id references a body instance. This should be treated as an opaque handle.
BodyId :: struct { BodyId :: struct {
index1: i32, index1: i32,
world0: u16, world0: u16,
revision: u16, generation: u16,
} }
/// Shape id references a shape instance. This should be treated as an opaque handle. /// Shape id references a shape instance. This should be treated as an opaque handle.
ShapeId :: struct { ShapeId :: struct {
index1: i32, index1: i32,
world0: u16, world0: u16,
revision: 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,
} }
/// Chain id references a chain instances. This should be treated as an opaque handle. /// Chain id references a chain instances. This should be treated as an opaque handle.
ChainId :: struct { ChainId :: struct {
index1: i32, index1: i32,
world0: u16, world0: u16,
revision: u16, generation: u16,
} }
/// Joint id references a joint instance. This should be treated as an opaque handle.
JointId :: struct {
index1: i32,
world0: u16,
generation: u16,
}
/// Use these to make your identifiers null. /// Use these to make your identifiers null.
/// You may also use zero initialization to get null. /// You may also use zero initialization to get null.
nullWorldId :: WorldId{} nullWorldId :: WorldId{}
nullBodyId :: BodyId{} nullBodyId :: BodyId{}
nullShapeId :: ShapeId{} nullShapeId :: ShapeId{}
nullJointId :: JointId{}
nullChainId :: ChainId{} nullChainId :: ChainId{}
nullJointId :: JointId{}
/// Macro to determine if any id is null. /// Macro to determine if any id is null.
IS_NULL :: #force_inline proc "c" (id: $T) -> bool 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), where intrinsics.type_is_struct(T),
intrinsics.type_has_field(T, "index1"), intrinsics.type_has_field(T, "index1"),
intrinsics.type_has_field(T, "world0"), intrinsics.type_has_field(T, "world0"),
intrinsics.type_has_field(T, "revision") { intrinsics.type_has_field(T, "generation") {
return id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.revision == id2.revision return id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.generation == id2.generation
} }
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
+211 -106
View File
@@ -3,9 +3,18 @@ package vendor_box2d
import "core:c" import "core:c"
import "core:math" import "core:math"
pi :: 3.14159265359 EPSILON :: 1e-23
Vec2 :: [2]f32 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 { Rot :: struct {
c, s: f32, // cosine and sine c, s: f32, // cosine and sine
} }
@@ -21,11 +30,43 @@ AABB :: struct {
upperBound: Vec2, upperBound: Vec2,
} }
// separation = dot(normal, point) - offset
Plane :: struct {
normal: Vec2,
offset: f32,
}
PI :: math.PI
Vec2_zero :: Vec2{0, 0} Vec2_zero :: Vec2{0, 0}
Rot_identity :: Rot{1, 0} Rot_identity :: Rot{1, 0}
Transform_identity :: Transform{{0, 0}, {1, 0}} Transform_identity :: Transform{{0, 0}, {1, 0}}
Mat22_zero :: Mat22{0, 0, 0, 0} Mat22_zero :: Mat22{0, 0, 0, 0}
// @return the minimum of two integers
@(deprecated="Prefer the built-in 'min(a, b)'", require_results)
MinInt :: proc "c" (a, b: c.int) -> c.int {
return min(a, b)
}
// @return the maximum of two integers
@(deprecated="Prefer the built-in 'max(a, b)'", require_results)
MaxInt :: proc "c" (a, b: c.int) -> c.int {
return max(a, b)
}
// @return the absolute value of an integer
@(deprecated="Prefer the built-in 'abs(a)'", require_results)
AbsInt :: proc "c" (a: c.int) -> c.int {
return abs(a)
}
// @return an integer clamped between a lower and upper bound
@(deprecated="Prefer the built-in 'clamp(a, lower, upper)'", require_results)
ClampInt :: proc "c" (a, lower, upper: c.int) -> c.int {
return clamp(a, lower, upper)
}
// @return the minimum of two floats // @return the minimum of two floats
@(deprecated="Prefer the built-in 'min(a, b)'", require_results) @(deprecated="Prefer the built-in 'min(a, b)'", require_results)
@@ -51,28 +92,15 @@ ClampFloat :: proc "c" (a, lower, upper: f32) -> f32 {
return clamp(a, lower, upper) return clamp(a, lower, upper)
} }
// @return the minimum of two integers @(require_results)
@(deprecated="Prefer the built-in 'min(a, b)'", require_results) Atan2 :: proc "c" (y, x: f32) -> f32 {
MinInt :: proc "c" (a, b: c.int) -> c.int { return math.atan2(y, x)
return min(a, b)
} }
// @return the maximum of two integers @(require_results)
@(deprecated="Prefer the built-in 'max(a, b)'", require_results) ComputeCosSin :: proc "c" (radians: f32) -> (res: CosSin) {
MaxInt :: proc "c" (a, b: c.int) -> c.int { res.sine, res.cosine = math.sincos(radians)
return max(a, b) return
}
// @return the absolute value of an integer
@(deprecated="Prefer the built-in 'abs(a)'", require_results)
AbsInt :: proc "c" (a: c.int) -> c.int {
return abs(a)
}
// @return an integer clamped between a lower and upper bound
@(deprecated="Prefer the built-in 'clamp(a, lower, upper)'", require_results)
ClampInt :: proc "c" (a, lower, upper: c.int) -> c.int {
return clamp(a, lower, upper)
} }
// Vector dot product // Vector dot product
@@ -198,12 +226,6 @@ Length :: proc "c" (v: Vec2) -> f32 {
return math.sqrt(v.x * v.x + v.y * v.y) 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 // Get the distance between two points
@(require_results) @(require_results)
Distance :: proc "c" (a, b: Vec2) -> f32 { Distance :: proc "c" (a, b: Vec2) -> f32 {
@@ -212,45 +234,41 @@ Distance :: proc "c" (a, b: Vec2) -> f32 {
return math.sqrt(dx * dx + dy * dy) return math.sqrt(dx * dx + dy * dy)
} }
// Get the distance squared between points
@(require_results) @(require_results)
DistanceSquared :: proc "c" (a, b: Vec2) -> f32 { Normalize :: proc "c" (v: Vec2) -> Vec2 {
c := Vec2{b.x - a.x, b.y - a.y} length := Length(v)
return c.x * c.x + c.y * c.y if length < EPSILON {
return Vec2_zero
}
invLength := 1 / length
return invLength * v
} }
// Make a rotation using an angle in radians
@(require_results) @(require_results)
MakeRot :: proc "c" (angle: f32) -> Rot { IsNormalized :: proc "c" (v: Vec2) -> bool {
// todo determinism aa := Dot(v, v)
return {math.cos(angle), math.sin(angle)} return abs(1. - aa) < 10. * EPSILON
} }
// Normalize rotation
@(require_results) @(require_results)
NormalizeRot :: proc "c" (q: Rot) -> Rot { NormalizeChecked :: proc "odin" (v: Vec2) -> Vec2 {
mag := math.sqrt(q.s * q.s + q.c * q.c) length := Length(v)
invMag := f32(mag > 0.0 ? 1.0 / mag : 0.0) if length < 1e-23 {
return {q.c * invMag, q.s * invMag} panic("zero-length Vec2")
}
invLength := 1 / length
return invLength * v
} }
// Is this rotation normalized?
@(require_results) @(require_results)
IsNormalized :: proc "c" (q: Rot) -> bool { GetLengthAndNormalize :: proc "c" (v: Vec2) -> (length: f32, vn: Vec2) {
// larger tolerance due to failure on mingw 32-bit length = Length(v)
qq := q.s * q.s + q.c * q.c if length < 1e-23 {
return 1.0 - 0.0006 < qq && qq < 1 + 0.0006 return
} }
invLength := 1 / length
// Normalized linear interpolation vn = invLength * v
// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/ return
@(require_results)
NLerp :: proc "c" (q1: Rot, q2: Rot, t: f32) -> Rot {
omt := 1 - t
return NormalizeRot({
omt * q1.c + t * q2.c,
omt * q1.s + t * q2.s,
})
} }
// Integration rotation from angular velocity // Integration rotation from angular velocity
@@ -268,6 +286,63 @@ IntegrateRotation :: proc "c" (q1: Rot, deltaAngle: f32) -> Rot {
return {q2.c * invMag, q2.s * invMag} return {q2.c * invMag, q2.s * invMag}
} }
// Get the length squared of this vector
@(require_results)
LengthSquared :: proc "c" (v: Vec2) -> f32 {
return v.x * v.x + v.y * v.y
}
// Get the distance squared between points
@(require_results)
DistanceSquared :: proc "c" (a, b: Vec2) -> f32 {
c := Vec2{b.x - a.x, b.y - a.y}
return c.x * c.x + c.y * c.y
}
// Make a rotation using an angle in radians
@(require_results)
MakeRot :: proc "c" (angle: f32) -> Rot {
cs := ComputeCosSin(angle)
return Rot{c=cs.cosine, s=cs.sine}
}
// Compute the rotation between two unit vectors
@(require_results)
ComputeRotationBetweenUnitVectors :: proc(v1, v2: Vec2) -> Rot {
return NormalizeRot({
c = Dot(v1, v2),
s = Cross(v1, v2),
})
}
// Is this rotation normalized?
@(require_results)
IsNormalizedRot :: proc "c" (q: Rot) -> bool {
// larger tolerance due to failure on mingw 32-bit
qq := q.s * q.s + q.c * q.c
return 1.0 - 0.0006 < qq && qq < 1 + 0.0006
}
// Normalize rotation
@(require_results)
NormalizeRot :: proc "c" (q: Rot) -> Rot {
mag := math.sqrt(q.s * q.s + q.c * q.c)
invMag := f32(mag > 0.0 ? 1.0 / mag : 0.0)
return {q.c * invMag, q.s * invMag}
}
// Normalized linear interpolation
// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
// https://web.archive.org/web/20170825184056/http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
@(require_results)
NLerp :: proc "c" (q1: Rot, q2: Rot, t: f32) -> Rot {
omt := 1 - t
return NormalizeRot({
omt * q1.c + t * q2.c,
omt * q1.s + t * q2.s,
})
}
// Compute the angular velocity necessary to rotate between two rotations over a give time // Compute the angular velocity necessary to rotate between two rotations over a give time
// @param q1 initial rotation // @param q1 initial rotation
// @param q2 final 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] // Get the angle in radians in the range [-pi, pi]
@(require_results) @(require_results)
Rot_GetAngle :: proc "c" (q: Rot) -> f32 { Rot_GetAngle :: proc "c" (q: Rot) -> f32 {
// todo determinism return Atan2(q.s, q.c)
return math.atan2(q.s, q.c)
} }
// Get the x-axis // Get the x-axis
@@ -338,18 +412,34 @@ RelativeAngle :: proc "c" (b, a: Rot) -> f32 {
// cos(b - a) = bc * ac + bs * as // cos(b - a) = bc * ac + bs * as
s := b.s * a.c - b.c * a.s s := b.s * a.c - b.c * a.s
c := b.c * a.c + b.s * 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] // Convert an angle in the range [-2*pi, 2*pi] into the range [-pi, pi]
@(require_results) @(require_results)
UnwindAngle :: proc "c" (angle: f32) -> f32 { UnwindAngle :: proc "c" (radians: f32) -> f32 {
if angle < -pi { if radians < -PI {
return angle + 2.0 * pi return radians + 2.0 * PI
} else if angle > pi { } else if radians > PI {
return angle - 2.0 * 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 // 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} 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 // 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 // = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p
@(require_results) @(require_results)
@@ -389,6 +482,7 @@ MulTransforms :: proc "c" (A, B: Transform) -> (C: Transform) {
return 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) // v2 = A.q' * (B.q * v1 + B.p - A.p)
// = A.q' * B.q * v1 + A.q' * (B.p - A.p) // = A.q' * B.q * v1 + A.q' * (B.p - A.p)
@(require_results) @(require_results)
@@ -469,54 +563,65 @@ AABB_Union :: proc "c" (a, b: AABB) -> (c: AABB) {
return return
} }
// Compute the bounding box of an array of circles
@(require_results) @(require_results)
Float_IsValid :: proc "c" (a: f32) -> bool { MakeAABB :: proc "c" (points: []Vec2, radius: f32) -> AABB {
math.is_nan(a) or_return a := AABB{points[0], points[0]}
math.is_inf(a) or_return for point in points {
a.lowerBound = Min(a.lowerBound, point)
a.upperBound = Max(a.upperBound, point)
}
r := Vec2{radius, radius}
a.lowerBound = a.lowerBound - r
a.upperBound = a.upperBound + r
return a
}
// Signed separation of a point from a plane
@(require_results)
PlaneSeparation :: proc "c" (plane: Plane, point: Vec2) -> f32 {
return Dot(plane.normal, point) - plane.offset
}
@(require_results)
IsValidFloat :: proc "c" (a: f32) -> bool {
#partial switch math.classify(a) {
case .NaN, .Inf, .Neg_Inf: return false
case: return true
}
}
@(require_results)
IsValidVec2 :: proc "c" (v: Vec2) -> bool {
IsValidFloat(v.x) or_return
IsValidFloat(v.y) or_return
return true return true
} }
@(require_results) @(require_results)
Vec2_IsValid :: proc "c" (v: Vec2) -> bool { IsValidRotation :: proc "c" (q: Rot) -> bool {
(math.is_nan(v.x) || math.is_nan(v.y)) or_return IsValidFloat(q.s) or_return
(math.is_inf(v.x) || math.is_inf(v.y)) or_return IsValidFloat(q.c) or_return
return IsNormalizedRot(q)
}
// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound.
@(require_results)
IsValidAABB :: proc "c" (aabb: AABB) -> bool {
IsValidVec2(aabb.lowerBound) or_return
IsValidVec2(aabb.upperBound) or_return
(aabb.upperBound.x >= aabb.lowerBound.x) or_return
(aabb.upperBound.y >= aabb.lowerBound.y) or_return
return true return true
} }
// Is this a valid plane? Normal is a unit vector. Not Nan or infinity.
@(require_results) @(require_results)
Rot_IsValid :: proc "c" (q: Rot) -> bool { IsValidPlane :: proc "c" (plane: Plane) -> bool {
(math.is_nan(q.s) || math.is_nan(q.c)) or_return IsValidFloat(plane.offset) or_return
(math.is_inf(q.s) || math.is_inf(q.c)) or_return IsValidVec2(plane.normal) or_return
return IsNormalized(q) IsNormalized(plane.normal) or_return
} 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
}
@(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
} }
+348 -231
View File
@@ -37,14 +37,28 @@ EnqueueTaskCallback :: #type proc "c" (task: TaskCallback, itemCount: i32, minRa
// @ingroup world // @ingroup world
FinishTaskCallback :: #type proc "c" (userTask: rawptr, userContext: rawptr) 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 // Result from b2World_RayCastClosest
// @ingroup world // @ingroup world
RayResult :: struct { RayResult :: struct {
shapeId: ShapeId, shapeId: ShapeId,
point: Vec2, point: Vec2,
normal: Vec2, normal: Vec2,
fraction: f32, fraction: f32,
hit: bool, nodeVisits: i32,
leafVisits: i32,
hit: bool,
} }
// World definition used to create a simulation world. // World definition used to create a simulation world.
@@ -54,37 +68,53 @@ WorldDef :: struct {
// Gravity vector. Box2D has no up-vector defined. // Gravity vector. Box2D has no up-vector defined.
gravity: Vec2, 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). // speed have restitution applied (will bounce).
restitutionThreshold: f32, restitutionThreshold: f32,
// This parameter controls how fast overlap is resolved and has units of meters per second // Threshold speed for hit events. Usually meters per second.
contactPushoutVelocity: f32,
// Threshold velocity for hit events. Usually meters per second.
hitEventThreshold: f32, 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, 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, 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. // Joint stiffness. Cycles per second.
jointHertz: f32, jointHertz: f32,
// Joint bounciness. Non-dimensional. // Joint bounciness. Non-dimensional.
jointDampingRatio: f32, 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 // Can bodies go to sleep to improve performance
enableSleep: bool, enableSleep: bool,
// Enable continuous collision // Enable continuous collision
enableContinous: bool, enableContinuous: bool,
// Number of workers to use with the provided task system. Box2D performs best when using only // 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 // performance cores and accessing a single L2 cache. Efficiency cores and hyper-threading provide
// little benefit and may even harm performance. // 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, workerCount: i32,
// Function to spawn tasks // Function to spawn tasks
@@ -96,6 +126,9 @@ WorldDef :: struct {
// User context that is provided to enqueueTask and finishTask // User context that is provided to enqueueTask and finishTask
userTaskContext: rawptr, userTaskContext: rawptr,
// User data
userData: rawptr,
// Used internally to detect a valid definition. DO NOT SET. // Used internally to detect a valid definition. DO NOT SET.
internalValue: i32, internalValue: i32,
} }
@@ -138,20 +171,20 @@ BodyDef :: struct {
// The initial world rotation of the body. Use b2MakeRot() if you have an angle. // The initial world rotation of the body. Use b2MakeRot() if you have an angle.
rotation: Rot, 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, linearVelocity: Vec2,
// The initial angular velocity of the body. Radians per second. // The initial angular velocity of the body. Radians per second.
angularVelocity: f32, 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 // can be larger than 1 but the damping effect becomes sensitive to the
// time step when the damping parameter is large. // time step when the damping parameter is large.
// Generally linear damping is undesirable because it makes objects move slowly // Generally linear damping is undesirable because it makes objects move slowly
// as if they are f32ing. // as if they are f32ing.
linearDamping: f32, 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 // can be larger than 1.0f but the damping effect becomes sensitive to the
// time step when the damping parameter is large. // time step when the damping parameter is large.
// Angular damping can be use slow down rotating bodies. // Angular damping can be use slow down rotating bodies.
@@ -160,9 +193,12 @@ BodyDef :: struct {
// Scale the gravity applied to this body. Non-dimensional. // Scale the gravity applied to this body. Non-dimensional.
gravityScale: f32, gravityScale: f32,
// Sleep velocity threshold, default is 0.05 meter per second // Sleep speed threshold, default is 0.05 meters per second
sleepThreshold: f32, sleepThreshold: f32,
// Optional body name for debugging. Up to 32 characters (excluding null termination)
name: cstring,
// Use this to store application specific body data. // Use this to store application specific body data.
userData: rawptr, userData: rawptr,
@@ -184,10 +220,6 @@ BodyDef :: struct {
// Used to disable a body. A disabled body does not move or collide. // Used to disable a body. A disabled body does not move or collide.
isEnabled: bool, 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 // This allows this body to bypass rotational speed limits. Should only be used
// for circular objects, like wheels. // for circular objects, like wheels.
allowFastRotation: bool, allowFastRotation: bool,
@@ -204,7 +236,7 @@ Filter :: struct {
// The collision category bits. Normally you would just set one bit. The category bits should // The collision category bits. Normally you would just set one bit. The category bits should
// represent your application object types. For example: // represent your application object types. For example:
// @code{.odin} // @code{.odin}
// My_Categories :: enum u32 { // My_Categories :: enum u64 {
// Static = 0x00000001, // Static = 0x00000001,
// Dynamic = 0x00000002, // Dynamic = 0x00000002,
// Debris = 0x00000004, // Debris = 0x00000004,
@@ -213,16 +245,16 @@ Filter :: struct {
// }; // };
// @endcode // @endcode
// Or use a bit_set. // Or use a bit_set.
categoryBits: u32, categoryBits: u64,
// The collision mask bits. This states the categories that this // The collision mask bits. This states the categories that this
// shape would accept for collision. // shape would accept for collision.
// For example, you may want your player to only collide with static objects // For example, you may want your player to only collide with static objects
// and other players. // and other players.
// @code{.odin} // @code{.odin}
// maskBits = u32(My_Categories.Static | My_Categories.Player); // maskBits = u64(My_Categories.Static | My_Categories.Player);
// @endcode // @endcode
maskBits: u32, maskBits: u64,
// Collision groups allow a certain group of objects to never collide (negative) // 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 // or always collide (positive). A group index of zero has no effect. Non-zero group filtering
@@ -240,11 +272,11 @@ Filter :: struct {
// @ingroup shape // @ingroup shape
QueryFilter :: struct { QueryFilter :: struct {
// The collision category bits of this query. Normally you would just set one bit. // 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 // The collision mask bits. This states the shape categories that this
// query would accept for collision. // query would accept for collision.
maskBits: u32, maskBits: u64,
} }
@@ -263,13 +295,37 @@ ShapeType :: enum c.int {
// A convex polygon // A convex polygon
polygonShape, polygonShape,
// A smooth segment owned by a chain shape // A line segment owned by a chain shape
smoothSegmentShape, chainSegmentShape,
} }
// The number of shape types // The number of shape types
shapeTypeCount :: len(ShapeType) 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. // Used to create a shape.
// This is a temporary object used to bundle shape creation parameters. You may use // This is a temporary object used to bundle shape creation parameters. You may use
// the same shape definition to create multiple shapes. // the same shape definition to create multiple shapes.
@@ -279,58 +335,61 @@ ShapeDef :: struct {
// Use this to store application specific shape data. // Use this to store application specific shape data.
userData: rawptr, userData: rawptr,
// The Coulomb (dry) friction coefficient, usually in the range [0,1]. // The surface material for this shape.
friction: f32, material: SurfaceMaterial,
// The restitution (bounce) usually in the range [0,1].
restitution: f32,
// The density, usually in kg/m^2. // 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, density: f32,
// Collision filtering data. // Collision filtering data.
filter: Filter, filter: Filter,
// Custom debug draw color.
customColor: u32,
// A sensor shape generates overlap events but never generates a collision response. // 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, 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, 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, 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, enableHitEvents: bool,
// Enable pre-solve contact events for this shape. Only applies to dynamic bodies. These are expensive // 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. // and must be carefully handled due to threading. Ignored for sensors.
enablePreSolveEvents: bool, enablePreSolveEvents: bool,
// Normally shapes on static bodies don't invoke contact creation when they are added to the world. This overrides // When shapes are created they will scan the environment for collision the next time step. This can significantly slow down
// that behavior and causes contact creation. This significantly slows down static body creation which can be important // static body creation when there are many static shapes.
// when there are many static shapes. // This is flag is ignored for dynamic and kinematic shapes which always invoke contact creation.
forceContactCreation: bool, 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. // Used internally to detect a valid definition. DO NOT SET.
internalValue: i32, 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 are one-sided
// - chains have no mass and should be used on static bodies // - 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 // - chains are either a loop or open
// - a chain must have at least 4 points // - 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) // - a chain shape should not self intersect (this is not validated)
// - an open chain shape has NO COLLISION on the first and final edge // - 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 // - 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 // https://en.wikipedia.org/wiki/Polygonal_chain
// Must be initialized using b2DefaultChainDef(). // Must be initialized using b2DefaultChainDef().
// @warning Do not use chain shapes unless you understand the limitations. This is an advanced feature. // @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. // The point count, must be 4 or more.
count: i32, count: i32,
// The friction coefficient, usually in the range [0,1]. // Surface materials for each segment. These are cloned.
friction: f32, materials: [^]SurfaceMaterial `fmt:"v,materialCount"`,
// The restitution (elasticity) usually in the range [0,1]. // The material count. Must be 1 or count. This allows you to provide one
restitution: f32, // material for all segments or a unique material per segment.
materialCount: i32,
// Contact filtering data. // Contact filtering data.
filter: Filter, filter: Filter,
@@ -357,6 +417,9 @@ ChainDef :: struct {
// Indicates a closed chain formed by connecting the first and last points // Indicates a closed chain formed by connecting the first and last points
isLoop: bool, isLoop: bool,
// Enable sensors to detect this chain. False by default.
enableSensorEvents: bool,
// Used internally to detect a valid definition. DO NOT SET. // Used internally to detect a valid definition. DO NOT SET.
internalValue: i32, internalValue: i32,
} }
@@ -369,29 +432,28 @@ Profile :: struct {
pairs: f32, pairs: f32,
collide: f32, collide: f32,
solve: f32, solve: f32,
buildIslands: f32, mergeIslands: f32,
prepareStages: f32,
solveConstraints: f32, solveConstraints: f32,
prepareTasks: f32,
solverTasks: f32,
prepareConstraints: f32, prepareConstraints: f32,
integrateVelocities: f32, integrateVelocities: f32,
warmStart: f32, warmStart: f32,
solveVelocities: f32, solveImpulses: f32,
integratePositions: f32, integratePositions: f32,
relaxVelocities: f32, relaxImpulses: f32,
applyRestitution: f32, applyRestitution: f32,
storeImpulses: f32, storeImpulses: f32,
finalizeBodies: f32,
splitIslands: f32, splitIslands: f32,
sleepIslands: f32, transforms: f32,
hitEvents: f32, hitEvents: f32,
broadphase: f32, refit: f32,
continuous: f32, bullets: f32,
sleepIslands: f32,
sensors: f32,
} }
// Counters that give details of the simulation size. // Counters that give details of the simulation size.
Counters :: struct { Counters :: struct {
staticBodyCount: i32,
bodyCount: i32, bodyCount: i32,
shapeCount: i32, shapeCount: i32,
contactCount: i32, contactCount: i32,
@@ -413,6 +475,7 @@ Counters :: struct {
// @ingroup joint // @ingroup joint
JointType :: enum c.int { JointType :: enum c.int {
distanceJoint, distanceJoint,
filterJoint,
motorJoint, motorJoint,
mouseJoint, mouseJoint,
prismaticJoint, prismaticJoint,
@@ -526,7 +589,7 @@ MotorJointDef :: struct {
// applying huge forces. This also applies rotation constraint heuristic to improve control. // applying huge forces. This also applies rotation constraint heuristic to improve control.
// @ingroup mouse_joint // @ingroup mouse_joint
MouseJointDef :: struct { MouseJointDef :: struct {
// The first attached body. // The first attached body. This is assumed to be static.
bodyIdA: BodyId, bodyIdA: BodyId,
// The second attached body. // The second attached body.
@@ -554,6 +617,22 @@ MouseJointDef :: struct {
internalValue: i32, 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 // Prismatic joint definition
// //
@@ -660,10 +739,10 @@ RevoluteJointDef :: struct {
// A flag to enable joint limits // A flag to enable joint limits
enableLimit: bool, 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, 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, upperAngle: f32,
// A flag to enable the joint motor // A flag to enable the joint motor
@@ -794,6 +873,27 @@ WheelJointDef :: struct {
internalValue: i32, 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 * @defgroup events Events
@@ -822,11 +922,18 @@ SensorBeginTouchEvent :: struct {
} }
// An end touch event is generated when a shape stops overlapping a sensor shape. // 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 { SensorEndTouchEvent :: struct {
// The id of the sensor shape // The id of the sensor shape
// @warning this shape may have been destroyed
// @see b2Shape_IsValid
sensorShapeId: ShapeId, sensorShapeId: ShapeId,
// The id of the dynamic shape that stopped touching the sensor shape // The id of the dynamic shape that stopped touching the sensor shape
// @warning this shape may have been destroyed
// @see b2Shape_IsValid
visitorShapeId: ShapeId, visitorShapeId: ShapeId,
} }
@@ -854,14 +961,25 @@ ContactBeginTouchEvent :: struct {
// Id of the second shape // Id of the second shape
shapeIdB: ShapeId, 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. // 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 { ContactEndTouchEvent :: struct {
// Id of the first shape // Id of the first shape
// @warning this shape may have been destroyed
// @see b2Shape_IsValid
shapeIdA: ShapeId, shapeIdA: ShapeId,
// Id of the second shape // Id of the second shape
// @warning this shape may have been destroyed
// @see b2Shape_IsValid
shapeIdB: ShapeId, shapeIdB: ShapeId,
} }
@@ -998,201 +1116,191 @@ OverlapResultFcn :: #type proc "c" (shapeId: ShapeId, ctx: rawptr) -> bool
// @ingroup world // @ingroup world
CastResultFcn :: #type proc "c" (shapeId: ShapeId, point: Vec2, normal: Vec2, fraction: f32, ctx: rawptr) -> f32 CastResultFcn :: #type proc "c" (shapeId: ShapeId, point: Vec2, normal: Vec2, fraction: f32, ctx: rawptr) -> f32
// These colors are used for debug draw. // Used to collect collision planes for character movers.
// See https://www.rapidtables.com/web/color/index.html // 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 { HexColor :: enum c.int {
AliceBlue = 0xf0f8ff, AliceBlue = 0xF0F8FF,
AntiqueWhite = 0xfaebd7, AntiqueWhite = 0xFAEBD7,
Aqua = 0x00ffff, Aqua = 0x00FFFF,
Aquamarine = 0x7fffd4, Aquamarine = 0x7FFFD4,
Azure = 0xf0ffff, Azure = 0xF0FFFF,
Beige = 0xf5f5dc, Beige = 0xF5F5DC,
Bisque = 0xffe4c4, Bisque = 0xFFE4C4,
Black = 0x000000, Black = 0x000000,
BlanchedAlmond = 0xffebcd, BlanchedAlmond = 0xFFEBCD,
Blue = 0x0000ff, Blue = 0x0000FF,
BlueViolet = 0x8a2be2, BlueViolet = 0x8A2BE2,
Brown = 0xa52a2a, Brown = 0xA52A2A,
Burlywood = 0xdeb887, Burlywood = 0xDEB887,
CadetBlue = 0x5f9ea0, CadetBlue = 0x5F9EA0,
Chartreuse = 0x7fff00, Chartreuse = 0x7FFF00,
Chocolate = 0xd2691e, Chocolate = 0xD2691E,
Coral = 0xff7f50, Coral = 0xFF7F50,
CornflowerBlue = 0x6495ed, CornflowerBlue = 0x6495ED,
Cornsilk = 0xfff8dc, Cornsilk = 0xFFF8DC,
Crimson = 0xdc143c, Crimson = 0xDC143C,
Cyan = 0x00ffff, Cyan = 0x00FFFF,
DarkBlue = 0x00008b, DarkBlue = 0x00008B,
DarkCyan = 0x008b8b, DarkCyan = 0x008B8B,
DarkGoldenrod = 0xb8860b, DarkGoldenRod = 0xB8860B,
DarkGray = 0xa9a9a9, DarkGray = 0xA9A9A9,
DarkGreen = 0x006400, DarkGreen = 0x006400,
DarkKhaki = 0xbdb76b, DarkKhaki = 0xBDB76B,
DarkMagenta = 0x8b008b, DarkMagenta = 0x8B008B,
DarkOliveGreen = 0x556b2f, DarkOliveGreen = 0x556B2F,
DarkOrange = 0xff8c00, DarkOrange = 0xFF8C00,
DarkOrchid = 0x9932cc, DarkOrchid = 0x9932CC,
DarkRed = 0x8b0000, DarkRed = 0x8B0000,
DarkSalmon = 0xe9967a, DarkSalmon = 0xE9967A,
DarkSeaGreen = 0x8fbc8f, DarkSeaGreen = 0x8FBC8F,
DarkSlateBlue = 0x483d8b, DarkSlateBlue = 0x483D8B,
DarkSlateGray = 0x2f4f4f, DarkSlateGray = 0x2F4F4F,
DarkTurquoise = 0x00ced1, DarkTurquoise = 0x00CED1,
DarkViolet = 0x9400d3, DarkViolet = 0x9400D3,
DeepPink = 0xff1493, DeepPink = 0xFF1493,
DeepSkyBlue = 0x00bfff, DeepSkyBlue = 0x00BFFF,
DimGray = 0x696969, DimGray = 0x696969,
DodgerBlue = 0x1e90ff, DodgerBlue = 0x1E90FF,
Firebrick = 0xb22222, FireBrick = 0xB22222,
FloralWhite = 0xfffaf0, FloralWhite = 0xFFFAF0,
ForestGreen = 0x228b22, ForestGreen = 0x228B22,
Fuchsia = 0xff00ff, Fuchsia = 0xFF00FF,
Gainsboro = 0xdcdcdc, Gainsboro = 0xDCDCDC,
GhostWhite = 0xf8f8ff, GhostWhite = 0xF8F8FF,
Gold = 0xffd700, Gold = 0xFFD700,
Goldenrod = 0xdaa520, GoldenRod = 0xDAA520,
Gray = 0xbebebe, Gray = 0x808080,
Gray1 = 0x1a1a1a, Green = 0x008000,
Gray2 = 0x333333, GreenYellow = 0xADFF2F,
Gray3 = 0x4d4d4d, HoneyDew = 0xF0FFF0,
Gray4 = 0x666666, HotPink = 0xFF69B4,
Gray5 = 0x7f7f7f, IndianRed = 0xCD5C5C,
Gray6 = 0x999999, Indigo = 0x4B0082,
Gray7 = 0xb3b3b3, Ivory = 0xFFFFF0,
Gray8 = 0xcccccc, Khaki = 0xF0E68C,
Gray9 = 0xe5e5e5, Lavender = 0xE6E6FA,
Green = 0x00ff00, LavenderBlush = 0xFFF0F5,
GreenYellow = 0xadff2f, LawnGreen = 0x7CFC00,
Honeydew = 0xf0fff0, LemonChiffon = 0xFFFACD,
HotPink = 0xff69b4, LightBlue = 0xADD8E6,
IndianRed = 0xcd5c5c, LightCoral = 0xF08080,
Indigo = 0x4b0082, LightCyan = 0xE0FFFF,
Ivory = 0xfffff0, LightGoldenRodYellow = 0xFAFAD2,
Khaki = 0xf0e68c, LightGray = 0xD3D3D3,
Lavender = 0xe6e6fa, LightGreen = 0x90EE90,
LavenderBlush = 0xfff0f5, LightPink = 0xFFB6C1,
LawnGreen = 0x7cfc00, LightSalmon = 0xFFA07A,
LemonChiffon = 0xfffacd, LightSeaGreen = 0x20B2AA,
LightBlue = 0xadd8e6, LightSkyBlue = 0x87CEFA,
LightCoral = 0xf08080,
LightCyan = 0xe0ffff,
LightGoldenrod = 0xeedd82,
LightGoldenrodYellow = 0xfafad2,
LightGray = 0xd3d3d3,
LightGreen = 0x90ee90,
LightPink = 0xffb6c1,
LightSalmon = 0xffa07a,
LightSeaGreen = 0x20b2aa,
LightSkyBlue = 0x87cefa,
LightSlateBlue = 0x8470ff,
LightSlateGray = 0x778899, LightSlateGray = 0x778899,
LightSteelBlue = 0xb0c4de, LightSteelBlue = 0xB0C4DE,
LightYellow = 0xffffe0, LightYellow = 0xFFFFE0,
Lime = 0x00ff00, Lime = 0x00FF00,
LimeGreen = 0x32cd32, LimeGreen = 0x32CD32,
Linen = 0xfaf0e6, Linen = 0xFAF0E6,
Magenta = 0xff00ff, Magenta = 0xFF00FF,
Maroon = 0xb03060, Maroon = 0x800000,
MediumAquamarine = 0x66cdaa, MediumAquaMarine = 0x66CDAA,
MediumBlue = 0x0000cd, MediumBlue = 0x0000CD,
MediumOrchid = 0xba55d3, MediumOrchid = 0xBA55D3,
MediumPurple = 0x9370db, MediumPurple = 0x9370DB,
MediumSeaGreen = 0x3cb371, MediumSeaGreen = 0x3CB371,
MediumSlateBlue = 0x7b68ee, MediumSlateBlue = 0x7B68EE,
MediumSpringGreen = 0x00fa9a, MediumSpringGreen = 0x00FA9A,
MediumTurquoise = 0x48d1cc, MediumTurquoise = 0x48D1CC,
MediumVioletRed = 0xc71585, MediumVioletRed = 0xC71585,
MidnightBlue = 0x191970, MidnightBlue = 0x191970,
MintCream = 0xf5fffa, MintCream = 0xF5FFFA,
MistyRose = 0xffe4e1, MistyRose = 0xFFE4E1,
Moccasin = 0xffe4b5, Moccasin = 0xFFE4B5,
NavajoWhite = 0xffdead, NavajoWhite = 0xFFDEAD,
Navy = 0x000080, Navy = 0x000080,
NavyBlue = 0x000080, OldLace = 0xFDF5E6,
OldLace = 0xfdf5e6,
Olive = 0x808000, Olive = 0x808000,
OliveDrab = 0x6b8e23, OliveDrab = 0x6B8E23,
Orange = 0xffa500, Orange = 0xFFA500,
OrangeRed = 0xff4500, OrangeRed = 0xFF4500,
Orchid = 0xda70d6, Orchid = 0xDA70D6,
PaleGoldenrod = 0xeee8aa, PaleGoldenRod = 0xEEE8AA,
PaleGreen = 0x98fb98, PaleGreen = 0x98FB98,
PaleTurquoise = 0xafeeee, PaleTurquoise = 0xAFEEEE,
PaleVioletRed = 0xdb7093, PaleVioletRed = 0xDB7093,
PapayaWhip = 0xffefd5, PapayaWhip = 0xFFEFD5,
PeachPuff = 0xffdab9, PeachPuff = 0xFFDAB9,
Peru = 0xcd853f, Peru = 0xCD853F,
Pink = 0xffc0cb, Pink = 0xFFC0CB,
Plum = 0xdda0dd, Plum = 0xDDA0DD,
PowderBlue = 0xb0e0e6, PowderBlue = 0xB0E0E6,
Purple = 0xa020f0, Purple = 0x800080,
RebeccaPurple = 0x663399, RebeccaPurple = 0x663399,
Red = 0xff0000, Red = 0xFF0000,
RosyBrown = 0xbc8f8f, RosyBrown = 0xBC8F8F,
RoyalBlue = 0x4169e1, RoyalBlue = 0x4169E1,
SaddleBrown = 0x8b4513, SaddleBrown = 0x8B4513,
Salmon = 0xfa8072, Salmon = 0xFA8072,
SandyBrown = 0xf4a460, SandyBrown = 0xF4A460,
SeaGreen = 0x2e8b57, SeaGreen = 0x2E8B57,
Seashell = 0xfff5ee, SeaShell = 0xFFF5EE,
Sienna = 0xa0522d, Sienna = 0xA0522D,
Silver = 0xc0c0c0, Silver = 0xC0C0C0,
SkyBlue = 0x87ceeb, SkyBlue = 0x87CEEB,
SlateBlue = 0x6a5acd, SlateBlue = 0x6A5ACD,
SlateGray = 0x708090, SlateGray = 0x708090,
Snow = 0xfffafa, Snow = 0xFFFAFA,
SpringGreen = 0x00ff7f, SpringGreen = 0x00FF7F,
SteelBlue = 0x4682b4, SteelBlue = 0x4682B4,
Tan = 0xd2b48c, Tan = 0xD2B48C,
Teal = 0x008080, Teal = 0x008080,
Thistle = 0xd8bfd8, Thistle = 0xD8BFD8,
Tomato = 0xff6347, Tomato = 0xFF6347,
Turquoise = 0x40e0d0, Turquoise = 0x40E0D0,
Violet = 0xee82ee, Violet = 0xEE82EE,
VioletRed = 0xd02090, Wheat = 0xF5DEB3,
Wheat = 0xf5deb3, White = 0xFFFFFF,
White = 0xffffff, WhiteSmoke = 0xF5F5F5,
WhiteSmoke = 0xf5f5f5, Yellow = 0xFFFF00,
Yellow = 0xffff00, YellowGreen = 0x9ACD32,
YellowGreen = 0x9acd32, Box2DRed = 0xDC3132,
Box2DRed = 0xdc3132, Box2DBlue = 0x30AEBF,
Box2DBlue = 0x30aebf, Box2DGreen = 0x8CC924,
Box2DGreen = 0x8cc924, Box2DYellow = 0xFFEE8C,
Box2DYellow = 0xffee8c,
} }
// This struct holds callbacks you can implement to draw a Box2D world. // This struct holds callbacks you can implement to draw a Box2D world.
// @ingroup world // @ingroup world
DebugDraw :: struct { DebugDraw :: struct {
// Draw a closed polygon provided in CCW order. // 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. // 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. // 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. // Draw a solid circle.
DrawSolidCircle: proc "c" (transform: Transform, radius: f32, color: HexColor, ctx: rawptr), DrawSolidCircleFcn: 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),
// Draw a solid capsule. // 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. // 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. // 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. // 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. // Draw a string in world space.
DrawString: proc "c" (p: Vec2, s: cstring, ctx: rawptr), DrawStringFcn: proc "c" (p: Vec2, s: cstring, color: HexColor, ctx: rawptr),
// Bounds to use if restricting drawing to a rectangular region // Bounds to use if restricting drawing to a rectangular region
drawingBounds: AABB, drawingBounds: AABB,
@@ -1210,11 +1318,14 @@ DebugDraw :: struct {
drawJointExtras: bool, drawJointExtras: bool,
// Option to draw the bounding boxes for shapes // Option to draw the bounding boxes for shapes
drawAABBs: bool, drawBounds: bool,
// Option to draw the mass and center of mass of dynamic bodies // Option to draw the mass and center of mass of dynamic bodies
drawMass: bool, drawMass: bool,
// Option to draw body names
drawBodyNames: bool,
// Option to draw contact points // Option to draw contact points
drawContacts: bool, drawContacts: bool,
@@ -1227,9 +1338,15 @@ DebugDraw :: struct {
// Option to draw contact normal impulses // Option to draw contact normal impulses
drawContactImpulses: bool, drawContactImpulses: bool,
// Option to draw contact feature ids
drawContactFeatures: bool,
// Option to draw contact friction impulses // Option to draw contact friction impulses
drawFrictionImpulses: bool, drawFrictionImpulses: bool,
// Option to draw islands as bounding boxes
drawIslands: bool,
// User context that is passed as an argument to drawing callback functions // User context that is passed as an argument to drawing callback functions
userContext: rawptr, userContext: rawptr,
} }
+4 -4
View File
@@ -7,20 +7,20 @@
# CC = $(shell brew --prefix llvm)/bin/clang # CC = $(shell brew --prefix llvm)/bin/clang
# LD = $(shell brew --prefix llvm)/bin/wasm-ld # LD = $(shell brew --prefix llvm)/bin/wasm-ld
VERSION = 3.0.0 VERSION = 3.1.0
SRCS = $(wildcard box2d-$(VERSION)/src/*.c) SRCS = $(wildcard box2d-$(VERSION)/src/*.c)
OBJS_SIMD = $(SRCS:.c=_simd.o) OBJS_SIMD = $(SRCS:.c=_simd.o)
OBJS = $(SRCS:.c=.o) OBJS = $(SRCS:.c=.o)
SYSROOT = $(shell odin root)/vendor/libc 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 all: lib/box2d_wasm.o lib/box2d_wasm_simd.o clean
%.o: %.c %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@ $(CC) -c $(CFLAGS) -DBOX2D_DISABLE_SIMD $< -o $@
%_simd.o: %.c %_simd.o: %.c
$(CC) -c $(CFLAGS) -msimd128 $< -o $@ $(CC) -c $(CFLAGS) -DBOX2D_DISABLE_SIMD -msimd128 $< -o $@
lib/box2d_wasm.o: $(OBJS) lib/box2d_wasm.o: $(OBJS)
$(LD) -r -o lib/box2d_wasm.o $(OBJS) $(LD) -r -o lib/box2d_wasm.o $(OBJS)
+3 -3
View File
@@ -47,19 +47,19 @@ bool __isnanf(float);
bool __isnand(double); bool __isnand(double);
#define isnan(x) \ #define isnan(x) \
( sizeof(x) == sizeof(float) ? __isnanf((float)(x)) \ ( sizeof(x) == sizeof(float) ? __isnanf((float)(x)) \
: : __isnand((double)(x))) : __isnand((double)(x)))
bool __isinff(float); bool __isinff(float);
bool __isinfd(double); bool __isinfd(double);
#define isinf(x) \ #define isinf(x) \
( sizeof(x) == sizeof(float) ? __isinff((float)(x)) \ ( sizeof(x) == sizeof(float) ? __isinff((float)(x)) \
: : __isinfd((double)(x))) : __isinfd((double)(x)))
bool __isfinitef(float); bool __isfinitef(float);
bool __isfinited(double); bool __isfinited(double);
#define isfinite(x) \ #define isfinite(x) \
( sizeof(x) == sizeof(float) ? __isfinitef((float)(x)) \ ( sizeof(x) == sizeof(float) ? __isfinitef((float)(x)) \
: : __isfinited((double)(x))) : __isfinited((double)(x)))
#ifdef __cplusplus #ifdef __cplusplus
} }
+23
View File
@@ -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
+30
View File
@@ -0,0 +1,30 @@
package odin_libc
import "core:time"
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 {
return 0
}