Merge pull request #5069 from laytan/box2d-3.1.0

box2d: update to 3.1.0
This commit is contained in:
gingerBill
2025-04-25 08:26:43 +01:00
committed by GitHub
17 changed files with 1209 additions and 706 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -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"

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
// don't use more vertices.
maxPolygonVertices :: 8
MAX_POLYGON_VERTICES :: 8
// Low level ray-cast input data
// Low level ray cast input data
RayCastInput :: struct {
// Start point of the ray cast
origin: Vec2,
@@ -19,27 +19,37 @@ RayCastInput :: struct {
maxFraction: f32,
}
// A distance proxy is used by the GJK algorithm. It encapsulates any shape.
// You can provide between 1 and MAX_POLYGON_VERTICES and a radius.
ShapeProxy :: struct {
// The point cloud
points: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`,
// The number of points. Must be greater than 0.
count: c.int,
// The external radius of the point cloud. May be zero.
radius: f32,
}
// Low level shape cast input in generic form. This allows casting an arbitrary point
// cloud wrap with a radius. For example, a circle is a single point with a non-zero radius.
// A capsule is two points with a non-zero radius. A box is four points with a zero radius.
// cloud wrap with a radius. For example, a circle is a single point with a non-zero radius.
// A capsule is two points with a non-zero radius. A box is four points with a zero radius.
ShapeCastInput :: struct {
// A point cloud to cast
points: [maxPolygonVertices]Vec2 `fmt:"v,count"`,
// The number of points
count: i32,
// The radius around the point cloud
radius: f32,
// A generic shape
proxy: ShapeProxy,
// The translation of the shape cast
translation: Vec2,
// The maximum fraction of the translation to consider, typically 1
maxFraction: f32,
// Allow shape cast to encroach when initially touching. This only works if the radius is greater than zero.
canEncroach: bool,
}
// Low level ray-cast or shape-cast output data
// Low level ray cast or shape-cast output data
CastOutput :: struct {
// The surface normal at the hit point
normal: Vec2,
@@ -51,7 +61,7 @@ CastOutput :: struct {
fraction: f32,
// The number of iterations used
iterations: i32,
iterations: c.int,
// Did the cast hit?
hit: bool,
@@ -93,16 +103,16 @@ Capsule :: struct {
// A solid convex polygon. It is assumed that the interior of the polygon is to
// the left of each edge.
// Polygons have a maximum number of vertices equal to maxPolygonVertices.
// Polygons have a maximum number of vertices equal to MAX_POLYGON_VERTICES.
// In most cases you should not need many vertices for a convex polygon.
// @warning DO NOT fill this out manually, instead use a helper function like
// b2MakePolygon or b2MakeBox.
// @warning DO NOT fill this out manually, instead use a helper function like
// b2MakePolygon or b2MakeBox.
Polygon :: struct {
// The polygon vertices
vertices: [maxPolygonVertices]Vec2 `fmt:"v,count"`,
vertices: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`,
// The outward normal vectors of the polygon sides
normals: [maxPolygonVertices]Vec2 `fmt:"v,count"`,
normals: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`,
// The centroid of the polygon
centroid: Vec2,
@@ -111,7 +121,7 @@ Polygon :: struct {
radius: f32,
// The number of polygon vertices
count: i32,
count: c.int,
}
// A line segment with two-sided collision.
@@ -123,10 +133,10 @@ Segment :: struct {
point2: Vec2,
}
// A smooth line segment with one-sided collision. Only collides on the right side.
// A line segment with one-sided collision. Only collides on the right side.
// Several of these are generated for a chain shape.
// ghost1 -> point1 -> point2 -> ghost2
SmoothSegment :: struct {
ChainSegment :: struct {
// The tail ghost vertex
ghost1: Vec2,
@@ -137,7 +147,7 @@ SmoothSegment :: struct {
ghost2: Vec2,
// The owning chain shape index (internal usage only)
chainId: i32,
chainId: c.int,
}
@@ -145,10 +155,10 @@ SmoothSegment :: struct {
// @warning Do not modify these values directly, instead use b2ComputeHull()
Hull :: struct {
// The final points of the hull
points: [maxPolygonVertices]Vec2 `fmt:"v,count"`,
points: [MAX_POLYGON_VERTICES]Vec2 `fmt:"v,count"`,
// The number of points
count: i32,
count: c.int,
}
/**
@@ -178,21 +188,11 @@ SegmentDistanceResult :: struct {
distanceSquared: f32,
}
// A distance proxy is used by the GJK algorithm. It encapsulates any shape.
DistanceProxy :: struct {
// The point cloud
points: [maxPolygonVertices]Vec2 `fmt:"v,count"`,
// The number of points
count: i32,
// The external radius of the point cloud
radius: f32,
}
// Used to warm start b2Distance. Set count to zero on first call or
// use zero initialization.
DistanceCache :: struct {
// Used to warm start the GJK simplex. If you call this function multiple times with nearby
// transforms this might improve performance. Otherwise you can zero initialize this.
// The distance cache must be initialized to zero on the first call.
// Users should generally just zero initialize this structure for each call.
SimplexCache :: struct {
// The number of stored simplex points
count: u16,
@@ -203,15 +203,15 @@ DistanceCache :: struct {
indexB: [3]u8 `fmt:"v,count"`,
}
emptyDistanceCache :: DistanceCache{}
emptySimplexCache :: SimplexCache{}
// Input for b2ShapeDistance
DistanceInput :: struct {
// The proxy for shape A
proxyA: DistanceProxy,
proxyA: ShapeProxy,
// The proxy for shape B
proxyB: DistanceProxy,
proxyB: ShapeProxy,
// The world transform for shape A
transformA: Transform,
@@ -227,6 +227,7 @@ DistanceInput :: struct {
DistanceOutput :: struct {
pointA: Vec2, // Closest point on shapeA
pointB: Vec2, // Closest point on shapeB
normal: Vec2, // Normal vector that points from A to B
distance: f32, // The final distance, zero if overlapped
iterations: i32, // Number of GJK iterations used
simplexCount: i32, // The number of simplexes stored in the simplex array
@@ -234,28 +235,29 @@ DistanceOutput :: struct {
// Simplex vertex for debugging the GJK algorithm
SimplexVertex :: struct {
wA: Vec2, // support point in proxyA
wB: Vec2, // support point in proxyB
w: Vec2, // wB - wA
a: f32, // barycentric coordinate for closest point
indexA: i32, // wA index
indexB: i32, // wB index
wA: Vec2, // support point in proxyA
wB: Vec2, // support point in proxyB
w: Vec2, // wB - wA
a: f32, // barycentric coordinate for closest point
indexA: c.int, // wA index
indexB: c.int, // wB index
}
// Simplex from the GJK algorithm
Simplex :: struct {
v1, v2, v3: SimplexVertex `fmt:"v,count"`, // vertices
count: i32, // number of valid vertices
count: c.int, // number of valid vertices
}
// Input parameters for b2ShapeCast
ShapeCastPairInput :: struct {
proxyA: DistanceProxy, // The proxy for shape A
proxyB: DistanceProxy, // The proxy for shape B
proxyA: ShapeProxy, // The proxy for shape A
proxyB: ShapeProxy, // The proxy for shape B
transformA: Transform, // The world transform for shape A
transformB: Transform, // The world transform for shape B
translationB: Vec2, // The translation of shape B
maxFraction: f32, // The fraction of the translation to consider, typically 1
canEncroach: bool, // Allows shapes with a radius to move slightly closer if already touching
}
@@ -272,11 +274,11 @@ Sweep :: struct {
// Input parameters for b2TimeOfImpact
TOIInput :: struct {
proxyA: DistanceProxy, // The proxy for shape A
proxyB: DistanceProxy, // The proxy for shape B
sweepA: Sweep, // The movement of shape A
sweepB: Sweep, // The movement of shape B
tMax: f32, // Defines the sweep interval [0, tMax]
proxyA: ShapeProxy, // The proxy for shape A
proxyB: ShapeProxy, // The proxy for shape B
sweepA: Sweep, // The movement of shape A
sweepB: Sweep, // The movement of shape B
maxFraction: f32, // Defines the sweep interval [0, maxFraction]
}
// Describes the TOI output
@@ -290,8 +292,8 @@ TOIState :: enum c.int {
// Output parameters for b2TimeOfImpact.
TOIOutput :: struct {
state: TOIState, // The type of result
t: f32, // The time of the collision
state: TOIState, // The type of result
fraction: f32, // The sweep time of the collision
}
@@ -301,26 +303,30 @@ TOIOutput :: struct {
* @brief Functions for colliding pairs of shapes
*/
// A manifold point is a contact point belonging to a contact
// manifold. It holds details related to the geometry and dynamics
// of the contact points.
// A manifold point is a contact point belonging to a contact manifold.
// It holds details related to the geometry and dynamics of the contact points.
// Box2D uses speculative collision so some contact points may be separated.
// You may use the totalNormalImpulse to determine if there was an interaction during
// the time step.
ManifoldPoint :: struct {
// Location of the contact point in world space. Subject to precision loss at large coordinates.
// @note Should only be used for debugging.
point: Vec2,
// Location of the contact point relative to bodyA's origin in world space
// @note When used internally to the Box2D solver, these are relative to the center of mass.
// Location of the contact point relative to shapeA's origin in world space
// @note When used internally to the Box2D solver, this is relative to the body center of mass.
anchorA: Vec2,
// Location of the contact point relative to bodyB's origin in world space
// Location of the contact point relative to shapeB's origin in world space
// @note When used internally to the Box2D solver, this is relative to the body center of mass.
anchorB: Vec2,
// The separation of the contact point, negative if penetrating
separation: f32,
// The impulse along the manifold normal vector.
normalImpulse: f32,
// The total normal impulse applied across sub-stepping and restitution. This is important
// to identify speculative contact points that had an interaction in the time step.
totalNormalImpulse: f32,
// The friction impulse
tangentImpulse: f32,
@@ -340,16 +346,21 @@ ManifoldPoint :: struct {
persisted: bool,
}
// A contact manifold describes the contact points between colliding shapes
// A contact manifold describes the contact points between colliding shapes.
// @note Box2D uses speculative collision so some contact points may be separated.
Manifold :: struct {
// The manifold points, up to two are possible in 2D
points: [2]ManifoldPoint,
// The unit normal vector in world space, points from shape A to bodyB
normal: Vec2,
normal: Vec2,
// Angular impulse applied for rolling resistance. N * m * s = kg * m^2 / s
rollingImpulse: f32,
// The manifold points, up to two are possible in 2D
points: [2]ManifoldPoint,
// The number of contacts points, will be 0, 1, or 2
pointCount: i32,
pointCount: c.int,
}
@@ -364,63 +375,17 @@ Manifold :: struct {
* A dynamic AABB tree broad-phase, inspired by Nathanael Presson's btDbvt.
* A dynamic tree arranges data in a binary tree to accelerate
* queries such as AABB queries and ray casts. Leaf nodes are proxies
* with an AABB. These are used to hold a user collision object, such as a reference to a b2Shape.
* with an AABB. These are used to hold a user collision object.
* Nodes are pooled and relocatable, so I use node indices rather than pointers.
* The dynamic tree is made available for advanced users that would like to use it to organize
* spatial game data besides rigid bodies.
*
* @note This is an advanced feature and normally not used by applications directly.
*/
// The default category bit for a tree proxy. Used for collision filtering.
defaultCategoryBits :: 0x00000001
// Convenience mask bits to use when you don't need collision filtering and just want
// all results.
defaultMaskBits :: 0xFFFFFFFF
// A node in the dynamic tree. This is private data placed here for performance reasons.
// 16 + 16 + 8 + pad(8)
TreeNode :: struct {
// The node bounding box
aabb: AABB, // 16
// Category bits for collision filtering
categoryBits: u32, // 4
using _: struct #raw_union {
// The node parent index
parent: i32,
// The node freelist next index
next: i32,
}, // 4
// Child 1 index
child1: i32, // 4
// Child 2 index
child2: i32, // 4
// User data
// todo could be union with child index
userData: i32, // 4
// Leaf = 0, free node = -1
height: i16, // 2
// Has the AABB been enlarged?
enlarged: bool, // 1
// Padding for clarity
_: [9]byte,
}
// The dynamic tree structure. This should be considered private data.
// It is placed here for performance reasons.
DynamicTree :: struct {
// The tree nodes
nodes: [^]TreeNode `fmt"v,nodeCount"`,
nodes: rawptr,
// The root index
root: i32,
@@ -453,16 +418,25 @@ DynamicTree :: struct {
rebuildCapacity: i32,
}
// These are performance results returned by dynamic tree queries.
TreeStats :: struct {
// Number of internal nodes visited during the query
nodeVisits: c.int,
// Number of leaf nodes visited during the query
leafVisits: c.int,
}
// This function receives proxies found in the AABB query.
// @return true if the query should continue
TreeQueryCallbackFcn :: #type proc "c" (proxyId: i32, userData: i32, ctx: rawptr) -> bool
TreeQueryCallbackFcn :: #type proc "c" (proxyId: i32, userData: u64, ctx: rawptr) -> bool
// This function receives clipped ray-cast input for a proxy. The function
// This function receives clipped ray cast input for a proxy. The function
// returns the new ray fraction.
// - return a value of 0 to terminate the ray-cast
// - return a value of 0 to terminate the ray cast
// - return a value less than input->maxFraction to clip the ray
// - return a value of input->maxFraction to continue the ray cast without clipping
TreeShapeCastCallbackFcn :: #type proc "c" (#by_ptr input: ShapeCastInput, proxyId: i32, userData: i32, ctx: rawptr) -> f32
TreeShapeCastCallbackFcn :: #type proc "c" (#by_ptr input: ShapeCastInput, proxyId: i32, userData: u64, ctx: rawptr) -> f32
// This function receives clipped raycast input for a proxy. The function
@@ -470,4 +444,47 @@ TreeShapeCastCallbackFcn :: #type proc "c" (#by_ptr input: ShapeCastInput, proxy
// - return a value of 0 to terminate the ray cast
// - return a value less than input->maxFraction to clip the ray
// - return a value of input->maxFraction to continue the ray cast without clipping
TreeRayCastCallbackFcn :: #type proc "c" (#by_ptr input: RayCastInput, proxyId: i32, userData: i32, ctx: rawptr) -> f32
TreeRayCastCallbackFcn :: #type proc "c" (#by_ptr input: RayCastInput, proxyId: i32, userData: u64, ctx: rawptr) -> f32
/**@}*/
/**
* @defgroup character Character mover
* Character movement solver
* @{
*/
/// These are the collision planes returned from b2World_CollideMover
PlaneResult :: struct {
// The collision plane between the mover and convex shape
plane: Plane,
// Did the collision register a hit? If not this plane should be ignored.
hit: bool,
}
// These are collision planes that can be fed to b2SolvePlanes. Normally
// this is assembled by the user from plane results in b2PlaneResult
CollisionPlane :: struct {
// The collision plane between the mover and some shape
plane: Plane,
// Setting this to FLT_MAX makes the plane as rigid as possible. Lower values can
// make the plane collision soft. Usually in meters.
pushLimit: f32,
// The push on the mover determined by b2SolvePlanes. Usually in meters.
push: f32,
// Indicates if b2ClipVector should clip against this plane. Should be false for soft collision.
clipVelocity: bool,
}
// Result returned by b2SolvePlanes
PlaneSolverResult :: struct {
// The final position of the mover
position: Vec2,
// The number of iterations used by the plane solver. For diagnostics.
iterationCount: i32,
}

43
vendor/box2d/id.odin vendored
View File

@@ -23,45 +23,46 @@ import "base:intrinsics"
/// World id references a world instance. This should be treated as an opaque handle.
WorldId :: struct {
index1: u16,
revision: u16,
index1: u16,
generation: u16,
}
/// Body id references a body instance. This should be treated as an opaque handle.
BodyId :: struct {
index1: i32,
world0: u16,
revision: u16,
index1: i32,
world0: u16,
generation: u16,
}
/// Shape id references a shape instance. This should be treated as an opaque handle.
ShapeId :: struct {
index1: i32,
world0: u16,
revision: u16,
}
/// Joint id references a joint instance. This should be treated as an opaque handle.
JointId :: struct {
index1: i32,
world0: u16,
revision: u16,
index1: i32,
world0: u16,
generation: u16,
}
/// Chain id references a chain instances. This should be treated as an opaque handle.
ChainId :: struct {
index1: i32,
world0: u16,
revision: u16,
index1: i32,
world0: u16,
generation: u16,
}
/// Joint id references a joint instance. This should be treated as an opaque handle.
JointId :: struct {
index1: i32,
world0: u16,
generation: u16,
}
/// Use these to make your identifiers null.
/// You may also use zero initialization to get null.
nullWorldId :: WorldId{}
nullBodyId :: BodyId{}
nullShapeId :: ShapeId{}
nullJointId :: JointId{}
nullChainId :: ChainId{}
nullJointId :: JointId{}
/// Macro to determine if any id is null.
IS_NULL :: #force_inline proc "c" (id: $T) -> bool
@@ -82,6 +83,6 @@ ID_EQUALS :: #force_inline proc "c" (id1, id2: $T) -> bool
where intrinsics.type_is_struct(T),
intrinsics.type_has_field(T, "index1"),
intrinsics.type_has_field(T, "world0"),
intrinsics.type_has_field(T, "revision") {
return id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.revision == id2.revision
intrinsics.type_has_field(T, "generation") {
return id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.generation == id2.generation
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -3,9 +3,18 @@ package vendor_box2d
import "core:c"
import "core:math"
pi :: 3.14159265359
EPSILON :: 1e-23
Vec2 :: [2]f32
// Cosine and sine pair
// This uses a custom implementation designed for cross-platform determinism
CosSin :: struct {
// cosine and sine
cosine: f32,
sine: f32,
}
Rot :: struct {
c, s: f32, // cosine and sine
}
@@ -21,11 +30,43 @@ AABB :: struct {
upperBound: Vec2,
}
// separation = dot(normal, point) - offset
Plane :: struct {
normal: Vec2,
offset: f32,
}
PI :: math.PI
Vec2_zero :: Vec2{0, 0}
Rot_identity :: Rot{1, 0}
Transform_identity :: Transform{{0, 0}, {1, 0}}
Mat22_zero :: Mat22{0, 0, 0, 0}
// @return the minimum of two integers
@(deprecated="Prefer the built-in 'min(a, b)'", require_results)
MinInt :: proc "c" (a, b: c.int) -> c.int {
return min(a, b)
}
// @return the maximum of two integers
@(deprecated="Prefer the built-in 'max(a, b)'", require_results)
MaxInt :: proc "c" (a, b: c.int) -> c.int {
return max(a, b)
}
// @return the absolute value of an integer
@(deprecated="Prefer the built-in 'abs(a)'", require_results)
AbsInt :: proc "c" (a: c.int) -> c.int {
return abs(a)
}
// @return an integer clamped between a lower and upper bound
@(deprecated="Prefer the built-in 'clamp(a, lower, upper)'", require_results)
ClampInt :: proc "c" (a, lower, upper: c.int) -> c.int {
return clamp(a, lower, upper)
}
// @return the minimum of two floats
@(deprecated="Prefer the built-in 'min(a, b)'", require_results)
@@ -51,28 +92,15 @@ ClampFloat :: proc "c" (a, lower, upper: f32) -> f32 {
return clamp(a, lower, upper)
}
// @return the minimum of two integers
@(deprecated="Prefer the built-in 'min(a, b)'", require_results)
MinInt :: proc "c" (a, b: c.int) -> c.int {
return min(a, b)
@(require_results)
Atan2 :: proc "c" (y, x: f32) -> f32 {
return math.atan2(y, x)
}
// @return the maximum of two integers
@(deprecated="Prefer the built-in 'max(a, b)'", require_results)
MaxInt :: proc "c" (a, b: c.int) -> c.int {
return max(a, b)
}
// @return the absolute value of an integer
@(deprecated="Prefer the built-in 'abs(a)'", require_results)
AbsInt :: proc "c" (a: c.int) -> c.int {
return abs(a)
}
// @return an integer clamped between a lower and upper bound
@(deprecated="Prefer the built-in 'clamp(a, lower, upper)'", require_results)
ClampInt :: proc "c" (a, lower, upper: c.int) -> c.int {
return clamp(a, lower, upper)
@(require_results)
ComputeCosSin :: proc "c" (radians: f32) -> (res: CosSin) {
res.sine, res.cosine = math.sincos(radians)
return
}
// Vector dot product
@@ -198,12 +226,6 @@ Length :: proc "c" (v: Vec2) -> f32 {
return math.sqrt(v.x * v.x + v.y * v.y)
}
// Get the length squared of this vector
@(require_results)
LengthSquared :: proc "c" (v: Vec2) -> f32 {
return v.x * v.x + v.y * v.y
}
// Get the distance between two points
@(require_results)
Distance :: proc "c" (a, b: Vec2) -> f32 {
@@ -212,45 +234,41 @@ Distance :: proc "c" (a, b: Vec2) -> f32 {
return math.sqrt(dx * dx + dy * dy)
}
// Get the distance squared between points
@(require_results)
DistanceSquared :: proc "c" (a, b: Vec2) -> f32 {
c := Vec2{b.x - a.x, b.y - a.y}
return c.x * c.x + c.y * c.y
Normalize :: proc "c" (v: Vec2) -> Vec2 {
length := Length(v)
if length < EPSILON {
return Vec2_zero
}
invLength := 1 / length
return invLength * v
}
// Make a rotation using an angle in radians
@(require_results)
MakeRot :: proc "c" (angle: f32) -> Rot {
// todo determinism
return {math.cos(angle), math.sin(angle)}
IsNormalized :: proc "c" (v: Vec2) -> bool {
aa := Dot(v, v)
return abs(1. - aa) < 10. * EPSILON
}
// Normalize rotation
@(require_results)
NormalizeRot :: proc "c" (q: Rot) -> Rot {
mag := math.sqrt(q.s * q.s + q.c * q.c)
invMag := f32(mag > 0.0 ? 1.0 / mag : 0.0)
return {q.c * invMag, q.s * invMag}
NormalizeChecked :: proc "odin" (v: Vec2) -> Vec2 {
length := Length(v)
if length < 1e-23 {
panic("zero-length Vec2")
}
invLength := 1 / length
return invLength * v
}
// Is this rotation normalized?
@(require_results)
IsNormalized :: proc "c" (q: Rot) -> bool {
// larger tolerance due to failure on mingw 32-bit
qq := q.s * q.s + q.c * q.c
return 1.0 - 0.0006 < qq && qq < 1 + 0.0006
}
// Normalized linear interpolation
// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
@(require_results)
NLerp :: proc "c" (q1: Rot, q2: Rot, t: f32) -> Rot {
omt := 1 - t
return NormalizeRot({
omt * q1.c + t * q2.c,
omt * q1.s + t * q2.s,
})
GetLengthAndNormalize :: proc "c" (v: Vec2) -> (length: f32, vn: Vec2) {
length = Length(v)
if length < 1e-23 {
return
}
invLength := 1 / length
vn = invLength * v
return
}
// Integration rotation from angular velocity
@@ -268,6 +286,63 @@ IntegrateRotation :: proc "c" (q1: Rot, deltaAngle: f32) -> Rot {
return {q2.c * invMag, q2.s * invMag}
}
// Get the length squared of this vector
@(require_results)
LengthSquared :: proc "c" (v: Vec2) -> f32 {
return v.x * v.x + v.y * v.y
}
// Get the distance squared between points
@(require_results)
DistanceSquared :: proc "c" (a, b: Vec2) -> f32 {
c := Vec2{b.x - a.x, b.y - a.y}
return c.x * c.x + c.y * c.y
}
// Make a rotation using an angle in radians
@(require_results)
MakeRot :: proc "c" (angle: f32) -> Rot {
cs := ComputeCosSin(angle)
return Rot{c=cs.cosine, s=cs.sine}
}
// Compute the rotation between two unit vectors
@(require_results)
ComputeRotationBetweenUnitVectors :: proc(v1, v2: Vec2) -> Rot {
return NormalizeRot({
c = Dot(v1, v2),
s = Cross(v1, v2),
})
}
// Is this rotation normalized?
@(require_results)
IsNormalizedRot :: proc "c" (q: Rot) -> bool {
// larger tolerance due to failure on mingw 32-bit
qq := q.s * q.s + q.c * q.c
return 1.0 - 0.0006 < qq && qq < 1 + 0.0006
}
// Normalize rotation
@(require_results)
NormalizeRot :: proc "c" (q: Rot) -> Rot {
mag := math.sqrt(q.s * q.s + q.c * q.c)
invMag := f32(mag > 0.0 ? 1.0 / mag : 0.0)
return {q.c * invMag, q.s * invMag}
}
// Normalized linear interpolation
// https://fgiesen.wordpress.com/2012/08/15/linear-interpolation-past-present-and-future/
// https://web.archive.org/web/20170825184056/http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/
@(require_results)
NLerp :: proc "c" (q1: Rot, q2: Rot, t: f32) -> Rot {
omt := 1 - t
return NormalizeRot({
omt * q1.c + t * q2.c,
omt * q1.s + t * q2.s,
})
}
// Compute the angular velocity necessary to rotate between two rotations over a give time
// @param q1 initial rotation
// @param q2 final rotation
@@ -291,8 +366,7 @@ ComputeAngularVelocity :: proc "c" (q1: Rot, q2: Rot, inv_h: f32) -> f32 {
// Get the angle in radians in the range [-pi, pi]
@(require_results)
Rot_GetAngle :: proc "c" (q: Rot) -> f32 {
// todo determinism
return math.atan2(q.s, q.c)
return Atan2(q.s, q.c)
}
// Get the x-axis
@@ -338,18 +412,34 @@ RelativeAngle :: proc "c" (b, a: Rot) -> f32 {
// cos(b - a) = bc * ac + bs * as
s := b.s * a.c - b.c * a.s
c := b.c * a.c + b.s * a.s
return math.atan2(s, c)
return Atan2(s, c)
}
// Convert an angle in the range [-2*pi, 2*pi] into the range [-pi, pi]
@(require_results)
UnwindAngle :: proc "c" (angle: f32) -> f32 {
if angle < -pi {
return angle + 2.0 * pi
} else if angle > pi {
return angle - 2.0 * pi
UnwindAngle :: proc "c" (radians: f32) -> f32 {
if radians < -PI {
return radians + 2.0 * PI
} else if radians > PI {
return radians - 2.0 * PI
}
return angle
return radians
}
// Convert any into the range [-pi, pi] (slow)
@(require_results)
UnwindLargeAngle :: proc "c" (radians: f32) -> f32 {
radians := radians
for radians > PI {
radians -= 2. * PI
}
for radians < -PI {
radians += 2. * PI
}
return radians
}
// Rotate a vector
@@ -380,6 +470,9 @@ InvTransformPoint :: proc "c" (t: Transform, p: Vec2) -> Vec2 {
return {t.q.c * vx + t.q.s * vy, -t.q.s * vx + t.q.c * vy}
}
// Multiply two transforms. If the result is applied to a point p local to frame B,
// the transform would first convert p to a point local to frame A, then into a point
// in the world frame.
// v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p
// = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p
@(require_results)
@@ -389,6 +482,7 @@ MulTransforms :: proc "c" (A, B: Transform) -> (C: Transform) {
return
}
// Creates a transform that converts a local point in frame B to a local point in frame A.
// v2 = A.q' * (B.q * v1 + B.p - A.p)
// = A.q' * B.q * v1 + A.q' * (B.p - A.p)
@(require_results)
@@ -469,54 +563,65 @@ AABB_Union :: proc "c" (a, b: AABB) -> (c: AABB) {
return
}
// Compute the bounding box of an array of circles
@(require_results)
Float_IsValid :: proc "c" (a: f32) -> bool {
math.is_nan(a) or_return
math.is_inf(a) or_return
MakeAABB :: proc "c" (points: []Vec2, radius: f32) -> AABB {
a := AABB{points[0], points[0]}
for point in points {
a.lowerBound = Min(a.lowerBound, point)
a.upperBound = Max(a.upperBound, point)
}
r := Vec2{radius, radius}
a.lowerBound = a.lowerBound - r
a.upperBound = a.upperBound + r
return a
}
// Signed separation of a point from a plane
@(require_results)
PlaneSeparation :: proc "c" (plane: Plane, point: Vec2) -> f32 {
return Dot(plane.normal, point) - plane.offset
}
@(require_results)
IsValidFloat :: proc "c" (a: f32) -> bool {
#partial switch math.classify(a) {
case .NaN, .Inf, .Neg_Inf: return false
case: return true
}
}
@(require_results)
IsValidVec2 :: proc "c" (v: Vec2) -> bool {
IsValidFloat(v.x) or_return
IsValidFloat(v.y) or_return
return true
}
@(require_results)
Vec2_IsValid :: proc "c" (v: Vec2) -> bool {
(math.is_nan(v.x) || math.is_nan(v.y)) or_return
(math.is_inf(v.x) || math.is_inf(v.y)) or_return
IsValidRotation :: proc "c" (q: Rot) -> bool {
IsValidFloat(q.s) or_return
IsValidFloat(q.c) or_return
return IsNormalizedRot(q)
}
// Is this a valid bounding box? Not Nan or infinity. Upper bound greater than or equal to lower bound.
@(require_results)
IsValidAABB :: proc "c" (aabb: AABB) -> bool {
IsValidVec2(aabb.lowerBound) or_return
IsValidVec2(aabb.upperBound) or_return
(aabb.upperBound.x >= aabb.lowerBound.x) or_return
(aabb.upperBound.y >= aabb.lowerBound.y) or_return
return true
}
// Is this a valid plane? Normal is a unit vector. Not Nan or infinity.
@(require_results)
Rot_IsValid :: proc "c" (q: Rot) -> bool {
(math.is_nan(q.s) || math.is_nan(q.c)) or_return
(math.is_inf(q.s) || math.is_inf(q.c)) or_return
return IsNormalized(q)
}
@(require_results)
Normalize :: proc "c" (v: Vec2) -> Vec2 {
length := Length(v)
if length < 1e-23 {
return Vec2_zero
}
invLength := 1 / length
return invLength * v
}
@(require_results)
NormalizeChecked :: proc "odin" (v: Vec2) -> Vec2 {
length := Length(v)
if length < 1e-23 {
panic("zero-length Vec2")
}
invLength := 1 / length
return invLength * v
}
@(require_results)
GetLengthAndNormalize :: proc "c" (v: Vec2) -> (length: f32, vn: Vec2) {
length = Length(v)
if length < 1e-23 {
return
}
invLength := 1 / length
vn = invLength * v
return
IsValidPlane :: proc "c" (plane: Plane) -> bool {
IsValidFloat(plane.offset) or_return
IsValidVec2(plane.normal) or_return
IsNormalized(plane.normal) or_return
return true
}

View File

@@ -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,
}
}

View File

@@ -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)

View File

@@ -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
}

23
vendor/libc/include/sched.h vendored Normal file
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

35
vendor/libc/sched.odin vendored Normal file
View File

@@ -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
}