mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-14 09:44:26 +00:00
6
.gitattributes
vendored
6
.gitattributes
vendored
@@ -5,6 +5,10 @@ Makefile text eol=lf
|
||||
*.sh text eol=lf
|
||||
vendor/box2d/lib/box2d_windows_amd64_avx2.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/box2d/lib/box2d_windows_amd64_sse2.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/box3d/lib/linux-amd64/libbox3d.a filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/box3d/lib/linux-arm64/libbox3d.a filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/box3d/lib/darwin/libbox3d.a filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/box3d/lib/box3d.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/miniaudio/lib/miniaudio.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/SDL3.dll filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/sdl3/SDL3.lib filter=lfs diff=lfs merge=lfs -text
|
||||
@@ -31,5 +35,3 @@ vendor/raylib/windows/raylibdll.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/raylib/windows/raygui.dll filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/raylib/windows/raygui.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/raylib/windows/rayguidll.lib filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/box3d/lib/linux-amd64/libbox3d.a filter=lfs diff=lfs merge=lfs -text
|
||||
vendor/box3d/lib/darwin/libbox3d.a filter=lfs diff=lfs merge=lfs -text
|
||||
|
||||
18
vendor/box3d/box3d.odin
vendored
18
vendor/box3d/box3d.odin
vendored
@@ -833,6 +833,14 @@ foreign lib {
|
||||
// Is this body a bullet?
|
||||
Body_IsBullet :: proc(bodyId: BodyId) -> bool ---
|
||||
|
||||
// Allow this body to rotate fast. Useful for axially symmetric bodies, such as vehicle wheels.
|
||||
// Normally rotation speed is clamped to improve CCD. However, this clamping is unnecessary for
|
||||
// bodies that only rotate fast around an axis of symmetry.
|
||||
Body_AllowFastRotation :: proc(bodyId: BodyId, flag: bool) ---
|
||||
|
||||
// Is this body allowed to rotate fast?
|
||||
Body_IsFastRotationAllowed :: proc(bodyId: BodyId) -> bool ---
|
||||
|
||||
// Enable or disable contact recycling for this body. Contact recycling is a performance optimization
|
||||
// that reuses contact manifolds when bodies move slightly. Disabling it can avoid ghost collisions
|
||||
// on characters at the cost of higher per-step work. Existing contacts retain their prior setting;
|
||||
@@ -895,6 +903,10 @@ foreign lib {
|
||||
* @defgroup shape Shape
|
||||
* Functions to create, destroy, and access.
|
||||
* Shapes bind raw geometry to bodies and hold material properties including friction and restitution.
|
||||
* You may add multiple shapes to a single body. There are no hard limits on shape count per body.
|
||||
*
|
||||
* When you create a shape on a body the center of mass moves. This can lead to the body linear velocity
|
||||
* changing if the angular velocity is non-zero.
|
||||
* @{
|
||||
*/
|
||||
|
||||
@@ -933,8 +945,10 @@ foreign lib {
|
||||
// @return the shape id for accessing the shape
|
||||
CreateHeightFieldShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, heightField: ^HeightFieldData) -> ShapeId ---
|
||||
|
||||
// Compound shapes are only allowed on static bodies.
|
||||
CreateCompoundShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, compound: ^CompoundData) -> ShapeId ---
|
||||
// Baked compound shapes are only allowed on static bodies.
|
||||
// Note: runtime compounds are achieved by adding multiple shapes to a body.
|
||||
// Runtime compounds can be dynamic and/or kinematic.
|
||||
CreateBakedCompoundShape :: proc(bodyId: BodyId, #by_ptr def: ShapeDef, compound: ^CompoundData) -> ShapeId ---
|
||||
|
||||
// Destroy a shape. You may defer the body mass update which can improve performance if several shapes on a
|
||||
// body are destroyed at once.
|
||||
|
||||
67
vendor/box3d/box3d_collision.odin
vendored
67
vendor/box3d/box3d_collision.odin
vendored
@@ -265,14 +265,12 @@ foreign lib {
|
||||
// Destroy a compound shape.
|
||||
DestroyCompound :: proc(compound: ^CompoundData) ---
|
||||
|
||||
// If bytes is null then this returns the number of required bytes. This clones all the
|
||||
// data into the bytes buffer. This is expected to run offline or asynchronously.
|
||||
// This mutates the compound to nullify pointers, leaving the compound in an unusable state.
|
||||
// Cast the provided compound data to bytes, setting the internal pointers to null.
|
||||
// Use this before serializing the compound bytes.
|
||||
ConvertCompoundToBytes :: proc(compound: ^CompoundData) -> [^]u8 ---
|
||||
|
||||
// Convert bytes to compound. This does not clone. The bytes must remain in scope while the
|
||||
// compound is used. This is done to improve run-time performance and allow for instancing.
|
||||
// The bytes are mutated to fixup pointers.
|
||||
// Cast the provided bytes to compound data, setting up internal pointers.
|
||||
// Use this after de-serializing the compound bytes.
|
||||
ConvertBytesToCompound :: proc(bytes: [^]u8, byteCount: c.int) -> ^CompoundData ---
|
||||
|
||||
/**@}*/ // compound
|
||||
@@ -440,15 +438,16 @@ foreign lib {
|
||||
// Collide two hulls.
|
||||
CollideHulls :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr hullA: HullData, #by_ptr hullB: HullData, transformBtoA: Transform, cache: ^SATCache) ---
|
||||
|
||||
// Collide a capsule and a triangle.
|
||||
CollideCapsuleAndTriangle :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr capsuleA: Capsule, #by_ptr triangleB: [3]Vec3, cache: ^SimplexCache) ---
|
||||
// Collide a triangle and capsule. Normal points from triangle to capsule.
|
||||
CollideTriangleAndCapsule :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr triangleA: [3]Vec3, #by_ptr capsuleB: Capsule, cache: ^SimplexCache) ---
|
||||
|
||||
// Collide a hull and a triangle.
|
||||
CollideHullAndTriangle :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr hullA: HullData, v1, v2, v3: Vec3,
|
||||
triangleFlags: c.int, cache: ^SATCache, enableSpeculative: bool) ---
|
||||
// Collide a triangle and hull. Normal points from triangle to hull.
|
||||
CollideTriangleAndHull :: proc(manifold: ^LocalManifold, capacity: c.int, v1, v2, v3: Vec3, triangleFlags: c.int,
|
||||
#by_ptr hullB: HullData, cache: ^SATCache, enableSpeculative: bool) ---
|
||||
|
||||
// Collide a triangle and sphere. Normal points from triangle to sphere.
|
||||
CollideTriangleAndSphere :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr triangleA: [3]Vec3, #by_ptr sphereB: Sphere) ---
|
||||
|
||||
// Collide a sphere and a triangle.
|
||||
CollideSphereAndTriangle :: proc(manifold: ^LocalManifold, capacity: c.int, #by_ptr sphereA: Sphere, #by_ptr triangleB: [3]Vec3) ---
|
||||
|
||||
/**@}*/ // collision
|
||||
|
||||
@@ -513,16 +512,6 @@ GetHullEdges :: proc "c" (hull: ^HullData) -> Maybe(^HullHalfEdge) {
|
||||
return (^HullHalfEdge)(uintptr(hull) + uintptr(hull.edgeOffset))
|
||||
}
|
||||
|
||||
// Get read only hull faces.
|
||||
@(require_results)
|
||||
GetHullFaces :: proc "c" (hull: ^HullData) -> Maybe(^HullFace) {
|
||||
if hull.faceOffset == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (^HullFace)(uintptr(hull) + uintptr(hull.faceOffset))
|
||||
}
|
||||
|
||||
// Get read only hull planes.
|
||||
@(require_results)
|
||||
GetHullPlanes :: proc "c" (hull: ^HullData) -> Maybe(^Plane) {
|
||||
@@ -533,6 +522,38 @@ GetHullPlanes :: proc "c" (hull: ^HullData) -> Maybe(^Plane) {
|
||||
return (^Plane)(uintptr(hull) + uintptr(hull.planeOffset))
|
||||
}
|
||||
|
||||
// Get read only hull faces.
|
||||
@(require_results)
|
||||
GetHullFaces :: proc "c" (hull: ^HullData) -> Maybe(^HullFace) {
|
||||
if hull.faceOffset == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (^HullFace)(uintptr(hull) + uintptr(hull.faceOffset))
|
||||
}
|
||||
|
||||
// Get read only SOA vertices. This is an array of vertices with all x values,
|
||||
// y values, and z values as separate arrays. The array lengths are padded to
|
||||
// a multiple of 4. The padded values are repeats of the first value.
|
||||
@(require_results)
|
||||
GetHullSoaVertices :: proc "c" (hull: ^HullData) -> Maybe([^]f32) {
|
||||
if hull.soaVertexOffset == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ([^]f32)(uintptr(hull) + uintptr(hull.soaVertexOffset))
|
||||
}
|
||||
|
||||
// Get read only SOA unit normal vectors. This is an array of normals with all x values,
|
||||
// y values, and z values as separate arrays. The array lengths are padded to
|
||||
// a multiple of 4. The padded values are repeats of the first value.
|
||||
@(require_results)
|
||||
GetHullSoaNormals :: proc "c" (hull: ^HullData) -> Maybe([^]f32) {
|
||||
if hull.soaNormalOffset == 0 {
|
||||
return nil
|
||||
}
|
||||
return ([^]f32)(uintptr(hull) + uintptr(hull.soaNormalOffset))
|
||||
}
|
||||
|
||||
// Get read only mesh BVH nodes.
|
||||
@(require_results)
|
||||
|
||||
27
vendor/box3d/box3d_constants.odin
vendored
27
vendor/box3d/box3d_constants.odin
vendored
@@ -62,6 +62,10 @@ MIN_CAPSULE_LENGTH :: #force_inline proc "c" () -> f32 {
|
||||
return LINEAR_SLOP()
|
||||
}
|
||||
|
||||
// Minimum contact point friction weight, lower bound for speculative points. Made small
|
||||
// enough to be washed away by weights that hit 1.
|
||||
MIN_FRICTION_WEIGHT :: 1e-10
|
||||
|
||||
// The distance between shapes where they are considered overlapped. This is needed
|
||||
// because GJK may return small positive values for overlapped shapes in degenerate
|
||||
// configurations.
|
||||
@@ -121,11 +125,30 @@ TIME_TO_SLEEP :: 0.5
|
||||
// The maximum number of contact points between two touching shapes.
|
||||
MAX_MANIFOLD_POINTS :: 4
|
||||
|
||||
// The number of iterations for gyroscopic torques.
|
||||
GYROSCOPIC_ITERATIONS :: 1
|
||||
|
||||
// The maximum number of convex hull vertices. This is fixed for performance reasons.
|
||||
MAX_HULL_VERTICES :: 128
|
||||
|
||||
// The maximum number of convex hull faces.
|
||||
MAX_HULL_FACES :: 128
|
||||
|
||||
// The maximum number of convex hull edges. Full edges, not half-edges.
|
||||
MAX_HULL_EDGES :: 128
|
||||
|
||||
// Relative tolerance used to determine if two edges are parallel.
|
||||
PARALLEL_EDGE_TOL :: 0.005
|
||||
|
||||
// The maximum number points to use for shape cast proxies (swept point cloud).
|
||||
MAX_SHAPE_CAST_POINTS :: 64
|
||||
MAX_SHAPE_CAST_POINTS :: MAX_HULL_VERTICES
|
||||
|
||||
// These generous limits allow for easy hashing. See b3ShapePairKey.
|
||||
SHAPE_POWER :: 22
|
||||
CHILD_POWER :: 64 - 2 * SHAPE_POWER
|
||||
MAX_SHAPES :: 1 << SHAPE_POWER
|
||||
MAX_CHILD_SHAPES :: 1 << CHILD_POWER
|
||||
MAX_CHILD_SHAPES :: 1 << CHILD_POWER
|
||||
|
||||
// Increase this if your application needs more accurate restitution. Doing so will
|
||||
// slow down the simulation. Must be 1 or more.
|
||||
RESTITUTION_ITERATIONS :: 1
|
||||
44
vendor/box3d/box3d_types.odin
vendored
44
vendor/box3d/box3d_types.odin
vendored
@@ -479,6 +479,9 @@ SurfaceMaterial :: struct {
|
||||
// carry a b3DebugMaterial preset, see b3MakeDebugColor.
|
||||
// @see b3HexColor
|
||||
customColor: u32,
|
||||
|
||||
// Explicit padding. Must be zero.
|
||||
padding: u32,
|
||||
}
|
||||
|
||||
|
||||
@@ -542,6 +545,7 @@ ShapeDef :: struct {
|
||||
isSensor: bool,
|
||||
|
||||
// Enable sensor events for this shape. This applies to sensors and non-sensors. False by default, even for sensors.
|
||||
// Only convex shapes may act as sensor visitors.
|
||||
enableSensorEvents: bool,
|
||||
|
||||
// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default.
|
||||
@@ -1871,8 +1875,7 @@ Capsule :: struct {
|
||||
* @{
|
||||
*/
|
||||
|
||||
// A hull vertex. Identified by a half-edge with this
|
||||
// vertex as its tail.
|
||||
// A hull vertex. Identified by a half-edge with this vertex as its tail.
|
||||
HullVertex :: struct {
|
||||
// A half-edge that has this vertex as the origin
|
||||
// Can be used along with edge twins and winding order
|
||||
@@ -1903,7 +1906,7 @@ HullFace :: struct {
|
||||
}
|
||||
|
||||
// 64-bit hull version. Useful for validating serialized data.
|
||||
HULL_VERSION :: 0x9D4716CE3793900E
|
||||
HULL_VERSION :: 0xDA5150191B994C01
|
||||
|
||||
// A convex hull.
|
||||
// @note This data structure has data hanging off the end and cannot be directly copied.
|
||||
@@ -1953,11 +1956,17 @@ HullData :: struct {
|
||||
// The face count. Hulls faces are convex polygons.
|
||||
faceCount: c.int,
|
||||
|
||||
// Offset of the face plane array in bytes from the struct address.
|
||||
planeOffset: c.int,
|
||||
|
||||
// Offset of the face array in bytes from the struct address.
|
||||
faceOffset: c.int,
|
||||
|
||||
// Offset of the face plane array in bytes from the struct address.
|
||||
planeOffset: c.int,
|
||||
// Offset of structure of array (SOA) vertices
|
||||
soaVertexOffset: c.int,
|
||||
|
||||
// Offset of structure of array (SOA) unit normal vectors
|
||||
soaNormalOffset: c.int,
|
||||
|
||||
// Explicit padding. Hull identity is a content hash and memcmp over raw bytes,
|
||||
// so there must be no unnamed padding for struct copies to scramble.
|
||||
@@ -1971,9 +1980,15 @@ BoxHull :: struct {
|
||||
boxVertices: [8]HullVertex, //< Box vertices.
|
||||
boxPoints: [8]Vec3, //< Box points.
|
||||
boxEdges: [24]HullHalfEdge, //< Box half-edges.
|
||||
boxFaces: [6]HullFace, //< Box faces.
|
||||
padding: [2]u8, //< Explicit padding, see b3HullData::padding.
|
||||
boxPlanes: [6]Plane, //< Box face planes.
|
||||
boxFaces: [6]HullFace, //< Box faces.
|
||||
padding: [10]u8, //< Explicit padding, see b3HullData::padding.
|
||||
vx: [8]f32, //< vertex x
|
||||
vy: [8]f32, //< vertex y
|
||||
vz: [8]f32, //< vertex z
|
||||
nx: [8]f32, //< normal x, padded to multiple of 4
|
||||
ny: [8]f32, //< normal y, padded to multiple of 4
|
||||
nz: [8]f32, //< normal z, padded to multiple of 4
|
||||
}
|
||||
|
||||
/**@}*/ // hull
|
||||
@@ -1989,7 +2004,7 @@ MeshDef :: struct {
|
||||
// Triangle vertices
|
||||
vertices: [^]Vec3 `fmt:"v,vertexCount"`,
|
||||
|
||||
// Triangle vertex indices. 3 for each triangle.
|
||||
// Triangle vertex indices. 3 for each triangle. CCW winding.
|
||||
indices: [^]i32,
|
||||
|
||||
// Triangle material index. 1 per triangle. Indexes into b3ShapeDef::materials.
|
||||
@@ -2333,13 +2348,13 @@ CompoundDef :: struct {
|
||||
}
|
||||
|
||||
// The compound version depends on the tree, mesh, and hull versions.
|
||||
COMPOUND_VERSION :: 0x830778DB07086EB4 ~ DYNAMIC_TREE_VERSION ~ MESH_VERSION ~ HULL_VERSION
|
||||
COMPOUND_VERSION :: 0xB11DCE70FAD5622B ~ DYNAMIC_TREE_VERSION ~ MESH_VERSION ~ HULL_VERSION
|
||||
|
||||
// Meshes used in compounds have limited space for materials. If you have
|
||||
// a mesh with many materials, you can use it outside of the compound.
|
||||
MAX_COMPOUND_MESH_MATERIALS :: 4
|
||||
|
||||
// The runtime data for a baked compound shape. This is a potentially large yet highly optimized
|
||||
// The data for a baked compound shape. This is a potentially large yet highly optimized
|
||||
// data structure. It can contain thousands of child shapes, yet at runtime it populates
|
||||
// into the world as a single shape in the runtime broad-phase.
|
||||
// This data structure has data living off the end and must be accessed using offsets.
|
||||
@@ -2851,7 +2866,9 @@ DebugShape :: struct {
|
||||
// Callbacks receive world coordinates. In large world mode the translation is double precision so
|
||||
// it stays accurate far from the origin. Shift into your own camera frame inside the callbacks.
|
||||
DebugDraw :: struct {
|
||||
// Draws a shape and returns true if drawing should continue
|
||||
// Draws a user shape. The userShape pointer is owned by the application and is known to Box3D as
|
||||
// an opaque pointer returned from b3CreateDebugShapeCallback. When this is called the drawn shape has
|
||||
// passed a culling test against drawingBounds below.
|
||||
DrawShapeFcn: proc "c" (userShape: rawptr, transform: WorldTransform, color: HexColor, ctx: rawptr) -> bool,
|
||||
|
||||
// Draw a line segment.
|
||||
@@ -2912,7 +2929,7 @@ DebugDraw :: struct {
|
||||
drawContacts: bool,
|
||||
|
||||
// Draw contact anchor A or B
|
||||
drawAnchorA: c.int,
|
||||
drawAnchorA: bool,
|
||||
|
||||
// Option to visualize the graph coloring used for contacts and joints
|
||||
drawGraphColors: bool,
|
||||
@@ -2926,9 +2943,6 @@ DebugDraw :: struct {
|
||||
// Option to draw contact normal forces
|
||||
drawContactForces: bool,
|
||||
|
||||
// Option to draw contact friction forces
|
||||
drawFrictionForces: bool,
|
||||
|
||||
// Option to draw islands as bounding boxes
|
||||
drawIslands: bool,
|
||||
|
||||
|
||||
BIN
vendor/box3d/lib/box3d.lib
vendored
BIN
vendor/box3d/lib/box3d.lib
vendored
Binary file not shown.
4
vendor/box3d/lib/darwin/libbox3d.a
vendored
4
vendor/box3d/lib/darwin/libbox3d.a
vendored
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6b000b0b69028af14d6a481b6bf6ca3b70d2c3a29cd9e73eab8b96e0deffa07d
|
||||
size 2524600
|
||||
oid sha256:92ad74c3967667704cb7589ae6c64612a66112ea12ce4462b849f7aac4bc689f
|
||||
size 2551264
|
||||
|
||||
4
vendor/box3d/lib/linux-amd64/libbox3d.a
vendored
4
vendor/box3d/lib/linux-amd64/libbox3d.a
vendored
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:948651aca53740a4a47a8251b68982b839fa62da91689035132be1c273cbd7da
|
||||
size 1536158
|
||||
oid sha256:d698fbbe655db1d1ec6f52dcc23bfed4a2c981fa88040ba449f9f83609edba88
|
||||
size 3137140
|
||||
|
||||
12
vendor/box3d/src/include/box3d/base.h
vendored
12
vendor/box3d/src/include/box3d/base.h
vendored
@@ -38,7 +38,7 @@
|
||||
#ifdef __cplusplus
|
||||
#define B3_API extern "C" BOX3D_EXPORT
|
||||
#define B3_INLINE inline
|
||||
|
||||
#define B3_ALIGN_AS(N) alignas(N)
|
||||
#if defined( _MSC_VER )
|
||||
#define B3_FORCE_INLINE __forceinline
|
||||
#elif defined( __GNUC__ ) || defined( __clang__ )
|
||||
@@ -52,6 +52,7 @@
|
||||
#else
|
||||
#define B3_API BOX3D_EXPORT
|
||||
#define B3_INLINE static inline
|
||||
#define B3_ALIGN_AS(N) _Alignas(N)
|
||||
|
||||
#if defined( _MSC_VER )
|
||||
#define B3_FORCE_INLINE static __forceinline
|
||||
@@ -67,6 +68,13 @@
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
// This is used to validate arguments for functions similar to printf.
|
||||
#if defined( __GNUC__ ) || defined( __clang__ )
|
||||
#define B3_PRINTF_FORMAT( INDEX1, INDEX2 ) __attribute__( ( format( printf, INDEX1, INDEX2 ) ) )
|
||||
#else
|
||||
#define B3_PRINTF_FORMAT( INDEX1, INDEX2 )
|
||||
#endif
|
||||
|
||||
#if defined( BOX3D_VALIDATE ) && !defined( NDEBUG )
|
||||
#define B3_ENABLE_VALIDATION 1
|
||||
#else
|
||||
@@ -124,7 +132,7 @@ B3_API void b3SetAssertFcn( b3AssertFcn* assertFcn );
|
||||
/// Internal assertion handler. Allows for host intervention.
|
||||
B3_API int b3InternalAssert( const char* condition, const char* fileName, int lineNumber );
|
||||
/// Assert that a condition is true.
|
||||
#define B3_ASSERT( condition ) \
|
||||
#define B3_ASSERT( condition ) \
|
||||
( (void)( ( !!( condition ) ) || ( b3InternalAssert( #condition, __FILE__, (int)( __LINE__ ) ), 0 ) ) )
|
||||
#else
|
||||
#define B3_ASSERT( ... ) ( (void)0 )
|
||||
|
||||
34
vendor/box3d/src/include/box3d/box3d.h
vendored
34
vendor/box3d/src/include/box3d/box3d.h
vendored
@@ -524,31 +524,31 @@ B3_API b3WorldTransform b3Body_GetTransform( b3BodyId bodyId );
|
||||
|
||||
/// Set the world transform of a body. This acts as a teleport and is fairly expensive.
|
||||
/// @note Generally you should create a body with the intended transform.
|
||||
/// @see b3BodyDef::position and b3BodyDef::rotation
|
||||
/// @see b3BodyDef::position and b3BodyDef::rotation.
|
||||
B3_API void b3Body_SetTransform( b3BodyId bodyId, b3Pos position, b3Quat rotation );
|
||||
|
||||
/// Get a local point on a body given a world point
|
||||
/// Get a local point on a body given a world point.
|
||||
B3_API b3Vec3 b3Body_GetLocalPoint( b3BodyId bodyId, b3Pos worldPoint );
|
||||
|
||||
/// Get a world point on a body given a local point
|
||||
/// Get a world point on a body given a local point.
|
||||
B3_API b3Pos b3Body_GetWorldPoint( b3BodyId bodyId, b3Vec3 localPoint );
|
||||
|
||||
/// Get a local vector on a body given a world vector
|
||||
/// Get a local vector on a body given a world vector.
|
||||
B3_API b3Vec3 b3Body_GetLocalVector( b3BodyId bodyId, b3Vec3 worldVector );
|
||||
|
||||
/// Get a world vector on a body given a local vector
|
||||
/// Get a world vector on a body given a local vector.
|
||||
B3_API b3Vec3 b3Body_GetWorldVector( b3BodyId bodyId, b3Vec3 localVector );
|
||||
|
||||
/// Get the linear velocity of a body's center of mass. Usually in meters per second.
|
||||
B3_API b3Vec3 b3Body_GetLinearVelocity( b3BodyId bodyId );
|
||||
|
||||
/// Get the angular velocity of a body in radians per second
|
||||
/// Get the angular velocity of a body in radians per second.
|
||||
B3_API b3Vec3 b3Body_GetAngularVelocity( b3BodyId bodyId );
|
||||
|
||||
/// Set the linear velocity of a body. Usually in meters per second.
|
||||
/// Set the linear velocity of a body at the center of mass. Usually in meters per second.
|
||||
B3_API void b3Body_SetLinearVelocity( b3BodyId bodyId, b3Vec3 linearVelocity );
|
||||
|
||||
/// Set the angular velocity of a body in radians per second
|
||||
/// Set the angular velocity of a body in radians per second.
|
||||
B3_API void b3Body_SetAngularVelocity( b3BodyId bodyId, b3Vec3 angularVelocity );
|
||||
|
||||
/// Set the velocity to reach the given transform after a given time step.
|
||||
@@ -710,6 +710,14 @@ B3_API void b3Body_SetBullet( b3BodyId bodyId, bool flag );
|
||||
/// Is this body a bullet?
|
||||
B3_API bool b3Body_IsBullet( b3BodyId bodyId );
|
||||
|
||||
/// Allow this body to rotate fast. Useful for axially symmetric bodies, such as vehicle wheels.
|
||||
/// Normally rotation speed is clamped to improve CCD. However, this clamping is unnecessary for
|
||||
/// bodies that only rotate fast around an axis of symmetry.
|
||||
B3_API void b3Body_AllowFastRotation( b3BodyId bodyId, bool flag );
|
||||
|
||||
/// Is this body allowed to rotate fast?
|
||||
B3_API bool b3Body_IsFastRotationAllowed( b3BodyId bodyId );
|
||||
|
||||
/// Enable or disable contact recycling for this body. Contact recycling is a performance optimization
|
||||
/// that reuses contact manifolds when bodies move slightly. Disabling it can avoid ghost collisions
|
||||
/// on characters at the cost of higher per-step work. Existing contacts retain their prior setting;
|
||||
@@ -777,6 +785,10 @@ B3_API int b3Body_CollideMover( b3BodyId bodyId, b3BodyPlaneResult* bodyPlanes,
|
||||
* @defgroup shape Shape
|
||||
* Functions to create, destroy, and access.
|
||||
* Shapes bind raw geometry to bodies and hold material properties including friction and restitution.
|
||||
* You may add multiple shapes to a single body. There are no hard limits on shape count per body.
|
||||
*
|
||||
* When you create a shape on a body the center of mass moves. This can lead to the body linear velocity
|
||||
* changing if the angular velocity is non-zero.
|
||||
* @{
|
||||
*/
|
||||
|
||||
@@ -816,8 +828,10 @@ B3_API b3ShapeId b3CreateMeshShape( b3BodyId bodyId, const b3ShapeDef* def, cons
|
||||
/// @return the shape id for accessing the shape
|
||||
B3_API b3ShapeId b3CreateHeightFieldShape( b3BodyId bodyId, const b3ShapeDef* def, const b3HeightFieldData* heightField );
|
||||
|
||||
/// Compound shapes are only allowed on static bodies.
|
||||
B3_API b3ShapeId b3CreateCompoundShape( b3BodyId bodyId, b3ShapeDef* def, const b3CompoundData* compound );
|
||||
/// Baked compound shapes are only allowed on static bodies.
|
||||
/// Note: runtime compounds are achieved by adding multiple shapes to a body.
|
||||
/// Runtime compounds can be dynamic and/or kinematic.
|
||||
B3_API b3ShapeId b3CreateBakedCompoundShape( b3BodyId bodyId, b3ShapeDef* def, const b3CompoundData* compound );
|
||||
|
||||
/// Destroy a shape. You may defer the body mass update which can improve performance if several shapes on a
|
||||
/// body are destroyed at once.
|
||||
|
||||
65
vendor/box3d/src/include/box3d/collision.h
vendored
65
vendor/box3d/src/include/box3d/collision.h
vendored
@@ -44,7 +44,8 @@ B3_API uint64_t b3DynamicTree_GetCategoryBits( b3DynamicTree* tree, int proxyId
|
||||
B3_API b3TreeStats b3DynamicTree_Query( const b3DynamicTree* tree, b3AABB aabb, uint64_t maskBits, bool requireAllBits,
|
||||
b3TreeQueryCallbackFcn* callback, void* context );
|
||||
|
||||
/// Query an AABB for the closest object. The callback function is called for each proxy that might be closest to the supplied point.
|
||||
/// Query an AABB for the closest object. The callback function is called for each proxy that might be closest to the supplied
|
||||
/// point.
|
||||
/// @param tree the dynamic tree to query
|
||||
/// @param point the query point
|
||||
/// @param maskBits nodes are skipped if the bit-wise AND with the node category bits is zero
|
||||
@@ -163,6 +164,17 @@ B3_INLINE const b3HullHalfEdge* b3GetHullEdges( const b3HullData* hull )
|
||||
return (const b3HullHalfEdge*)( (intptr_t)hull + hull->edgeOffset );
|
||||
}
|
||||
|
||||
/// Get read only hull planes.
|
||||
B3_INLINE const b3Plane* b3GetHullPlanes( const b3HullData* hull )
|
||||
{
|
||||
if ( hull->planeOffset == 0 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (const b3Plane*)( (intptr_t)hull + hull->planeOffset );
|
||||
}
|
||||
|
||||
/// Get read only hull faces.
|
||||
B3_INLINE const b3HullFace* b3GetHullFaces( const b3HullData* hull )
|
||||
{
|
||||
@@ -174,15 +186,30 @@ B3_INLINE const b3HullFace* b3GetHullFaces( const b3HullData* hull )
|
||||
return (const b3HullFace*)( (intptr_t)hull + hull->faceOffset );
|
||||
}
|
||||
|
||||
/// Get read only hull planes.
|
||||
B3_INLINE const b3Plane* b3GetHullPlanes( const b3HullData* hull )
|
||||
/// Get read only SOA vertices. This is an array of vertices with all x values,
|
||||
/// y values, and z values as separate arrays. The array lengths are padded to
|
||||
/// a multiple of 4. The padded values are repeats of the first value.
|
||||
B3_INLINE const float* b3GetHullSoaVertices( const b3HullData* hull )
|
||||
{
|
||||
if ( hull->planeOffset == 0 )
|
||||
if ( hull->soaVertexOffset == 0 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (const b3Plane*)( (intptr_t)hull + hull->planeOffset );
|
||||
return (const float*)( (intptr_t)hull + hull->soaVertexOffset );
|
||||
}
|
||||
|
||||
/// Get read only SOA unit normal vectors. This is an array of normals with all x values,
|
||||
/// y values, and z values as separate arrays. The array lengths are padded to
|
||||
/// a multiple of 4. The padded values are repeats of the first value.
|
||||
B3_INLINE const float* b3GetHullSoaNormals( const b3HullData* hull )
|
||||
{
|
||||
if ( hull->soaNormalOffset == 0 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return (const float*)( (intptr_t)hull + hull->soaNormalOffset );
|
||||
}
|
||||
|
||||
/// Create a tessellated cylinder as a hull.
|
||||
@@ -426,14 +453,12 @@ B3_API b3CompoundData* b3CreateCompound( const b3CompoundDef* def );
|
||||
/// Destroy a compound shape.
|
||||
B3_API void b3DestroyCompound( b3CompoundData* compound );
|
||||
|
||||
/// If bytes is null then this returns the number of required bytes. This clones all the
|
||||
/// data into the bytes buffer. This is expected to run offline or asynchronously.
|
||||
/// This mutates the compound to nullify pointers, leaving the compound in an unusable state.
|
||||
/// Cast the provided compound data to bytes, setting the internal pointers to null.
|
||||
/// Use this before serializing the compound bytes.
|
||||
B3_API uint8_t* b3ConvertCompoundToBytes( b3CompoundData* compound );
|
||||
|
||||
/// Convert bytes to compound. This does not clone. The bytes must remain in scope while the
|
||||
/// compound is used. This is done to improve run-time performance and allow for instancing.
|
||||
/// The bytes are mutated to fixup pointers.
|
||||
/// Cast the provided bytes to compound data, setting up internal pointers.
|
||||
/// Use this after de-serializing the compound bytes.
|
||||
B3_API b3CompoundData* b3ConvertBytesToCompound( uint8_t* bytes, int byteCount );
|
||||
|
||||
/**@}*/ // compound
|
||||
@@ -610,17 +635,17 @@ B3_API void b3CollideHullAndCapsule( b3LocalManifold* manifold, int capacity, co
|
||||
B3_API void b3CollideHulls( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, const b3HullData* hullB,
|
||||
b3Transform transformBtoA, b3SATCache* cache );
|
||||
|
||||
/// Collide a capsule and a triangle.
|
||||
B3_API void b3CollideCapsuleAndTriangle( b3LocalManifold* manifold, int capacity, const b3Capsule* capsuleA,
|
||||
const b3Vec3* triangleB, b3SimplexCache* cache );
|
||||
/// Collide a triangle and capsule. Normal points from triangle to capsule.
|
||||
B3_API void b3CollideTriangleAndCapsule( b3LocalManifold* manifold, int capacity, const b3Vec3* triangleA,
|
||||
const b3Capsule* capsuleB, b3SimplexCache* cache );
|
||||
|
||||
/// Collide a hull and a triangle.
|
||||
B3_API void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, b3Vec3 v1, b3Vec3 v2,
|
||||
b3Vec3 v3, int triangleFlags, b3SATCache* cache, bool enableSpeculative );
|
||||
/// Collide a triangle and hull. Normal points from triangle to hull.
|
||||
B3_API void b3CollideTriangleAndHull( b3LocalManifold* manifold, int capacity, b3Vec3 v1, b3Vec3 v2, b3Vec3 v3, int triangleFlags,
|
||||
const b3HullData* hullB, b3SATCache* cache, bool enableSpeculative );
|
||||
|
||||
/// Collide a sphere and a triangle.
|
||||
B3_API void b3CollideSphereAndTriangle( b3LocalManifold* manifold, int capacity, const b3Sphere* sphereA,
|
||||
const b3Vec3* triangleB );
|
||||
/// Collide a triangle and sphere. Normal points from triangle to sphere.
|
||||
B3_API void b3CollideTriangleAndSphere( b3LocalManifold* manifold, int capacity, const b3Vec3* triangleA,
|
||||
const b3Sphere* sphereB );
|
||||
|
||||
/**@}*/ // collision
|
||||
|
||||
|
||||
4
vendor/box3d/src/include/box3d/config.h
vendored
4
vendor/box3d/src/include/box3d/config.h
vendored
@@ -29,3 +29,7 @@
|
||||
// so this suits embedding more than shipping Box3D as its own DLL.
|
||||
//#define BOX3D_EXPORT MYENGINE_API
|
||||
|
||||
// Enable assertions when NDEBUG is defined
|
||||
// #define B3_ENABLE_ASSERT
|
||||
|
||||
// #define B3_RESTITUTION_ITERATIONS
|
||||
|
||||
31
vendor/box3d/src/include/box3d/constants.h
vendored
31
vendor/box3d/src/include/box3d/constants.h
vendored
@@ -52,8 +52,14 @@ B3_API float b3GetStallThreshold( void );
|
||||
// @warning modifying this can have a significant impact on stability
|
||||
#define B3_LINEAR_SLOP ( 0.005f * b3GetLengthUnitsPerMeter() )
|
||||
|
||||
/// The minimum length of a capsules. Very short capsules should be created as spheres
|
||||
/// to avoid numerical problems.
|
||||
#define B3_MIN_CAPSULE_LENGTH ( B3_LINEAR_SLOP )
|
||||
|
||||
/// Minimum contact point friction weight, lower bound for speculative points. Made small
|
||||
/// enough to be washed away by weights that hit 1.
|
||||
#define B3_MIN_FRICTION_WEIGHT ( 1e-10f )
|
||||
|
||||
/// The distance between shapes where they are considered overlapped. This is needed
|
||||
/// because GJK may return small positive values for overlapped shapes in degenerate
|
||||
/// configurations.
|
||||
@@ -100,11 +106,34 @@ B3_API float b3GetStallThreshold( void );
|
||||
/// The maximum number of contact points between two touching shapes.
|
||||
#define B3_MAX_MANIFOLD_POINTS 4
|
||||
|
||||
/// The number of iterations for gyroscopic torques.
|
||||
#ifndef B3_GYROSCOPIC_ITERATIONS
|
||||
#define B3_GYROSCOPIC_ITERATIONS 1
|
||||
#endif
|
||||
|
||||
/// The maximum number of convex hull vertices. This is fixed for performance reasons.
|
||||
#define B3_MAX_HULL_VERTICES 128
|
||||
|
||||
/// The maximum number of convex hull faces.
|
||||
#define B3_MAX_HULL_FACES 128
|
||||
|
||||
/// The maximum number of convex hull edges. Full edges, not half-edges.
|
||||
#define B3_MAX_HULL_EDGES 128
|
||||
|
||||
/// Relative tolerance used to determine if two edges are parallel.
|
||||
#define B3_PARALLEL_EDGE_TOL 0.005f
|
||||
|
||||
/// The maximum number points to use for shape cast proxies (swept point cloud).
|
||||
#define B3_MAX_SHAPE_CAST_POINTS 64
|
||||
#define B3_MAX_SHAPE_CAST_POINTS B3_MAX_HULL_VERTICES
|
||||
|
||||
/// These generous limits allow for easy hashing. See b3ShapePairKey.
|
||||
#define B3_SHAPE_POWER 22
|
||||
#define B3_CHILD_POWER ( 64 - 2 * B3_SHAPE_POWER )
|
||||
#define B3_MAX_SHAPES ( 1 << B3_SHAPE_POWER )
|
||||
#define B3_MAX_CHILD_SHAPES ( 1 << B3_CHILD_POWER )
|
||||
|
||||
/// Increase this if your application needs more accurate restitution. Doing so will
|
||||
/// slow down the simulation. Must be 1 or more.
|
||||
#ifndef B3_RESTITUTION_ITERATIONS
|
||||
#define B3_RESTITUTION_ITERATIONS 1
|
||||
#endif
|
||||
|
||||
7
vendor/box3d/src/include/box3d/id.h
vendored
7
vendor/box3d/src/include/box3d/id.h
vendored
@@ -96,13 +96,14 @@ static const b3JointId b3_nullJointId = B3_NULL_ID;
|
||||
static const b3ContactId b3_nullContactId = B3_NULL_ID;
|
||||
|
||||
/// Macro to determine if any id is null.
|
||||
#define B3_IS_NULL( id ) ( id.index1 == 0 )
|
||||
#define B3_IS_NULL( id ) ( ( id ).index1 == 0 )
|
||||
|
||||
/// Macro to determine if any id is non-null.
|
||||
#define B3_IS_NON_NULL( id ) ( id.index1 != 0 )
|
||||
#define B3_IS_NON_NULL( id ) ( ( id ).index1 != 0 )
|
||||
|
||||
/// Compare two ids for equality. Doesn't work for b3WorldId. Don't mix types.
|
||||
#define B3_ID_EQUALS( id1, id2 ) ( id1.index1 == id2.index1 && id1.world0 == id2.world0 && id1.generation == id2.generation )
|
||||
#define B3_ID_EQUALS( id1, id2 ) \
|
||||
( ( id1 ).index1 == ( id2 ).index1 && ( id1 ).world0 == ( id2 ).world0 && ( id1 ).generation == ( id2 ).generation )
|
||||
|
||||
/// Store a world id into a uint32_t.
|
||||
B3_ID_INLINE uint32_t b3StoreWorldId( b3WorldId id )
|
||||
|
||||
64
vendor/box3d/src/include/box3d/types.h
vendored
64
vendor/box3d/src/include/box3d/types.h
vendored
@@ -419,6 +419,9 @@ typedef struct b3SurfaceMaterial
|
||||
/// carry a b3DebugMaterial preset, see b3MakeDebugColor.
|
||||
/// @see b3HexColor
|
||||
uint32_t customColor;
|
||||
|
||||
/// Explicit padding. Must be zero.
|
||||
uint32_t padding;
|
||||
} b3SurfaceMaterial;
|
||||
|
||||
/// Use this to initialize your surface material
|
||||
@@ -490,6 +493,7 @@ typedef struct b3ShapeDef
|
||||
bool isSensor;
|
||||
|
||||
/// Enable sensor events for this shape. This applies to sensors and non-sensors. False by default, even for sensors.
|
||||
/// Only convex shapes may act as sensor visitors.
|
||||
bool enableSensorEvents;
|
||||
|
||||
/// Enable contact events for this shape. Only applies to kinematic and dynamic bodies. Ignored for sensors. False by default.
|
||||
@@ -1930,8 +1934,7 @@ typedef struct b3Capsule
|
||||
* @{
|
||||
*/
|
||||
|
||||
/// A hull vertex. Identified by a half-edge with this
|
||||
/// vertex as its tail.
|
||||
/// A hull vertex. Identified by a half-edge with this vertex as its tail.
|
||||
typedef struct b3HullVertex
|
||||
{
|
||||
/// A half-edge that has this vertex as the origin
|
||||
@@ -1965,7 +1968,7 @@ typedef struct b3HullFace
|
||||
} b3HullFace;
|
||||
|
||||
/// 64-bit hull version. Useful for validating serialized data.
|
||||
#define B3_HULL_VERSION 0x9D4716CE3793900Eull
|
||||
#define B3_HULL_VERSION 0xDA5150191B994C01ull
|
||||
|
||||
/// A convex hull.
|
||||
/// @note This data structure has data hanging off the end and cannot be directly copied.
|
||||
@@ -2016,11 +2019,17 @@ typedef struct b3HullData
|
||||
/// The face count. Hulls faces are convex polygons.
|
||||
int faceCount;
|
||||
|
||||
/// Offset of the face plane array in bytes from the struct address.
|
||||
int planeOffset;
|
||||
|
||||
/// Offset of the face array in bytes from the struct address.
|
||||
int faceOffset;
|
||||
|
||||
/// Offset of the face plane array in bytes from the struct address.
|
||||
int planeOffset;
|
||||
/// Offset of structure of array (SOA) vertices
|
||||
int soaVertexOffset;
|
||||
|
||||
/// Offset of structure of array (SOA) unit normal vectors
|
||||
int soaNormalOffset;
|
||||
|
||||
/// Explicit padding. Hull identity is a content hash and memcmp over raw bytes,
|
||||
/// so there must be no unnamed padding for struct copies to scramble.
|
||||
@@ -2035,9 +2044,15 @@ typedef struct b3BoxHull
|
||||
b3HullVertex boxVertices[8]; ///< Box vertices.
|
||||
b3Vec3 boxPoints[8]; ///< Box points.
|
||||
b3HullHalfEdge boxEdges[24]; ///< Box half-edges.
|
||||
b3HullFace boxFaces[6]; ///< Box faces.
|
||||
uint8_t padding[2]; ///< Explicit padding, see b3HullData::padding.
|
||||
b3Plane boxPlanes[6]; ///< Box face planes.
|
||||
b3HullFace boxFaces[6]; ///< Box faces.
|
||||
uint8_t padding[10]; ///< Explicit padding, see b3HullData::padding.
|
||||
float vx[8]; ///< vertex x
|
||||
float vy[8]; ///< vertex y
|
||||
float vz[8]; ///< vertex z
|
||||
float nx[8]; ///< normal x, padded to multiple of 4
|
||||
float ny[8]; ///< normal y, padded to multiple of 4
|
||||
float nz[8]; ///< normal z, padded to multiple of 4
|
||||
} b3BoxHull;
|
||||
|
||||
/**@}*/ // hull
|
||||
@@ -2048,13 +2063,13 @@ typedef struct b3BoxHull
|
||||
* @{
|
||||
*/
|
||||
|
||||
/// This is used to create a re-usable collision mesh
|
||||
/// This is used to create a re-usable collision mesh.
|
||||
typedef struct b3MeshDef
|
||||
{
|
||||
/// Triangle vertices
|
||||
b3Vec3* vertices;
|
||||
|
||||
/// Triangle vertex indices. 3 for each triangle.
|
||||
/// Triangle vertex indices. 3 for each triangle. CCW winding.
|
||||
int32_t* indices;
|
||||
|
||||
/// Triangle material index. 1 per triangle. Indexes into b3ShapeDef::materials.
|
||||
@@ -2407,14 +2422,14 @@ typedef struct b3CompoundDef
|
||||
int sphereCount;
|
||||
} b3CompoundDef;
|
||||
|
||||
/// The compound version depends on the tree, mesh, and hull versions.
|
||||
#define B3_COMPOUND_VERSION ( 0x830778DB07086EB4ull ^ B3_DYNAMIC_TREE_VERSION ^ B3_MESH_VERSION ^ B3_HULL_VERSION )
|
||||
/// The baked compound version depends on the tree, mesh, and hull versions.
|
||||
#define B3_COMPOUND_VERSION ( 0xB11DCE70FAD5622Bull ^ B3_DYNAMIC_TREE_VERSION ^ B3_MESH_VERSION ^ B3_HULL_VERSION )
|
||||
|
||||
/// Meshes used in compounds have limited space for materials. If you have
|
||||
/// a mesh with many materials, you can use it outside of the compound.
|
||||
#define B3_MAX_COMPOUND_MESH_MATERIALS 4
|
||||
|
||||
/// The runtime data for a baked compound shape. This is a potentially large yet highly optimized
|
||||
/// The data for a baked compound shape. This is a potentially large yet highly optimized
|
||||
/// data structure. It can contain thousands of child shapes, yet at runtime it populates
|
||||
/// into the world as a single shape in the runtime broad-phase.
|
||||
/// This data structure has data living off the end and must be accessed using offsets.
|
||||
@@ -2530,10 +2545,10 @@ typedef struct b3ChildShape
|
||||
/// Tagged union.
|
||||
union
|
||||
{
|
||||
b3Capsule capsule; ///< Capsule.
|
||||
b3Capsule capsule; ///< Capsule.
|
||||
const b3HullData* hull; ///< Hull.
|
||||
b3Mesh mesh; ///< Mesh.
|
||||
b3Sphere sphere; ///< Sphere.
|
||||
b3Mesh mesh; ///< Mesh.
|
||||
b3Sphere sphere; ///< Sphere.
|
||||
};
|
||||
|
||||
/// Transform of the shape into compound local space.
|
||||
@@ -2943,12 +2958,12 @@ typedef struct b3DebugShape
|
||||
/// Tagged union.
|
||||
union
|
||||
{
|
||||
const b3Capsule* capsule; ///< Capsule shape.
|
||||
const b3Capsule* capsule; ///< Capsule shape.
|
||||
const b3CompoundData* compound; ///< Compound shape.
|
||||
const b3HeightFieldData* heightField; ///< Height-field shape.
|
||||
const b3HullData* hull; ///< Convex hull shape.
|
||||
const b3Mesh* mesh; ///< Mesh shape with scale.
|
||||
const b3Sphere* sphere; ///< Sphere shape.
|
||||
const b3HullData* hull; ///< Convex hull shape.
|
||||
const b3Mesh* mesh; ///< Mesh shape with scale.
|
||||
const b3Sphere* sphere; ///< Sphere shape.
|
||||
};
|
||||
} b3DebugShape;
|
||||
|
||||
@@ -2957,8 +2972,10 @@ typedef struct b3DebugShape
|
||||
/// it stays accurate far from the origin. Shift into your own camera frame inside the callbacks.
|
||||
typedef struct b3DebugDraw
|
||||
{
|
||||
/// Draws a shape and returns true if drawing should continue
|
||||
bool ( *DrawShapeFcn )( void* userShape, b3WorldTransform transform, b3HexColor color, void* context );
|
||||
/// Draws a user shape. The userShape pointer is owned by the application and is known to Box3D as
|
||||
/// an opaque pointer returned from b3CreateDebugShapeCallback. When this is called the drawn shape has
|
||||
/// passed a culling test against drawingBounds below.
|
||||
void ( *DrawShapeFcn )( void* userShape, b3WorldTransform transform, b3HexColor color, void* context );
|
||||
|
||||
/// Draw a line segment.
|
||||
void ( *DrawSegmentFcn )( b3Pos p1, b3Pos p2, b3HexColor color, void* context );
|
||||
@@ -3018,7 +3035,7 @@ typedef struct b3DebugDraw
|
||||
bool drawContacts;
|
||||
|
||||
/// Draw contact anchor A or B
|
||||
int drawAnchorA;
|
||||
bool drawAnchorA;
|
||||
|
||||
/// Option to visualize the graph coloring used for contacts and joints
|
||||
bool drawGraphColors;
|
||||
@@ -3032,9 +3049,6 @@ typedef struct b3DebugDraw
|
||||
/// Option to draw contact normal forces
|
||||
bool drawContactForces;
|
||||
|
||||
/// Option to draw contact friction forces
|
||||
bool drawFrictionForces;
|
||||
|
||||
/// Option to draw islands as bounding boxes
|
||||
bool drawIslands;
|
||||
|
||||
|
||||
12
vendor/box3d/src/src/CMakeLists.txt
vendored
12
vendor/box3d/src/src/CMakeLists.txt
vendored
@@ -33,6 +33,7 @@ set(BOX3D_SOURCE_FILES
|
||||
dynamic_tree.c
|
||||
height_field.c
|
||||
hull.c
|
||||
hull.h
|
||||
id_pool.c
|
||||
id_pool.h
|
||||
island.c
|
||||
@@ -154,7 +155,8 @@ if (BOX3D_PROFILE)
|
||||
endif()
|
||||
|
||||
if(BOX3D_VALIDATE)
|
||||
message(STATUS "Box3D validation ON")
|
||||
# Only effective when NDEBUG is undefined, see base.h
|
||||
message(STATUS "Box3D validation ON (debug builds)")
|
||||
target_compile_definitions(box3d PRIVATE BOX3D_VALIDATE)
|
||||
endif()
|
||||
|
||||
@@ -169,8 +171,11 @@ if (BOX3D_DOUBLE_PRECISION)
|
||||
target_compile_definitions(box3d PUBLIC BOX3D_DOUBLE_PRECISION)
|
||||
endif()
|
||||
|
||||
# Enable asserts in release with debug info
|
||||
target_compile_definitions(box3d PUBLIC "$<$<CONFIG:RELWITHDEBINFO>:B3_ENABLE_ASSERT>")
|
||||
|
||||
if (MSVC)
|
||||
message(STATUS "Box3D on MSVC")
|
||||
message(STATUS "Box3D on MSVC")
|
||||
if (BUILD_SHARED_LIBS)
|
||||
# this is needed by DLL users to import Box3D symbols
|
||||
target_compile_definitions(box3d INTERFACE BOX3D_DLL)
|
||||
@@ -179,9 +184,6 @@ if (MSVC)
|
||||
# Visual Studio won't load the natvis unless it is in the project
|
||||
target_sources(box3d PRIVATE box3d.natvis)
|
||||
|
||||
# Enable asserts in release with debug info
|
||||
target_compile_definitions(box3d PUBLIC "$<$<CONFIG:RELWITHDEBINFO>:B3_ENABLE_ASSERT>")
|
||||
|
||||
# Warnings Wall is problematic in mixed C/C++, so using W4
|
||||
target_compile_options(box3d PRIVATE /W4)
|
||||
# target_compile_options(box3d PRIVATE /wd4820 /wd5045 /wd4061 /wd4711 /wd4514 /wd4365 /wd5219 /wd5039)
|
||||
|
||||
5
vendor/box3d/src/src/block_allocator.c
vendored
5
vendor/box3d/src/src/block_allocator.c
vendored
@@ -9,6 +9,10 @@ b3BlockAllocator b3CreateBlockAllocator( int elementSize, int initialCount )
|
||||
{
|
||||
B3_ASSERT( elementSize >= (int)sizeof( void* ) );
|
||||
|
||||
// The free list stores a pointer inside each element, so the stride must keep every element aligned.
|
||||
// Blocks come from b3Alloc, so a stride that is a multiple of B3_ALIGNMENT aligns all elements.
|
||||
elementSize = ( ( elementSize - 1 ) | ( B3_ALIGNMENT - 1 ) ) + 1;
|
||||
|
||||
b3BlockAllocator allocator = { 0 };
|
||||
|
||||
b3Array_Create( allocator.blocks );
|
||||
@@ -81,6 +85,7 @@ void b3FreeElement( b3BlockAllocator* allocator, void* element )
|
||||
B3_ASSERT( allocator != NULL );
|
||||
B3_ASSERT( element != NULL );
|
||||
B3_ASSERT( allocator->allocationCount > 0 );
|
||||
B3_ASSERT( ( (uintptr_t)element & ( B3_ALIGNMENT - 1 ) ) == 0 );
|
||||
|
||||
allocator->allocationCount -= 1;
|
||||
|
||||
|
||||
2
vendor/box3d/src/src/block_allocator.h
vendored
2
vendor/box3d/src/src/block_allocator.h
vendored
@@ -26,7 +26,7 @@ typedef struct b3BlockAllocator
|
||||
int allocationCount;
|
||||
} b3BlockAllocator;
|
||||
|
||||
// Element must be large enough to hold a pointer
|
||||
// Element must be large enough to hold a pointer. The element size is rounded up to B3_ALIGNMENT.
|
||||
b3BlockAllocator b3CreateBlockAllocator( int elementSize, int initialCount );
|
||||
void b3DestroyBlockAllocator( b3BlockAllocator* allocator );
|
||||
|
||||
|
||||
33
vendor/box3d/src/src/body.c
vendored
33
vendor/box3d/src/src/body.c
vendored
@@ -253,8 +253,6 @@ b3BodyId b3CreateBody( b3WorldId worldId, const b3BodyDef* def )
|
||||
bodyState->angularVelocity = def->angularVelocity;
|
||||
bodyState->deltaRotation = b3Quat_identity;
|
||||
bodyState->flags = bodySim->flags;
|
||||
|
||||
bodySim->maxAngularVelocity = b3Length( def->angularVelocity ) + 5.0f;
|
||||
}
|
||||
|
||||
if ( bodyId == world->bodies.count )
|
||||
@@ -2305,6 +2303,37 @@ bool b3Body_IsBullet( b3BodyId bodyId )
|
||||
return ( body->flags & b3_isBullet ) != 0;
|
||||
}
|
||||
|
||||
void b3Body_AllowFastRotation(b3BodyId bodyId, bool flag)
|
||||
{
|
||||
b3World* world = b3GetUnlockedWorld( bodyId.world0 );
|
||||
if ( world == NULL )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
B3_REC( world, BodyAllowFastRotation, bodyId, flag );
|
||||
|
||||
uint32_t newFlag = flag ? b3_allowFastRotation : 0;
|
||||
|
||||
b3Body* body = b3GetBodyFullId( world, bodyId );
|
||||
if ( ( body->flags & b3_allowFastRotation ) == newFlag )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
body->flags &= ~b3_allowFastRotation;
|
||||
body->flags |= newFlag;
|
||||
|
||||
b3SyncBodyFlags( world, body );
|
||||
}
|
||||
|
||||
bool b3Body_IsFastRotationAllowed(b3BodyId bodyId)
|
||||
{
|
||||
b3World* world = b3GetWorld( bodyId.world0 );
|
||||
b3Body* body = b3GetBodyFullId( world, bodyId );
|
||||
return ( body->flags & b3_allowFastRotation ) != 0;
|
||||
}
|
||||
|
||||
void b3Body_EnableContactRecycling( b3BodyId bodyId, bool flag )
|
||||
{
|
||||
b3World* world = b3GetUnlockedWorld( bodyId.world0 );
|
||||
|
||||
1
vendor/box3d/src/src/body.h
vendored
1
vendor/box3d/src/src/body.h
vendored
@@ -208,7 +208,6 @@ typedef struct b3BodySim
|
||||
|
||||
float minExtent;
|
||||
b3Vec3 maxExtent;
|
||||
float maxAngularVelocity;
|
||||
float linearDamping;
|
||||
float angularDamping;
|
||||
float gravityScale;
|
||||
|
||||
75
vendor/box3d/src/src/compound.c
vendored
75
vendor/box3d/src/src/compound.c
vendored
@@ -3,7 +3,7 @@
|
||||
|
||||
#include "compound.h"
|
||||
|
||||
#include "hull_map.h"
|
||||
#include "hull.h"
|
||||
#include "math_internal.h"
|
||||
#include "shape.h"
|
||||
|
||||
@@ -226,13 +226,19 @@ static bool b3CompareMeshes( const b3MeshData* mesh1, const b3MeshData* mesh2 )
|
||||
#define FREE_FN b3Free
|
||||
#include "verstable.h"
|
||||
|
||||
_Static_assert( sizeof( b3SurfaceMaterial ) == 40, "review padding" );
|
||||
|
||||
static inline uint64_t b3HashMaterial( const b3SurfaceMaterial* material )
|
||||
{
|
||||
B3_ASSERT( material->padding == 0 );
|
||||
|
||||
return vt_wyhash( material, sizeof( b3SurfaceMaterial ) );
|
||||
}
|
||||
|
||||
static bool b3CompareMaterials( const b3SurfaceMaterial* mat1, const b3SurfaceMaterial* mat2 )
|
||||
{
|
||||
B3_ASSERT( mat1->padding == 0 && mat2->padding == 0);
|
||||
|
||||
if ( mat1 == mat2 )
|
||||
{
|
||||
return true;
|
||||
@@ -276,7 +282,7 @@ b3CompoundData* b3CreateCompound( const b3CompoundDef* def )
|
||||
int sphereCount = def->sphereCount;
|
||||
b3CompoundSphere* sphereInstances = b3AllocZeroed( sphereCount * sizeof( b3CompoundSphere ) );
|
||||
|
||||
// Determine material capacity
|
||||
// Determine material capacity.
|
||||
int materialCapacity = convexCount;
|
||||
for ( int i = 0; i < def->meshCount; ++i )
|
||||
{
|
||||
@@ -296,10 +302,10 @@ b3CompoundData* b3CreateCompound( const b3CompoundDef* def )
|
||||
const b3CompoundCapsuleDef* capsuleDef = def->capsules + i;
|
||||
capsuleInstances[i].capsule = capsuleDef->capsule;
|
||||
|
||||
// Look for an existing material
|
||||
// Look for an existing material.
|
||||
b3MaterialMap_itr materialItr = b3MaterialMap_get_or_insert( &materialMap, &capsuleDef->material, materialCount );
|
||||
|
||||
// Get the shared material index
|
||||
// Get the shared material index.
|
||||
int materialIndex = materialItr.data->val;
|
||||
capsuleInstances[i].materialIndex = materialIndex;
|
||||
|
||||
@@ -333,10 +339,10 @@ b3CompoundData* b3CreateCompound( const b3CompoundDef* def )
|
||||
b3DynamicTree_CreateProxy( &tree, aabb, ~0ull, childIndex );
|
||||
childIndex += 1;
|
||||
|
||||
// Look for an existing material
|
||||
// Look for an existing material.
|
||||
b3MaterialMap_itr materialItr = b3MaterialMap_get_or_insert( &materialMap, &hullDef->material, materialCount );
|
||||
|
||||
// Get the shared material index
|
||||
// Get the shared material index.
|
||||
int materialIndex = materialItr.data->val;
|
||||
hullInstances[i].materialIndex = materialIndex;
|
||||
|
||||
@@ -395,11 +401,11 @@ b3CompoundData* b3CreateCompound( const b3CompoundDef* def )
|
||||
|
||||
for ( int j = 0; j < meshDef->materialCount; ++j )
|
||||
{
|
||||
// Look for an existing material
|
||||
// Look for an existing material.
|
||||
b3MaterialMap_itr materialItr =
|
||||
b3MaterialMap_get_or_insert( &materialMap, &meshDef->materials[j], materialCount );
|
||||
|
||||
// Get the shared material index
|
||||
// Get the shared material index.
|
||||
int materialIndex = materialItr.data->val;
|
||||
meshInstances[i].materialIndices[j] = materialIndex;
|
||||
|
||||
@@ -411,17 +417,17 @@ b3CompoundData* b3CreateCompound( const b3CompoundDef* def )
|
||||
}
|
||||
}
|
||||
|
||||
// Look for an existing matching mesh
|
||||
// Look for an existing matching mesh.
|
||||
b3MeshMap_itr itr = b3MeshMap_get_or_insert( &meshMap, meshData, sharedMeshCount );
|
||||
|
||||
// Get the shared mesh index
|
||||
// Get the shared mesh index.
|
||||
int sharedMeshIndex = itr.data->val;
|
||||
|
||||
// Create mesh instance
|
||||
// Create mesh instance.
|
||||
meshInstances[i].transform = def->meshes[i].transform;
|
||||
meshInstances[i].scale = def->meshes[i].scale;
|
||||
|
||||
// The offset isn't known yet, so store the index of the shared mesh
|
||||
// The offset isn't known yet, so store the index of the shared mesh.
|
||||
meshInstances[i].meshOffset = sharedMeshIndex;
|
||||
|
||||
// Is this a new mesh?
|
||||
@@ -443,10 +449,10 @@ b3CompoundData* b3CreateCompound( const b3CompoundDef* def )
|
||||
const b3CompoundSphereDef* sphereDef = def->spheres + i;
|
||||
sphereInstances[i].sphere = sphereDef->sphere;
|
||||
|
||||
// Look for an existing material
|
||||
// Look for an existing material.
|
||||
b3MaterialMap_itr materialItr = b3MaterialMap_get_or_insert( &materialMap, &sphereDef->material, materialCount );
|
||||
|
||||
// Get the shared material index
|
||||
// Get the shared material index.
|
||||
int materialIndex = materialItr.data->val;
|
||||
sphereInstances[i].materialIndex = materialIndex;
|
||||
|
||||
@@ -467,58 +473,55 @@ b3CompoundData* b3CreateCompound( const b3CompoundDef* def )
|
||||
|
||||
b3DynamicTree_Rebuild( &tree, true );
|
||||
|
||||
int byteCount = sizeof( b3CompoundData );
|
||||
|
||||
// Tree nodes - todo 64 byte alignment
|
||||
int nodeOffset = byteCount;
|
||||
byteCount += tree.nodeCapacity * sizeof( b3TreeNode );
|
||||
|
||||
int materialOffset = byteCount;
|
||||
byteCount += materialCount * sizeof( b3SurfaceMaterial );
|
||||
|
||||
int capsuleOffset = byteCount;
|
||||
byteCount += def->capsuleCount * sizeof( b3CompoundCapsule );
|
||||
// Tree nodes
|
||||
size_t byteCount = b3AlignUp8( sizeof( b3CompoundData ) );
|
||||
int nodeOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( tree.nodeCapacity * sizeof( b3TreeNode ) );
|
||||
int materialOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( materialCount * sizeof( b3SurfaceMaterial ) );
|
||||
int capsuleOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( def->capsuleCount * sizeof( b3CompoundCapsule ) );
|
||||
|
||||
// Hull data layout has another level of indirection to allow for tight data packing
|
||||
// 1. hull instance array : hull count array of b3HullInstance with individual hull transforms and offsets
|
||||
// 2. heterogeneous array of shared hull data : each shared hull can have a different byte count, so direct indexing is not
|
||||
// possible
|
||||
int hullArrayOffset = byteCount;
|
||||
int hullArrayOffset = (int)byteCount;
|
||||
|
||||
// Array of hull instances
|
||||
byteCount += hullCount * sizeof( b3HullInstance );
|
||||
byteCount += b3AlignUp8( hullCount * sizeof( b3HullInstance ) );
|
||||
|
||||
// Packed shared hull blobs
|
||||
for ( int i = 0; i < sharedHullCount; ++i )
|
||||
{
|
||||
sharedHulls[i].hullOffset = byteCount;
|
||||
byteCount += sharedHulls[i].hull->byteCount;
|
||||
sharedHulls[i].hullOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( sharedHulls[i].hull->byteCount );
|
||||
}
|
||||
|
||||
// Mesh data layout has another level of indirection to allow for tight data packing
|
||||
// 1. mesh instance array : mesh count array of b3MeshInstance with individual mesh transform, scale, and offset
|
||||
// 2. heterogeneous array of shared mesh data : each shared mesh can have a different byte count, so direct indexing is not
|
||||
// possible
|
||||
int meshArrayOffset = byteCount;
|
||||
int meshArrayOffset = (int)byteCount;
|
||||
|
||||
// Array of mesh instances
|
||||
byteCount += meshCount * sizeof( b3MeshInstance );
|
||||
byteCount += b3AlignUp8( meshCount * sizeof( b3MeshInstance ) );
|
||||
|
||||
// Packed shared mesh blobs
|
||||
for ( int i = 0; i < sharedMeshCount; ++i )
|
||||
{
|
||||
sharedMeshes[i].meshOffset = byteCount;
|
||||
byteCount += sharedMeshes[i].meshData->byteCount;
|
||||
sharedMeshes[i].meshOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( sharedMeshes[i].meshData->byteCount );
|
||||
}
|
||||
|
||||
int sphereOffset = byteCount;
|
||||
byteCount += def->sphereCount * sizeof( b3CompoundSphere );
|
||||
int sphereOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( def->sphereCount * sizeof( b3CompoundSphere ) );
|
||||
|
||||
b3CompoundData* compound = b3Alloc( byteCount );
|
||||
memset( compound, 0, byteCount );
|
||||
|
||||
compound->version = B3_COMPOUND_VERSION;
|
||||
compound->byteCount = byteCount;
|
||||
compound->byteCount = (int)byteCount;
|
||||
compound->nodeOffset = nodeOffset;
|
||||
memcpy( &compound->tree, &tree, sizeof( b3DynamicTree ) );
|
||||
|
||||
|
||||
10
vendor/box3d/src/src/contact.c
vendored
10
vendor/box3d/src/src/contact.c
vendored
@@ -745,6 +745,16 @@ bool b3UpdateContact( b3World* world, int workerIndex, b3Contact* contact, b3Sha
|
||||
|
||||
childShapeA.type = child.type;
|
||||
|
||||
// Handle child material for non-meshes.
|
||||
if ( child.type != b3_meshShape )
|
||||
{
|
||||
B3_ASSERT( 0 <= child.materialIndices[0] && child.materialIndices[0] < shapeA->materialCount );
|
||||
const b3SurfaceMaterial* parentMaterials = b3GetShapeMaterials( shapeA );
|
||||
childShapeA.material = parentMaterials[child.materialIndices[0]];
|
||||
childShapeA.materials = NULL;
|
||||
childShapeA.materialCount = 1;
|
||||
}
|
||||
|
||||
if ( child.type == b3_capsuleShape )
|
||||
{
|
||||
childShapeA.capsule = child.capsule;
|
||||
|
||||
532
vendor/box3d/src/src/contact_solver.c
vendored
532
vendor/box3d/src/src/contact_solver.c
vendored
@@ -10,14 +10,13 @@
|
||||
#include "math_internal.h"
|
||||
#include "physics_world.h"
|
||||
#include "platform.h"
|
||||
#include "simd.h"
|
||||
#include "solver_set.h"
|
||||
|
||||
#if B3_ENABLE_VALIDATION
|
||||
#include "shape.h"
|
||||
#endif
|
||||
|
||||
#define FIXED_ANCHORS 1
|
||||
|
||||
// contact separation for sub-stepping
|
||||
// s = s0 + dot(cB + rB - cA - rA, normal)
|
||||
// normal is held constant
|
||||
@@ -38,6 +37,9 @@ void b3PrepareContacts_Mesh( b3SolverBlock block, b3StepContext* context )
|
||||
|
||||
float warmStartScale = world->enableWarmStarting ? 1.0f : 0.0f;
|
||||
|
||||
// Used for friction center weighting.
|
||||
float invTau = 1.0f / B3_SPECULATIVE_DISTANCE;
|
||||
|
||||
// Need to use spans in order to find the associated b2Contact, which is per color
|
||||
b3ContactPrepareSpan* spans = context->contactPrepareSpans;
|
||||
b3ManifoldConstraint* manifoldBase = context->manifoldConstraints;
|
||||
@@ -77,7 +79,7 @@ void b3PrepareContacts_Mesh( b3SolverBlock block, b3StepContext* context )
|
||||
int localIndex = index - colorStart;
|
||||
B3_ASSERT( 0 <= localIndex && localIndex < spans[colorIndex].count );
|
||||
int contactId = specs[localIndex].contactId;
|
||||
b3Contact* contact = b3Array_Get( world->contacts, contactId );
|
||||
b3Contact* contact = b3Array_Get( world->contacts, contactId );
|
||||
B3_ASSERT( contact->contactId == contactId );
|
||||
|
||||
int indexA = contact->bodySimIndexA;
|
||||
@@ -86,13 +88,13 @@ void b3PrepareContacts_Mesh( b3SolverBlock block, b3StepContext* context )
|
||||
#if B3_ENABLE_VALIDATION
|
||||
if ( indexA != B3_NULL_INDEX )
|
||||
{
|
||||
b3Body* bodyA = b3Array_Get( world->bodies, contact->edges[0].bodyId );
|
||||
b3Body* bodyA = b3Array_Get( world->bodies, contact->edges[0].bodyId );
|
||||
B3_ASSERT( indexA == bodyA->localIndex );
|
||||
}
|
||||
|
||||
if ( indexB != B3_NULL_INDEX )
|
||||
{
|
||||
b3Body* bodyB = b3Array_Get( world->bodies, contact->edges[1].bodyId );
|
||||
b3Body* bodyB = b3Array_Get( world->bodies, contact->edges[1].bodyId );
|
||||
B3_ASSERT( indexB == bodyB->localIndex );
|
||||
}
|
||||
#endif
|
||||
@@ -184,6 +186,7 @@ void b3PrepareContacts_Mesh( b3SolverBlock block, b3StepContext* context )
|
||||
|
||||
b3Vec3 centerA = b3Vec3_zero;
|
||||
b3Vec3 centerB = b3Vec3_zero;
|
||||
float totalFrictionWeight = 0.0f;
|
||||
|
||||
for ( int pointIndex = 0; pointIndex < pointCount; ++pointIndex )
|
||||
{
|
||||
@@ -193,7 +196,9 @@ void b3PrepareContacts_Mesh( b3SolverBlock block, b3StepContext* context )
|
||||
b3ManifoldPoint* mp = manifold->points + pointIndex;
|
||||
cp->rA = mp->anchorA;
|
||||
cp->rB = mp->anchorB;
|
||||
cp->baseSeparation = mp->separation - b3Dot( b3Sub( cp->rB, cp->rA ), normal );
|
||||
|
||||
float s = mp->separation;
|
||||
cp->baseSeparation = s - b3Dot( b3Sub( cp->rB, cp->rA ), normal );
|
||||
cp->normalImpulse = warmStartScale * mp->normalImpulse;
|
||||
cp->totalNormalImpulse = 0.0f;
|
||||
|
||||
@@ -210,15 +215,22 @@ void b3PrepareContacts_Mesh( b3SolverBlock block, b3StepContext* context )
|
||||
b3Vec3 vrB = b3Add( vB, b3Cross( wB, rB ) );
|
||||
cp->relativeVelocity = b3Dot( normal, b3Sub( vrB, vrA ) );
|
||||
|
||||
centerA = b3Add( centerA, rA );
|
||||
centerB = b3Add( centerB, rB );
|
||||
// C0 friction center decay. Needed to prevent spinning top drift (GyroscopicPrecession sample).
|
||||
// Contacts with separation greater than twice the speculative distance only matter for CCD and
|
||||
// should not contribute to the friction center. They are not important for jitter reduction. Closer
|
||||
// points may begin to touch on and off, so the friction center needs to move smoothly.
|
||||
// Epsilon to avoid a branch below (or divide by zero). Small enough to get washed out normally.
|
||||
float weight = b3ClampFloat( 2.0f - s * invTau, B3_MIN_FRICTION_WEIGHT, 1.0f );
|
||||
centerA = b3MulAdd( centerA, weight, rA );
|
||||
centerB = b3MulAdd( centerB, weight, rB );
|
||||
totalFrictionWeight += weight;
|
||||
}
|
||||
|
||||
float invCount = 1.0f / pointCount;
|
||||
centerA = b3MulSV( invCount, centerA );
|
||||
centerB = b3MulSV( invCount, centerB );
|
||||
constraint->originA = centerA;
|
||||
constraint->originB = centerB;
|
||||
float invWeight = 1.0f / totalFrictionWeight;
|
||||
centerA = b3MulSV( invWeight, centerA );
|
||||
centerB = b3MulSV( invWeight, centerB );
|
||||
constraint->centerA = centerA;
|
||||
constraint->centerB = centerB;
|
||||
|
||||
for ( int pointIndex = 0; pointIndex < pointCount; ++pointIndex )
|
||||
{
|
||||
@@ -265,7 +277,7 @@ void b3WarmStartContacts_Mesh( b3SolverBlock block, b3StepContext* context )
|
||||
{
|
||||
b3World* world = context->world;
|
||||
b3GraphColor* color = world->constraintGraph.colors + block.colorIndex;
|
||||
b3SolverSet* awakeSet = b3Array_Get( world->solverSets, b3_awakeSet );
|
||||
b3SolverSet* awakeSet = b3Array_Get( world->solverSets, b3_awakeSet );
|
||||
b3BodyState* states = awakeSet->bodyStates.data;
|
||||
b3ContactConstraint* constraints = color->contactConstraints;
|
||||
|
||||
@@ -319,8 +331,8 @@ void b3WarmStartContacts_Mesh( b3SolverBlock block, b3StepContext* context )
|
||||
|
||||
// Central friction
|
||||
{
|
||||
b3Vec3 rA = constraint->originA;
|
||||
b3Vec3 rB = constraint->originB;
|
||||
b3Vec3 rA = constraint->centerA;
|
||||
b3Vec3 rB = constraint->centerB;
|
||||
b3Vec3 impulse = b3MulSV( constraint->frictionImpulse.x, constraint->tangent1 );
|
||||
impulse = b3Add( impulse, b3MulSV( constraint->frictionImpulse.y, constraint->tangent2 ) );
|
||||
|
||||
@@ -515,8 +527,8 @@ void b3SolveContacts_Mesh( b3SolverBlock block, b3StepContext* context, bool use
|
||||
b3Vec3 tangent2 = constraint->tangent2;
|
||||
|
||||
// Fixed anchor points for applying impulses
|
||||
b3Vec3 rA = constraint->originA;
|
||||
b3Vec3 rB = constraint->originB;
|
||||
b3Vec3 rA = constraint->centerA;
|
||||
b3Vec3 rB = constraint->centerB;
|
||||
|
||||
// Relative tangent velocity at contact
|
||||
b3Vec3 vrA = b3Add( vA, b3Cross( wA, rA ) );
|
||||
@@ -756,8 +768,8 @@ void b3StoreImpulses_Mesh( b3SolverBlock block, b3StepContext* context, int work
|
||||
mp->totalNormalImpulse = cp->totalNormalImpulse;
|
||||
mp->normalVelocity = cp->relativeVelocity;
|
||||
|
||||
if ( checkHitEvents && flagged == false &&
|
||||
mp->normalVelocity < negHitThreshold && mp->totalNormalImpulse > 0.0f )
|
||||
if ( checkHitEvents && flagged == false && mp->normalVelocity < negHitThreshold &&
|
||||
mp->totalNormalImpulse > 0.0f )
|
||||
{
|
||||
b3SetBit( hitEventBitSet, contact->contactId );
|
||||
hasHitEvents = true;
|
||||
@@ -774,30 +786,6 @@ void b3StoreImpulses_Mesh( b3SolverBlock block, b3StepContext* context, int work
|
||||
taskContext->hasHitEvents = hasHitEvents;
|
||||
}
|
||||
|
||||
#if defined( B3_SIMD_NEON )
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
// wide float holds 4 numbers
|
||||
typedef float32x4_t b3FloatW;
|
||||
|
||||
#elif defined( B3_SIMD_SSE2 )
|
||||
|
||||
#include <emmintrin.h>
|
||||
|
||||
// wide float holds 4 numbers
|
||||
typedef __m128 b3FloatW;
|
||||
|
||||
#else
|
||||
|
||||
// scalar math
|
||||
typedef struct b3FloatW
|
||||
{
|
||||
float x, y, z, w;
|
||||
} b3FloatW;
|
||||
|
||||
#endif
|
||||
|
||||
// Wide vec2
|
||||
typedef struct b3Vec2W
|
||||
{
|
||||
@@ -829,372 +817,6 @@ typedef struct b3SymMatrix3W
|
||||
b3FloatW cxx, cxy, cxz, cyy, cyz, czz;
|
||||
} b3SymMatrix3W;
|
||||
|
||||
#if defined( B3_SIMD_NEON )
|
||||
|
||||
static inline b3FloatW b3ZeroW( void )
|
||||
{
|
||||
return vdupq_n_f32( 0.0f );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SplatW( float scalar )
|
||||
{
|
||||
return vdupq_n_f32( scalar );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3NegW( b3FloatW a )
|
||||
{
|
||||
return vnegq_f32( a );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SetW( float a, float b, float c, float d )
|
||||
{
|
||||
float32_t array[4] = { a, b, c, d };
|
||||
return vld1q_f32( array );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3AddW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vaddq_f32( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SubW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vsubq_f32( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MulW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vmulq_f32( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3DivW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vdivq_f32( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SqrtW( b3FloatW a )
|
||||
{
|
||||
return vsqrtq_f32( a );
|
||||
}
|
||||
|
||||
// Cannot use real FMA because it doesn't match the non-SIMD path
|
||||
static inline b3FloatW b3MulAddW( b3FloatW a, b3FloatW b, b3FloatW c )
|
||||
{
|
||||
return vaddq_f32( a, vmulq_f32( b, c ) );
|
||||
}
|
||||
|
||||
// static inline b3FloatW b3MulSubW( b3FloatW a, b3FloatW b, b3FloatW c )
|
||||
//{
|
||||
// return vsubq_f32( a, vmulq_f32( b, c ) );
|
||||
// }
|
||||
|
||||
static inline b3FloatW b3MinW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vminq_f32( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MaxW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vmaxq_f32( a, b );
|
||||
}
|
||||
|
||||
// clamp a to [-b, b]
|
||||
static inline b3FloatW b3SymClampW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW nb = b3NegW( b );
|
||||
b3FloatW c = b3MaxW( nb, a );
|
||||
return b3MinW( c, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3OrW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vreinterpretq_f32_u32( vorrq_u32( vreinterpretq_u32_f32( a ), vreinterpretq_u32_f32( b ) ) );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3GreaterThanW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vreinterpretq_f32_u32( vcgtq_f32( a, b ) );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3EqualsW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vreinterpretq_f32_u32( vceqq_f32( a, b ) );
|
||||
}
|
||||
|
||||
static inline bool b3AllZeroW( b3FloatW a )
|
||||
{
|
||||
// Create a zero vector for comparison
|
||||
b3FloatW zero = vdupq_n_f32( 0.0f );
|
||||
|
||||
// Compare the input vector with zero
|
||||
uint32x4_t cmp_result = vceqq_f32( a, zero );
|
||||
|
||||
// Check if all comparison results are non-zero using vminvq
|
||||
#ifdef __ARM_FEATURE_SVE
|
||||
// ARM v8.2+ has horizontal minimum instruction
|
||||
return vminvq_u32( cmp_result ) != 0;
|
||||
#else
|
||||
// For older ARM architectures, we need to manually check all lanes
|
||||
return vgetq_lane_u32( cmp_result, 0 ) != 0 && vgetq_lane_u32( cmp_result, 1 ) != 0 && vgetq_lane_u32( cmp_result, 2 ) != 0 &&
|
||||
vgetq_lane_u32( cmp_result, 3 ) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// component-wise returns mask ? b : a
|
||||
static inline b3FloatW b3BlendW( b3FloatW a, b3FloatW b, b3FloatW mask )
|
||||
{
|
||||
uint32x4_t mask32 = vreinterpretq_u32_f32( mask );
|
||||
return vbslq_f32( mask32, b, a );
|
||||
}
|
||||
|
||||
#elif defined( B3_SIMD_SSE2 )
|
||||
|
||||
static inline b3FloatW b3ZeroW( void )
|
||||
{
|
||||
return _mm_setzero_ps();
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SplatW( float scalar )
|
||||
{
|
||||
return _mm_set1_ps( scalar );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3NegW( b3FloatW a )
|
||||
{
|
||||
// Create a mask with the sign bit set for each element
|
||||
__m128 mask = _mm_set1_ps( -0.0f );
|
||||
|
||||
// XOR the input with the mask to negate each element
|
||||
return _mm_xor_ps( a, mask );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SetW( float a, float b, float c, float d )
|
||||
{
|
||||
return _mm_setr_ps( a, b, c, d );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3AddW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_add_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SubW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_sub_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MulW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_mul_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3DivW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_div_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SqrtW( b3FloatW a )
|
||||
{
|
||||
return _mm_sqrt_ps( a );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MulAddW( b3FloatW a, b3FloatW b, b3FloatW c )
|
||||
{
|
||||
return _mm_add_ps( a, _mm_mul_ps( b, c ) );
|
||||
}
|
||||
|
||||
// static inline b3FloatW b3MulSubW( b3FloatW a, b3FloatW b, b3FloatW c )
|
||||
//{
|
||||
// return _mm_sub_ps( a, _mm_mul_ps( b, c ) );
|
||||
// }
|
||||
|
||||
static inline b3FloatW b3MinW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_min_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MaxW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_max_ps( a, b );
|
||||
}
|
||||
|
||||
// clamp a to [-b, b]
|
||||
static inline b3FloatW b3SymClampW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW nb = b3NegW( b );
|
||||
b3FloatW c = b3MaxW( nb, a );
|
||||
return b3MinW( c, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3OrW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_or_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3GreaterThanW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_cmpgt_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3EqualsW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_cmpeq_ps( a, b );
|
||||
}
|
||||
|
||||
static inline bool b3AllZeroW( b3FloatW a )
|
||||
{
|
||||
// Compare each element with zero
|
||||
b3FloatW zero = _mm_setzero_ps();
|
||||
b3FloatW cmp = _mm_cmpeq_ps( a, zero );
|
||||
|
||||
// Create a mask from the comparison results
|
||||
int mask = _mm_movemask_ps( cmp );
|
||||
|
||||
// If all elements are zero, the mask will be 0xF (1111 in binary)
|
||||
return mask == 0xF;
|
||||
}
|
||||
|
||||
// component-wise returns mask ? b : a
|
||||
static inline b3FloatW b3BlendW( b3FloatW a, b3FloatW b, b3FloatW mask )
|
||||
{
|
||||
return _mm_or_ps( _mm_and_ps( mask, b ), _mm_andnot_ps( mask, a ) );
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline b3FloatW b3ZeroW( void )
|
||||
{
|
||||
return (b3FloatW){ 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SplatW( float scalar )
|
||||
{
|
||||
return (b3FloatW){ scalar, scalar, scalar, scalar };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3NegW( b3FloatW a )
|
||||
{
|
||||
return (b3FloatW){ -a.x, -a.y, -a.z, -a.w };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3AddW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return (b3FloatW){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SubW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return (b3FloatW){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MulW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return (b3FloatW){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3DivW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return (b3FloatW){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SqrtW( b3FloatW a )
|
||||
{
|
||||
return (b3FloatW){ sqrtf( a.x ), sqrtf( a.y ), sqrtf( a.z ), sqrtf( a.w ) };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MulAddW( b3FloatW a, b3FloatW b, b3FloatW c )
|
||||
{
|
||||
return (b3FloatW){ a.x + b.x * c.x, a.y + b.y * c.y, a.z + b.z * c.z, a.w + b.w * c.w };
|
||||
}
|
||||
|
||||
// static inline b3FloatW b3MulSubW( b3FloatW a, b3FloatW b, b3FloatW c )
|
||||
//{
|
||||
// return { a.x - b.x * c.x, a.y - b.y * c.y, a.z - b.z * c.z, a.w - b.w * c.w };
|
||||
// }
|
||||
|
||||
// static inline b3FloatW b3MinW( b3FloatW a, b3FloatW b )
|
||||
//{
|
||||
// b3FloatW r;
|
||||
// r.x = a.x <= b.x ? a.x : b.x;
|
||||
// r.y = a.y <= b.y ? a.y : b.y;
|
||||
// r.z = a.z <= b.z ? a.z : b.z;
|
||||
// r.w = a.w <= b.w ? a.w : b.w;
|
||||
// return r;
|
||||
// }
|
||||
|
||||
static inline b3FloatW b3MaxW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x >= b.x ? a.x : b.x;
|
||||
r.y = a.y >= b.y ? a.y : b.y;
|
||||
r.z = a.z >= b.z ? a.z : b.z;
|
||||
r.w = a.w >= b.w ? a.w : b.w;
|
||||
return r;
|
||||
}
|
||||
|
||||
// clamp a to [-b, b]
|
||||
static inline b3FloatW b3SymClampW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x <= b.x ? a.x : b.x;
|
||||
r.y = a.y <= b.y ? a.y : b.y;
|
||||
r.z = a.z <= b.z ? a.z : b.z;
|
||||
r.w = a.w <= b.w ? a.w : b.w;
|
||||
r.x = r.x <= -b.x ? -b.x : r.x;
|
||||
r.y = r.y <= -b.y ? -b.y : r.y;
|
||||
r.z = r.z <= -b.z ? -b.z : r.z;
|
||||
r.w = r.w <= -b.w ? -b.w : r.w;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline b3FloatW b3OrW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x != 0.0f || b.x != 0.0f ? 1.0f : 0.0f;
|
||||
r.y = a.y != 0.0f || b.y != 0.0f ? 1.0f : 0.0f;
|
||||
r.z = a.z != 0.0f || b.z != 0.0f ? 1.0f : 0.0f;
|
||||
r.w = a.w != 0.0f || b.w != 0.0f ? 1.0f : 0.0f;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline b3FloatW b3GreaterThanW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x > b.x ? 1.0f : 0.0f;
|
||||
r.y = a.y > b.y ? 1.0f : 0.0f;
|
||||
r.z = a.z > b.z ? 1.0f : 0.0f;
|
||||
r.w = a.w > b.w ? 1.0f : 0.0f;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline b3FloatW b3EqualsW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x == b.x ? 1.0f : 0.0f;
|
||||
r.y = a.y == b.y ? 1.0f : 0.0f;
|
||||
r.z = a.z == b.z ? 1.0f : 0.0f;
|
||||
r.w = a.w == b.w ? 1.0f : 0.0f;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline bool b3AllZeroW( b3FloatW a )
|
||||
{
|
||||
return a.x == 0.0f && a.y == 0.0f && a.z == 0.0f && a.w == 0.0f;
|
||||
}
|
||||
|
||||
// component-wise returns mask ? b : a
|
||||
static inline b3FloatW b3BlendW( b3FloatW a, b3FloatW b, b3FloatW mask )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = mask.x != 0.0f ? b.x : a.x;
|
||||
r.y = mask.y != 0.0f ? b.y : a.y;
|
||||
r.z = mask.z != 0.0f ? b.z : a.z;
|
||||
r.w = mask.w != 0.0f ? b.w : a.w;
|
||||
return r;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// s * a
|
||||
static inline b3Vec3W b3MulSVW( b3FloatW s, b3Vec3W a )
|
||||
{
|
||||
@@ -1342,6 +964,8 @@ typedef struct b3ContactConstraintWide
|
||||
int indexA[B3_SIMD_WIDTH];
|
||||
int indexB[B3_SIMD_WIDTH];
|
||||
|
||||
int pointCounts[B3_SIMD_WIDTH];
|
||||
|
||||
b3FloatW invMassA, invMassB;
|
||||
b3SymMatrix3W invIA, invIB;
|
||||
b3Vec3W normal;
|
||||
@@ -1350,7 +974,8 @@ typedef struct b3ContactConstraintWide
|
||||
b3Vec3W tangent1;
|
||||
b3Vec3W tangent2;
|
||||
|
||||
b3Vec3W originA, originB;
|
||||
// Friction centers
|
||||
b3Vec3W centerA, centerB;
|
||||
b3FloatW twistMass;
|
||||
b3FloatW twistImpulse;
|
||||
b3SymMatrix2W tangentMass;
|
||||
@@ -1629,6 +1254,7 @@ void b3PrepareContacts_Convex( b3SolverBlock block, b3StepContext* context )
|
||||
b3Softness staticSoftness = context->staticSoftness;
|
||||
|
||||
float warmStartScale = world->enableWarmStarting ? 1.0f : 0.0f;
|
||||
float invTau = 1.0f / B3_SPECULATIVE_DISTANCE;
|
||||
|
||||
int wideIndex = block.startIndex;
|
||||
int endWideIndex = block.startIndex + block.count;
|
||||
@@ -1664,7 +1290,7 @@ void b3PrepareContacts_Convex( b3SolverBlock block, b3StepContext* context )
|
||||
}
|
||||
|
||||
int contactId = contactIds[contactIndex];
|
||||
b3Contact* contact = b3Array_Get( world->contacts, contactId );
|
||||
b3Contact* contact = b3Array_Get( world->contacts, contactId );
|
||||
B3_ASSERT( contact->manifoldCount == 1 );
|
||||
b3Manifold* manifold = contact->manifolds + 0;
|
||||
|
||||
@@ -1779,8 +1405,11 @@ void b3PrepareContacts_Convex( b3SolverBlock block, b3StepContext* context )
|
||||
( (float*)&constraint->impulseScale )[lane] = soft.impulseScale;
|
||||
|
||||
int pointCount = manifold->pointCount;
|
||||
b3Vec3 originA = b3Vec3_zero;
|
||||
b3Vec3 originB = b3Vec3_zero;
|
||||
constraint->pointCounts[lane] = pointCount;
|
||||
|
||||
b3Vec3 centerA = b3Vec3_zero;
|
||||
b3Vec3 centerB = b3Vec3_zero;
|
||||
float totalFrictionWeight = 0.0f;
|
||||
|
||||
for ( int pointIndex = 0; pointIndex < pointCount; ++pointIndex )
|
||||
{
|
||||
@@ -1789,8 +1418,14 @@ void b3PrepareContacts_Convex( b3SolverBlock block, b3StepContext* context )
|
||||
|
||||
b3Vec3 rA = mp->anchorA;
|
||||
b3Vec3 rB = mp->anchorB;
|
||||
originA = b3Add( originA, rA );
|
||||
originB = b3Add( originB, rB );
|
||||
float s = mp->separation;
|
||||
|
||||
// C0 friction center decay. Needed to prevent spinning top drift (GyroscopicPrecession sample).
|
||||
// See details in b3PrepareContacts_Mesh. This code should stay in sync.
|
||||
float weight = b3ClampFloat( 2.0f - s * invTau, B3_MIN_FRICTION_WEIGHT, 1.0f );
|
||||
centerA = b3MulAdd( centerA, weight, rA );
|
||||
centerB = b3MulAdd( centerB, weight, rB );
|
||||
totalFrictionWeight += weight;
|
||||
|
||||
( (float*)&cp->anchorAs.X )[lane] = rA.x;
|
||||
( (float*)&cp->anchorAs.Y )[lane] = rA.y;
|
||||
@@ -1800,7 +1435,7 @@ void b3PrepareContacts_Convex( b3SolverBlock block, b3StepContext* context )
|
||||
( (float*)&cp->anchorBs.Y )[lane] = rB.y;
|
||||
( (float*)&cp->anchorBs.Z )[lane] = rB.z;
|
||||
|
||||
float baseSeparation = mp->separation - b3Dot( b3Sub( rB, rA ), normal );
|
||||
float baseSeparation = s - b3Dot( b3Sub( rB, rA ), normal );
|
||||
( (float*)&cp->baseSeparations )[lane] = baseSeparation;
|
||||
|
||||
( (float*)&cp->normalImpulses )[lane] = warmStartScale * mp->normalImpulse;
|
||||
@@ -1817,29 +1452,29 @@ void b3PrepareContacts_Convex( b3SolverBlock block, b3StepContext* context )
|
||||
( (float*)&cp->relativeVelocities )[lane] = b3Dot( normal, b3Sub( vrB, vrA ) );
|
||||
}
|
||||
|
||||
float invCount = 1.0f / pointCount;
|
||||
originA = b3MulSV( invCount, originA );
|
||||
originB = b3MulSV( invCount, originB );
|
||||
float invWeight = 1.0f / totalFrictionWeight;
|
||||
centerA = b3MulSV( invWeight, centerA );
|
||||
centerB = b3MulSV( invWeight, centerB );
|
||||
|
||||
( (float*)&constraint->originA.X )[lane] = originA.x;
|
||||
( (float*)&constraint->originA.Y )[lane] = originA.y;
|
||||
( (float*)&constraint->originA.Z )[lane] = originA.z;
|
||||
( (float*)&constraint->originB.X )[lane] = originB.x;
|
||||
( (float*)&constraint->originB.Y )[lane] = originB.y;
|
||||
( (float*)&constraint->originB.Z )[lane] = originB.z;
|
||||
( (float*)&constraint->centerA.X )[lane] = centerA.x;
|
||||
( (float*)&constraint->centerA.Y )[lane] = centerA.y;
|
||||
( (float*)&constraint->centerA.Z )[lane] = centerA.z;
|
||||
( (float*)&constraint->centerB.X )[lane] = centerB.x;
|
||||
( (float*)&constraint->centerB.Y )[lane] = centerB.y;
|
||||
( (float*)&constraint->centerB.Z )[lane] = centerB.z;
|
||||
|
||||
for ( int pointIndex = 0; pointIndex < pointCount; ++pointIndex )
|
||||
{
|
||||
const b3ManifoldPoint* mp = manifold->points + pointIndex;
|
||||
b3ContactConstraintPointWide* cp = constraint->points + pointIndex;
|
||||
( (float*)&cp->leverArms )[lane] = b3Distance( mp->anchorA, originA );
|
||||
( (float*)&cp->leverArms )[lane] = b3Distance( mp->anchorA, centerA );
|
||||
}
|
||||
|
||||
b3Vec3 rtA1 = b3Cross( originA, tangent1 );
|
||||
b3Vec3 rtA2 = b3Cross( originA, tangent2 );
|
||||
b3Vec3 rtA1 = b3Cross( centerA, tangent1 );
|
||||
b3Vec3 rtA2 = b3Cross( centerA, tangent2 );
|
||||
|
||||
b3Vec3 rtB1 = b3Cross( originB, tangent1 );
|
||||
b3Vec3 rtB2 = b3Cross( originB, tangent2 );
|
||||
b3Vec3 rtB1 = b3Cross( centerB, tangent1 );
|
||||
b3Vec3 rtB2 = b3Cross( centerB, tangent2 );
|
||||
|
||||
{
|
||||
b3Matrix2 k;
|
||||
@@ -1919,8 +1554,14 @@ void b3WarmStartContacts_Convex( b3SolverBlock block, b3StepContext* context )
|
||||
b3BodyStateW bA = b3GatherBodies( states, c->indexA );
|
||||
b3BodyStateW bB = b3GatherBodies( states, c->indexB );
|
||||
|
||||
_Static_assert( B3_SIMD_WIDTH == 4, "width" );
|
||||
int pointCount1 = b3MaxInt( c->pointCounts[0], c->pointCounts[1] );
|
||||
int pointCount2 = b3MaxInt( c->pointCounts[2], c->pointCounts[3] );
|
||||
int pointCount = b3MaxInt( pointCount1, pointCount2 );
|
||||
B3_VALIDATE( 0 < pointCount && pointCount <= B3_MAX_MANIFOLD_POINTS );
|
||||
|
||||
// Normal impulses
|
||||
for ( int j = 0; j < B3_MAX_MANIFOLD_POINTS; ++j )
|
||||
for ( int j = 0; j < pointCount; ++j )
|
||||
{
|
||||
b3ContactConstraintPointWide* cp = c->points + j;
|
||||
|
||||
@@ -1940,8 +1581,8 @@ void b3WarmStartContacts_Convex( b3SolverBlock block, b3StepContext* context )
|
||||
|
||||
// Central friction
|
||||
{
|
||||
b3Vec3W rA = c->originA;
|
||||
b3Vec3W rB = c->originB;
|
||||
b3Vec3W rA = c->centerA;
|
||||
b3Vec3W rB = c->centerB;
|
||||
b3Vec3W impulse = b3MulSVW( c->frictionImpulse.x, c->tangent1 );
|
||||
impulse = b3MulAddSVW( impulse, c->frictionImpulse.y, c->tangent2 );
|
||||
|
||||
@@ -1987,6 +1628,12 @@ void b3SolveContacts_Convex( b3SolverBlock block, b3StepContext* context, bool u
|
||||
{
|
||||
b3ContactConstraintWide* c = constraints + wideIndex;
|
||||
|
||||
_Static_assert( B3_SIMD_WIDTH == 4, "width" );
|
||||
int pointCount1 = b3MaxInt( c->pointCounts[0], c->pointCounts[1] );
|
||||
int pointCount2 = b3MaxInt( c->pointCounts[2], c->pointCounts[3] );
|
||||
int pointCount = b3MaxInt( pointCount1, pointCount2 );
|
||||
B3_VALIDATE( 0 < pointCount && pointCount <= B3_MAX_MANIFOLD_POINTS );
|
||||
|
||||
b3BodyStateW bA = b3GatherBodies( states, c->indexA );
|
||||
b3BodyStateW bB = b3GatherBodies( states, c->indexB );
|
||||
|
||||
@@ -2009,8 +1656,7 @@ void b3SolveContacts_Convex( b3SolverBlock block, b3StepContext* context, bool u
|
||||
b3FloatW totalNormalImpulse = b3ZeroW();
|
||||
b3FloatW totalTwistLimit = b3ZeroW();
|
||||
|
||||
// todo_erin use the max point count of the four manifolds
|
||||
for ( int pointIndex = 0; pointIndex < B3_MAX_MANIFOLD_POINTS; ++pointIndex )
|
||||
for ( int pointIndex = 0; pointIndex < pointCount; ++pointIndex )
|
||||
{
|
||||
b3ContactConstraintPointWide* cp = c->points + pointIndex;
|
||||
|
||||
@@ -2120,8 +1766,8 @@ void b3SolveContacts_Convex( b3SolverBlock block, b3StepContext* context, bool u
|
||||
b3Vec3W tangent2 = c->tangent2;
|
||||
|
||||
// Fixed anchor points for applying impulses
|
||||
b3Vec3W rA = c->originA;
|
||||
b3Vec3W rB = c->originB;
|
||||
b3Vec3W rA = c->centerA;
|
||||
b3Vec3W rB = c->centerB;
|
||||
|
||||
// Relative tangent velocity at contact
|
||||
b3Vec3W vrA = b3AddVW( bA.v, b3CrossW( bA.w, rA ) );
|
||||
@@ -2197,6 +1843,12 @@ void b3ApplyRestitution_Convex( b3SolverBlock block, b3StepContext* context )
|
||||
continue;
|
||||
}
|
||||
|
||||
_Static_assert( B3_SIMD_WIDTH == 4, "width" );
|
||||
int pointCount1 = b3MaxInt( c->pointCounts[0], c->pointCounts[1] );
|
||||
int pointCount2 = b3MaxInt( c->pointCounts[2], c->pointCounts[3] );
|
||||
int pointCount = b3MaxInt( pointCount1, pointCount2 );
|
||||
B3_VALIDATE( 0 < pointCount && pointCount <= B3_MAX_MANIFOLD_POINTS );
|
||||
|
||||
// Single gather for all manifolds
|
||||
b3BodyStateW bA = b3GatherBodies( states, c->indexA );
|
||||
b3BodyStateW bB = b3GatherBodies( states, c->indexB );
|
||||
@@ -2205,7 +1857,7 @@ void b3ApplyRestitution_Convex( b3SolverBlock block, b3StepContext* context )
|
||||
// by the calculations below.
|
||||
b3FloatW restitutionMask = b3EqualsW( c->restitution, zero );
|
||||
|
||||
for ( int pointIndex = 0; pointIndex < B3_MAX_MANIFOLD_POINTS; ++pointIndex )
|
||||
for ( int pointIndex = 0; pointIndex < pointCount; ++pointIndex )
|
||||
{
|
||||
b3ContactConstraintPointWide* cp = c->points + pointIndex;
|
||||
|
||||
@@ -2372,7 +2024,7 @@ void b3PrepareContacts_Overflow( b3StepContext* context )
|
||||
b3GraphColor* color = graph->colors + B3_OVERFLOW_INDEX;
|
||||
|
||||
uint16_t count = (uint16_t)color->contacts.count;
|
||||
if (count == 0)
|
||||
if ( count == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
4
vendor/box3d/src/src/contact_solver.h
vendored
4
vendor/box3d/src/src/contact_solver.h
vendored
@@ -19,13 +19,13 @@ typedef struct b3ManifoldConstraintPoint
|
||||
|
||||
typedef struct b3ManifoldConstraint
|
||||
{
|
||||
// todo use pointer buffer
|
||||
b3ManifoldConstraintPoint points[4];
|
||||
int pointCount;
|
||||
b3Vec3 normal;
|
||||
b3Vec3 tangent1;
|
||||
b3Vec3 tangent2;
|
||||
b3Vec3 originA, originB;
|
||||
// Friction centers
|
||||
b3Vec3 centerA, centerB;
|
||||
float twistMass;
|
||||
float twistImpulse;
|
||||
b3Matrix2 tangentMass;
|
||||
|
||||
1508
vendor/box3d/src/src/convex_manifold.c
vendored
1508
vendor/box3d/src/src/convex_manifold.c
vendored
File diff suppressed because it is too large
Load Diff
6
vendor/box3d/src/src/core.h
vendored
6
vendor/box3d/src/src/core.h
vendored
@@ -57,7 +57,12 @@
|
||||
#define B3_SIMD_WIDTH 4
|
||||
//#pragma message("B3_SIMD_SSE2")
|
||||
#elif defined( B3_CPU_ARM )
|
||||
// ARMv7 Neon doesn't have divide or sqrt so cannot be used.
|
||||
#if defined( __aarch64__ ) || defined( _M_ARM64 )
|
||||
#define B3_SIMD_NEON
|
||||
#else
|
||||
#define B3_SIMD_NONE
|
||||
#endif
|
||||
#define B3_SIMD_WIDTH 4
|
||||
//#pragma message("B3_SIMD_NEON")
|
||||
#elif defined( B3_CPU_WASM )
|
||||
@@ -132,6 +137,7 @@ void* b3AllocZeroed( size_t size );
|
||||
void b3Free( void* mem, size_t size );
|
||||
void* b3GrowAlloc( void* oldMem, int oldSize, int newSize );
|
||||
|
||||
B3_PRINTF_FORMAT( 1, 2 )
|
||||
void b3Log( const char* format, ... );
|
||||
|
||||
// Geometry content hashes reserve zero to mean unhashed
|
||||
|
||||
12
vendor/box3d/src/src/height_field.c
vendored
12
vendor/box3d/src/src/height_field.c
vendored
@@ -749,7 +749,7 @@ b3CastOutput b3ShapeCastHeightField( const b3HeightFieldData* heightField, const
|
||||
// nextFractionX / nextFractionZ advance in units of the clamped sweep
|
||||
// [minFraction, maxFraction], but bestFraction is a fraction of the full input
|
||||
// translation. Precompute the affine map from clamped space to input space so
|
||||
// the loop termination test compares like with like — otherwise it can exit
|
||||
// the loop termination test compares like with like. Otherwise it can exit
|
||||
// early and miss a closer hit in a later cell.
|
||||
float gridFractionScale = input->maxFraction * ( maxFraction - minFraction );
|
||||
float gridFractionOffset = input->maxFraction * minFraction;
|
||||
@@ -1382,7 +1382,7 @@ b3HeightFieldData* b3CreateGrid( int rowCount, int columnCount, b3Vec3 scale, bo
|
||||
}
|
||||
|
||||
b3HeightFieldData* b3CreateWave( int rowCount, int columnCount, b3Vec3 scale, float rowFrequency, float columnFrequency,
|
||||
bool makeHoles )
|
||||
bool makeHoles )
|
||||
{
|
||||
int heightCount = rowCount * columnCount;
|
||||
float* heights = (float*)b3Alloc( heightCount * sizeof( float ) );
|
||||
@@ -1390,14 +1390,18 @@ b3HeightFieldData* b3CreateWave( int rowCount, int columnCount, b3Vec3 scale, fl
|
||||
float omegaZ = 2.0f * B3_PI * rowFrequency;
|
||||
float omegaX = 2.0f * B3_PI * columnFrequency;
|
||||
|
||||
b3CosSin cs = { 0 };
|
||||
|
||||
for ( int i = 0; i < rowCount; ++i )
|
||||
{
|
||||
float rowHeight = sinf( omegaZ * i );
|
||||
cs = b3ComputeCosSin( omegaZ * i );
|
||||
float rowHeight = cs.sine;
|
||||
|
||||
for ( int j = 0; j < columnCount; ++j )
|
||||
{
|
||||
int k = i * columnCount + j;
|
||||
float columnHeight = sinf( omegaX * j );
|
||||
cs = b3ComputeCosSin( omegaX * j );
|
||||
float columnHeight = cs.sine;
|
||||
heights[k] = rowHeight * columnHeight;
|
||||
}
|
||||
}
|
||||
|
||||
303
vendor/box3d/src/src/hull.c
vendored
303
vendor/box3d/src/src/hull.c
vendored
@@ -4,7 +4,7 @@
|
||||
// Dirk Gregorius contributed portions of this code
|
||||
|
||||
#include "algorithm.h"
|
||||
#include "hull_map.h"
|
||||
#include "hull.h"
|
||||
#include "math_internal.h"
|
||||
#include "shape.h"
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
#define B3_MARK_VISIBLE 0
|
||||
#define B3_MARK_DELETE 1
|
||||
|
||||
// Final hull is index-encoded with uint8_t, so vertex/edge/face counts are capped at UINT8_MAX.
|
||||
#define B3_HULL_LIMIT UINT8_MAX
|
||||
// Final hull is index-encoded with uint8_t, so vertex/edge/face counts are capped at 256.
|
||||
#define B3_HULL_MAX_COUNT ( UINT8_MAX + 1 )
|
||||
|
||||
typedef struct b3QHListNode
|
||||
{
|
||||
@@ -120,12 +120,16 @@ typedef struct b3HullBuilder
|
||||
b3QHHalfEdge* edgeBase;
|
||||
int edgeCapacity;
|
||||
int edgeCount;
|
||||
b3QHHalfEdge* edgeFreeHead; // LIFO free list; overlays edge->next
|
||||
|
||||
// LIFO free list. Built with edge->next.
|
||||
b3QHHalfEdge* edgeFreeHead;
|
||||
|
||||
b3QHFace* faceBase;
|
||||
int faceCapacity;
|
||||
int faceCount;
|
||||
b3QHFace* faceFreeHead; // LIFO free list; overlays face->link.next
|
||||
|
||||
// LIFO free list. Built with face->link.next.
|
||||
b3QHFace* faceFreeHead;
|
||||
|
||||
// Reusable scratch buffers.
|
||||
b3QHHalfEdge** horizon;
|
||||
@@ -145,7 +149,6 @@ typedef struct b3HullBuilder
|
||||
int horizonStackCapacity;
|
||||
|
||||
// Final counts of the constructed hull (vertexList / faceList / half-edges around faces).
|
||||
// Populated by CleanHull; zero until then.
|
||||
int finalVertexCount;
|
||||
int finalHalfEdgeCount;
|
||||
int finalFaceCount;
|
||||
@@ -300,17 +303,18 @@ static b3QHFace* b3HullBuilder_NewFace( b3HullBuilder* b, b3QHVertex* v1, b3QHVe
|
||||
return face;
|
||||
}
|
||||
|
||||
// Remove face from faceList if still linked, clear its edge pointer, then push onto faceFreeHead.
|
||||
// Uses face->link.next as the free-list next pointer (link.prev stays NULL, so b3QHList_Contains
|
||||
// returns false on a free slot, as required by the retire-guard in ResolveFaces).
|
||||
// Remove face and add to free list.
|
||||
static void b3HullBuilder_RetireFace( b3HullBuilder* b, b3QHFace* face )
|
||||
{
|
||||
// Sometimes a cone face gets merged and never added to the list.
|
||||
if ( b3QHList_Contains( &face->link ) )
|
||||
{
|
||||
b3QHList_Remove( &face->link );
|
||||
}
|
||||
|
||||
face->edge = NULL;
|
||||
// link.prev is already NULL after Remove (or was never set). link.next holds free-list ptr.
|
||||
// link.prev is already NULL after Remove (or was never set).
|
||||
B3_VALIDATE( face->link.prev == NULL );
|
||||
face->link.next = (b3QHListNode*)b->faceFreeHead;
|
||||
b->faceFreeHead = face;
|
||||
}
|
||||
@@ -1232,7 +1236,7 @@ static void b3HullBuilder_ResolveVertices( b3HullBuilder* b )
|
||||
static void b3HullBuilder_ResolveFaces( b3HullBuilder* b )
|
||||
{
|
||||
// Splice deleted faces out of the face list. Faces already retired by AbsorbFaces are no
|
||||
// longer on faceList, so we guard with b3QHList_Contains before removing.
|
||||
// longer on faceList, so guard with b3QHList_Contains before removing.
|
||||
b3QHListNode* node = b->faceList.link.next;
|
||||
while ( node != &b->faceList.link )
|
||||
{
|
||||
@@ -1242,7 +1246,21 @@ static void b3HullBuilder_ResolveFaces( b3HullBuilder* b )
|
||||
if ( face->mark == B3_MARK_DELETE && b3QHList_Contains( &face->link ) )
|
||||
{
|
||||
B3_ASSERT( B3_LIST_EMPTY( &face->conflictListHead.link ) );
|
||||
b3QHList_Remove( &face->link );
|
||||
|
||||
// Each half-edge is owned by exactly one face, so ring walks over the
|
||||
// dead region retire every interior edge exactly once. Merge deleted
|
||||
// faces are already off the face list.
|
||||
b3QHHalfEdge* start = face->edge;
|
||||
b3QHHalfEdge* edge = start;
|
||||
do
|
||||
{
|
||||
b3QHHalfEdge* next = edge->next;
|
||||
b3HullBuilder_RetireEdge( b, edge );
|
||||
edge = next;
|
||||
}
|
||||
while ( edge != start );
|
||||
|
||||
b3HullBuilder_RetireFace( b, face );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1421,12 +1439,13 @@ static bool b3HullBuilder_Construct( b3HullBuilder* b, const b3Vec3* points, int
|
||||
}
|
||||
|
||||
b3HullBuilder_ComputeTolerance( b, pointCount, shiftedPoints );
|
||||
if ( !b3HullBuilder_BuildInitialHull( b, pointCount, shiftedPoints ) )
|
||||
bool haveInitialHull = b3HullBuilder_BuildInitialHull( b, pointCount, shiftedPoints );
|
||||
if ( haveInitialHull == false )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int budget = b3ClampInt( maxVertexCount - 4, 0, B3_HULL_LIMIT - 4 );
|
||||
int budget = b3ClampInt( maxVertexCount - 4, 0, B3_HULL_MAX_COUNT - 4 );
|
||||
|
||||
b3QHVertex* vertex = b3HullBuilder_NextConflictVertex( b );
|
||||
while ( vertex && budget > 0 )
|
||||
@@ -1447,8 +1466,10 @@ static bool b3HullBuilder_Construct( b3HullBuilder* b, const b3Vec3* points, int
|
||||
|
||||
typedef struct b3HullWorkSizes
|
||||
{
|
||||
int N; // pointCount
|
||||
int M; // clamped maxVertexCount, in [4, B3_HULL_LIMIT]
|
||||
// Input point count
|
||||
int N;
|
||||
// Output point limit
|
||||
int M;
|
||||
int vertexCapacity;
|
||||
int edgeCapacity;
|
||||
int faceCapacity;
|
||||
@@ -1478,39 +1499,19 @@ static b3HullWorkSizes b3ComputeHullWorkSizes( int pointCount, int clampedMaxCou
|
||||
s.vertexCapacity = pointCount + 4;
|
||||
|
||||
// Edges and faces use free-list recycling; capacity is proportional to live hull size.
|
||||
// edgeCapacity: peak is ~twice live edges plus cone edges; floor 48.
|
||||
s.edgeCapacity = 24 * s.M - 48;
|
||||
if ( s.edgeCapacity < 48 )
|
||||
{
|
||||
s.edgeCapacity = 48;
|
||||
}
|
||||
// edgeCapacity: peak is ~twice live edges plus cone edges. Minimum 48.
|
||||
s.edgeCapacity = b3MaxInt( 48, 24 * s.M - 48 );
|
||||
|
||||
// faceCapacity: peak intermediate state live faces (<=2*M-4) plus full cone (<=3*M-6); floor 16.
|
||||
s.faceCapacity = 5 * s.M - 10;
|
||||
if ( s.faceCapacity < 16 )
|
||||
{
|
||||
s.faceCapacity = 16;
|
||||
}
|
||||
// faceCapacity: peak intermediate state live faces (<=2*M-4) plus full cone (<=3*M-6). Minimum 16.
|
||||
s.faceCapacity = b3MaxInt( 16, 5 * s.M - 10 );
|
||||
|
||||
// Horizon/cone bounded by current half-edge count; mergedFaces by face count.
|
||||
s.horizonCapacity = 3 * s.M - 6;
|
||||
if ( s.horizonCapacity < 6 )
|
||||
{
|
||||
s.horizonCapacity = 6;
|
||||
}
|
||||
// Horizon/cone bounded by current half-edge count. Merged faces by face count.
|
||||
s.horizonCapacity = b3MaxInt( 6, 3 * s.M - 6 );
|
||||
s.coneCapacity = s.horizonCapacity;
|
||||
s.mergedFacesCapacity = 2 * s.M - 4;
|
||||
if ( s.mergedFacesCapacity < 4 )
|
||||
{
|
||||
s.mergedFacesCapacity = 4;
|
||||
}
|
||||
s.mergedFacesCapacity = b3MaxInt( 4, 2 * s.M - 4 );
|
||||
|
||||
// Horizon DFS depth is bounded by the number of live faces (Euler: <=2*M-4).
|
||||
s.horizonStackCapacity = 2 * s.M - 4;
|
||||
if ( s.horizonStackCapacity < 4 )
|
||||
{
|
||||
s.horizonStackCapacity = 4;
|
||||
}
|
||||
s.horizonStackCapacity = b3MaxInt( 4, 2 * s.M - 4 );
|
||||
|
||||
size_t offset = 0;
|
||||
|
||||
@@ -1607,6 +1608,24 @@ static b3HullHalfEdge* b3GetHullEdgesWrite( b3HullData* hull )
|
||||
return (b3HullHalfEdge*)( (intptr_t)hull + hull->edgeOffset );
|
||||
}
|
||||
|
||||
static float* b3GetHullSoaVerticesWrite( b3HullData* hull )
|
||||
{
|
||||
if ( hull->soaVertexOffset == 0 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return (float*)( (intptr_t)hull + hull->soaVertexOffset );
|
||||
}
|
||||
|
||||
static float* b3GetHullSoaNormalsWrite( b3HullData* hull )
|
||||
{
|
||||
if ( hull->soaNormalOffset == 0 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return (float*)( (intptr_t)hull + hull->soaNormalOffset );
|
||||
}
|
||||
|
||||
int b3FindHullSupportVertex( const b3HullData* hull, b3Vec3 direction )
|
||||
{
|
||||
int bestIndex = B3_NULL_INDEX;
|
||||
@@ -2028,7 +2047,7 @@ b3HullData* b3CreateHull( const b3Vec3* points, int pointCount, int maxVertexCou
|
||||
}
|
||||
|
||||
b3Vec3 origin = points[0];
|
||||
int clampedMaxCount = b3ClampInt( maxVertexCount, 4, B3_HULL_LIMIT );
|
||||
int clampedMaxCount = b3ClampInt( maxVertexCount, 4, B3_MAX_HULL_VERTICES );
|
||||
|
||||
// Single allocation for all working memory.
|
||||
b3HullWorkSizes sizes = b3ComputeHullWorkSizes( pointCount, clampedMaxCount );
|
||||
@@ -2040,40 +2059,41 @@ b3HullData* b3CreateHull( const b3Vec3* points, int pointCount, int maxVertexCou
|
||||
b3Vec3* shiftedPoints = (b3Vec3*)( work + sizes.offsetShiftedPoints );
|
||||
|
||||
bool ok = b3HullBuilder_Construct( &builder, points, pointCount, clampedMaxCount, origin, shiftedPoints );
|
||||
if ( !ok )
|
||||
if ( ok == false )
|
||||
{
|
||||
b3Free( work, sizes.totalBytes );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( builder.finalVertexCount >= B3_HULL_LIMIT )
|
||||
if ( builder.finalVertexCount > B3_MAX_HULL_VERTICES )
|
||||
{
|
||||
b3Log( "hull final vertex count of %d exceeds limit of %d", builder.finalVertexCount, B3_HULL_LIMIT );
|
||||
b3Log( "hull final vertex count of %d exceeds limit of %d", builder.finalVertexCount, B3_MAX_HULL_VERTICES );
|
||||
b3Free( work, sizes.totalBytes );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( builder.finalFaceCount >= B3_HULL_LIMIT )
|
||||
if ( builder.finalFaceCount > B3_MAX_HULL_FACES )
|
||||
{
|
||||
b3Log( "hull final face count of %d exceeds limit of %d", builder.finalFaceCount, B3_HULL_LIMIT );
|
||||
b3Log( "hull final face count of %d exceeds limit of %d", builder.finalFaceCount, B3_MAX_HULL_FACES );
|
||||
b3Free( work, sizes.totalBytes );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( builder.finalHalfEdgeCount >= B3_HULL_LIMIT )
|
||||
int maxHalfEdgeCount = 2 * B3_MAX_HULL_EDGES;
|
||||
if ( builder.finalHalfEdgeCount > maxHalfEdgeCount )
|
||||
{
|
||||
b3Log( "hull final half edge count of %d exceeds limit of %d", builder.finalHalfEdgeCount, B3_HULL_LIMIT );
|
||||
b3Log( "hull final half edge count of %d exceeds limit of %d", builder.finalHalfEdgeCount, maxHalfEdgeCount );
|
||||
b3Free( work, sizes.totalBytes );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Walk lists into temp arrays bounded by B3_HULL_LIMIT, stamping finalIndex on each node so
|
||||
// Walk lists into temp arrays bounded by B3_HULL_MAX_COUNT, stamping finalIndex on each node so
|
||||
// the resolution pass below is O(E + F) instead of O(E^2 + F^2).
|
||||
const b3QHVertex* tempVertices[B3_HULL_LIMIT];
|
||||
const b3QHVertex* tempVertices[B3_HULL_MAX_COUNT];
|
||||
int vertexCount = 0;
|
||||
for ( b3QHListNode* node = builder.vertexList.link.next; node != &builder.vertexList.link; node = node->next )
|
||||
{
|
||||
B3_ASSERT( vertexCount <= B3_HULL_LIMIT - 1 );
|
||||
B3_ASSERT( vertexCount < B3_HULL_MAX_COUNT );
|
||||
|
||||
b3QHVertex* vertex = (b3QHVertex*)node;
|
||||
vertex->finalIndex = vertexCount;
|
||||
@@ -2081,15 +2101,14 @@ b3HullData* b3CreateHull( const b3Vec3* points, int pointCount, int maxVertexCou
|
||||
}
|
||||
|
||||
// Collect edges in twin-paired order (i, i+1) by stamping each pair as we discover it.
|
||||
// Replaces b3SortEdges' O(E^2) twin pairing.
|
||||
const b3QHFace* tempFaces[B3_HULL_LIMIT];
|
||||
const b3QHHalfEdge* tempEdges[B3_HULL_LIMIT];
|
||||
const b3QHFace* tempFaces[B3_HULL_MAX_COUNT];
|
||||
const b3QHHalfEdge* tempEdges[B3_HULL_MAX_COUNT];
|
||||
int faceCount = 0;
|
||||
int edgeCount = 0;
|
||||
|
||||
for ( b3QHListNode* faceNode = builder.faceList.link.next; faceNode != &builder.faceList.link; faceNode = faceNode->next )
|
||||
{
|
||||
B3_ASSERT( faceCount <= B3_HULL_LIMIT - 1 );
|
||||
B3_ASSERT( faceCount < B3_HULL_MAX_COUNT );
|
||||
|
||||
b3QHFace* face = (b3QHFace*)faceNode;
|
||||
face->finalIndex = faceCount;
|
||||
@@ -2100,7 +2119,7 @@ b3HullData* b3CreateHull( const b3Vec3* points, int pointCount, int maxVertexCou
|
||||
{
|
||||
if ( edge->finalIndex < 0 )
|
||||
{
|
||||
B3_ASSERT( edgeCount + 1 <= B3_HULL_LIMIT - 1 );
|
||||
B3_ASSERT( edgeCount + 1 < B3_HULL_MAX_COUNT );
|
||||
|
||||
edge->finalIndex = edgeCount;
|
||||
tempEdges[edgeCount++] = edge;
|
||||
@@ -2112,6 +2131,9 @@ b3HullData* b3CreateHull( const b3Vec3* points, int pointCount, int maxVertexCou
|
||||
while ( edge != face->edge );
|
||||
}
|
||||
|
||||
int soaVertexCount = ( vertexCount + 3 ) & ~3;
|
||||
int soaNormalCount = ( faceCount + 3 ) & ~3;
|
||||
|
||||
// Allocate the hull. Arrays hang off the end.
|
||||
size_t byteCount = b3AlignUp8( sizeof( b3HullData ) );
|
||||
int vertexOffset = (int)byteCount;
|
||||
@@ -2120,10 +2142,14 @@ b3HullData* b3CreateHull( const b3Vec3* points, int pointCount, int maxVertexCou
|
||||
byteCount += b3AlignUp8( vertexCount * (int)sizeof( b3Vec3 ) );
|
||||
int edgeOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( edgeCount * (int)sizeof( b3HullHalfEdge ) );
|
||||
int faceOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( faceCount * (int)sizeof( b3HullFace ) );
|
||||
int planeOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( faceCount * (int)sizeof( b3Plane ) );
|
||||
int faceOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( faceCount * (int)sizeof( b3HullFace ) );
|
||||
int soaVertexOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( 3 * soaVertexCount * (int)sizeof( float ) );
|
||||
int soaNormalOffset = (int)byteCount;
|
||||
byteCount += b3AlignUp8( 3 * soaNormalCount * (int)sizeof( float ) );
|
||||
|
||||
b3HullData* hull = b3Alloc( byteCount );
|
||||
memset( hull, 0, byteCount );
|
||||
@@ -2132,8 +2158,10 @@ b3HullData* b3CreateHull( const b3Vec3* points, int pointCount, int maxVertexCou
|
||||
hull->vertexOffset = vertexOffset;
|
||||
hull->pointOffset = pointOffset;
|
||||
hull->edgeOffset = edgeOffset;
|
||||
hull->faceOffset = faceOffset;
|
||||
hull->planeOffset = planeOffset;
|
||||
hull->faceOffset = faceOffset;
|
||||
hull->soaVertexOffset = soaVertexOffset;
|
||||
hull->soaNormalOffset = soaNormalOffset;
|
||||
|
||||
hull->vertexCount = vertexCount;
|
||||
hull->edgeCount = edgeCount;
|
||||
@@ -2146,11 +2174,29 @@ b3HullData* b3CreateHull( const b3Vec3* points, int pointCount, int maxVertexCou
|
||||
b3HullFace* faces = (b3HullFace*)( (intptr_t)hull + hull->faceOffset );
|
||||
b3Vec3* finalPoints = b3GetHullPointsWrite( hull );
|
||||
b3Plane* planes = b3GetHullPlanesWrite( hull );
|
||||
float* soaVertices = b3GetHullSoaVerticesWrite( hull );
|
||||
float* soaNormals = b3GetHullSoaNormalsWrite( hull );
|
||||
|
||||
float* vx = soaVertices;
|
||||
float* vy = vx + soaVertexCount;
|
||||
float* vz = vy + soaVertexCount;
|
||||
|
||||
for ( int index = 0; index < vertexCount; ++index )
|
||||
{
|
||||
vertices[index].edge = 0;
|
||||
finalPoints[index] = tempVertices[index]->position;
|
||||
b3Vec3 p = tempVertices[index]->position;
|
||||
finalPoints[index] = p;
|
||||
|
||||
vx[index] = p.x;
|
||||
vy[index] = p.y;
|
||||
vz[index] = p.z;
|
||||
}
|
||||
|
||||
for ( int index = vertexCount; index < soaVertexCount; ++index )
|
||||
{
|
||||
vx[index] = vx[0];
|
||||
vy[index] = vy[0];
|
||||
vz[index] = vz[0];
|
||||
}
|
||||
|
||||
for ( int index = 0; index < edgeCount; ++index )
|
||||
@@ -2169,6 +2215,10 @@ b3HullData* b3CreateHull( const b3Vec3* points, int pointCount, int maxVertexCou
|
||||
vertices[edge->origin->finalIndex].edge = (uint8_t)index;
|
||||
}
|
||||
|
||||
float* nx = soaNormals;
|
||||
float* ny = nx + soaNormalCount;
|
||||
float* nz = ny + soaNormalCount;
|
||||
|
||||
for ( int index = 0; index < faceCount; ++index )
|
||||
{
|
||||
const b3QHFace* face = tempFaces[index];
|
||||
@@ -2176,6 +2226,18 @@ b3HullData* b3CreateHull( const b3Vec3* points, int pointCount, int maxVertexCou
|
||||
|
||||
faces[index].edge = (uint8_t)face->edge->finalIndex;
|
||||
planes[index] = face->plane;
|
||||
|
||||
b3Vec3 n = face->plane.normal;
|
||||
nx[index] = n.x;
|
||||
ny[index] = n.y;
|
||||
nz[index] = n.z;
|
||||
}
|
||||
|
||||
for ( int index = faceCount; index < soaNormalCount; ++index )
|
||||
{
|
||||
nx[index] = 0.0f;
|
||||
ny[index] = 0.0f;
|
||||
nz[index] = 0.0f;
|
||||
}
|
||||
|
||||
// All builder pointers are dead from here on.
|
||||
@@ -2238,9 +2300,10 @@ bool b3CompareHullData( const b3HullData* hull1, const b3HullData* hull2 )
|
||||
|
||||
// Hull identity covers every byte, so the structs carry explicit padding. These lock
|
||||
// the layout, re-audit padding if a size changes.
|
||||
_Static_assert( sizeof( b3HullData ) == 136, "unexpected hull data size" );
|
||||
_Static_assert( sizeof( b3BoxHull ) == 440, "unexpected box hull size" );
|
||||
_Static_assert( sizeof( b3HullData ) == 144, "unexpected hull data size" );
|
||||
_Static_assert( sizeof( b3BoxHull ) == 648, "unexpected box hull size" );
|
||||
|
||||
// Implement b3HullMap.
|
||||
#define NAME b3HullMap
|
||||
#define KEY_TY const b3HullData*
|
||||
#define VAL_TY int
|
||||
@@ -2269,7 +2332,7 @@ b3HullData* b3CloneAndTransformHull( const b3HullData* original, b3Transform tra
|
||||
return NULL;
|
||||
}
|
||||
|
||||
b3HullData* hull = (b3HullData*)b3Alloc( original->byteCount );
|
||||
b3HullData* hull = b3Alloc( original->byteCount );
|
||||
memcpy( hull, original, original->byteCount );
|
||||
|
||||
b3Vec3 safeScale = b3SafeScale( scale );
|
||||
@@ -2286,9 +2349,9 @@ b3HullData* b3CloneAndTransformHull( const b3HullData* original, b3Transform tra
|
||||
{
|
||||
const b3HullFace* face = faces + i;
|
||||
|
||||
uint8_t startEdgeIndex = face->edge;
|
||||
uint8_t currentEdgeIndex = startEdgeIndex;
|
||||
uint8_t prevEdgeIndex = UINT8_MAX;
|
||||
int startEdgeIndex = face->edge;
|
||||
int currentEdgeIndex = startEdgeIndex;
|
||||
int prevEdgeIndex = B3_NULL_INDEX;
|
||||
|
||||
do
|
||||
{
|
||||
@@ -2304,7 +2367,7 @@ b3HullData* b3CloneAndTransformHull( const b3HullData* original, b3Transform tra
|
||||
}
|
||||
while ( currentEdgeIndex != startEdgeIndex );
|
||||
|
||||
B3_ASSERT( prevEdgeIndex != UINT8_MAX );
|
||||
B3_ASSERT( prevEdgeIndex != B3_NULL_INDEX );
|
||||
|
||||
currentEdgeIndex = startEdgeIndex;
|
||||
|
||||
@@ -2312,7 +2375,7 @@ b3HullData* b3CloneAndTransformHull( const b3HullData* original, b3Transform tra
|
||||
{
|
||||
b3HullHalfEdge* edge = edges + currentEdgeIndex;
|
||||
uint8_t nextIndex = edge->next;
|
||||
edge->next = prevEdgeIndex;
|
||||
edge->next = (uint8_t)prevEdgeIndex;
|
||||
|
||||
if ( currentEdgeIndex < edge->twin )
|
||||
{
|
||||
@@ -2339,12 +2402,32 @@ b3HullData* b3CloneAndTransformHull( const b3HullData* original, b3Transform tra
|
||||
b3Matrix3 matrix = b3MakeMatrixFromQuat( transform.q );
|
||||
b3Vec3* points = b3GetHullPointsWrite( hull );
|
||||
|
||||
int soaVertexCount = ( vertexCount + 3 ) & ~3;
|
||||
float* vx = b3GetHullSoaVerticesWrite( hull );
|
||||
float* vy = vx + soaVertexCount;
|
||||
float* vz = vy + soaVertexCount;
|
||||
|
||||
for ( int i = 0; i < vertexCount; ++i )
|
||||
{
|
||||
points[i] = b3Add( b3MulMV( matrix, b3Mul( safeScale, points[i] ) ), transform.p );
|
||||
b3Vec3 p = b3Add( b3MulMV( matrix, b3Mul( safeScale, points[i] ) ), transform.p );
|
||||
points[i] = p;
|
||||
vx[i] = p.x;
|
||||
vy[i] = p.y;
|
||||
vz[i] = p.z;
|
||||
}
|
||||
|
||||
for ( int i = vertexCount; i < soaVertexCount; ++i )
|
||||
{
|
||||
vx[i] = vx[0];
|
||||
vy[i] = vy[0];
|
||||
vz[i] = vz[0];
|
||||
}
|
||||
|
||||
b3Plane* planes = b3GetHullPlanesWrite( hull );
|
||||
int soaNormalCount = ( faceCount + 3 ) & ~3;
|
||||
float* nx = b3GetHullSoaNormalsWrite( hull );
|
||||
float* ny = nx + soaNormalCount;
|
||||
float* nz = ny + soaNormalCount;
|
||||
|
||||
for ( int i = 0; i < faceCount; ++i )
|
||||
{
|
||||
@@ -2390,6 +2473,17 @@ b3HullData* b3CloneAndTransformHull( const b3HullData* original, b3Transform tra
|
||||
normal = b3MulSV( 1.0f / area, normal );
|
||||
|
||||
planes[i] = b3MakePlaneFromNormalAndPoint( normal, centroid );
|
||||
|
||||
nx[i] = normal.x;
|
||||
ny[i] = normal.y;
|
||||
nz[i] = normal.z;
|
||||
}
|
||||
|
||||
for ( int i = faceCount; i < soaNormalCount; ++i )
|
||||
{
|
||||
nx[i] = 0.0f;
|
||||
ny[i] = 0.0f;
|
||||
nz[i] = 0.0f;
|
||||
}
|
||||
|
||||
b3UpdateHullBounds( hull );
|
||||
@@ -2641,8 +2735,10 @@ static const b3BoxHull s_boxHull = {
|
||||
.vertexOffset = offsetof( b3BoxHull, boxVertices ),
|
||||
.pointOffset = offsetof( b3BoxHull, boxPoints ),
|
||||
.edgeOffset = offsetof( b3BoxHull, boxEdges ),
|
||||
.faceOffset = offsetof( b3BoxHull, boxFaces ),
|
||||
.planeOffset = offsetof( b3BoxHull, boxPlanes ),
|
||||
.faceOffset = offsetof( b3BoxHull, boxFaces ),
|
||||
.soaVertexOffset = offsetof( b3BoxHull, vx ),
|
||||
.soaNormalOffset = offsetof( b3BoxHull, nx ),
|
||||
},
|
||||
.boxVertices =
|
||||
{
|
||||
@@ -2677,6 +2773,8 @@ static const b3BoxHull s_boxHull = {
|
||||
|
||||
b3BoxHull b3MakeTransformedBoxHull( float hx, float hy, float hz, b3Transform transform )
|
||||
{
|
||||
B3_ASSERT( b3IsValidTransform( transform ) );
|
||||
|
||||
b3BoxHull boxHull = s_boxHull;
|
||||
|
||||
float minH = 0.2f * B3_LINEAR_SLOP;
|
||||
@@ -2710,6 +2808,61 @@ b3BoxHull b3MakeTransformedBoxHull( float hx, float hy, float hz, b3Transform tr
|
||||
boxHull.boxPoints[6] = b3TransformPoint( transform, (b3Vec3){ -h.x, -h.y, -h.z } );
|
||||
boxHull.boxPoints[7] = b3TransformPoint( transform, (b3Vec3){ h.x, -h.y, -h.z } );
|
||||
|
||||
// SOA
|
||||
boxHull.vx[0] = boxHull.boxPoints[0].x;
|
||||
boxHull.vx[1] = boxHull.boxPoints[1].x;
|
||||
boxHull.vx[2] = boxHull.boxPoints[2].x;
|
||||
boxHull.vx[3] = boxHull.boxPoints[3].x;
|
||||
boxHull.vx[4] = boxHull.boxPoints[4].x;
|
||||
boxHull.vx[5] = boxHull.boxPoints[5].x;
|
||||
boxHull.vx[6] = boxHull.boxPoints[6].x;
|
||||
boxHull.vx[7] = boxHull.boxPoints[7].x;
|
||||
|
||||
boxHull.vy[0] = boxHull.boxPoints[0].y;
|
||||
boxHull.vy[1] = boxHull.boxPoints[1].y;
|
||||
boxHull.vy[2] = boxHull.boxPoints[2].y;
|
||||
boxHull.vy[3] = boxHull.boxPoints[3].y;
|
||||
boxHull.vy[4] = boxHull.boxPoints[4].y;
|
||||
boxHull.vy[5] = boxHull.boxPoints[5].y;
|
||||
boxHull.vy[6] = boxHull.boxPoints[6].y;
|
||||
boxHull.vy[7] = boxHull.boxPoints[7].y;
|
||||
|
||||
boxHull.vz[0] = boxHull.boxPoints[0].z;
|
||||
boxHull.vz[1] = boxHull.boxPoints[1].z;
|
||||
boxHull.vz[2] = boxHull.boxPoints[2].z;
|
||||
boxHull.vz[3] = boxHull.boxPoints[3].z;
|
||||
boxHull.vz[4] = boxHull.boxPoints[4].z;
|
||||
boxHull.vz[5] = boxHull.boxPoints[5].z;
|
||||
boxHull.vz[6] = boxHull.boxPoints[6].z;
|
||||
boxHull.vz[7] = boxHull.boxPoints[7].z;
|
||||
|
||||
boxHull.nx[0] = boxHull.boxPlanes[0].normal.x;
|
||||
boxHull.nx[1] = boxHull.boxPlanes[1].normal.x;
|
||||
boxHull.nx[2] = boxHull.boxPlanes[2].normal.x;
|
||||
boxHull.nx[3] = boxHull.boxPlanes[3].normal.x;
|
||||
boxHull.nx[4] = boxHull.boxPlanes[4].normal.x;
|
||||
boxHull.nx[5] = boxHull.boxPlanes[5].normal.x;
|
||||
boxHull.nx[6] = 0.0f;
|
||||
boxHull.nx[7] = 0.0f;
|
||||
|
||||
boxHull.ny[0] = boxHull.boxPlanes[0].normal.y;
|
||||
boxHull.ny[1] = boxHull.boxPlanes[1].normal.y;
|
||||
boxHull.ny[2] = boxHull.boxPlanes[2].normal.y;
|
||||
boxHull.ny[3] = boxHull.boxPlanes[3].normal.y;
|
||||
boxHull.ny[4] = boxHull.boxPlanes[4].normal.y;
|
||||
boxHull.ny[5] = boxHull.boxPlanes[5].normal.y;
|
||||
boxHull.ny[6] = 0.0f;
|
||||
boxHull.ny[7] = 0.0f;
|
||||
|
||||
boxHull.nz[0] = boxHull.boxPlanes[0].normal.z;
|
||||
boxHull.nz[1] = boxHull.boxPlanes[1].normal.z;
|
||||
boxHull.nz[2] = boxHull.boxPlanes[2].normal.z;
|
||||
boxHull.nz[3] = boxHull.boxPlanes[3].normal.z;
|
||||
boxHull.nz[4] = boxHull.boxPlanes[4].normal.z;
|
||||
boxHull.nz[5] = boxHull.boxPlanes[5].normal.z;
|
||||
boxHull.nz[6] = 0.0f;
|
||||
boxHull.nz[7] = 0.0f;
|
||||
|
||||
boxHull.base.hash = 0;
|
||||
boxHull.base.hash = b3NonZeroHash( b3Hash( B3_HASH_INIT, (uint8_t*)&boxHull, sizeof( b3BoxHull ) ) );
|
||||
|
||||
@@ -2784,6 +2937,8 @@ void b3ScaleBox( b3Vec3* halfWidths, b3Transform* transform, b3Vec3 postScale, f
|
||||
// todo use new hull scaling technique
|
||||
b3BoxHull b3MakeScaledBoxHull( b3Vec3 halfWidths, b3Transform transform, b3Vec3 postScale )
|
||||
{
|
||||
B3_ASSERT( b3IsValidTransform( transform ) );
|
||||
|
||||
b3Vec3 h = halfWidths;
|
||||
b3Transform xf = transform;
|
||||
b3ScaleBox( &h, &xf, postScale, 4.0f * B3_LINEAR_SLOP );
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
uint64_t b3HashHullData( const b3HullData* hull );
|
||||
bool b3CompareHullData( const b3HullData* hull1, const b3HullData* hull2 );
|
||||
|
||||
// Map keyed by hull content. The world hull database stores a reference count,
|
||||
// compound baking stores a byte offset. Implementation lives in hull.c.
|
||||
// Map keyed by hull content. The world hull database uses the value as a reference count,
|
||||
// while compound baking stores uses the value as a byte offset. Implementation is in hull.c.
|
||||
#define NAME b3HullMap
|
||||
#define KEY_TY const b3HullData*
|
||||
#define VAL_TY int
|
||||
2
vendor/box3d/src/src/island.c
vendored
2
vendor/box3d/src/src/island.c
vendored
@@ -498,7 +498,7 @@ void b3SplitIsland( b3World* world, int baseId )
|
||||
}
|
||||
}
|
||||
|
||||
// Early return — island is still fully connected, no split needed.
|
||||
// Island is still fully connected, no split needed.
|
||||
if ( componentCount == 1 )
|
||||
{
|
||||
baseIsland->constraintRemoveCount = 0;
|
||||
|
||||
54
vendor/box3d/src/src/manifold.c
vendored
54
vendor/box3d/src/src/manifold.c
vendored
@@ -10,58 +10,6 @@
|
||||
|
||||
#include "box3d/math_functions.h"
|
||||
|
||||
// p1 : origin on edge 1
|
||||
// e1 : edge 1
|
||||
// c1 : shape 1 centroid
|
||||
// p2 : origin on edge 2
|
||||
// e2 : edge 2
|
||||
// c2 : shape 2 centroid
|
||||
float b3EdgeEdgeSeparation( b3Vec3 p1, b3Vec3 e1, b3Vec3 c1, b3Vec3 p2, b3Vec3 e2, b3Vec3 c2 )
|
||||
{
|
||||
// Build search direction
|
||||
b3Vec3 u = b3Cross( e1, e2 );
|
||||
float length = b3Length( u );
|
||||
|
||||
// Skip near parallel edges: |e1 x e1| = sin(alpha) * |e1| * |e2|
|
||||
const float kTolerance = 0.005f;
|
||||
if ( length < kTolerance * sqrtf( b3LengthSquared( e1 ) * b3LengthSquared( e2 ) ) )
|
||||
{
|
||||
return -FLT_MAX;
|
||||
}
|
||||
|
||||
if ( length * length < 1000.0f * FLT_MIN )
|
||||
{
|
||||
return -FLT_MAX;
|
||||
}
|
||||
|
||||
b3Vec3 n = b3MulSV( 1.0f / length, u );
|
||||
|
||||
// Make sure normal points away from the first shape
|
||||
// For a triangle, it is possible that N is aligned with the triangle normal and the sign
|
||||
// value can be close to zero and flicker between small negative and positive values, leading to
|
||||
// an incorrect separation value. So we assume the other hull has some volume and pick the most
|
||||
// significant sign value to orient N.
|
||||
float sign1 = b3Dot( n, b3Sub( p1, c1 ) );
|
||||
float sign2 = b3Dot( n, b3Sub( p2, c2 ) );
|
||||
if ( b3AbsFloat( sign1 ) > b3AbsFloat( sign2 ) )
|
||||
{
|
||||
if ( sign1 < 0.0f )
|
||||
{
|
||||
n = b3Neg( n );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( sign2 > 0.0f )
|
||||
{
|
||||
n = b3Neg( n );
|
||||
}
|
||||
}
|
||||
|
||||
// s = Dot(n, p2) - d = Dot(n, p2) - Dot(n, p1) = Dot(n, p2 - p1)
|
||||
return b3Dot( n, b3Sub( p2, p1 ) );
|
||||
}
|
||||
|
||||
// This was extended to make the wedge shape get the correct incident face.
|
||||
// Instead of looking directly for the most anti-parallel face, we first find the closest vertex (passed in).
|
||||
// Then we look for all edges coming out of that vertex and look for the edge that is
|
||||
@@ -69,6 +17,8 @@ float b3EdgeEdgeSeparation( b3Vec3 p1, b3Vec3 e1, b3Vec3 c1, b3Vec3 p2, b3Vec3 e
|
||||
// Then from that edge, we select the adjacent face that is most anti-parallel to the reference normal.
|
||||
int b3FindIncidentFace( const b3HullData* hull, b3Vec3 refNormal, int vertexIndex )
|
||||
{
|
||||
B3_ASSERT( 0 <= vertexIndex && vertexIndex < hull->vertexCount );
|
||||
|
||||
const b3HullVertex* vertices = b3GetHullVertices( hull );
|
||||
const b3HullHalfEdge* edges = b3GetHullEdges( hull );
|
||||
const b3Plane* planes = b3GetHullPlanes( hull );
|
||||
|
||||
48
vendor/box3d/src/src/manifold.h
vendored
48
vendor/box3d/src/src/manifold.h
vendored
@@ -8,19 +8,22 @@
|
||||
|
||||
#define B3_MAX_CLIP_POINTS 64
|
||||
|
||||
typedef struct b3FaceQuery
|
||||
{
|
||||
float separation;
|
||||
int faceIndex;
|
||||
int vertexIndex;
|
||||
} b3FaceQuery;
|
||||
|
||||
typedef struct b3EdgeQuery
|
||||
typedef struct b3SeparatingAxis
|
||||
{
|
||||
b3Vec3 normal;
|
||||
float separation;
|
||||
int indexA;
|
||||
int indexB;
|
||||
} b3EdgeQuery;
|
||||
b3SeparatingFeature type;
|
||||
} b3SeparatingAxis;
|
||||
|
||||
typedef struct b3AxisQuery
|
||||
{
|
||||
b3SeparatingAxis faceA;
|
||||
b3SeparatingAxis faceB;
|
||||
b3SeparatingAxis edge;
|
||||
b3SeparatingFeature separatedFeature;
|
||||
} b3AxisQuery;
|
||||
|
||||
typedef struct b3ClipVertex
|
||||
{
|
||||
@@ -35,7 +38,6 @@ typedef enum b3FeatureOwner
|
||||
b3_featureShapeB = 1
|
||||
} b3FeatureOwner;
|
||||
|
||||
float b3EdgeEdgeSeparation( b3Vec3 p1, b3Vec3 e1, b3Vec3 c1, b3Vec3 p2, b3Vec3 e2, b3Vec3 c2 );
|
||||
int b3FindIncidentFace( const b3HullData* hull, b3Vec3 refNormal, int vertexIndex );
|
||||
b3FeaturePair b3MakeFeaturePair( b3FeatureOwner owner1, int index1, b3FeatureOwner owner2, int index2 );
|
||||
|
||||
@@ -43,6 +45,8 @@ b3FeaturePair b3FlipPair( b3FeaturePair pair );
|
||||
|
||||
int b3ClipPolygon( b3ClipVertex* out, b3ClipVertex* polygon, int count, b3Plane clipPlane, int edge, b3Plane refPlane );
|
||||
|
||||
b3AxisQuery b3ComputeSeparatingAxis( const b3HullData* hullA, const b3HullData* hullB, b3Transform xfB, bool earlyReturn );
|
||||
|
||||
#if B3_ENABLE_VALIDATION
|
||||
bool b3ValidatePolygon( b3ClipVertex* polygon, int count );
|
||||
#endif
|
||||
@@ -55,3 +59,27 @@ static inline uint32_t b3MakeFeatureId( b3FeaturePair pair )
|
||||
return ( (uint32_t)pair.owner1 << 24 ) | ( (uint32_t)pair.index1 << 16 ) | ( (uint32_t)pair.owner2 << 8 ) |
|
||||
(uint32_t)pair.index2;
|
||||
}
|
||||
|
||||
static inline b3SeparatingAxis b3GetBestAxis( const b3AxisQuery* query )
|
||||
{
|
||||
B3_VALIDATE( query->faceA.type == b3_faceAxisA );
|
||||
B3_VALIDATE( query->edge.type == b3_edgePairAxis );
|
||||
B3_VALIDATE( query->faceB.type == b3_faceAxisB );
|
||||
|
||||
if ( query->faceA.separation > query->faceB.separation )
|
||||
{
|
||||
if ( query->edge.separation > query->faceA.separation )
|
||||
{
|
||||
return query->edge;
|
||||
}
|
||||
|
||||
return query->faceA;
|
||||
}
|
||||
|
||||
if ( query->edge.separation > query->faceB.separation )
|
||||
{
|
||||
return query->edge;
|
||||
}
|
||||
|
||||
return query->faceB;
|
||||
}
|
||||
|
||||
1
vendor/box3d/src/src/math_internal.h
vendored
1
vendor/box3d/src/src/math_internal.h
vendored
@@ -61,6 +61,7 @@ b3Matrix3 b3BoxInertia( float mass, b3Vec3 min, b3Vec3 max );
|
||||
int b3GetProxySupport( const b3ShapeProxy* proxy, b3Vec3 axis );
|
||||
int b3GetPointSupport( const b3Vec3* points, int count, b3Vec3 axis );
|
||||
|
||||
// Align up to 8 byte alignment.
|
||||
static inline size_t b3AlignUp8( size_t x )
|
||||
{
|
||||
return ( x + 7u ) & ~(size_t)7u;
|
||||
|
||||
12
vendor/box3d/src/src/mesh.c
vendored
12
vendor/box3d/src/src/mesh.c
vendored
@@ -928,7 +928,8 @@ static int b3BuildRecursive( b3Array( b3MeshNode ) * nodes, int count, b3Primiti
|
||||
node->data.asNode.childOffset = rightIndex - index;
|
||||
node->lowerBound = aabb.lowerBound;
|
||||
node->upperBound = aabb.upperBound;
|
||||
// triangleOffset is leaf-only, but lives outside the union — zero it so mesh->hash is deterministic
|
||||
|
||||
// Zero so mesh->hash is deterministic
|
||||
node->triangleOffset = 0;
|
||||
|
||||
return index;
|
||||
@@ -1321,14 +1322,17 @@ b3MeshData* b3CreateWaveMesh( int xCount, int zCount, float cellWidth, float amp
|
||||
float omegaX = 2.0f * B3_PI * columnFrequency * cellWidth;
|
||||
|
||||
float x = -0.5f * xWidth;
|
||||
b3CosSin cs = { 0 };
|
||||
for ( int ix = 0; ix <= xCount; ++ix )
|
||||
{
|
||||
float rowHeight = sinf( omegaX * ix );
|
||||
cs = b3ComputeCosSin( omegaX * ix );
|
||||
float rowHeight = cs.sine;
|
||||
|
||||
float z = -0.5f * zWidth;
|
||||
for ( int iz = 0; iz <= zCount; ++iz )
|
||||
{
|
||||
float columnHeight = sinf( omegaZ * iz );
|
||||
cs = b3ComputeCosSin( omegaZ * iz );
|
||||
float columnHeight = cs.sine;
|
||||
|
||||
float y = amplitude * rowHeight * columnHeight;
|
||||
vertices.data[index] = (b3Vec3){ x, y, z };
|
||||
@@ -1482,7 +1486,7 @@ b3MeshData* b3CreateBoxMesh( b3Vec3 center, b3Vec3 extent, bool identifyEdges )
|
||||
return b3CreateMesh( &def, NULL, 0 );
|
||||
}
|
||||
|
||||
b3MeshData* b3CreateHollowBoxMesh(b3Vec3 center, b3Vec3 extent)
|
||||
b3MeshData* b3CreateHollowBoxMesh( b3Vec3 center, b3Vec3 extent )
|
||||
{
|
||||
float x = extent.x;
|
||||
float y = extent.y;
|
||||
|
||||
6
vendor/box3d/src/src/mesh_contact.c
vendored
6
vendor/box3d/src/src/mesh_contact.c
vendored
@@ -608,7 +608,7 @@ bool b3ComputeMeshManifolds( b3World* world, int workerIndex, b3Contact* contact
|
||||
switch ( shapeB->type )
|
||||
{
|
||||
case b3_capsuleShape:
|
||||
b3CollideCapsuleAndTriangle( manifold, pointCapacity, &shapeB->capsule, vertices, &cache->simplexCache );
|
||||
b3CollideTriangleAndCapsule( manifold, pointCapacity, vertices, &shapeB->capsule, &cache->simplexCache );
|
||||
break;
|
||||
|
||||
case b3_hullShape:
|
||||
@@ -619,14 +619,14 @@ bool b3ComputeMeshManifolds( b3World* world, int workerIndex, b3Contact* contact
|
||||
cache->satCache = (b3SATCache){ 0 };
|
||||
}
|
||||
|
||||
b3CollideHullAndTriangle( manifold, pointCapacity, hullB, vertices[0], vertices[1], vertices[2], triangle.flags,
|
||||
b3CollideTriangleAndHull( manifold, pointCapacity, vertices[0], vertices[1], vertices[2], triangle.flags, hullB,
|
||||
&cache->satCache, enableSpeculative );
|
||||
context->satCallCount += 1;
|
||||
context->satCacheHitCount += cache->satCache.hit;
|
||||
break;
|
||||
|
||||
case b3_sphereShape:
|
||||
b3CollideSphereAndTriangle( manifold, pointCapacity, &shapeB->sphere, vertices );
|
||||
b3CollideTriangleAndSphere( manifold, pointCapacity, vertices, &shapeB->sphere );
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
3
vendor/box3d/src/src/parallel_for.c
vendored
3
vendor/box3d/src/src/parallel_for.c
vendored
@@ -77,7 +77,8 @@ void b3ParallelFor( b3World* world, b3ParallelForCallback* callback, int itemCou
|
||||
// Target multiple blocks per worker to reduce thread stalls.
|
||||
// block size grows once items exceed maxBlockCount * minRange
|
||||
// so the block count stays bounded and per-block sync overhead stays low.
|
||||
int blocksPerWorker = 4;
|
||||
// Benchmarking shows 32 is optimal for the convex pile benchmark and others.
|
||||
int blocksPerWorker = 32;
|
||||
int maxBlockCount = blocksPerWorker * workerCount;
|
||||
|
||||
int blockSize;
|
||||
|
||||
39
vendor/box3d/src/src/physics_world.c
vendored
39
vendor/box3d/src/src/physics_world.c
vendored
@@ -11,7 +11,7 @@
|
||||
#include "contact.h"
|
||||
#include "core.h"
|
||||
#include "ctz.h"
|
||||
#include "hull_map.h"
|
||||
#include "hull.h"
|
||||
#include "island.h"
|
||||
#include "joint.h"
|
||||
#include "parallel_for.h"
|
||||
@@ -41,18 +41,21 @@ const b3HullData* b3AddHullToDatabase( b3World* world, const b3HullData* src )
|
||||
{
|
||||
b3HullMap* database = world->hullDatabase;
|
||||
|
||||
// Compare by content so an unowned query hull finds the shared copy.
|
||||
// Compare by content to de-duplicate. Not trusting the hash.
|
||||
b3HullMap_itr itr = b3HullMap_get( database, src );
|
||||
if ( b3HullMap_is_end( itr ) == false )
|
||||
{
|
||||
// Bump reference count.
|
||||
itr.data->val += 1;
|
||||
return itr.data->key;
|
||||
}
|
||||
|
||||
b3HullData* owned = b3CloneHull( src );
|
||||
B3_ASSERT( owned != NULL );
|
||||
b3HullMap_insert( database, owned, 1 );
|
||||
return owned;
|
||||
b3HullData* clone = b3CloneHull( src );
|
||||
B3_ASSERT( clone != NULL );
|
||||
|
||||
// Start with reference count of 1.
|
||||
b3HullMap_insert( database, clone, 1 );
|
||||
return clone;
|
||||
}
|
||||
|
||||
const b3HullData* b3AddOwnedHullToDatabase( b3World* world, b3HullData* owned )
|
||||
@@ -1426,20 +1429,20 @@ void b3World_Draw( b3WorldId worldId, b3DebugDraw* draw, uint64_t maskBits )
|
||||
const char* name = b3FindName( &world->names, body->nameId );
|
||||
if ( name != NULL )
|
||||
{
|
||||
draw->DrawStringFcn( p, name, b3_colorOrange, draw->context );
|
||||
draw->DrawStringFcn( p, name, b3_colorWhite, draw->context );
|
||||
}
|
||||
}
|
||||
|
||||
if ( draw->drawMass && body->type == b3_dynamicBody )
|
||||
{
|
||||
b3Vec3 offset = { 0.1f, 0.1f, 0.1f };
|
||||
b3Vec3 offset = { 0.05f, 0.05f, 0.05f };
|
||||
|
||||
b3WorldTransform transform = { bodySim->center, bodySim->transform.q };
|
||||
draw->DrawTransformFcn( transform, draw->context );
|
||||
b3Pos p = b3TransformWorldPoint( transform, offset );
|
||||
|
||||
char buffer[32];
|
||||
snprintf( buffer, 32, " %.2f", body->mass );
|
||||
snprintf( buffer, 32, "%.2f", body->mass );
|
||||
draw->DrawStringFcn( p, buffer, b3_colorWhite, draw->context );
|
||||
}
|
||||
|
||||
@@ -1523,8 +1526,10 @@ void b3World_Draw( b3WorldId worldId, b3DebugDraw* draw, uint64_t maskBits )
|
||||
b3Vec3 normal = manifold->normal;
|
||||
|
||||
// Average the anchors not the world points so the friction center stays exact far from the origin
|
||||
b3Pos contactCenter = draw->drawAnchorA == 1 ? bodySimA->center : bodySimB->center;
|
||||
b3Vec3 anchorSum = b3Vec3_zero;
|
||||
b3Pos contactCenter = draw->drawAnchorA ? bodySimA->center : bodySimB->center;
|
||||
b3Vec3 frictionAnchor = b3Vec3_zero;
|
||||
float totalWeight = 0.0f;
|
||||
float invTau = 1.0f / B3_SPECULATIVE_DISTANCE;
|
||||
|
||||
const b3ManifoldPoint* points = manifold->points;
|
||||
for ( int pointIndex = 0; pointIndex < manifold->pointCount; ++pointIndex )
|
||||
@@ -1533,10 +1538,13 @@ void b3World_Draw( b3WorldId worldId, b3DebugDraw* draw, uint64_t maskBits )
|
||||
|
||||
char buffer[32];
|
||||
|
||||
b3Vec3 anchor = draw->drawAnchorA == 1 ? mp->anchorA : mp->anchorB;
|
||||
b3Vec3 anchor = draw->drawAnchorA ? mp->anchorA : mp->anchorB;
|
||||
b3Pos p = b3OffsetPos( contactCenter, anchor );
|
||||
|
||||
anchorSum = b3Add( anchorSum, anchor );
|
||||
// See similar friction anchor weights in b3PrepareContacts_Mesh.
|
||||
float weight = b3ClampFloat( 2.0f - mp->separation * invTau, B3_MIN_FRICTION_WEIGHT, 1.0f );
|
||||
frictionAnchor = b3MulAdd( frictionAnchor, weight, anchor );
|
||||
totalWeight += weight;
|
||||
|
||||
if ( draw->drawContactNormals )
|
||||
{
|
||||
@@ -1599,9 +1607,8 @@ void b3World_Draw( b3WorldId worldId, b3DebugDraw* draw, uint64_t maskBits )
|
||||
{
|
||||
// Hack inv_dt for single step debugging
|
||||
float inv_dt = world->inv_dt > 0.0f ? world->inv_dt : 60.0f;
|
||||
|
||||
b3Vec3 avgAnchor = b3MulSV( 1.0f / manifold->pointCount, anchorSum );
|
||||
b3Pos p1 = b3OffsetPos( contactCenter, avgAnchor );
|
||||
frictionAnchor = b3MulSV( 1.0f / totalWeight, frictionAnchor );
|
||||
b3Pos p1 = b3OffsetPos( contactCenter, frictionAnchor );
|
||||
b3Vec3 frictionForce = b3MulSV( 0.5f * inv_dt, manifold->frictionImpulse );
|
||||
b3Pos p2 = b3OffsetPos( p1, b3MulSV( draw->forceScale, frictionForce ) );
|
||||
draw->DrawSegmentFcn( p1, p2, frictionColor, draw->context );
|
||||
|
||||
2
vendor/box3d/src/src/physics_world.h
vendored
2
vendor/box3d/src/src/physics_world.h
vendored
@@ -178,7 +178,7 @@ typedef struct b3World
|
||||
b3Array( b3Shape ) shapes;
|
||||
|
||||
// Reference counted store of shared hull data keyed by content. Shapes hold a
|
||||
// pointer to the owned copy here. Opaque to avoid leaking the verstable map
|
||||
// pointer to the hull stored in the db. Type erased to avoid leaking the verstable map
|
||||
// type into this header.
|
||||
void* hullDatabase;
|
||||
|
||||
|
||||
56
vendor/box3d/src/src/recording.c
vendored
56
vendor/box3d/src/src/recording.c
vendored
@@ -799,23 +799,15 @@ uint64_t b3Hash64Blob( const uint8_t* bytes, int n )
|
||||
static uint32_t b3RegistryPush( b3GeometryRegistry* reg, b3GeometryHashMap* map, b3GeometryHashMap_itr itr, bool hashPresent,
|
||||
b3GeometryKind kind, uint64_t contentHash, uint8_t* bytes, int byteCount )
|
||||
{
|
||||
if ( reg->count >= reg->capacity )
|
||||
{
|
||||
int newCap = reg->capacity < 8 ? 8 : reg->capacity * 2;
|
||||
reg->entries = (b3GeometryEntry*)b3GrowAlloc( reg->entries, reg->capacity * (int)sizeof( b3GeometryEntry ),
|
||||
newCap * (int)sizeof( b3GeometryEntry ) );
|
||||
reg->capacity = newCap;
|
||||
}
|
||||
|
||||
uint32_t id = (uint32_t)reg->count;
|
||||
b3GeometryEntry* entry = reg->entries + reg->count;
|
||||
uint32_t id = (uint32_t)reg->entries.count;
|
||||
b3GeometryEntry* entry = b3Array_Emplace( reg->entries );
|
||||
entry->contentHash = contentHash;
|
||||
entry->id = id;
|
||||
entry->kind = kind;
|
||||
entry->byteCount = byteCount;
|
||||
entry->bytes = bytes; // take ownership
|
||||
// Take ownership.
|
||||
entry->bytes = bytes;
|
||||
entry->hashNext = hashPresent ? (int)itr.data->val : B3_NULL_INDEX;
|
||||
reg->count++;
|
||||
|
||||
if ( hashPresent )
|
||||
{
|
||||
@@ -832,11 +824,11 @@ static b3GeometryHashMap* b3RegistryMap( b3GeometryRegistry* reg )
|
||||
{
|
||||
if ( reg->dedupMap == NULL )
|
||||
{
|
||||
b3GeometryHashMap* fresh = (b3GeometryHashMap*)b3Alloc( sizeof( b3GeometryHashMap ) );
|
||||
b3GeometryHashMap* fresh = b3Alloc( sizeof( b3GeometryHashMap ) );
|
||||
b3GeometryHashMap_init( fresh );
|
||||
reg->dedupMap = fresh;
|
||||
}
|
||||
return (b3GeometryHashMap*)reg->dedupMap;
|
||||
return reg->dedupMap;
|
||||
}
|
||||
|
||||
uint32_t b3InternGeometry( b3GeometryRegistry* reg, b3GeometryKind kind, uint64_t contentHash, uint8_t* bytes, int byteCount )
|
||||
@@ -848,12 +840,12 @@ uint32_t b3InternGeometry( b3GeometryRegistry* reg, b3GeometryKind kind, uint64_
|
||||
if ( hashPresent )
|
||||
{
|
||||
// Walk every entry sharing this hash so a collision still finds the identical blob.
|
||||
for ( int idx = (int)itr.data->val; idx != B3_NULL_INDEX; idx = reg->entries[idx].hashNext )
|
||||
for ( int index = (int)itr.data->val; index != B3_NULL_INDEX; index = reg->entries.data[index].hashNext )
|
||||
{
|
||||
b3GeometryEntry* e = reg->entries + idx;
|
||||
b3GeometryEntry* e = reg->entries.data + index;
|
||||
if ( e->byteCount == byteCount && memcmp( e->bytes, bytes, (size_t)byteCount ) == 0 )
|
||||
{
|
||||
// Duplicate: the caller transferred ownership; return existing id
|
||||
// Duplicate. Free bytes because the caller transferred ownership. Return existing id.
|
||||
b3Free( bytes, (size_t)byteCount );
|
||||
return e->id;
|
||||
}
|
||||
@@ -873,22 +865,18 @@ uint32_t b3AppendGeometry( b3GeometryRegistry* reg, b3GeometryKind kind, uint64_
|
||||
|
||||
void b3FreeRegistry( b3GeometryRegistry* reg )
|
||||
{
|
||||
for ( int i = 0; i < reg->count; ++i )
|
||||
for ( int i = 0; i < reg->entries.count; ++i )
|
||||
{
|
||||
b3Free( reg->entries[i].bytes, (size_t)reg->entries[i].byteCount );
|
||||
}
|
||||
if ( reg->entries != NULL )
|
||||
{
|
||||
b3Free( reg->entries, (size_t)( reg->capacity * (int)sizeof( b3GeometryEntry ) ) );
|
||||
b3Free( reg->entries.data[i].bytes, (size_t)reg->entries.data[i].byteCount );
|
||||
}
|
||||
|
||||
b3Array_Destroy( reg->entries );
|
||||
|
||||
if ( reg->dedupMap != NULL )
|
||||
{
|
||||
b3GeometryHashMap_cleanup( (b3GeometryHashMap*)reg->dedupMap );
|
||||
b3Free( reg->dedupMap, sizeof( b3GeometryHashMap ) );
|
||||
}
|
||||
reg->entries = NULL;
|
||||
reg->count = 0;
|
||||
reg->capacity = 0;
|
||||
reg->dedupMap = NULL;
|
||||
}
|
||||
|
||||
@@ -955,10 +943,10 @@ void b3RecInternTag( b3Recording* rec, uint64_t key, uint64_t id, const char* na
|
||||
// the tag table stops after the geometry entries and ignores the trailing tag bytes.
|
||||
void b3RecWriteRegistry( b3Recording* rec )
|
||||
{
|
||||
b3RecW_U32( &rec->buffer, (uint32_t)rec->registry.count );
|
||||
for ( int i = 0; i < rec->registry.count; ++i )
|
||||
b3RecW_U32( &rec->buffer, (uint32_t)rec->registry.entries.count );
|
||||
for ( int i = 0; i < rec->registry.entries.count; ++i )
|
||||
{
|
||||
b3GeometryEntry* e = rec->registry.entries + i;
|
||||
b3GeometryEntry* e = rec->registry.entries.data + i;
|
||||
b3RecW_U8( &rec->buffer, (uint8_t)e->kind );
|
||||
b3RecW_U32( &rec->buffer, (uint32_t)e->byteCount );
|
||||
b3RecBufAppend( &rec->buffer, e->bytes, e->byteCount );
|
||||
@@ -977,7 +965,7 @@ void b3RecWriteRegistry( b3Recording* rec )
|
||||
|
||||
b3Recording* b3CreateRecording( int byteCapacity )
|
||||
{
|
||||
b3Recording* rec = (b3Recording*)b3Alloc( sizeof( b3Recording ) );
|
||||
b3Recording* rec = b3Alloc( sizeof( b3Recording ) );
|
||||
*rec = (b3Recording){ 0 };
|
||||
|
||||
int initCap = byteCapacity > 0 ? byteCapacity : 65536;
|
||||
@@ -1196,7 +1184,7 @@ b3Recording* b3LoadRecordingFromFile( const char* path )
|
||||
uint32_t b3RecInternHull( b3Recording* rec, const b3HullData* hull )
|
||||
{
|
||||
int byteCount = hull->byteCount;
|
||||
uint8_t* bytes = (uint8_t*)b3Alloc( (size_t)byteCount );
|
||||
uint8_t* bytes = b3Alloc( (size_t)byteCount );
|
||||
memcpy( bytes, hull, (size_t)byteCount );
|
||||
uint64_t h = b3Hash64Blob( bytes, byteCount );
|
||||
return b3InternGeometry( &rec->registry, b3_geometryHull, h, bytes, byteCount );
|
||||
@@ -1205,7 +1193,7 @@ uint32_t b3RecInternHull( b3Recording* rec, const b3HullData* hull )
|
||||
uint32_t b3RecInternMesh( b3Recording* rec, const b3MeshData* mesh )
|
||||
{
|
||||
int byteCount = mesh->byteCount;
|
||||
uint8_t* bytes = (uint8_t*)b3Alloc( (size_t)byteCount );
|
||||
uint8_t* bytes = b3Alloc( (size_t)byteCount );
|
||||
memcpy( bytes, mesh, (size_t)byteCount );
|
||||
uint64_t h = b3Hash64Blob( bytes, byteCount );
|
||||
return b3InternGeometry( &rec->registry, b3_geometryMesh, h, bytes, byteCount );
|
||||
@@ -1214,7 +1202,7 @@ uint32_t b3RecInternMesh( b3Recording* rec, const b3MeshData* mesh )
|
||||
uint32_t b3RecInternHeightField( b3Recording* rec, const b3HeightFieldData* hf )
|
||||
{
|
||||
int byteCount = hf->byteCount;
|
||||
uint8_t* bytes = (uint8_t*)b3Alloc( (size_t)byteCount );
|
||||
uint8_t* bytes = b3Alloc( (size_t)byteCount );
|
||||
memcpy( bytes, hf, (size_t)byteCount );
|
||||
uint64_t h = b3Hash64Blob( bytes, byteCount );
|
||||
return b3InternGeometry( &rec->registry, b3_geometryHeightField, h, bytes, byteCount );
|
||||
@@ -1223,7 +1211,7 @@ uint32_t b3RecInternHeightField( b3Recording* rec, const b3HeightFieldData* hf )
|
||||
uint32_t b3RecInternCompound( b3Recording* rec, const b3CompoundData* compound )
|
||||
{
|
||||
int byteCount = compound->byteCount;
|
||||
uint8_t* bytes = (uint8_t*)b3Alloc( (size_t)byteCount );
|
||||
uint8_t* bytes = b3Alloc( (size_t)byteCount );
|
||||
memcpy( bytes, compound, (size_t)byteCount );
|
||||
// Null the tree node pointer in the copy so the canonical bytes are pointer-free.
|
||||
// b3ConvertBytesToCompound fixes it back on load via nodeOffset.
|
||||
|
||||
15
vendor/box3d/src/src/recording.h
vendored
15
vendor/box3d/src/src/recording.h
vendored
@@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "container.h"
|
||||
#include "core.h"
|
||||
|
||||
#include "box3d/id.h"
|
||||
@@ -50,10 +51,10 @@ typedef struct b3World b3World;
|
||||
#define B3_REC_VERSION_MAJOR 4
|
||||
|
||||
// Minor tracks op-stream additions that keep the 48 byte header shape.
|
||||
// Minor version 3 added name cache.
|
||||
#define B3_REC_VERSION_MINOR 3
|
||||
// Minor version 4 added b3Shape_SetMeshMaterial, b3Shape_SetHull, b3Shape_SetMesh
|
||||
#define B3_REC_VERSION_MINOR 4
|
||||
|
||||
// File header, fixed 48 bytes, little-endian. Contains the registry locator so the player
|
||||
// File header, fixed 48 bytes. Contains the registry locator so the player
|
||||
// can load geometry before replaying any ops.
|
||||
typedef struct b3RecHeader
|
||||
{
|
||||
@@ -106,13 +107,13 @@ typedef struct b3GeometryEntry
|
||||
int hashNext;
|
||||
} b3GeometryEntry;
|
||||
|
||||
b3DeclareArray( b3GeometryEntry );
|
||||
|
||||
// Growable array of geometry entries. Ids are array indices, so the array is serialized in order.
|
||||
// dedupMap maps content hash to entry id for O(1) dedup; it is opaque here and owned by recording.c.
|
||||
typedef struct b3GeometryRegistry
|
||||
{
|
||||
b3GeometryEntry* entries;
|
||||
int count;
|
||||
int capacity;
|
||||
b3Array( b3GeometryEntry ) entries;
|
||||
void* dedupMap;
|
||||
} b3GeometryRegistry;
|
||||
|
||||
@@ -126,7 +127,7 @@ typedef struct b3GeometryRegistry
|
||||
typedef struct b3RecTag
|
||||
{
|
||||
// hash of (id, queryName)
|
||||
uint64_t key;
|
||||
uint64_t key;
|
||||
uint64_t id;
|
||||
char queryName[B3_MAX_QUERY_NAME_LENGTH + 1];
|
||||
} b3RecTag;
|
||||
|
||||
7
vendor/box3d/src/src/recording_ops.inl
vendored
7
vendor/box3d/src/src/recording_ops.inl
vendored
@@ -68,6 +68,7 @@ B3_REC_OP( 0x36, BodySetMotionLocks, RET_NONE, ARG( BODYID, body ) ARG( LOCKS, l
|
||||
B3_REC_OP( 0x37, BodySetBullet, RET_NONE, ARG( BODYID, body ) ARG( BOOL, flag ) )
|
||||
B3_REC_OP( 0x38, BodyEnableContactRecycling, RET_NONE, ARG( BODYID, body ) ARG( BOOL, flag ) )
|
||||
B3_REC_OP( 0x39, BodyEnableHitEvents, RET_NONE, ARG( BODYID, body ) ARG( BOOL, flag ) )
|
||||
B3_REC_OP( 0x3A, BodyAllowFastRotation, RET_NONE, ARG( BODYID, body ) ARG( BOOL, flag ) )
|
||||
|
||||
// Shape create/destroy
|
||||
B3_REC_OP( 0x40, CreateSphereShape, RET_SHAPEID, ARG( BODYID, body ) ARG( SHAPEDEF, def ) ARG( SPHERE, sphere ) )
|
||||
@@ -90,9 +91,11 @@ B3_REC_OP( 0x57, ShapeEnablePreSolveEvents, RET_NONE, ARG( SHAPEID, shape ) ARG(
|
||||
B3_REC_OP( 0x58, ShapeEnableHitEvents, RET_NONE, ARG( SHAPEID, shape ) ARG( BOOL, flag ) )
|
||||
B3_REC_OP( 0x59, ShapeSetSphere, RET_NONE, ARG( SHAPEID, shape ) ARG( SPHERE, sphere ) )
|
||||
B3_REC_OP( 0x5A, ShapeSetCapsule, RET_NONE, ARG( SHAPEID, shape ) ARG( CAPSULE, capsule ) )
|
||||
B3_REC_OP( 0x5B, ShapeApplyWind, RET_NONE,
|
||||
ARG( SHAPEID, shape ) ARG( VEC3, wind ) ARG( F32, drag ) ARG( F32, lift ) ARG( F32, maxSpeed ) ARG( BOOL, wake ) )
|
||||
B3_REC_OP( 0x5B, ShapeApplyWind, RET_NONE, ARG( SHAPEID, shape ) ARG( VEC3, wind ) ARG( F32, drag ) ARG( F32, lift ) ARG( F32, maxSpeed ) ARG( BOOL, wake ) )
|
||||
B3_REC_OP( 0x5C, ShapeSetName, RET_NONE, ARG( SHAPEID, shape ) ARG( STR, name ) )
|
||||
B3_REC_OP( 0x5D, ShapeSetMeshMaterial, RET_NONE, ARG( SHAPEID, shape ) ARG( MATERIAL, material ) ARG( I32, index ) )
|
||||
B3_REC_OP( 0x5E, ShapeSetHull, RET_NONE, ARG( SHAPEID, shape ) ARG( GEOMID, geometryId ) )
|
||||
B3_REC_OP( 0x5F, ShapeSetMesh, RET_NONE, ARG( SHAPEID, shape ) ARG( GEOMID, geometryId ) ARG( VEC3, scale ) )
|
||||
|
||||
// Joint create and destroy
|
||||
B3_REC_OP( 0x90, CreateParallelJoint, RET_JOINTID, ARG( WORLDID, world ) ARG( PARALLELJOINTDEF, def ) )
|
||||
|
||||
47
vendor/box3d/src/src/recording_replay.c
vendored
47
vendor/box3d/src/src/recording_replay.c
vendored
@@ -957,6 +957,11 @@ static void b3RecDispatch_BodySetBullet( const b3RecArgs_BodySetBullet* a, b3Rec
|
||||
b3Body_SetBullet( b3RecMakeBodyId( rdr, a->body ), a->flag );
|
||||
}
|
||||
|
||||
static void b3RecDispatch_BodyAllowFastRotation( const b3RecArgs_BodyAllowFastRotation* a, b3RecReader* rdr )
|
||||
{
|
||||
b3Body_AllowFastRotation( b3RecMakeBodyId( rdr, a->body ), a->flag );
|
||||
}
|
||||
|
||||
static void b3RecDispatch_BodyEnableContactRecycling( const b3RecArgs_BodyEnableContactRecycling* a, b3RecReader* rdr )
|
||||
{
|
||||
b3Body_EnableContactRecycling( b3RecMakeBodyId( rdr, a->body ), a->flag );
|
||||
@@ -1019,7 +1024,7 @@ static void b3RecDispatch_CreateMeshShape( const b3RecArgs_CreateMeshShape* a, b
|
||||
return;
|
||||
}
|
||||
b3RegistrySlot* slot = rdr->slots + id;
|
||||
const b3MeshData* mesh = (const b3MeshData*)b3RecGetLiveMesh( slot );
|
||||
const b3MeshData* mesh = b3RecGetLiveMesh( slot );
|
||||
b3BodyId bodyId = b3RecMakeBodyId( rdr, a->body );
|
||||
b3ShapeId gotId = b3CreateMeshShape( bodyId, &a->def, mesh, a->scale );
|
||||
b3RecCheckShapeId( rdr, gotId, recId );
|
||||
@@ -1071,7 +1076,7 @@ static void b3RecDispatch_CreateCompoundShape( const b3RecArgs_CreateCompoundSha
|
||||
b3BodyId bodyId = b3RecMakeBodyId( rdr, a->body );
|
||||
// b3CreateCompoundShape takes a non-const def pointer; cast away const for the scratch def
|
||||
b3ShapeDef shapeDef = a->def;
|
||||
b3ShapeId gotId = b3CreateCompoundShape( bodyId, &shapeDef, compound );
|
||||
b3ShapeId gotId = b3CreateBakedCompoundShape( bodyId, &shapeDef, compound );
|
||||
b3RecCheckShapeId( rdr, gotId, recId );
|
||||
}
|
||||
|
||||
@@ -1105,6 +1110,11 @@ static void b3RecDispatch_ShapeSetSurfaceMaterial( const b3RecArgs_ShapeSetSurfa
|
||||
b3Shape_SetSurfaceMaterial( b3RecMakeShapeId( rdr, a->shape ), a->material );
|
||||
}
|
||||
|
||||
static void b3RecDispatch_ShapeSetMeshMaterial( const b3RecArgs_ShapeSetMeshMaterial* a, b3RecReader* rdr )
|
||||
{
|
||||
b3Shape_SetMeshMaterial( b3RecMakeShapeId( rdr, a->shape ), a->material, a->index );
|
||||
}
|
||||
|
||||
static void b3RecDispatch_ShapeSetFilter( const b3RecArgs_ShapeSetFilter* a, b3RecReader* rdr )
|
||||
{
|
||||
b3Shape_SetFilter( b3RecMakeShapeId( rdr, a->shape ), a->filter, a->invokeContacts );
|
||||
@@ -1140,6 +1150,35 @@ static void b3RecDispatch_ShapeSetCapsule( const b3RecArgs_ShapeSetCapsule* a, b
|
||||
b3Shape_SetCapsule( b3RecMakeShapeId( rdr, a->shape ), &a->capsule );
|
||||
}
|
||||
|
||||
static void b3RecDispatch_ShapeSetHull( const b3RecArgs_ShapeSetHull* a, b3RecReader* rdr )
|
||||
{
|
||||
uint32_t id = a->geometryId;
|
||||
if ( id >= (uint32_t)rdr->slotCount )
|
||||
{
|
||||
printf( "b3ReplayFile: hull geometryId %u out of range\n", id );
|
||||
rdr->ok = false;
|
||||
return;
|
||||
}
|
||||
b3RegistrySlot* slot = rdr->slots + id;
|
||||
b3ShapeId shapeId = b3RecMakeShapeId( rdr, a->shape );
|
||||
b3Shape_SetHull( shapeId, (const b3HullData*)slot->bytes );
|
||||
}
|
||||
|
||||
static void b3RecDispatch_ShapeSetMesh( const b3RecArgs_ShapeSetMesh* a, b3RecReader* rdr )
|
||||
{
|
||||
uint32_t id = a->geometryId;
|
||||
if ( id >= (uint32_t)rdr->slotCount )
|
||||
{
|
||||
printf( "b3ReplayFile: mesh geometryId %u out of range\n", id );
|
||||
rdr->ok = false;
|
||||
return;
|
||||
}
|
||||
b3RegistrySlot* slot = rdr->slots + id;
|
||||
b3ShapeId shapeId = b3RecMakeShapeId( rdr, a->shape );
|
||||
const b3MeshData* mesh = b3RecGetLiveMesh( slot );
|
||||
b3Shape_SetMesh( shapeId, mesh, a->scale );
|
||||
}
|
||||
|
||||
static void b3RecDispatch_ShapeApplyWind( const b3RecArgs_ShapeApplyWind* a, b3RecReader* rdr )
|
||||
{
|
||||
b3Shape_ApplyWind( b3RecMakeShapeId( rdr, a->shape ), a->wind, a->drag, a->lift, a->maxSpeed, a->wake );
|
||||
@@ -2576,12 +2615,12 @@ static void b3RecCaptureKeyframe( b3RecPlayer* player )
|
||||
b3World* world = b3GetWorldFromId( player->rdr.replayWorldId );
|
||||
b3RecBuffer buf = { 0 };
|
||||
|
||||
int regCountBefore = player->keyframeRec->registry.count;
|
||||
int regCountBefore = player->keyframeRec->registry.entries.count;
|
||||
B3_UNUSED( regCountBefore );
|
||||
|
||||
b3SerializeWorld( world, &buf, player->keyframeRec );
|
||||
// Registry must not grow: all geometry was pre-seeded and the registry dedups exactly.
|
||||
B3_ASSERT( player->keyframeRec->registry.count == regCountBefore );
|
||||
B3_ASSERT( player->keyframeRec->registry.entries.count == regCountBefore );
|
||||
|
||||
size_t bodyBytes = (size_t)player->bodyIdCount * sizeof( b3BodyId );
|
||||
size_t newBytes = (size_t)buf.capacity + bodyBytes;
|
||||
|
||||
5
vendor/box3d/src/src/sensor.c
vendored
5
vendor/box3d/src/src/sensor.c
vendored
@@ -107,9 +107,8 @@ static bool b3SensorQueryCallback( int proxyId, uint64_t userData, void* context
|
||||
b3World* world = queryContext->world;
|
||||
b3Shape* otherShape = b3Array_Get( world->shapes, shapeId );
|
||||
|
||||
// Mesh vs mesh is not supported
|
||||
if ( ( otherShape->type == b3_meshShape || otherShape->type == b3_heightShape ) &&
|
||||
( sensorShape->type == b3_meshShape || sensorShape->type == b3_heightShape ) )
|
||||
// Visitors must be convex.
|
||||
if ( b3IsConvex( otherShape->type ) == false )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
19
vendor/box3d/src/src/shape.c
vendored
19
vendor/box3d/src/src/shape.c
vendored
@@ -424,7 +424,7 @@ b3ShapeId b3CreateHeightFieldShape( b3BodyId bodyId, const b3ShapeDef* def, cons
|
||||
return shapeId;
|
||||
}
|
||||
|
||||
b3ShapeId b3CreateCompoundShape( b3BodyId bodyId, b3ShapeDef* def, const b3CompoundData* compound )
|
||||
b3ShapeId b3CreateBakedCompoundShape( b3BodyId bodyId, b3ShapeDef* def, const b3CompoundData* compound )
|
||||
{
|
||||
b3ShapeId shapeId = b3CreateShape( bodyId, def, compound, b3_compoundShape, b3Transform_identity, b3Vec3_one, false );
|
||||
if ( shapeId.index1 != 0 )
|
||||
@@ -1295,6 +1295,8 @@ void b3Shape_SetMeshMaterial( b3ShapeId shapeId, b3SurfaceMaterial surfaceMateri
|
||||
|
||||
B3_ASSERT( 0 <= index && index < shape->materialCount );
|
||||
B3_ASSERT( shape->type != b3_compoundShape );
|
||||
|
||||
B3_REC( world, ShapeSetMeshMaterial, shapeId, surfaceMaterial, index );
|
||||
b3GetShapeMaterials( shape )[index] = surfaceMaterial;
|
||||
}
|
||||
|
||||
@@ -1613,6 +1615,14 @@ void b3Shape_SetHull( b3ShapeId shapeId, const b3HullData* hull )
|
||||
return;
|
||||
}
|
||||
|
||||
if ( world->recording != NULL )
|
||||
{
|
||||
// Intern the shared hull.
|
||||
uint32_t geometryId = b3RecInternHull( world->recording, data );
|
||||
b3RecArgs_ShapeSetHull setArgs = { shapeId, geometryId };
|
||||
b3RecWrite_ShapeSetHull( world->recording, &setArgs );
|
||||
}
|
||||
|
||||
b3DestroyShapeAllocationForShapeChange( world, shape );
|
||||
|
||||
shape->hull = data;
|
||||
@@ -1640,6 +1650,13 @@ void b3Shape_SetMesh( b3ShapeId shapeId, const b3MeshData* meshData, b3Vec3 scal
|
||||
|
||||
world->locked = true;
|
||||
|
||||
if ( world->recording != NULL )
|
||||
{
|
||||
uint32_t geometryId = b3RecInternMesh( world->recording, meshData );
|
||||
b3RecArgs_ShapeSetMesh setArgs = { shapeId, geometryId, scale };
|
||||
b3RecWrite_ShapeSetMesh( world->recording, &setArgs );
|
||||
}
|
||||
|
||||
b3Shape* shape = b3GetShape( world, shapeId );
|
||||
|
||||
b3DestroyShapeAllocationForShapeChange( world, shape );
|
||||
|
||||
5
vendor/box3d/src/src/shape.h
vendored
5
vendor/box3d/src/src/shape.h
vendored
@@ -153,3 +153,8 @@ static inline bool b3ShouldQueryCollide( const b3Filter* shapeFilter, const b3Qu
|
||||
return ( shapeFilter->categoryBits & queryFilter->maskBits ) != 0 &&
|
||||
( shapeFilter->maskBits & queryFilter->categoryBits ) != 0;
|
||||
}
|
||||
|
||||
static inline bool b3IsConvex( b3ShapeType type )
|
||||
{
|
||||
return type == b3_sphereShape || type == b3_capsuleShape || type == b3_hullShape;
|
||||
}
|
||||
|
||||
568
vendor/box3d/src/src/simd.h
vendored
568
vendor/box3d/src/src/simd.h
vendored
@@ -7,10 +7,35 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#if defined( B3_SIMD_SSE2 )
|
||||
#if defined( B3_SIMD_NEON )
|
||||
|
||||
#include <arm_neon.h>
|
||||
|
||||
// wide float holds 4 numbers
|
||||
typedef float32x4_t b3FloatW;
|
||||
|
||||
#elif defined( B3_SIMD_SSE2 )
|
||||
|
||||
#include <emmintrin.h>
|
||||
|
||||
// wide float holds 4 numbers
|
||||
typedef __m128 b3FloatW;
|
||||
|
||||
#else
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
// scalar math
|
||||
typedef struct b3FloatW
|
||||
{
|
||||
float x, y, z, w;
|
||||
} b3FloatW;
|
||||
|
||||
#endif
|
||||
|
||||
#if defined( B3_SIMD_SSE2 )
|
||||
|
||||
// wide float holds 4 numbers
|
||||
typedef __m128 b3V32;
|
||||
|
||||
@@ -355,3 +380,544 @@ static inline bool b3TestBoundsRayOverlap( b3V32 nodeMin, b3V32 nodeMax, b3V32 r
|
||||
|
||||
bool b3TestBoundsTriangleOverlap( b3V32 nodeCenter, b3V32 nodeExtent, b3V32 vertex1, b3V32 vertex2, b3V32 vertex3 );
|
||||
float b3IntersectRayTriangle( b3V32 rayStart, b3V32 rayDelta, b3V32 vertex1, b3V32 vertex2, b3V32 vertex3 );
|
||||
|
||||
#if defined( B3_SIMD_NEON )
|
||||
|
||||
static inline b3FloatW b3ZeroW( void )
|
||||
{
|
||||
return vdupq_n_f32( 0.0f );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SplatW( float scalar )
|
||||
{
|
||||
return vdupq_n_f32( scalar );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SetW( float a, float b, float c, float d )
|
||||
{
|
||||
float32_t array[4] = { a, b, c, d };
|
||||
return vld1q_f32( array );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3LoadW( const float* data )
|
||||
{
|
||||
return vld1q_f32( data );
|
||||
}
|
||||
|
||||
static inline void b3StoreW( float* data, b3FloatW a )
|
||||
{
|
||||
vst1q_f32( data, a );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3NegW( b3FloatW a )
|
||||
{
|
||||
return vnegq_f32( a );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3AddW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vaddq_f32( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SubW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vsubq_f32( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MulW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vmulq_f32( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3DivW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vdivq_f32( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SqrtW( b3FloatW a )
|
||||
{
|
||||
return vsqrtq_f32( a );
|
||||
}
|
||||
|
||||
// Cannot use real FMA because it doesn't match the non-SIMD path
|
||||
static inline b3FloatW b3MulAddW( b3FloatW a, b3FloatW b, b3FloatW c )
|
||||
{
|
||||
return vaddq_f32( a, vmulq_f32( b, c ) );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MinW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vminq_f32( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MaxW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vmaxq_f32( a, b );
|
||||
}
|
||||
|
||||
// clamp a to [-b, b]
|
||||
static inline b3FloatW b3SymClampW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW nb = b3NegW( b );
|
||||
b3FloatW c = b3MaxW( nb, a );
|
||||
return b3MinW( c, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3AndW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32( a ), vreinterpretq_u32_f32( b ) ) );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3OrW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vreinterpretq_f32_u32( vorrq_u32( vreinterpretq_u32_f32( a ), vreinterpretq_u32_f32( b ) ) );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3GreaterThanW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vreinterpretq_f32_u32( vcgtq_f32( a, b ) );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3LessThanW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vreinterpretq_f32_u32( vcltq_f32( a, b ) );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3EqualsW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return vreinterpretq_f32_u32( vceqq_f32( a, b ) );
|
||||
}
|
||||
|
||||
static inline bool b3AllZeroW( b3FloatW a )
|
||||
{
|
||||
// Create a zero vector for comparison
|
||||
b3FloatW zero = vdupq_n_f32( 0.0f );
|
||||
|
||||
// Compare the input vector with zero
|
||||
uint32x4_t cmp_result = vceqq_f32( a, zero );
|
||||
|
||||
// Check if all comparison results are non-zero using vminvq
|
||||
#ifdef __ARM_FEATURE_SVE
|
||||
// ARM v8.2+ has horizontal minimum instruction
|
||||
return vminvq_u32( cmp_result ) != 0;
|
||||
#else
|
||||
// For older ARM architectures, we need to manually check all lanes
|
||||
return vgetq_lane_u32( cmp_result, 0 ) != 0 && vgetq_lane_u32( cmp_result, 1 ) != 0 && vgetq_lane_u32( cmp_result, 2 ) != 0 &&
|
||||
vgetq_lane_u32( cmp_result, 3 ) != 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// _mm_movemask_ps equivalent, compatible with ARM v7.
|
||||
static inline bool b3AnyTrueW( b3FloatW mask )
|
||||
{
|
||||
uint32x4_t m = vreinterpretq_u32_f32( mask );
|
||||
uint32x2_t p = vorr_u32( vget_low_u32( m ), vget_high_u32( m ) );
|
||||
return ( vget_lane_u32( p, 0 ) | vget_lane_u32( p, 1 ) ) != 0;
|
||||
}
|
||||
|
||||
// component-wise returns mask ? b : a
|
||||
static inline b3FloatW b3BlendW( b3FloatW a, b3FloatW b, b3FloatW mask )
|
||||
{
|
||||
uint32x4_t mask32 = vreinterpretq_u32_f32( mask );
|
||||
return vbslq_f32( mask32, b, a );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3Dot3W( b3FloatW ax, b3FloatW ay, b3FloatW az, b3FloatW bx, b3FloatW by, b3FloatW bz )
|
||||
{
|
||||
return vaddq_f32( vmulq_f32( ax, bx ), vaddq_f32( vmulq_f32( ay, by ), vmulq_f32( az, bz ) ) );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3EmbedIndexW( b3FloatW value, int baseIndex, int bitCount )
|
||||
{
|
||||
uint32_t mask = ( 1u << bitCount ) - 1;
|
||||
const int32_t lanes[4] = { 0, 1, 2, 3 };
|
||||
int32x4_t index = vaddq_s32( vdupq_n_s32( baseIndex ), vld1q_s32( lanes ) );
|
||||
uint32x4_t clearLow = vdupq_n_u32( ~mask );
|
||||
uint32x4_t bits = vorrq_u32( vandq_u32( vreinterpretq_u32_f32( value ), clearLow ), vreinterpretq_u32_s32( index ) );
|
||||
return vreinterpretq_f32_u32( bits );
|
||||
}
|
||||
|
||||
static inline int b3MinIndexW( b3FloatW a, int bitCount )
|
||||
{
|
||||
float32x2_t m = vmin_f32( vget_low_f32( a ), vget_high_f32( a ) );
|
||||
m = vpmin_f32( m, m );
|
||||
uint32_t bits = vget_lane_u32( vreinterpret_u32_f32( m ), 0 );
|
||||
return (int)( bits & ( ( 1u << bitCount ) - 1 ) );
|
||||
}
|
||||
|
||||
#elif defined( B3_SIMD_SSE2 )
|
||||
|
||||
static inline b3FloatW b3ZeroW( void )
|
||||
{
|
||||
return _mm_setzero_ps();
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SplatW( float scalar )
|
||||
{
|
||||
return _mm_set1_ps( scalar );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SetW( float a, float b, float c, float d )
|
||||
{
|
||||
return _mm_setr_ps( a, b, c, d );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3LoadW( const float* data )
|
||||
{
|
||||
return _mm_loadu_ps( data );
|
||||
}
|
||||
|
||||
static inline void b3StoreW( float* data, b3FloatW a )
|
||||
{
|
||||
_mm_storeu_ps( data, a );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3NegW( b3FloatW a )
|
||||
{
|
||||
// Create a mask with the sign bit set for each element
|
||||
__m128 mask = _mm_set1_ps( -0.0f );
|
||||
|
||||
// XOR the input with the mask to negate each element
|
||||
return _mm_xor_ps( a, mask );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3AddW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_add_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SubW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_sub_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MulW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_mul_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3DivW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_div_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SqrtW( b3FloatW a )
|
||||
{
|
||||
return _mm_sqrt_ps( a );
|
||||
}
|
||||
|
||||
// a + b * c
|
||||
static inline b3FloatW b3MulAddW( b3FloatW a, b3FloatW b, b3FloatW c )
|
||||
{
|
||||
return _mm_add_ps( a, _mm_mul_ps( b, c ) );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MinW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_min_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MaxW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_max_ps( a, b );
|
||||
}
|
||||
|
||||
// Horizontal min over a 4-lane vector (result broadcast to all lanes).
|
||||
static inline b3FloatW b3HorizontalMinW( b3FloatW v )
|
||||
{
|
||||
v = _mm_min_ps( v, _mm_shuffle_ps( v, v, _MM_SHUFFLE( 2, 3, 0, 1 ) ) );
|
||||
return _mm_min_ps( v, _mm_shuffle_ps( v, v, _MM_SHUFFLE( 1, 0, 3, 2 ) ) );
|
||||
}
|
||||
|
||||
// clamp a to [-b, b]
|
||||
static inline b3FloatW b3SymClampW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW nb = b3NegW( b );
|
||||
b3FloatW c = b3MaxW( nb, a );
|
||||
return b3MinW( c, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3AndW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_and_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3OrW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_or_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3GreaterThanW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_cmpgt_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3LessThanW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_cmplt_ps( a, b );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3EqualsW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return _mm_cmpeq_ps( a, b );
|
||||
}
|
||||
|
||||
static inline bool b3AllZeroW( b3FloatW a )
|
||||
{
|
||||
// Compare each element with zero
|
||||
b3FloatW zero = _mm_setzero_ps();
|
||||
b3FloatW cmp = _mm_cmpeq_ps( a, zero );
|
||||
|
||||
// Create a mask from the comparison results
|
||||
int mask = _mm_movemask_ps( cmp );
|
||||
|
||||
// If all elements are zero, the mask will be 0xF (1111 in binary)
|
||||
return mask == 0xF;
|
||||
}
|
||||
|
||||
static inline bool b3AnyTrueW( b3FloatW mask )
|
||||
{
|
||||
return _mm_movemask_ps( mask ) != 0;
|
||||
}
|
||||
|
||||
// component-wise returns mask ? b : a
|
||||
static inline b3FloatW b3BlendW( b3FloatW a, b3FloatW b, b3FloatW mask )
|
||||
{
|
||||
return _mm_or_ps( _mm_and_ps( mask, b ), _mm_andnot_ps( mask, a ) );
|
||||
}
|
||||
|
||||
static inline b3FloatW b3Dot3W( b3FloatW ax, b3FloatW ay, b3FloatW az, b3FloatW bx, b3FloatW by, b3FloatW bz )
|
||||
{
|
||||
return _mm_add_ps( _mm_mul_ps( ax, bx ), _mm_add_ps( _mm_mul_ps( ay, by ), _mm_mul_ps( az, bz ) ) );
|
||||
}
|
||||
|
||||
// Replace the low bitCount mantissa bits of each lane with baseIndex + lane. The value must be
|
||||
// positive so the embedded index sorts with the value, and ties fall to the lower index.
|
||||
static inline b3FloatW b3EmbedIndexW( b3FloatW value, int baseIndex, int bitCount )
|
||||
{
|
||||
int mask = ( 1 << bitCount ) - 1;
|
||||
__m128i index = _mm_add_epi32( _mm_set1_epi32( baseIndex ), _mm_setr_epi32( 0, 1, 2, 3 ) );
|
||||
__m128 clearLow = _mm_castsi128_ps( _mm_set1_epi32( ~mask ) );
|
||||
return _mm_or_ps( _mm_and_ps( value, clearLow ), _mm_castsi128_ps( index ) );
|
||||
}
|
||||
|
||||
// Recovers the index embedded by b3EmbedIndexW from the lane holding the minimum.
|
||||
static inline int b3MinIndexW( b3FloatW a, int bitCount )
|
||||
{
|
||||
a = _mm_min_ps( a, _mm_shuffle_ps( a, a, _MM_SHUFFLE( 2, 3, 0, 1 ) ) );
|
||||
a = _mm_min_ps( a, _mm_shuffle_ps( a, a, _MM_SHUFFLE( 1, 0, 3, 2 ) ) );
|
||||
return _mm_cvtsi128_si32( _mm_castps_si128( a ) ) & ( ( 1 << bitCount ) - 1 );
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline b3FloatW b3ZeroW( void )
|
||||
{
|
||||
return (b3FloatW){ 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SplatW( float scalar )
|
||||
{
|
||||
return (b3FloatW){ scalar, scalar, scalar, scalar };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SetW( float a, float b, float c, float d )
|
||||
{
|
||||
return (b3FloatW){ a, b, c, d };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3LoadW( const float* data )
|
||||
{
|
||||
return (b3FloatW){ data[0], data[1], data[2], data[3] };
|
||||
}
|
||||
|
||||
static inline void b3StoreW( float* data, b3FloatW a )
|
||||
{
|
||||
data[0] = a.x;
|
||||
data[1] = a.y;
|
||||
data[2] = a.z;
|
||||
data[3] = a.w;
|
||||
}
|
||||
|
||||
static inline b3FloatW b3NegW( b3FloatW a )
|
||||
{
|
||||
return (b3FloatW){ -a.x, -a.y, -a.z, -a.w };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3AddW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return (b3FloatW){ a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SubW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return (b3FloatW){ a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MulW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return (b3FloatW){ a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3DivW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
return (b3FloatW){ a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3SqrtW( b3FloatW a )
|
||||
{
|
||||
return (b3FloatW){ sqrtf( a.x ), sqrtf( a.y ), sqrtf( a.z ), sqrtf( a.w ) };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MulAddW( b3FloatW a, b3FloatW b, b3FloatW c )
|
||||
{
|
||||
return (b3FloatW){ a.x + b.x * c.x, a.y + b.y * c.y, a.z + b.z * c.z, a.w + b.w * c.w };
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MinW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x <= b.x ? a.x : b.x;
|
||||
r.y = a.y <= b.y ? a.y : b.y;
|
||||
r.z = a.z <= b.z ? a.z : b.z;
|
||||
r.w = a.w <= b.w ? a.w : b.w;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline b3FloatW b3MaxW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x >= b.x ? a.x : b.x;
|
||||
r.y = a.y >= b.y ? a.y : b.y;
|
||||
r.z = a.z >= b.z ? a.z : b.z;
|
||||
r.w = a.w >= b.w ? a.w : b.w;
|
||||
return r;
|
||||
}
|
||||
|
||||
// clamp a to [-b, b]
|
||||
static inline b3FloatW b3SymClampW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x <= b.x ? a.x : b.x;
|
||||
r.y = a.y <= b.y ? a.y : b.y;
|
||||
r.z = a.z <= b.z ? a.z : b.z;
|
||||
r.w = a.w <= b.w ? a.w : b.w;
|
||||
r.x = r.x <= -b.x ? -b.x : r.x;
|
||||
r.y = r.y <= -b.y ? -b.y : r.y;
|
||||
r.z = r.z <= -b.z ? -b.z : r.z;
|
||||
r.w = r.w <= -b.w ? -b.w : r.w;
|
||||
return r;
|
||||
}
|
||||
|
||||
// Logical operations on the scalar path are 0/1 float values. Not bit-wise like SIMD.
|
||||
|
||||
static inline b3FloatW b3AndW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x != 0.0f && b.x != 0.0f ? 1.0f : 0.0f;
|
||||
r.y = a.y != 0.0f && b.y != 0.0f ? 1.0f : 0.0f;
|
||||
r.z = a.z != 0.0f && b.z != 0.0f ? 1.0f : 0.0f;
|
||||
r.w = a.w != 0.0f && b.w != 0.0f ? 1.0f : 0.0f;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline b3FloatW b3OrW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x != 0.0f || b.x != 0.0f ? 1.0f : 0.0f;
|
||||
r.y = a.y != 0.0f || b.y != 0.0f ? 1.0f : 0.0f;
|
||||
r.z = a.z != 0.0f || b.z != 0.0f ? 1.0f : 0.0f;
|
||||
r.w = a.w != 0.0f || b.w != 0.0f ? 1.0f : 0.0f;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline b3FloatW b3GreaterThanW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x > b.x ? 1.0f : 0.0f;
|
||||
r.y = a.y > b.y ? 1.0f : 0.0f;
|
||||
r.z = a.z > b.z ? 1.0f : 0.0f;
|
||||
r.w = a.w > b.w ? 1.0f : 0.0f;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline b3FloatW b3LessThanW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x < b.x ? 1.0f : 0.0f;
|
||||
r.y = a.y < b.y ? 1.0f : 0.0f;
|
||||
r.z = a.z < b.z ? 1.0f : 0.0f;
|
||||
r.w = a.w < b.w ? 1.0f : 0.0f;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline b3FloatW b3EqualsW( b3FloatW a, b3FloatW b )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = a.x == b.x ? 1.0f : 0.0f;
|
||||
r.y = a.y == b.y ? 1.0f : 0.0f;
|
||||
r.z = a.z == b.z ? 1.0f : 0.0f;
|
||||
r.w = a.w == b.w ? 1.0f : 0.0f;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline bool b3AllZeroW( b3FloatW a )
|
||||
{
|
||||
return a.x == 0.0f && a.y == 0.0f && a.z == 0.0f && a.w == 0.0f;
|
||||
}
|
||||
|
||||
static inline bool b3AnyTrueW( b3FloatW mask )
|
||||
{
|
||||
return mask.x != 0.0f || mask.y != 0.0f || mask.z != 0.0f || mask.w != 0.0f;
|
||||
}
|
||||
|
||||
// component-wise returns mask ? b : a
|
||||
static inline b3FloatW b3BlendW( b3FloatW a, b3FloatW b, b3FloatW mask )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = mask.x != 0.0f ? b.x : a.x;
|
||||
r.y = mask.y != 0.0f ? b.y : a.y;
|
||||
r.z = mask.z != 0.0f ? b.z : a.z;
|
||||
r.w = mask.w != 0.0f ? b.w : a.w;
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline b3FloatW b3Dot3W( b3FloatW ax, b3FloatW ay, b3FloatW az, b3FloatW bx, b3FloatW by, b3FloatW bz )
|
||||
{
|
||||
b3FloatW r;
|
||||
r.x = ax.x * bx.x + ( ay.x * by.x + az.x * bz.x );
|
||||
r.y = ax.y * bx.y + ( ay.y * by.y + az.y * bz.y );
|
||||
r.z = ax.z * bx.z + ( ay.z * by.z + az.z * bz.z );
|
||||
r.w = ax.w * bx.w + ( ay.w * by.w + az.w * bz.w );
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline b3FloatW b3EmbedIndexW( b3FloatW value, int baseIndex, int bitCount )
|
||||
{
|
||||
uint32_t mask = ( 1u << bitCount ) - 1;
|
||||
float lanes[4] = { value.x, value.y, value.z, value.w };
|
||||
|
||||
for ( int i = 0; i < 4; ++i )
|
||||
{
|
||||
uint32_t bits;
|
||||
memcpy( &bits, lanes + i, sizeof( bits ) );
|
||||
bits = ( bits & ~mask ) | (uint32_t)( baseIndex + i );
|
||||
memcpy( lanes + i, &bits, sizeof( bits ) );
|
||||
}
|
||||
|
||||
return (b3FloatW){ lanes[0], lanes[1], lanes[2], lanes[3] };
|
||||
}
|
||||
|
||||
static inline int b3MinIndexW( b3FloatW a, int bitCount )
|
||||
{
|
||||
float m = a.x;
|
||||
m = a.y < m ? a.y : m;
|
||||
m = a.z < m ? a.z : m;
|
||||
m = a.w < m ? a.w : m;
|
||||
|
||||
uint32_t bits;
|
||||
memcpy( &bits, &m, sizeof( bits ) );
|
||||
return (int)( bits & ( ( 1u << bitCount ) - 1 ) );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
43
vendor/box3d/src/src/solver.c
vendored
43
vendor/box3d/src/src/solver.c
vendored
@@ -24,6 +24,8 @@
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
_Static_assert( B3_RESTITUTION_ITERATIONS >= 1, "must be 1 or more" );
|
||||
|
||||
// these are useful for solver testing
|
||||
#define ITERATIONS 1
|
||||
#define RELAX_ITERATIONS 1
|
||||
@@ -118,33 +120,33 @@ static void b3IntegrateVelocitiesTask( b3SolverBlock block, b3StepContext* conte
|
||||
b3Vec3 omega2 = omega1;
|
||||
|
||||
// Symmetric inertia tensor: 6 unique entries (column-major)
|
||||
const float i00 = inertiaLocal.cx.x;
|
||||
const float i01 = inertiaLocal.cy.x;
|
||||
const float i02 = inertiaLocal.cz.x;
|
||||
const float i11 = inertiaLocal.cy.y;
|
||||
const float i12 = inertiaLocal.cz.y;
|
||||
const float i22 = inertiaLocal.cz.z;
|
||||
float i00 = inertiaLocal.cx.x;
|
||||
float i01 = inertiaLocal.cy.x;
|
||||
float i02 = inertiaLocal.cz.x;
|
||||
float i11 = inertiaLocal.cy.y;
|
||||
float i12 = inertiaLocal.cz.y;
|
||||
float i22 = inertiaLocal.cz.z;
|
||||
|
||||
for ( int gyroIteration = 0; gyroIteration < 1; ++gyroIteration )
|
||||
for ( int gyroIteration = 0; gyroIteration < B3_GYROSCOPIC_ITERATIONS; ++gyroIteration )
|
||||
{
|
||||
const float w1 = omega2.x;
|
||||
const float w2 = omega2.y;
|
||||
const float w3 = omega2.z;
|
||||
float w1 = omega2.x;
|
||||
float w2 = omega2.y;
|
||||
float w3 = omega2.z;
|
||||
|
||||
// Iw = I * omega2 (shared between residual and Jacobian)
|
||||
const float Iw1 = i00 * w1 + i01 * w2 + i02 * w3;
|
||||
const float Iw2 = i01 * w1 + i11 * w2 + i12 * w3;
|
||||
const float Iw3 = i02 * w1 + i12 * w2 + i22 * w3;
|
||||
float Iw1 = i00 * w1 + i01 * w2 + i02 * w3;
|
||||
float Iw2 = i01 * w1 + i11 * w2 + i12 * w3;
|
||||
float Iw3 = i02 * w1 + i12 * w2 + i22 * w3;
|
||||
|
||||
// Residual: b = I*(omega2 - omega1) + h * (omega2 × I*omega2)
|
||||
const b3Vec3 dw = b3Sub( omega2, omega1 );
|
||||
// Residual: b = I * (omega2 - omega1) + h * cross(omega2, I * omega2)
|
||||
b3Vec3 dw = b3Sub( omega2, omega1 );
|
||||
b3Vec3 b = {
|
||||
i00 * dw.x + i01 * dw.y + i02 * dw.z + h * ( w2 * Iw3 - w3 * Iw2 ),
|
||||
i01 * dw.x + i11 * dw.y + i12 * dw.z + h * ( w3 * Iw1 - w1 * Iw3 ),
|
||||
i02 * dw.x + i12 * dw.y + i22 * dw.z + h * ( w1 * Iw2 - w2 * Iw1 ),
|
||||
};
|
||||
|
||||
// Jacobian J = I + h * (skew(omega2) * I - skew(I*omega2))
|
||||
// Jacobian J = I + h * (skew(omega2) * I - skew(I * omega2))
|
||||
// Jacobian derived by Erin Catto, Ph.D. Do not attempt to do this without a Ph.D.
|
||||
// Doubled inertia terms above fold into Iw, e.g. row 2 col 1: i00*w3 - i02*w1 - Iw3.
|
||||
b3Matrix3 J = {
|
||||
@@ -1313,6 +1315,7 @@ static void b3SolverTask( void* taskContext )
|
||||
stageIndex += 1 + activeColorCount + ITERATIONS * activeColorCount + 1 + RELAX_ITERATIONS * activeColorCount;
|
||||
|
||||
// Restitution
|
||||
for ( int iteration = 0; iteration < B3_RESTITUTION_ITERATIONS; ++iteration )
|
||||
{
|
||||
b3ApplyRestitution_Overflow( context );
|
||||
|
||||
@@ -1324,7 +1327,7 @@ static void b3SolverTask( void* taskContext )
|
||||
b3ExecuteMainStage( stages + iterStageIndex, context, syncBits );
|
||||
iterStageIndex += 1;
|
||||
}
|
||||
// graphSyncIndex += 1;
|
||||
graphSyncIndex += 1;
|
||||
stageIndex += activeColorCount;
|
||||
}
|
||||
|
||||
@@ -1690,7 +1693,7 @@ void b3Solve( b3World* world, b3StepContext* stepContext )
|
||||
// b3_stageRelax
|
||||
stageCount += RELAX_ITERATIONS * activeColorCount;
|
||||
// b3_stageRestitution
|
||||
stageCount += activeColorCount;
|
||||
stageCount += B3_RESTITUTION_ITERATIONS * activeColorCount;
|
||||
// b3_stageStoreWideImpulses
|
||||
stageCount += 1;
|
||||
// b3_stageStoreImpulses
|
||||
@@ -1775,8 +1778,8 @@ void b3Solve( b3World* world, b3StepContext* stepContext )
|
||||
stage = b3InitColorStages( stage, b3_stageRelax, RELAX_ITERATIONS, activeColorCount, graphColorBlocks, graphBlockCounts,
|
||||
activeColorIndices );
|
||||
// Note: joint blocks mixed in, could have joint limit restitution
|
||||
stage = b3InitColorStages( stage, b3_stageRestitution, 1, activeColorCount, graphColorBlocks, graphBlockCounts,
|
||||
activeColorIndices );
|
||||
stage = b3InitColorStages( stage, b3_stageRestitution, B3_RESTITUTION_ITERATIONS, activeColorCount, graphColorBlocks,
|
||||
graphBlockCounts, activeColorIndices );
|
||||
stage = b3InitStage( stage, b3_stageStoreWideImpulses, convexBlocks, convexPrepareDim.count, UINT8_MAX );
|
||||
stage = b3InitStage( stage, b3_stageStoreImpulses, meshBlocks, meshPrepareDim.count, UINT8_MAX );
|
||||
|
||||
|
||||
5
vendor/box3d/src/src/timer.c
vendored
5
vendor/box3d/src/src/timer.c
vendored
@@ -23,7 +23,8 @@
|
||||
#define WIN32_LEAN_AND_MEAN 1
|
||||
#endif
|
||||
|
||||
#include <Windows.h>
|
||||
// Lower-case windows.h intentionally for cross compiling on mingw.
|
||||
#include <windows.h>
|
||||
#include <limits.h>
|
||||
|
||||
static double s_invFrequency = 0.0;
|
||||
@@ -517,7 +518,7 @@ typedef struct b3Thread
|
||||
char name[NAME_LENGTH];
|
||||
} b3Thread;
|
||||
|
||||
// macOS pthread_setname_np takes only the name — it always names the calling thread.
|
||||
// macOS pthread_setname_np takes only the name, it always names the calling thread.
|
||||
static void b3SetCurrentThreadName( const char* name )
|
||||
{
|
||||
if ( name == NULL || name[0] == 0 )
|
||||
|
||||
464
vendor/box3d/src/src/triangle_manifold.c
vendored
464
vendor/box3d/src/src/triangle_manifold.c
vendored
@@ -18,7 +18,6 @@ typedef struct b3TriangleData
|
||||
{
|
||||
b3Vec3 v1, v2, v3;
|
||||
b3Vec3 e1, e2, e3;
|
||||
b3Vec3 center;
|
||||
b3Plane plane;
|
||||
int flags;
|
||||
} b3TriangleData;
|
||||
@@ -51,7 +50,7 @@ static b3TriangleFeature b3GetTriangleFeature( const b3SimplexCache* cache )
|
||||
return s_triangleFeatures[mask];
|
||||
}
|
||||
|
||||
void b3CollideSphereAndTriangle( b3LocalManifold* manifold, int capacity, const b3Sphere* sphereA, const b3Vec3* triangleB )
|
||||
void b3CollideTriangleAndSphere( b3LocalManifold* manifold, int capacity, const b3Vec3* triangleA, const b3Sphere* sphereB )
|
||||
{
|
||||
manifold->pointCount = 0;
|
||||
|
||||
@@ -60,8 +59,8 @@ void b3CollideSphereAndTriangle( b3LocalManifold* manifold, int capacity, const
|
||||
return;
|
||||
}
|
||||
|
||||
b3Vec3 center = sphereA->center;
|
||||
b3Vec3 v1 = triangleB[0], v2 = triangleB[1], v3 = triangleB[2];
|
||||
b3Vec3 center = sphereB->center;
|
||||
b3Vec3 v1 = triangleA[0], v2 = triangleA[1], v3 = triangleA[2];
|
||||
b3Plane plane = b3MakePlaneFromPoints( v1, v2, v3 );
|
||||
|
||||
float offset = b3PlaneSeparation( plane, center );
|
||||
@@ -71,19 +70,23 @@ void b3CollideSphereAndTriangle( b3LocalManifold* manifold, int capacity, const
|
||||
return;
|
||||
}
|
||||
|
||||
float radius = sphereB->radius;
|
||||
|
||||
// Closest point on triangle to sphere center
|
||||
b3TrianglePoint closest = b3ClosestPointOnTriangle( v1, v2, v3, center );
|
||||
|
||||
// Test separating axis
|
||||
float squaredDistance = b3DistanceSquared( closest.point, center );
|
||||
float speculativeDistance = B3_SPECULATIVE_DISTANCE;
|
||||
float maxDistance = sphereA->radius + speculativeDistance;
|
||||
float maxDistance = radius + speculativeDistance;
|
||||
if ( squaredDistance > maxDistance * maxDistance )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
float distance = sqrtf( squaredDistance );
|
||||
|
||||
// Normal points from triangle to sphere
|
||||
b3Vec3 normal;
|
||||
if ( distance * distance > 1000.0f * FLT_MIN )
|
||||
{
|
||||
@@ -95,7 +98,8 @@ void b3CollideSphereAndTriangle( b3LocalManifold* manifold, int capacity, const
|
||||
}
|
||||
|
||||
// contact point mid-way
|
||||
b3Vec3 contactPoint = b3MulSV( 0.5f, b3Add( b3Sub( center, b3MulSV( sphereA->radius, normal ) ), closest.point ) );
|
||||
// p = 0.5 * (c + q) - 0.5 * r * n
|
||||
b3Vec3 contactPoint = b3MulSV( 0.5f, b3Add( b3Sub( center, b3MulSV( radius, normal ) ), closest.point ) );
|
||||
|
||||
manifold->normal = normal;
|
||||
manifold->pointCount = 1;
|
||||
@@ -104,7 +108,7 @@ void b3CollideSphereAndTriangle( b3LocalManifold* manifold, int capacity, const
|
||||
|
||||
b3LocalManifoldPoint* mp = manifold->points + 0;
|
||||
mp->point = contactPoint;
|
||||
mp->separation = distance - sphereA->radius;
|
||||
mp->separation = distance - radius;
|
||||
mp->pair = b3FeaturePair_single;
|
||||
}
|
||||
|
||||
@@ -157,56 +161,85 @@ static bool b3ClipSegmentToTriangleFace( b3ClipVertex segment[2], const b3Vec3*
|
||||
return true;
|
||||
}
|
||||
|
||||
static b3FaceQuery b3QueryTriangleFaceAndCapsule( b3Plane plane, const b3Capsule* capsule )
|
||||
static b3SeparatingAxis b3QueryTriangleFaceAndCapsule( b3Plane plane, const b3Capsule* capsule )
|
||||
{
|
||||
float separation1 = b3PlaneSeparation( plane, capsule->center1 );
|
||||
float separation2 = b3PlaneSeparation( plane, capsule->center2 );
|
||||
|
||||
if ( separation1 < separation2 )
|
||||
{
|
||||
return (b3FaceQuery){
|
||||
return (b3SeparatingAxis){
|
||||
.normal = plane.normal,
|
||||
.separation = separation1,
|
||||
.faceIndex = 0,
|
||||
.vertexIndex = 0,
|
||||
.indexA = 0,
|
||||
.indexB = 0,
|
||||
};
|
||||
}
|
||||
|
||||
return (b3FaceQuery){
|
||||
return (b3SeparatingAxis){
|
||||
.normal = plane.normal,
|
||||
.separation = separation2,
|
||||
.faceIndex = 0,
|
||||
.vertexIndex = 1,
|
||||
.indexA = 0,
|
||||
.indexB = 1,
|
||||
};
|
||||
}
|
||||
|
||||
static b3EdgeQuery b3QueryTriangleAndCapsuleEdges( const b3Vec3* vertices, const b3Capsule* capsule )
|
||||
static b3SeparatingAxis b3QueryTriangleAndCapsuleEdges( const b3Vec3* vertices, b3Plane plane, const b3Capsule* capsule )
|
||||
{
|
||||
// Work in the local space of the capsule
|
||||
b3Vec3 p1 = capsule->center1;
|
||||
b3Vec3 p2 = capsule->center2;
|
||||
b3Vec3 capsuleEdge = b3Sub( p2, p1 );
|
||||
|
||||
b3Vec3 capsuleCenter = b3Lerp( p1, p2, 0.5f );
|
||||
|
||||
b3Vec3 triangleCenter = b3MulSV( 1.0f / 3.0f, b3Add( vertices[0], b3Add( vertices[1], vertices[2] ) ) );
|
||||
|
||||
// Find axis of minimum penetration
|
||||
b3Vec3 maxNormal = b3Vec3_zero;
|
||||
float maxSeparation = -FLT_MAX;
|
||||
int maxIndex1 = UINT8_MAX;
|
||||
int maxIndex2 = UINT8_MAX;
|
||||
int maxIndex1 = B3_NULL_INDEX;
|
||||
int maxIndex2 = B3_NULL_INDEX;
|
||||
float squaredTolerance = 0.005f * 0.005f;
|
||||
|
||||
int edgeIndex = 2;
|
||||
b3Vec3 v1 = vertices[2];
|
||||
for ( int index = 0; index < 3; ++index )
|
||||
{
|
||||
b3Vec3 v2 = vertices[index];
|
||||
|
||||
b3Vec3 triangleEdge = b3Sub( v2, v1 );
|
||||
b3Vec3 sideNormal = b3Normalize( b3Cross( triangleEdge, plane.normal ) );
|
||||
|
||||
// Pretend the triangle edge embeds a zero area face with a side normal. This
|
||||
// provides a way to find an edge-edge normal that points outward from
|
||||
// the triangle.
|
||||
float a = b3Dot( capsuleEdge, plane.normal );
|
||||
float b = b3Dot( capsuleEdge, sideNormal );
|
||||
|
||||
// Is the capsule edge parallel to the triangle edge? If so, face contact can handle it.
|
||||
if ( a * a + b * b < squaredTolerance * b3LengthSquared( capsuleEdge ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Similar to hull vs hull (b3QueryEdgeDirections)
|
||||
b3Vec3 axis;
|
||||
if ( a * b <= 0.0f )
|
||||
{
|
||||
float t = b / ( b - a );
|
||||
axis = b3Lerp( sideNormal, plane.normal, t );
|
||||
}
|
||||
else
|
||||
{
|
||||
float t = b / ( a + b );
|
||||
axis = b3Lerp( sideNormal, b3Neg( plane.normal ), t );
|
||||
}
|
||||
|
||||
B3_VALIDATE( b3LengthSquared( axis ) > 1000.0f * FLT_MIN );
|
||||
axis = b3Normalize( axis );
|
||||
float separation = b3Dot( axis, b3Sub( p1, v1 ) );
|
||||
|
||||
float separation = b3EdgeEdgeSeparation( p1, capsuleEdge, capsuleCenter, v1, triangleEdge, triangleCenter );
|
||||
if ( separation > maxSeparation )
|
||||
{
|
||||
// Note: We don't exit early if we find a separating axis here since we want to
|
||||
// find the best one for caching and account for the convex radius later.
|
||||
maxNormal = axis;
|
||||
maxSeparation = separation;
|
||||
maxIndex1 = edgeIndex;
|
||||
maxIndex2 = 0;
|
||||
@@ -217,10 +250,11 @@ static b3EdgeQuery b3QueryTriangleAndCapsuleEdges( const b3Vec3* vertices, const
|
||||
}
|
||||
|
||||
// Save result
|
||||
return (b3EdgeQuery){
|
||||
return (b3SeparatingAxis){
|
||||
.normal = maxNormal,
|
||||
.separation = maxSeparation,
|
||||
.indexA = (uint8_t)maxIndex1,
|
||||
.indexB = (uint8_t)maxIndex2,
|
||||
.indexA = maxIndex1,
|
||||
.indexB = maxIndex2,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -272,8 +306,8 @@ static void b3BuildTriangleAndCapsuleFaceContact( b3LocalManifold* manifold, con
|
||||
pt->pair = segment[1].pair;
|
||||
}
|
||||
|
||||
static void b3BuildTriangleAndCapsuleEdgeContact( b3LocalManifold* manifold, const b3Vec3* triangle, const b3Capsule* capsule,
|
||||
b3EdgeQuery query )
|
||||
static void b3BuildTriangleAndCapsuleEdgeContact( b3LocalManifold* manifold, const b3Vec3* triangle, b3Plane plane,
|
||||
const b3Capsule* capsule, b3SeparatingAxis query )
|
||||
{
|
||||
B3_ASSERT( 0 <= query.indexA && query.indexA < 3 );
|
||||
|
||||
@@ -283,20 +317,27 @@ static void b3BuildTriangleAndCapsuleEdgeContact( b3LocalManifold* manifold, con
|
||||
|
||||
const b3Vec3* vs = triangle;
|
||||
|
||||
b3Vec3 triangleCenter = b3MulSV( 1.0f / 3.0f, b3Add( vs[0], b3Add( vs[1], vs[2] ) ) );
|
||||
b3Vec3 v1 = vs[query.indexA];
|
||||
b3Vec3 v2 = vs[( query.indexA + 1 ) % 3];
|
||||
b3Vec3 triangleEdge = b3Sub( v2, v1 );
|
||||
|
||||
b3Vec3 normal = b3Cross( capsuleEdge, triangleEdge );
|
||||
normal = b3Normalize( normal );
|
||||
b3Vec3 sideNormal = b3Normalize( b3Cross( triangleEdge, plane.normal ) );
|
||||
|
||||
// Normal should point away from triangle center
|
||||
if ( b3Dot( normal, b3Sub( v1, triangleCenter ) ) < 0.0f )
|
||||
// Pretend the triangle edge embeds a zero area face with a side normal. This
|
||||
// provides a way to find an edge-edge normal that points outward from
|
||||
// the triangle.
|
||||
float a = b3Dot( capsuleEdge, plane.normal );
|
||||
float b = b3Dot( capsuleEdge, sideNormal );
|
||||
|
||||
// Is the capsule edge parallel to the triangle edge? If so, face contact can handle it.
|
||||
float squaredTolerance = 0.005f * 0.005f;
|
||||
if ( a * a + b * b < squaredTolerance * b3LengthSquared( capsuleEdge ) )
|
||||
{
|
||||
normal = b3Neg( normal );
|
||||
return;
|
||||
}
|
||||
|
||||
// Similar to hull vs hull (b3QueryEdgeDirections)
|
||||
b3Vec3 normal = query.normal;
|
||||
b3SegmentDistanceResult result = b3LineDistance( v1, triangleEdge, p1, capsuleEdge );
|
||||
|
||||
if ( result.fraction1 < 0.0f || 1.0f < result.fraction1 || result.fraction2 < 0.0f || 1.0f < result.fraction2 )
|
||||
@@ -306,8 +347,7 @@ static void b3BuildTriangleAndCapsuleEdgeContact( b3LocalManifold* manifold, con
|
||||
}
|
||||
|
||||
b3Vec3 point = b3Lerp( b3MulSub( result.point1, capsule->radius, normal ), result.point2, 0.5f );
|
||||
|
||||
float separation = b3Dot( normal, b3Sub( result.point2, result.point1 ) );
|
||||
float separation = b3Dot( normal, b3Sub( p1, v1 ) );
|
||||
B3_VALIDATE( b3AbsFloat( separation - query.separation ) < B3_LINEAR_SLOP );
|
||||
|
||||
manifold->normal = normal;
|
||||
@@ -322,7 +362,7 @@ static void b3BuildTriangleAndCapsuleEdgeContact( b3LocalManifold* manifold, con
|
||||
pt->pair = b3MakeFeaturePair( b3_featureShapeA, query.indexA, b3_featureShapeB, query.indexB );
|
||||
}
|
||||
|
||||
void b3CollideCapsuleAndTriangle( b3LocalManifold* manifold, int capacity, const b3Capsule* capsuleA, const b3Vec3* triangleB,
|
||||
void b3CollideTriangleAndCapsule( b3LocalManifold* manifold, int capacity, const b3Vec3* triangleA, const b3Capsule* capsuleB,
|
||||
b3SimplexCache* cache )
|
||||
{
|
||||
manifold->pointCount = 0;
|
||||
@@ -332,9 +372,9 @@ void b3CollideCapsuleAndTriangle( b3LocalManifold* manifold, int capacity, const
|
||||
return;
|
||||
}
|
||||
|
||||
b3Vec3 v1 = triangleB[0], v2 = triangleB[1], v3 = triangleB[2];
|
||||
b3Vec3 v1 = triangleA[0], v2 = triangleA[1], v3 = triangleA[2];
|
||||
b3Plane plane = b3MakePlaneFromPoints( v1, v2, v3 );
|
||||
b3Vec3 capsuleCenter = b3Lerp( capsuleA->center1, capsuleA->center2, 0.5f );
|
||||
b3Vec3 capsuleCenter = b3Lerp( capsuleB->center1, capsuleB->center2, 0.5f );
|
||||
|
||||
float offset = b3PlaneSeparation( plane, capsuleCenter );
|
||||
if ( offset < 0.0f )
|
||||
@@ -344,15 +384,15 @@ void b3CollideCapsuleAndTriangle( b3LocalManifold* manifold, int capacity, const
|
||||
}
|
||||
|
||||
b3DistanceInput distanceInput;
|
||||
distanceInput.proxyA = (b3ShapeProxy){ triangleB, 3, 0.0f };
|
||||
distanceInput.proxyB = (b3ShapeProxy){ &capsuleA->center1, 2, 0.0f };
|
||||
distanceInput.proxyA = (b3ShapeProxy){ triangleA, 3, 0.0f };
|
||||
distanceInput.proxyB = (b3ShapeProxy){ &capsuleB->center1, 2, 0.0f };
|
||||
distanceInput.transform = b3Transform_identity;
|
||||
distanceInput.useRadii = false;
|
||||
|
||||
b3DistanceOutput distanceOutput = b3ShapeDistance( &distanceInput, cache, NULL, 0 );
|
||||
|
||||
float radius = capsuleA->radius;
|
||||
if ( distanceOutput.distance > radius + B3_SPECULATIVE_DISTANCE )
|
||||
float speculativeDistance = B3_SPECULATIVE_DISTANCE;
|
||||
float radius = capsuleB->radius;
|
||||
if ( distanceOutput.distance > radius + speculativeDistance )
|
||||
{
|
||||
// Shapes are separated, persist the cache
|
||||
return;
|
||||
@@ -370,14 +410,14 @@ void b3CollideCapsuleAndTriangle( b3LocalManifold* manifold, int capacity, const
|
||||
{
|
||||
// Clip capsule segment against side planes of reference face
|
||||
b3ClipVertex segment[2];
|
||||
segment[0].position = capsuleA->center1;
|
||||
segment[0].position = capsuleB->center1;
|
||||
segment[0].separation = 0.0f;
|
||||
segment[0].pair = b3MakeFeaturePair( b3_featureShapeA, 0, b3_featureShapeA, 0 );
|
||||
segment[1].position = capsuleA->center2;
|
||||
segment[1].position = capsuleB->center2;
|
||||
segment[1].separation = 0.0f;
|
||||
segment[1].pair = b3MakeFeaturePair( b3_featureShapeA, 1, b3_featureShapeA, 1 );
|
||||
|
||||
bool havePoints = b3ClipSegmentToTriangleFace( segment, triangleB, plane );
|
||||
bool havePoints = b3ClipSegmentToTriangleFace( segment, triangleA, plane );
|
||||
|
||||
if ( havePoints == true )
|
||||
{
|
||||
@@ -388,6 +428,7 @@ void b3CollideCapsuleAndTriangle( b3LocalManifold* manifold, int capacity, const
|
||||
b3Vec3 point1 = b3MulSub( segment[0].position, 0.5f * ( radius + distance1 ), normal );
|
||||
b3Vec3 point2 = b3MulSub( segment[1].position, 0.5f * ( radius + distance2 ), normal );
|
||||
|
||||
// Normal points from triangle to capsule
|
||||
manifold->normal = normal;
|
||||
manifold->feature = b3_featureTriangleFace;
|
||||
manifold->pointCount = 2;
|
||||
@@ -406,9 +447,10 @@ void b3CollideCapsuleAndTriangle( b3LocalManifold* manifold, int capacity, const
|
||||
}
|
||||
}
|
||||
|
||||
// Create contact from closest points
|
||||
// Create contact from closest points.
|
||||
b3Vec3 point = b3MulSV( 0.5f, b3Add( b3Sub( distanceOutput.pointA, b3MulSV( radius, delta ) ), distanceOutput.pointB ) );
|
||||
|
||||
// Normal points from triangle to capsule.
|
||||
manifold->normal = delta;
|
||||
manifold->pointCount = 1;
|
||||
manifold->feature = b3GetTriangleFeature( cache );
|
||||
@@ -423,38 +465,38 @@ void b3CollideCapsuleAndTriangle( b3LocalManifold* manifold, int capacity, const
|
||||
|
||||
// Deep penetration
|
||||
|
||||
b3FaceQuery faceQuery = b3QueryTriangleFaceAndCapsule( plane, capsuleA );
|
||||
b3SeparatingAxis faceQuery = b3QueryTriangleFaceAndCapsule( plane, capsuleB );
|
||||
if ( faceQuery.separation > radius )
|
||||
{
|
||||
// Shapes are separated
|
||||
// Shapes are separated. Should be impossible for a reasonable capsule radius.
|
||||
return;
|
||||
}
|
||||
|
||||
b3EdgeQuery edgeQuery = b3QueryTriangleAndCapsuleEdges( triangleB, capsuleA );
|
||||
b3SeparatingAxis edgeQuery = b3QueryTriangleAndCapsuleEdges( triangleA, plane, capsuleB );
|
||||
if ( edgeQuery.separation > radius )
|
||||
{
|
||||
// Shapes are separated
|
||||
// Shapes are separated. Should be impossible for a reasonable capsule radius.
|
||||
return;
|
||||
}
|
||||
|
||||
// Create face contact
|
||||
float faceSeparation = faceQuery.separation - radius;
|
||||
b3BuildTriangleAndCapsuleFaceContact( manifold, triangleB, plane, capsuleA );
|
||||
b3BuildTriangleAndCapsuleFaceContact( manifold, triangleA, plane, capsuleB );
|
||||
B3_VALIDATE( manifold->pointCount == 0 || manifold->pointCount == 2 );
|
||||
if ( manifold->pointCount == 2 )
|
||||
{
|
||||
// This becomes the clipped separation.
|
||||
faceSeparation = b3MinFloat( manifold->points[0].separation, manifold->points[1].separation );
|
||||
}
|
||||
B3_VALIDATE( faceSeparation <= 0.0f );
|
||||
|
||||
// Face contact can be empty if it does not realize the axis of minimum penetration.
|
||||
// Create edge contact if face contact fails or edge contact is significantly better!
|
||||
const float kRelEdgeTolerance = 0.50f;
|
||||
const float kAbsTolerance = 1.0f * B3_LINEAR_SLOP;
|
||||
float linearSlop = B3_LINEAR_SLOP;
|
||||
float edgeSeparation = edgeQuery.separation - radius;
|
||||
if ( manifold->pointCount == 0 || edgeSeparation > kRelEdgeTolerance * faceSeparation + kAbsTolerance )
|
||||
if ( manifold->pointCount == 0 || edgeSeparation > faceSeparation + linearSlop )
|
||||
{
|
||||
// Edge contact
|
||||
b3BuildTriangleAndCapsuleEdgeContact( manifold, triangleB, capsuleA, edgeQuery );
|
||||
b3BuildTriangleAndCapsuleEdgeContact( manifold, triangleA, plane, capsuleB, edgeQuery );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,31 +521,35 @@ static inline int b3GetTriangleSupport( b3Vec3* points, b3Vec3 direction )
|
||||
return index;
|
||||
}
|
||||
|
||||
static b3FaceQuery b3QueryTriangleFace( const b3TriangleData* triangle, const b3HullData* hull )
|
||||
static b3SeparatingAxis b3QueryTriangleFace( const b3TriangleData* triangle, const b3HullData* hull )
|
||||
{
|
||||
const b3Vec3* hullPoints = b3GetHullPoints( hull );
|
||||
b3Plane plane = triangle->plane;
|
||||
int vertexIndex = b3FindHullSupportVertex( hull, b3Neg( plane.normal ) );
|
||||
b3Vec3 normal = b3Neg( plane.normal );
|
||||
int vertexIndex = b3FindHullSupportVertex( hull, normal );
|
||||
b3Vec3 support = hullPoints[vertexIndex];
|
||||
float separation = b3PlaneSeparation( plane, support );
|
||||
|
||||
return (b3FaceQuery){
|
||||
return (b3SeparatingAxis){
|
||||
.normal = plane.normal,
|
||||
.separation = separation,
|
||||
.faceIndex = 0,
|
||||
.vertexIndex = (uint8_t)vertexIndex,
|
||||
.indexA = 0,
|
||||
.indexB = vertexIndex,
|
||||
.type = b3_faceAxisA,
|
||||
};
|
||||
}
|
||||
|
||||
static b3FaceQuery b3QueryHullFace( const b3TriangleData* triangle, const b3HullData* hull )
|
||||
static b3SeparatingAxis b3QueryHullFace( const b3TriangleData* triangle, const b3HullData* hull )
|
||||
{
|
||||
const b3Plane* hullPlanes = b3GetHullPlanes( hull );
|
||||
int faceCount = hull->faceCount;
|
||||
|
||||
b3Vec3 trianglePoints[] = { triangle->v1, triangle->v2, triangle->v3 };
|
||||
|
||||
int maxFaceIndex = -1;
|
||||
int maxVertexIndex = -1;
|
||||
float maxFaceSeparation = -FLT_MAX;
|
||||
b3Vec3 maxNormal = b3Vec3_zero;
|
||||
float maxFaceSeparation = -INFINITY;
|
||||
int maxFaceIndex = B3_NULL_INDEX;
|
||||
int maxVertexIndex = B3_NULL_INDEX;
|
||||
|
||||
for ( int faceIndex = 0; faceIndex < faceCount; ++faceIndex )
|
||||
{
|
||||
@@ -514,25 +560,32 @@ static b3FaceQuery b3QueryHullFace( const b3TriangleData* triangle, const b3Hull
|
||||
float separation = b3PlaneSeparation( plane, support );
|
||||
if ( separation > maxFaceSeparation )
|
||||
{
|
||||
maxNormal = plane.normal;
|
||||
maxFaceSeparation = separation;
|
||||
maxFaceIndex = faceIndex;
|
||||
maxVertexIndex = vertexIndex;
|
||||
maxFaceSeparation = separation;
|
||||
}
|
||||
}
|
||||
|
||||
return (b3FaceQuery){
|
||||
// Normal points from triangle to hull
|
||||
return (b3SeparatingAxis){
|
||||
.normal = b3Neg( maxNormal ),
|
||||
.separation = maxFaceSeparation,
|
||||
.faceIndex = maxFaceIndex,
|
||||
.vertexIndex = maxVertexIndex,
|
||||
.indexA = maxVertexIndex,
|
||||
.indexB = maxFaceIndex,
|
||||
.type = b3_faceAxisB,
|
||||
};
|
||||
}
|
||||
|
||||
static b3EdgeQuery b3TestEdgePairs( const b3TriangleData* triangle, const b3HullData* hull )
|
||||
// A: hull, B: triangle
|
||||
static b3SeparatingAxis b3QueryTriangleAndHullEdges( const b3TriangleData* triangle, const b3HullData* hull )
|
||||
{
|
||||
b3EdgeQuery result = {
|
||||
.separation = -FLT_MAX,
|
||||
b3SeparatingAxis result = {
|
||||
.normal = b3Vec3_zero,
|
||||
.separation = -INFINITY,
|
||||
.indexA = B3_NULL_INDEX,
|
||||
.indexB = B3_NULL_INDEX,
|
||||
.type = b3_edgePairAxis,
|
||||
};
|
||||
|
||||
b3Vec3 trianglePoints[] = { triangle->v1, triangle->v2, triangle->v3 };
|
||||
@@ -552,6 +605,7 @@ static b3EdgeQuery b3TestEdgePairs( const b3TriangleData* triangle, const b3Hull
|
||||
const b3Vec3* hullPoints = b3GetHullPoints( hull );
|
||||
const b3Plane* hullPlanes = b3GetHullPlanes( hull );
|
||||
int edgeCount = hull->edgeCount;
|
||||
float squaredTolerance = 0.005f * 0.005f;
|
||||
|
||||
for ( int i = 0; i < edgeCount; i += 2 )
|
||||
{
|
||||
@@ -577,14 +631,28 @@ static b3EdgeQuery b3TestEdgePairs( const b3TriangleData* triangle, const b3Hull
|
||||
continue;
|
||||
}
|
||||
|
||||
b3Vec3 triPoint = trianglePoints[j];
|
||||
float separation = b3EdgeEdgeSeparation( triPoint, triEdge, triangle->center, hullPoint, hullEdge, hull->center );
|
||||
// Avoid nearly parallel edges that may lead to invalid separation values at the noise floor.
|
||||
if ( b3MaxFloat( cab * cab, dab * dab ) < squaredTolerance * b3LengthSquared( triEdge ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Similar to hull vs hull (b3QueryEdgeDirections)
|
||||
// dot(hullNormal1 + t * (hullNormal2 - hullNormal1), triEdge) = 0
|
||||
// Normal points out of hull by construction.
|
||||
float t = cab / ( cab - dab );
|
||||
b3Vec3 axis = b3Lerp( hullNormal1, hullNormal2, t );
|
||||
B3_VALIDATE( b3LengthSquared( axis ) > 1000.0f * FLT_MIN );
|
||||
axis = b3Normalize( axis );
|
||||
float separation = b3Dot( axis, b3Sub( trianglePoints[j], hullPoint ) );
|
||||
|
||||
// if ( separation > result.separation && ( edgeFlags[j] & triangleFlags ) == 0 )
|
||||
if ( separation > result.separation )
|
||||
{
|
||||
// Note: We don't exit early if we find a separating axis here since we want to
|
||||
// find the best one for caching.
|
||||
// Flip normal to point from triangle to hull.
|
||||
result.normal = b3Neg( axis );
|
||||
result.separation = separation;
|
||||
result.indexA = j;
|
||||
result.indexB = i;
|
||||
@@ -596,8 +664,12 @@ static b3EdgeQuery b3TestEdgePairs( const b3TriangleData* triangle, const b3Hull
|
||||
}
|
||||
|
||||
static float b3CollideHullFace( b3LocalManifold* manifold, int pointCapacity, const b3TriangleData* triangle,
|
||||
const b3HullData* hull, b3FaceQuery query, b3SATCache* cache, bool enableSpeculative )
|
||||
const b3HullData* hull, b3SeparatingAxis query, b3SATCache* cache, bool enableSpeculative )
|
||||
{
|
||||
B3_VALIDATE( query.type == b3_faceAxisB );
|
||||
B3_VALIDATE( 0 <= query.indexA && query.indexA < 3 );
|
||||
B3_VALIDATE( 0 <= query.indexB && query.indexB < hull->faceCount );
|
||||
|
||||
manifold->pointCount = 0;
|
||||
|
||||
const b3HullFace* hullFaces = b3GetHullFaces( hull );
|
||||
@@ -606,8 +678,7 @@ static float b3CollideHullFace( b3LocalManifold* manifold, int pointCapacity, co
|
||||
const b3Vec3* hullPoints = b3GetHullPoints( hull );
|
||||
|
||||
// Reference hull face
|
||||
int refFace = query.faceIndex;
|
||||
b3Plane refPlane = hullPlanes[refFace];
|
||||
b3Plane refPlane = hullPlanes[query.indexB];
|
||||
|
||||
// Build clip polygon from triangle face (the incident face)
|
||||
b3ClipVertex buffer1[B3_MAX_CLIP_POINTS], buffer2[B3_MAX_CLIP_POINTS];
|
||||
@@ -630,7 +701,7 @@ static float b3CollideHullFace( b3LocalManifold* manifold, int pointCapacity, co
|
||||
b3ClipVertex* input = buffer1;
|
||||
b3ClipVertex* output = buffer2;
|
||||
|
||||
const b3HullFace* face = hullFaces + refFace;
|
||||
const b3HullFace* face = hullFaces + query.indexB;
|
||||
int edgeIndex = face->edge;
|
||||
|
||||
do
|
||||
@@ -702,14 +773,17 @@ static float b3CollideHullFace( b3LocalManifold* manifold, int pointCapacity, co
|
||||
// Save cache
|
||||
cache->separation = minSeparation;
|
||||
cache->type = b3_faceAxisB;
|
||||
cache->indexA = (uint8_t)query.vertexIndex;
|
||||
cache->indexB = (uint8_t)query.faceIndex;
|
||||
cache->indexA = (uint8_t)query.indexA;
|
||||
cache->indexB = (uint8_t)query.indexB;
|
||||
return minSeparation;
|
||||
}
|
||||
|
||||
static float b3CollideTriangleFace( b3LocalManifold* manifold, int pointCapacity, const b3TriangleData* triangle,
|
||||
const b3HullData* hull, b3FaceQuery query, b3SATCache* cache, bool enableSpeculative )
|
||||
const b3HullData* hull, b3SeparatingAxis query, b3SATCache* cache, bool enableSpeculative )
|
||||
{
|
||||
B3_VALIDATE( query.type == b3_faceAxisA );
|
||||
B3_VALIDATE( query.indexA == 0 );
|
||||
B3_VALIDATE( 0 <= query.indexB && query.indexB < hull->vertexCount );
|
||||
B3_VALIDATE( manifold->pointCount == 0 );
|
||||
|
||||
const b3HullFace* hullFaces = b3GetHullFaces( hull );
|
||||
@@ -717,10 +791,9 @@ static float b3CollideTriangleFace( b3LocalManifold* manifold, int pointCapacity
|
||||
const b3Vec3* hullPoints = b3GetHullPoints( hull );
|
||||
|
||||
// Find incident face
|
||||
B3_ASSERT( query.faceIndex == 0 );
|
||||
b3Plane refPlane = triangle->plane;
|
||||
|
||||
int incFace = b3FindIncidentFace( hull, refPlane.normal, query.vertexIndex );
|
||||
int incFace = b3FindIncidentFace( hull, refPlane.normal, query.indexB );
|
||||
|
||||
// Build clip polygon from incident face
|
||||
b3ClipVertex buffer1[2 * B3_MAX_CLIP_POINTS], buffer2[2 * B3_MAX_CLIP_POINTS];
|
||||
@@ -816,18 +889,19 @@ static float b3CollideTriangleFace( b3LocalManifold* manifold, int pointCapacity
|
||||
// Save cache
|
||||
cache->separation = minSeparation;
|
||||
cache->type = b3_faceAxisA;
|
||||
cache->indexA = (uint8_t)query.faceIndex;
|
||||
cache->indexB = (uint8_t)query.vertexIndex;
|
||||
cache->indexA = (uint8_t)query.indexA;
|
||||
cache->indexB = (uint8_t)query.indexB;
|
||||
return minSeparation;
|
||||
}
|
||||
|
||||
static void b3CollideHullAndTriangleEdges( b3LocalManifold* manifold, int capacity, b3Vec3 trianglePoint, b3Vec3 triangleEdge,
|
||||
b3Vec3 triangleCenter, const b3HullData* hull, b3EdgeQuery query, b3SATCache* cache )
|
||||
static void b3CollideTriangleAndHullEdges( b3LocalManifold* manifold, int capacity, b3Vec3 trianglePoint, b3Vec3 triangleEdge,
|
||||
const b3HullData* hull, b3SeparatingAxis query, b3SATCache* cache )
|
||||
{
|
||||
B3_VALIDATE( query.type == b3_edgePairAxis );
|
||||
B3_VALIDATE( 0 <= query.indexA && query.indexA < 3 );
|
||||
B3_VALIDATE( 0 <= query.indexB && query.indexB < hull->edgeCount );
|
||||
B3_VALIDATE( query.separation <= 2.0f * B3_SPECULATIVE_DISTANCE );
|
||||
B3_ASSERT( query.indexA < 3 );
|
||||
|
||||
b3Vec3 cA = triangleCenter;
|
||||
b3Vec3 pA = trianglePoint;
|
||||
b3Vec3 eA = triangleEdge;
|
||||
|
||||
@@ -839,32 +913,6 @@ static void b3CollideHullAndTriangleEdges( b3LocalManifold* manifold, int capaci
|
||||
b3Vec3 qB = pointsB[twinB->origin];
|
||||
b3Vec3 eB = b3Sub( qB, pB );
|
||||
|
||||
b3Vec3 normal = b3Cross( eA, eB );
|
||||
normal = b3Normalize( normal );
|
||||
|
||||
// Ensure normal points outward from triangle center
|
||||
float outwardA = b3Dot( normal, b3Sub( pA, cA ) );
|
||||
|
||||
// Ensure normal points towards hull center
|
||||
float outwardB = b3Dot( normal, b3Sub( hull->center, pB ) );
|
||||
|
||||
// Use the largest magnitude. The triangle outward value
|
||||
// may be unreliable as some angles.
|
||||
if ( b3AbsFloat( outwardA ) > b3AbsFloat( outwardB ) )
|
||||
{
|
||||
if ( outwardA < 0.0f )
|
||||
{
|
||||
normal = b3Neg( normal );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( outwardB < 0.0f )
|
||||
{
|
||||
normal = b3Neg( normal );
|
||||
}
|
||||
}
|
||||
|
||||
// Get the closest points between the infinite edge lines
|
||||
b3SegmentDistanceResult result = b3LineDistance( pA, eA, pB, eB );
|
||||
|
||||
@@ -879,7 +927,7 @@ static void b3CollideHullAndTriangleEdges( b3LocalManifold* manifold, int capaci
|
||||
}
|
||||
|
||||
// This can slide off the end from caching
|
||||
float separation = b3Dot( normal, b3Sub( result.point2, result.point1 ) );
|
||||
float separation = b3Dot( query.normal, b3Sub( pB, pA ) );
|
||||
B3_VALIDATE( b3AbsFloat( separation - query.separation ) < B3_LINEAR_SLOP );
|
||||
|
||||
b3Vec3 point = b3MulSV( 0.5f, b3Add( result.point1, result.point2 ) );
|
||||
@@ -895,30 +943,19 @@ static void b3CollideHullAndTriangleEdges( b3LocalManifold* manifold, int capaci
|
||||
cache->indexA = (uint8_t)query.indexA;
|
||||
cache->indexB = (uint8_t)query.indexB;
|
||||
|
||||
manifold->normal = normal;
|
||||
manifold->normal = query.normal;
|
||||
manifold->pointCount = 1;
|
||||
|
||||
b3TriangleFeature edgesFeatures[] = { b3_featureEdge1, b3_featureEdge2, b3_featureEdge3 };
|
||||
manifold->feature = edgesFeatures[query.indexA];
|
||||
}
|
||||
|
||||
// See "Collision Detection of Convex Polyhedra Based on Duality Transformation"
|
||||
// Simplified for triangle versus hull
|
||||
static inline bool b3IsTriangleMinkowskiFace( b3Vec3 triNormal, b3Vec3 triEdge, b3Vec3 hullNormal1, b3Vec3 hullNormal2,
|
||||
b3Vec3 hullEdge )
|
||||
{
|
||||
float cab = b3Dot( hullNormal1, triEdge );
|
||||
float dab = b3Dot( hullNormal2, triEdge );
|
||||
float bcd = b3Dot( triNormal, hullEdge );
|
||||
return cab * dab < 0.0f && cab * bcd > 0.0f;
|
||||
}
|
||||
|
||||
b3AtomicInt b3_triangleConvexCalls;
|
||||
b3AtomicInt b3_triangleCacheHits;
|
||||
|
||||
// Computes the manifold in the local space of the hull
|
||||
void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3HullData* hullA, b3Vec3 v1, b3Vec3 v2, b3Vec3 v3,
|
||||
int triangleFlags, b3SATCache* cache, bool enableSpeculative )
|
||||
// Triangle is in the local space of the hull for efficiency.
|
||||
void b3CollideTriangleAndHull( b3LocalManifold* manifold, int capacity, b3Vec3 v1, b3Vec3 v2, b3Vec3 v3, int triangleFlags,
|
||||
const b3HullData* hullB, b3SATCache* cache, bool enableSpeculative )
|
||||
{
|
||||
manifold->pointCount = 0;
|
||||
manifold->feature = b3_featureNone;
|
||||
@@ -931,7 +968,7 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
b3Plane trianglePlane = b3MakePlaneFromPoints( v1, v2, v3 );
|
||||
float linearSlop = B3_LINEAR_SLOP;
|
||||
|
||||
float offset = b3PlaneSeparation( trianglePlane, hullA->center );
|
||||
float offset = b3PlaneSeparation( trianglePlane, hullB->center );
|
||||
if ( cache->type == b3_backsideAxis )
|
||||
{
|
||||
// Use hysteresis to avoid jitter on wavy meshes
|
||||
@@ -951,7 +988,6 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
return;
|
||||
}
|
||||
|
||||
b3Vec3 triangleCenter = b3MulSV( 1.0f / 3.0f, b3Add( v1, b3Add( v2, v3 ) ) );
|
||||
b3Vec3 trianglePoints[] = { v1, v2, v3 };
|
||||
b3Vec3 triangleEdges[] = { b3Sub( v2, v1 ), b3Sub( v3, v2 ), b3Sub( v1, v3 ) };
|
||||
|
||||
@@ -962,14 +998,13 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
.e1 = triangleEdges[0],
|
||||
.e2 = triangleEdges[1],
|
||||
.e3 = triangleEdges[2],
|
||||
.center = triangleCenter,
|
||||
.plane = trianglePlane,
|
||||
.flags = triangleFlags,
|
||||
};
|
||||
|
||||
const b3HullHalfEdge* edges = b3GetHullEdges( hullA );
|
||||
const b3Plane* hullPlanes = b3GetHullPlanes( hullA );
|
||||
const b3Vec3* hullPoints = b3GetHullPoints( hullA );
|
||||
const b3HullHalfEdge* edges = b3GetHullEdges( hullB );
|
||||
const b3Plane* hullPlanes = b3GetHullPlanes( hullB );
|
||||
const b3Vec3* hullPoints = b3GetHullPoints( hullB );
|
||||
|
||||
float speculativeDistance = enableSpeculative ? B3_SPECULATIVE_DISTANCE : 0.0f;
|
||||
cache->hit = 1;
|
||||
@@ -981,7 +1016,7 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
{
|
||||
B3_ASSERT( cache->indexA == 0 );
|
||||
|
||||
int vertexIndex = b3FindHullSupportVertex( hullA, b3Neg( trianglePlane.normal ) );
|
||||
int vertexIndex = b3FindHullSupportVertex( hullB, b3Neg( trianglePlane.normal ) );
|
||||
b3Vec3 support = hullPoints[vertexIndex];
|
||||
float separation = b3PlaneSeparation( trianglePlane, support );
|
||||
if ( separation > speculativeDistance )
|
||||
@@ -990,15 +1025,17 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
return;
|
||||
}
|
||||
|
||||
b3FaceQuery faceQuery;
|
||||
b3SeparatingAxis faceQuery;
|
||||
faceQuery.normal = trianglePlane.normal;
|
||||
faceQuery.separation = separation;
|
||||
faceQuery.faceIndex = cache->indexA;
|
||||
faceQuery.vertexIndex = vertexIndex;
|
||||
faceQuery.indexA = cache->indexA;
|
||||
faceQuery.indexB = vertexIndex;
|
||||
faceQuery.type = b3_faceAxisA;
|
||||
|
||||
// Read cache but don't modify it
|
||||
b3SATCache localCache = *cache;
|
||||
float clippedSeparation =
|
||||
b3CollideTriangleFace( manifold, capacity, &triangle, hullA, faceQuery, &localCache, enableSpeculative );
|
||||
b3CollideTriangleFace( manifold, capacity, &triangle, hullB, faceQuery, &localCache, enableSpeculative );
|
||||
|
||||
if ( manifold->pointCount > 0 && b3AbsFloat( cache->separation - clippedSeparation ) < linearSlop )
|
||||
{
|
||||
@@ -1014,7 +1051,7 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
|
||||
case b3_faceAxisB:
|
||||
{
|
||||
B3_ASSERT( cache->indexB < hullA->faceCount );
|
||||
B3_ASSERT( cache->indexB < hullB->faceCount );
|
||||
|
||||
b3Plane plane = hullPlanes[cache->indexB];
|
||||
|
||||
@@ -1049,15 +1086,17 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
if ( isDeep == false )
|
||||
{
|
||||
// Try to rebuild contact from last features
|
||||
b3FaceQuery faceQuery;
|
||||
b3SeparatingAxis faceQuery;
|
||||
faceQuery.normal = b3Neg( plane.normal );
|
||||
faceQuery.separation = separation;
|
||||
faceQuery.faceIndex = cache->indexB;
|
||||
faceQuery.vertexIndex = vertexIndex;
|
||||
faceQuery.indexA = vertexIndex;
|
||||
faceQuery.indexB = cache->indexB;
|
||||
faceQuery.type = b3_faceAxisB;
|
||||
|
||||
// Read cache but don't modify it
|
||||
b3SATCache localCache = *cache;
|
||||
float clippedSeparation =
|
||||
b3CollideHullFace( manifold, capacity, &triangle, hullA, faceQuery, &localCache, enableSpeculative );
|
||||
b3CollideHullFace( manifold, capacity, &triangle, hullB, faceQuery, &localCache, enableSpeculative );
|
||||
|
||||
// Cache reuse is only successful if it creates contact points and the clipped separation didn't change much.
|
||||
if ( manifold->pointCount > 0 && b3AbsFloat( cache->separation - clippedSeparation ) < linearSlop )
|
||||
@@ -1081,7 +1120,7 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
b3Vec3 triPoint = trianglePoints[indexA];
|
||||
b3Vec3 triEdge = triangleEdges[indexA];
|
||||
|
||||
B3_ASSERT( cache->indexB < hullA->edgeCount - 1 );
|
||||
B3_ASSERT( cache->indexB < hullB->edgeCount - 1 );
|
||||
int indexB = cache->indexB;
|
||||
|
||||
const b3HullHalfEdge* edge2 = edges + indexB;
|
||||
@@ -1093,36 +1132,55 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
b3Vec3 hullNormal1 = hullPlanes[edge2->face].normal;
|
||||
b3Vec3 hullNormal2 = hullPlanes[twin2->face].normal;
|
||||
|
||||
// Confirm the edge pair is still a Minkowski face
|
||||
bool isMinkowski = b3IsTriangleMinkowskiFace( trianglePlane.normal, triEdge, hullNormal1, hullNormal2, hullEdge );
|
||||
if ( isMinkowski )
|
||||
// Confirm the edge pair is still a Minkowski face.
|
||||
// See "Collision Detection of Convex Polyhedra Based on Duality Transformation"
|
||||
// Simplified for triangle versus hull.
|
||||
float cab = b3Dot( hullNormal1, triEdge );
|
||||
float dab = b3Dot( hullNormal2, triEdge );
|
||||
float bcd = b3Dot( trianglePlane.normal, hullEdge );
|
||||
|
||||
if ( cab * dab < 0.0f && cab * bcd > 0.0f )
|
||||
{
|
||||
// Transform reference center of the first hull into local space of the second hull
|
||||
float separation = b3EdgeEdgeSeparation( triPoint, triEdge, triangleCenter, hullPoint, hullEdge, hullA->center );
|
||||
if ( separation > speculativeDistance )
|
||||
float squaredTolerance = 0.005f * 0.005f;
|
||||
|
||||
// Avoid nearly parallel edges that may lead to invalid separation values at the noise floor.
|
||||
if ( b3MaxFloat( cab * cab, dab * dab ) >= squaredTolerance * b3LengthSquared( triEdge ) )
|
||||
{
|
||||
// Cache hit, shapes are separated
|
||||
return;
|
||||
}
|
||||
|
||||
if ( b3AbsFloat( cache->separation - separation ) < linearSlop )
|
||||
{
|
||||
// Try to rebuild contact from last features
|
||||
b3EdgeQuery edgeQuery;
|
||||
edgeQuery.indexA = indexA;
|
||||
edgeQuery.indexB = indexB;
|
||||
edgeQuery.separation = separation;
|
||||
|
||||
// Read cache but don't modify it
|
||||
b3SATCache localCache = *cache;
|
||||
b3CollideHullAndTriangleEdges( manifold, capacity, triPoint, triEdge, triangleCenter, hullA, edgeQuery,
|
||||
&localCache );
|
||||
|
||||
if ( manifold->pointCount > 0 )
|
||||
// Similar to hull vs hull (b3QueryEdgeDirections)
|
||||
// dot(hullNormal1 + t * (hullNormal2 - hullNormal1), triEdge) = 0
|
||||
// Normal points out of hull by construction.
|
||||
float t = cab / ( cab - dab );
|
||||
b3Vec3 axis = b3Lerp( hullNormal1, hullNormal2, t );
|
||||
B3_VALIDATE( b3LengthSquared( axis ) > 1000.0f * FLT_MIN );
|
||||
axis = b3Normalize( axis );
|
||||
float separation = b3Dot( axis, b3Sub( triPoint, hullPoint ) );
|
||||
if ( separation > speculativeDistance )
|
||||
{
|
||||
// Cache hit, contact point generated
|
||||
// Cache hit, shapes are separated
|
||||
return;
|
||||
}
|
||||
|
||||
if ( b3AbsFloat( cache->separation - separation ) < linearSlop )
|
||||
{
|
||||
// Try to rebuild contact from last features
|
||||
// Flip normal to point from triangle to hull
|
||||
b3SeparatingAxis edgeQuery;
|
||||
edgeQuery.normal = b3Neg( axis );
|
||||
edgeQuery.indexA = indexA;
|
||||
edgeQuery.indexB = indexB;
|
||||
edgeQuery.separation = separation;
|
||||
edgeQuery.type = b3_edgePairAxis;
|
||||
|
||||
// Read cache but don't modify it
|
||||
b3SATCache localCache = *cache;
|
||||
b3CollideTriangleAndHullEdges( manifold, capacity, triPoint, triEdge, hullB, edgeQuery, &localCache );
|
||||
|
||||
if ( manifold->pointCount > 0 )
|
||||
{
|
||||
// Cache hit, contact point generated
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1134,29 +1192,28 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
// This case is for testing
|
||||
case b3_manualFaceAxisA:
|
||||
{
|
||||
b3FaceQuery faceQueryA = b3QueryTriangleFace( &triangle, hullA );
|
||||
b3CollideTriangleFace( manifold, capacity, &triangle, hullA, faceQueryA, cache, enableSpeculative );
|
||||
b3SeparatingAxis query = b3QueryTriangleFace( &triangle, hullB );
|
||||
b3CollideTriangleFace( manifold, capacity, &triangle, hullB, query, cache, enableSpeculative );
|
||||
return;
|
||||
}
|
||||
|
||||
// This case is for testing
|
||||
case b3_manualFaceAxisB:
|
||||
{
|
||||
b3FaceQuery faceQueryB = b3QueryHullFace( &triangle, hullA );
|
||||
b3CollideHullFace( manifold, capacity, &triangle, hullA, faceQueryB, cache, enableSpeculative );
|
||||
b3SeparatingAxis query = b3QueryHullFace( &triangle, hullB );
|
||||
b3CollideHullFace( manifold, capacity, &triangle, hullB, query, cache, enableSpeculative );
|
||||
return;
|
||||
}
|
||||
|
||||
// This case is for testing
|
||||
case b3_manualEdgePairAxis:
|
||||
{
|
||||
b3EdgeQuery edgeQuery = b3TestEdgePairs( &triangle, hullA );
|
||||
if ( edgeQuery.indexA != B3_NULL_INDEX )
|
||||
b3SeparatingAxis query = b3QueryTriangleAndHullEdges( &triangle, hullB );
|
||||
if ( query.indexA != B3_NULL_INDEX )
|
||||
{
|
||||
b3Vec3 trianglePoint = trianglePoints[edgeQuery.indexA];
|
||||
b3Vec3 triangleEdge = triangleEdges[edgeQuery.indexA];
|
||||
b3CollideHullAndTriangleEdges( manifold, capacity, trianglePoint, triangleEdge, triangleCenter, hullA, edgeQuery,
|
||||
cache );
|
||||
b3Vec3 trianglePoint = trianglePoints[query.indexA];
|
||||
b3Vec3 triangleEdge = triangleEdges[query.indexA];
|
||||
b3CollideTriangleAndHullEdges( manifold, capacity, trianglePoint, triangleEdge, hullB, query, cache );
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1170,29 +1227,29 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
cache->hit = 0;
|
||||
|
||||
// Find axis of minimum penetration
|
||||
b3FaceQuery faceQueryA = b3QueryTriangleFace( &triangle, hullA );
|
||||
b3SeparatingAxis faceQueryA = b3QueryTriangleFace( &triangle, hullB );
|
||||
if ( faceQueryA.separation > speculativeDistance )
|
||||
{
|
||||
// Separating axis found
|
||||
cache->separation = faceQueryA.separation;
|
||||
cache->type = b3_faceAxisA;
|
||||
cache->indexA = 0;
|
||||
cache->indexB = UINT8_MAX;
|
||||
cache->indexA = (uint8_t)faceQueryA.indexA;
|
||||
cache->indexB = (uint8_t)faceQueryA.indexB;
|
||||
return;
|
||||
}
|
||||
|
||||
b3FaceQuery faceQueryB = b3QueryHullFace( &triangle, hullA );
|
||||
b3SeparatingAxis faceQueryB = b3QueryHullFace( &triangle, hullB );
|
||||
if ( faceQueryB.separation > speculativeDistance )
|
||||
{
|
||||
// Separating axis found
|
||||
cache->separation = faceQueryB.separation;
|
||||
cache->type = b3_faceAxisB;
|
||||
cache->indexA = UINT8_MAX;
|
||||
cache->indexB = (uint8_t)faceQueryB.faceIndex;
|
||||
cache->indexA = (uint8_t)faceQueryB.indexA;
|
||||
cache->indexB = (uint8_t)faceQueryB.indexB;
|
||||
return;
|
||||
}
|
||||
|
||||
b3EdgeQuery edgeQuery = b3TestEdgePairs( &triangle, hullA );
|
||||
b3SeparatingAxis edgeQuery = b3QueryTriangleAndHullEdges( &triangle, hullB );
|
||||
if ( edgeQuery.separation > speculativeDistance )
|
||||
{
|
||||
// Separating axis found
|
||||
@@ -1203,21 +1260,18 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
return;
|
||||
}
|
||||
|
||||
float clippedFaceSeparation;
|
||||
float clipSeparation;
|
||||
|
||||
// Don't admit a hull face significantly opposed to the triangle face.
|
||||
// Need a tolerance to avoid ghost collisions.
|
||||
// todo hull query skips faces that point along the triangle normal
|
||||
b3Vec3 hullNormal = hullPlanes[faceQueryB.faceIndex].normal;
|
||||
bool pushingDown = b3Dot( hullNormal, trianglePlane.normal ) > 0.25f;
|
||||
if ( faceQueryB.separation > faceQueryA.separation + linearSlop && pushingDown == false )
|
||||
bool pushingDown = b3Dot( faceQueryB.normal, trianglePlane.normal ) < -0.25f;
|
||||
if ( faceQueryB.separation >= faceQueryA.separation && pushingDown == false )
|
||||
{
|
||||
clippedFaceSeparation = b3CollideHullFace( manifold, capacity, &triangle, hullA, faceQueryB, cache, enableSpeculative );
|
||||
clipSeparation = b3CollideHullFace( manifold, capacity, &triangle, hullB, faceQueryB, cache, enableSpeculative );
|
||||
}
|
||||
else
|
||||
{
|
||||
clippedFaceSeparation =
|
||||
b3CollideTriangleFace( manifold, capacity, &triangle, hullA, faceQueryA, cache, enableSpeculative );
|
||||
clipSeparation = b3CollideTriangleFace( manifold, capacity, &triangle, hullB, faceQueryA, cache, enableSpeculative );
|
||||
}
|
||||
|
||||
// Does an edge axis exist?
|
||||
@@ -1228,14 +1282,13 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
float maxFaceSeparation = b3MaxFloat( faceQueryA.separation, faceQueryB.separation );
|
||||
|
||||
if ( ( manifold->pointCount == 0 && edgeQuery.separation > maxFaceSeparation ) ||
|
||||
( manifold->pointCount == 1 && edgeQuery.separation > clippedFaceSeparation + linearSlop ) )
|
||||
( manifold->pointCount == 1 && edgeQuery.separation > clipSeparation + linearSlop ) )
|
||||
{
|
||||
B3_ASSERT( 0 <= edgeQuery.indexA && edgeQuery.indexA < 3 );
|
||||
b3Vec3 trianglePoint = trianglePoints[edgeQuery.indexA];
|
||||
b3Vec3 triangleEdge = triangleEdges[edgeQuery.indexA];
|
||||
manifold->pointCount = 0;
|
||||
b3CollideHullAndTriangleEdges( manifold, capacity, trianglePoint, triangleEdge, triangleCenter, hullA, edgeQuery,
|
||||
cache );
|
||||
b3CollideTriangleAndHullEdges( manifold, capacity, trianglePoint, triangleEdge, hullB, edgeQuery, cache );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1250,7 +1303,7 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
.count = 3,
|
||||
.radius = 0.0f,
|
||||
};
|
||||
input.proxyB = (b3ShapeProxy){ .points = hullPoints, .count = hullA->vertexCount, .radius = 0.0f };
|
||||
input.proxyB = (b3ShapeProxy){ .points = hullPoints, .count = hullB->vertexCount, .radius = 0.0f };
|
||||
input.transform = b3Transform_identity;
|
||||
input.useRadii = false;
|
||||
|
||||
@@ -1270,5 +1323,8 @@ void b3CollideHullAndTriangle( b3LocalManifold* manifold, int capacity, const b3
|
||||
// This feature pair not accurate but maybe it doesn't matter
|
||||
manifold->points[0].pair = b3FeaturePair_single;
|
||||
}
|
||||
|
||||
// No way to cache this scenario
|
||||
*cache = (b3SATCache){ 0 };
|
||||
}
|
||||
}
|
||||
|
||||
3
vendor/box3d/src/src/types.c
vendored
3
vendor/box3d/src/src/types.c
vendored
@@ -80,10 +80,9 @@ b3ShapeDef b3DefaultShapeDef( void )
|
||||
return def;
|
||||
}
|
||||
|
||||
static bool b3EmptyDrawShape( void* userShape, b3WorldTransform transform, b3HexColor color, void* context )
|
||||
static void b3EmptyDrawShape( void* userShape, b3WorldTransform transform, b3HexColor color, void* context )
|
||||
{
|
||||
B3_UNUSED( userShape, transform, color, context );
|
||||
return false;
|
||||
}
|
||||
|
||||
static void b3EmptyDrawSegment( b3Pos p1, b3Pos p2, b3HexColor color, void* context )
|
||||
|
||||
Reference in New Issue
Block a user