physics fixes

This commit is contained in:
ecker 2026-04-26 20:27:14 -05:00
parent 2c3da15a4d
commit fc36c77167
16 changed files with 150 additions and 123 deletions

View File

@ -90,8 +90,8 @@ ifneq (,$(findstring ffx:sdk,$(REQ_DEPS)))
ifneq (,$(findstring vulkan,$(REQ_DEPS))) ifneq (,$(findstring vulkan,$(REQ_DEPS)))
FLAGS += -DUF_USE_FFX_SDK=3 FLAGS += -DUF_USE_FFX_SDK=3
#INCS += -I./dep/include/ffx_fsr2/ #INCS += -I./dep/include/ffx_fsr2/
DEPS += -lffx_fsr3_x64drel -lffx_fsr2_x64drel -lffx_fsr3upscaler_x64drel -lffx_frameinterpolation_x64drel -lffx_opticalflow_x64drel -lffx_backend_vk_x64drel -lamd_fidelityfx_vkdrel #DEPS += -lffx_fsr3_x64drel -lffx_fsr2_x64drel -lffx_fsr3upscaler_x64drel -lffx_frameinterpolation_x64drel -lffx_opticalflow_x64drel -lffx_backend_vk_x64drel -lamd_fidelityfx_vkdrel
#DEPS += -lffx_fsr3_x64 -lffx_fsr2_x64 -lffx_fsr3upscaler_x64 -lffx_frameinterpolation_x64 -lffx_opticalflow_x64 -lffx_backend_vk_x64 -lamd_fidelityfx_vk DEPS += -lffx_fsr3_x64 -lffx_fsr2_x64 -lffx_fsr3upscaler_x64 -lffx_frameinterpolation_x64 -lffx_opticalflow_x64 -lffx_backend_vk_x64 -lamd_fidelityfx_vk
endif endif
endif endif
ifneq (,$(findstring vulkan,$(REQ_DEPS))) ifneq (,$(findstring vulkan,$(REQ_DEPS)))

View File

@ -66,9 +66,9 @@
"vulkan": { "vulkan": {
"version": 1.3, "version": 1.3,
"validation": { "validation": {
"enabled": false, "enabled": true,
"messages": false, "messages": true,
"checkpoints": false, "checkpoints": true,
"filters": [ "filters": [
// "0xe5d1743c" // VUID-vkCmdDispatch-None-02699 (problem when using VXGI) (seems to be fixed?) // "0xe5d1743c" // VUID-vkCmdDispatch-None-02699 (problem when using VXGI) (seems to be fixed?)
// ,"0x6714bd0c" // VUID-vkCmdDispatch-format-07753 (for some dumb shit) (seems to be fixed?) // ,"0x6714bd0c" // VUID-vkCmdDispatch-format-07753 (for some dumb shit) (seems to be fixed?)
@ -101,7 +101,7 @@
"experimental": { "experimental": {
"rebuild on tick begin": false, "rebuild on tick begin": false,
"batch queue submissions": true, "batch queue submissions": true,
"dedicated thread": true, // mostly works "dedicated thread": false, // mostly works
"memory budget": false, "memory budget": false,
"register render modes": true, "register render modes": true,
"skip render on rebuild": false "skip render on rebuild": false
@ -109,7 +109,7 @@
"invariant": { "invariant": {
"default stage buffers": true, "default stage buffers": true,
"default defer buffer destroy": true, "default defer buffer destroy": true,
"default command buffer immediate": false, "default command buffer immediate": true,
"multithreaded recording": true "multithreaded recording": true
}, },
"pipelines": { "pipelines": {
@ -117,10 +117,10 @@
"gui": true, "gui": true,
"vsync": true, // vsync on vulkan side rather than engine-side "vsync": true, // vsync on vulkan side rather than engine-side
"hdr": true, "hdr": true,
"vxgi": true, "vxgi": false,
"culling": true, "culling": false,
"bloom": true, "bloom": false,
"dof": true, "dof": false,
"rt": false, "rt": false,
"fsr": false, "fsr": false,
"postProcess": false // "postProcess.chromab" // false "postProcess": false // "postProcess.chromab" // false
@ -363,7 +363,7 @@
"render modes": { "gui": true, "deferred": true }, "render modes": { "gui": true, "deferred": true },
"limiters": { "limiters": {
"deltaTime": 5, "deltaTime": 5,
"framerate": 0 // "auto" // for some reason drops to 60 "framerate": "auto" // "auto" // for some reason drops to 60
}, },
"threads": { "threads": {
"workers" : "auto", "workers" : "auto",

View File

@ -161,8 +161,8 @@ namespace pod {
float sleepTimer = 0.0f; float sleepTimer = 0.0f;
int32_t islandID = -1; int32_t islandID = -1;
static constexpr float sleepThreshold = 0.5f; // seconds static constexpr float sleepThreshold = 0.5f; // seconds
static constexpr float linearSleepEpsilon = 0.01f; // m/s static constexpr float linearSleepEpsilon = 0.1f; // m/s
static constexpr float angularSleepEpsilon = 0.01f; // rad/s static constexpr float angularSleepEpsilon = 0.1f; // rad/s
}; };
struct World; // forward declare struct World; // forward declare
@ -232,7 +232,7 @@ namespace pod {
bool blockContactSolver = true; // use BlockNxN solvers (where N = number of contacts for a manifold) bool blockContactSolver = true; // use BlockNxN solvers (where N = number of contacts for a manifold)
bool psgContactSolver = true; // use PSG contact solver bool psgContactSolver = true; // use PSG contact solver
bool useGjk = false; // currently don't have a way to broadphase mesh => narrowphase tri via GJK bool useGjk = false; // currently don't have a way to broadphase mesh => narrowphase tri via GJK
bool fixedStep = true; // run physics simulation with a fixed delta time (with accumulation), rather than rely on actual engine deltatime bool fixedStep = false; // run physics simulation with a fixed delta time (with accumulation), rather than rely on actual engine deltatime
uint32_t substeps = 4; // number of substeps per frame tick uint32_t substeps = 4; // number of substeps per frame tick
uint32_t reserveCount = 32; // amount of elements to reserve for vectors used in this system, to-do: have it tie to a memory pool allocator uint32_t reserveCount = 32; // amount of elements to reserve for vectors used in this system, to-do: have it tie to a memory pool allocator

View File

@ -19,6 +19,8 @@
#include "../scene/behavior.h" #include "../scene/behavior.h"
// #include "../../gui/manager/behavior.h" // #include "../../gui/manager/behavior.h"
#define ONE_OVER_SIXTY 0.016666f
UF_BEHAVIOR_REGISTER_CPP(ext::PlayerBehavior) UF_BEHAVIOR_REGISTER_CPP(ext::PlayerBehavior)
UF_BEHAVIOR_TRAITS_CPP(ext::PlayerBehavior, ticks = true, renders = false, multithread = true) UF_BEHAVIOR_TRAITS_CPP(ext::PlayerBehavior, ticks = true, renders = false, multithread = true)
#define this (&self) #define this (&self)
@ -94,8 +96,8 @@ void ext::PlayerBehavior::initialize( uf::Object& self ) {
const pod::Vector2ui deadZone{0, 0}; const pod::Vector2ui deadZone{0, 0};
if ( (payload.mouse.delta.x == 0 && payload.mouse.delta.y == 0) || !metadata.system.control ) return; if ( (payload.mouse.delta.x == 0 && payload.mouse.delta.y == 0) || !metadata.system.control ) return;
if (abs(payload.mouse.delta.x) > deadZone.x) metadata.mouse.accum.x += payload.mouse.delta.x * uf::physics::time::delta / payload.window.size.x; if (abs(payload.mouse.delta.x) > deadZone.x) metadata.mouse.accum.x += payload.mouse.delta.x * (ONE_OVER_SIXTY)/* uf::physics::time::delta*/ / payload.window.size.x;
if (abs(payload.mouse.delta.y) > deadZone.y) metadata.mouse.accum.y += payload.mouse.delta.y * uf::physics::time::delta / payload.window.size.y; if (abs(payload.mouse.delta.y) > deadZone.y) metadata.mouse.accum.y += payload.mouse.delta.y * (ONE_OVER_SIXTY)/* uf::physics::time::delta*/ / payload.window.size.y;
}); });
#endif #endif
#endif #endif
@ -347,7 +349,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
if ( physicsBody.gravity == pod::Vector3f{0,0,0} ) stats.noclipped = true; if ( physicsBody.gravity == pod::Vector3f{0,0,0} ) stats.noclipped = true;
{ {
speed.rotate = metadata.movement.rotate * uf::physics::time::delta; speed.rotate = metadata.movement.rotate * ONE_OVER_SIXTY /*uf::physics::time::delta*/;
speed.move = metadata.movement.move; speed.move = metadata.movement.move;
speed.run = metadata.movement.run; speed.run = metadata.movement.run;
speed.walk = metadata.movement.walk; speed.walk = metadata.movement.walk;
@ -466,7 +468,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
if ( stats.noclipped ) { if ( stats.noclipped ) {
physicsBody.velocity += target * speed.move; physicsBody.velocity += target * speed.move;
} else { } else {
physicsBody.velocity += target * std::clamp( speed.move * factor - uf::vector::dot( physicsBody.velocity, target ), 0.0f, speed.move * 10 * uf::physics::time::delta ); physicsBody.velocity += target * std::clamp( speed.move * factor - uf::vector::dot( physicsBody.velocity, target ), 0.0f, speed.move * 10 * ONE_OVER_SIXTY /*uf::physics::time::delta*/ );
} }
auto dot = uf::vector::dot( transform.forward, target ); auto dot = uf::vector::dot( transform.forward, target );
@ -474,7 +476,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
// auto cross = uf::vector::normalize( uf::vector::cross( transform.forward, target ) ); // auto cross = uf::vector::normalize( uf::vector::cross( transform.forward, target ) );
// auto axis = cross == pod::Vector3f{0, 0, 0} ? transform.up : cross; // auto axis = cross == pod::Vector3f{0, 0, 0} ? transform.up : cross;
auto axis = transform.up; auto axis = transform.up;
float angle = uf::vector::signedAngle( transform.forward, target, axis ) * uf::physics::time::delta * 4; // speed.rotate; float angle = uf::vector::signedAngle( transform.forward, target, axis ) * ONE_OVER_SIXTY /*uf::physics::time::delta*/ * 4; // speed.rotate;
if ( physicsBody.object ) uf::physics::impl::applyRotation( physicsBody, axis, angle ); else if ( physicsBody.object ) uf::physics::impl::applyRotation( physicsBody, axis, angle ); else
uf::transform::rotate( transform, axis, angle ); uf::transform::rotate( transform, axis, angle );
@ -485,9 +487,9 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
TIMER(0.0625, stats.floored && keys.jump && !stats.noclipped ) { TIMER(0.0625, stats.floored && keys.jump && !stats.noclipped ) {
physicsBody.velocity += translator.up * metadata.movement.jump; physicsBody.velocity += translator.up * metadata.movement.jump;
} }
if ( stats.floored && keys.jump && stats.noclipped ) transform.position += translator.up * metadata.movement.jump * uf::physics::time::delta * 4.0f; if ( stats.floored && keys.jump && stats.noclipped ) transform.position += translator.up * metadata.movement.jump * ONE_OVER_SIXTY /*uf::physics::time::delta*/ * 4.0f;
if ( keys.crouch ) { if ( keys.crouch ) {
if ( stats.noclipped ) transform.position -= translator.up * metadata.movement.jump * uf::physics::time::delta * 4.0f; if ( stats.noclipped ) transform.position -= translator.up * metadata.movement.jump * ONE_OVER_SIXTY /*uf::physics::time::delta*/ * 4.0f;
else { else {
if ( !metadata.system.crouching ) stats.deltaCrouch = true; if ( !metadata.system.crouching ) stats.deltaCrouch = true;
metadata.system.crouching = true; metadata.system.crouching = true;
@ -504,8 +506,8 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
const auto& mouseDelta = uf::inputs::kbm::states::Mouse; const auto& mouseDelta = uf::inputs::kbm::states::Mouse;
bool shouldnt = (mouseDelta.x == 0 && mouseDelta.y == 0) || !metadata.system.control || metadata.camera.fixed; bool shouldnt = (mouseDelta.x == 0 && mouseDelta.y == 0) || !metadata.system.control || metadata.camera.fixed;
if ( !shouldnt ) { if ( !shouldnt ) {
if (abs(mouseDelta.x) > deadZone.x) metadata.mouse.accum.x += mouseDelta.x * uf::physics::time::delta; if (abs(mouseDelta.x) > deadZone.x) metadata.mouse.accum.x += mouseDelta.x * (ONE_OVER_SIXTY)/* uf::physics::time::delta*/;
if (abs(mouseDelta.y) > deadZone.y) metadata.mouse.accum.y += mouseDelta.y * uf::physics::time::delta; if (abs(mouseDelta.y) > deadZone.y) metadata.mouse.accum.y += mouseDelta.y * (ONE_OVER_SIXTY)/* uf::physics::time::delta*/;
} }
} }
#endif #endif
@ -564,7 +566,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
} }
{ {
if ( physicsBody.object ) uf::physics::impl::setVelocity( physicsBody, physicsBody.velocity ); else if ( physicsBody.object ) uf::physics::impl::setVelocity( physicsBody, physicsBody.velocity ); else
transform.position += physicsBody.velocity * uf::physics::time::delta; transform.position += physicsBody.velocity * ONE_OVER_SIXTY /*uf::physics::time::delta*/;
// if ( uf::vector::magnitude( physicsBody.velocity ) > 1.0e-6 ) UF_MSG_DEBUG("Velocity: {}", uf::vector::toString( physicsBody.velocity )); // if ( uf::vector::magnitude( physicsBody.velocity ) > 1.0e-6 ) UF_MSG_DEBUG("Velocity: {}", uf::vector::toString( physicsBody.velocity ));
} }

View File

@ -397,9 +397,6 @@ void ext::vulkan::Pipeline::record( const Graphic& graphic, VkCommandBuffer comm
} }
void ext::vulkan::Pipeline::record( const Graphic& graphic, const GraphicDescriptor& descriptor, VkCommandBuffer commandBuffer, size_t pass, size_t draw, size_t offset ) const { void ext::vulkan::Pipeline::record( const Graphic& graphic, const GraphicDescriptor& descriptor, VkCommandBuffer commandBuffer, size_t pass, size_t draw, size_t offset ) const {
auto shaders = getShaders( graphic.material.shaders, descriptor.pipeline ); auto shaders = getShaders( graphic.material.shaders, descriptor.pipeline );
for ( auto i = 0; i < shaders.size(); ++i ) {
// UF_MSG_DEBUG("{} | {}: {}", descriptor.pipeline, i, shaders[i]->filename);
}
// create dynamic offset ranges // create dynamic offset ranges
static thread_local uf::stl::vector<uint32_t> dynamicOffsets; static thread_local uf::stl::vector<uint32_t> dynamicOffsets;

View File

@ -15,6 +15,8 @@
#define VK_DEBUG_VALIDATION_MESSAGE(...)\ #define VK_DEBUG_VALIDATION_MESSAGE(...)\
//VK_VALIDATION_MESSAGE(__VA_ARGS__); //VK_VALIDATION_MESSAGE(__VA_ARGS__);
#define IS_DYNAMIC(name) name == "UBO_d"
#define UF_SHADER_PARSE_AS_JSON 0 #define UF_SHADER_PARSE_AS_JSON 0
#if UF_SHADER_PARSE_AS_JSON #if UF_SHADER_PARSE_AS_JSON
ext::json::Value ext::vulkan::definitionToJson(/*const*/ ext::json::Value& definition ) { ext::json::Value ext::vulkan::definitionToJson(/*const*/ ext::json::Value& definition ) {
@ -519,7 +521,10 @@ void ext::vulkan::Shader::initialize( ext::vulkan::Device& device, const uf::stl
}; };
if ( descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ) { if ( descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ) {
metadata.dynamicRanges.emplace_back( bufferSize ); if ( IS_DYNAMIC(name) ) {
UF_MSG_DEBUG("Registered dynamic UBO: {}", name);
}
metadata.dynamicRanges.emplace_back( IS_DYNAMIC(name) ? bufferSize : 0 );
} }
} break; } break;
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:

View File

@ -83,7 +83,7 @@ namespace {
float bestCost = std::numeric_limits<float>::infinity(); float bestCost = std::numeric_limits<float>::infinity();
for ( auto axis = 0; axis < 3; ++axis ) { for ( auto axis = 0; axis < 3; ++axis ) {
if ( extent[axis] < EPS(1e-6f) ) continue; if ( extent[axis] < EPS ) continue;
for ( auto i = 0; i < numBins; i++ ) { for ( auto i = 0; i < numBins; i++ ) {
bins[i].count = 0; bins[i].count = 0;
bins[i].bounds = {}; bins[i].bounds = {};
@ -819,7 +819,9 @@ namespace {
// union all pairs // union all pairs
for ( auto& [a, b] : pairs ) { for ( auto& [a, b] : pairs ) {
unionizer.unite(a, b); if ( !bodies[a]->isStatic && !bodies[b]->isStatic ) {
unionizer.unite(a, b);
}
} }
// map root to island index // map root to island index
@ -830,9 +832,11 @@ namespace {
islands.reserve(bodies.size()); islands.reserve(bodies.size());
for ( auto i = 0; i < bodies.size(); i++ ) { for ( auto i = 0; i < bodies.size(); i++ ) {
if ( bodies[i]->isStatic ) continue;
pod::BVH::index_t root = unionizer.find(i); pod::BVH::index_t root = unionizer.find(i);
if (rootToIsland.find(root) == rootToIsland.end()) { if ( rootToIsland.find(root) == rootToIsland.end() ) {
rootToIsland[root] = (pod::BVH::index_t) islands.size(); rootToIsland[root] = (pod::BVH::index_t) islands.size();
islands.emplace_back(); islands.emplace_back();
} }
@ -846,9 +850,19 @@ namespace {
// do not insert these pairs if they're non-colliding // do not insert these pairs if they're non-colliding
if ( !::shouldCollide( *bodies[a], *bodies[b] ) ) continue; if ( !::shouldCollide( *bodies[a], *bodies[b] ) ) continue;
// just in case
pod::BVH::index_t dynamicIndex = bodies[a]->isStatic ? b : a;
if ( bodies[a]->isStatic && bodies[b]->isStatic ) continue;
pod::BVH::index_t root = unionizer.find(a); pod::BVH::index_t root = unionizer.find(a);
pod::BVH::index_t islandID = rootToIsland[root]; if ( rootToIsland.find(root) != rootToIsland.end() ) {
islands[islandID].pairs.emplace(a, b); pod::BVH::index_t islandID = rootToIsland[root];
islands[islandID].pairs.emplace(a, b);
if ( bodies[a]->activity.awake || bodies[b]->activity.awake ) {
::wakeBody( *bodies[dynamicIndex] );
}
}
} }
} }
@ -859,10 +873,10 @@ namespace {
auto& body = *bodies[idx]; auto& body = *bodies[idx];
if ( !body.activity.awake ) continue; if ( !body.activity.awake ) continue;
float linSpeed = uf::vector::norm( body.velocity ); float linSpeedSq = uf::vector::magnitude( body.velocity );
float angSpeed = uf::vector::norm( body.angularVelocity ); float angSpeedSq = uf::vector::magnitude( body.angularVelocity );
if ( linSpeed < pod::Activity::linearSleepEpsilon && angSpeed < pod::Activity::angularSleepEpsilon) { if ( linSpeedSq < pod::Activity::linearSleepEpsilon && angSpeedSq < pod::Activity::angularSleepEpsilon) {
body.activity.sleepTimer += dt; body.activity.sleepTimer += dt;
} else { } else {
body.activity.sleepTimer = 0.0f; body.activity.sleepTimer = 0.0f;

View File

@ -17,7 +17,7 @@ namespace {
const auto& C = s.pts[2].p; const auto& C = s.pts[2].p;
const auto& D = s.pts[3].p; const auto& D = s.pts[3].p;
return fabs( uf::vector::dot( (B - A), uf::vector::cross(C - A, D - A) ) ) > EPS(1e-6f); return fabs( uf::vector::dot( (B - A), uf::vector::cross(C - A, D - A) ) ) > EPS;
} }
pod::Face makeFace( const pod::SupportPoint& a, const pod::SupportPoint& b, const pod::SupportPoint& c ) { pod::Face makeFace( const pod::SupportPoint& a, const pod::SupportPoint& b, const pod::SupportPoint& c ) {
@ -80,7 +80,7 @@ namespace {
} }
} }
pod::Contact epa( const pod::PhysicsBody& a, const pod::PhysicsBody& b, const pod::Simplex& simplex, uint32_t maxIterations = 64, float eps = EPS(1.0e-4f) ) { pod::Contact epa( const pod::PhysicsBody& a, const pod::PhysicsBody& b, const pod::Simplex& simplex, uint32_t maxIterations = 64, float eps = EPS ) {
UF_ASSERT( ::isValidSimplex(simplex) ); UF_ASSERT( ::isValidSimplex(simplex) );
auto faces = ::initialPolytope(simplex); auto faces = ::initialPolytope(simplex);

View File

@ -123,7 +123,7 @@ namespace {
return false; return false;
} }
bool isDegenerate( const pod::Simplex& s, const pod::SupportPoint& newPt, float eps = EPS(1.0e-6f) ) { bool isDegenerate( const pod::Simplex& s, const pod::SupportPoint& newPt, float eps = EPS ) {
const float eps2 = eps * eps; const float eps2 = eps * eps;
// compare to existing // compare to existing
for ( auto& sp : s.pts ) { for ( auto& sp : s.pts ) {
@ -150,7 +150,7 @@ namespace {
return false; return false;
} }
bool gjk( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Simplex& simplex, int maxIterations = 20, float eps = EPS(1e-6f) ) { bool gjk( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Simplex& simplex, int maxIterations = 20, float eps = EPS ) {
const float eps2 = eps * eps; const float eps2 = eps * eps;
auto dir = ::getPosition( b ) - ::getPosition( a ); auto dir = ::getPosition( b ) - ::getPosition( a );
if ( uf::vector::magnitude( dir ) < eps2 ) dir = {1,0,0}; // fallback direction if ( uf::vector::magnitude( dir ) < eps2 ) dir = {1,0,0}; // fallback direction

View File

@ -6,44 +6,44 @@
// forward declare // forward declare
namespace { namespace {
bool aabbAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool aabbAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool aabbSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool aabbSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool aabbPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool aabbPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool aabbCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool aabbCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool aabbMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool aabbMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool sphereSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool sphereSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool sphereAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool sphereAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool spherePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool spherePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool sphereCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool sphereCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool sphereMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool sphereMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool planeAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool planeAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool planeSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool planeSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool planePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool planePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool planeCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool planeCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool planeMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool planeMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool capsuleCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool capsuleCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool capsuleAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool capsuleAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool capsulePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool capsulePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool capsuleSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool capsuleSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool capsuleMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool capsuleMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool meshAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool meshAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool meshSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool meshSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool meshPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool meshPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool meshCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool meshCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool meshMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool meshMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold, float eps = EPS );
bool triangleTriangle( const pod::TriangleWithNormal& a, const pod::TriangleWithNormal& b, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool triangleTriangle( const pod::TriangleWithNormal& a, const pod::TriangleWithNormal& b, pod::Manifold& manifold, float eps = EPS );
bool triangleAabb( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool triangleAabb( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = EPS );
bool triangleSphere( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool triangleSphere( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = EPS );
bool trianglePlane( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool trianglePlane( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = EPS );
bool triangleCapsule( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = EPS(1.0e-6f) ); bool triangleCapsule( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold, float eps = EPS );
// ugh // ugh
FORCE_INLINE bool aabbOverlap( const pod::AABB& a, const pod::AABB& b, float eps = EPS(1.0e-6f) ); FORCE_INLINE bool aabbOverlap( const pod::AABB& a, const pod::AABB& b, float eps = EPS );
pod::Vector3f aabbCenter( const pod::AABB& aabb ); pod::Vector3f aabbCenter( const pod::AABB& aabb );
@ -69,10 +69,15 @@ namespace {
// marks a body as asleep // marks a body as asleep
void wakeBody( pod::PhysicsBody& body ) { void wakeBody( pod::PhysicsBody& body ) {
bool wasAwake = body.activity.awake;
if ( !wasAwake ) UF_MSG_DEBUG("name={} waking up", body.object->getName());
body.activity.awake = true; body.activity.awake = true;
body.activity.sleepTimer = 0.0f; body.activity.sleepTimer = 0.0f;
} }
void sleepBody( pod::PhysicsBody& body ) { void sleepBody( pod::PhysicsBody& body ) {
if ( body.activity.awake ) UF_MSG_DEBUG("name={} sleeping", body.object->getName());
body.activity.awake = false; body.activity.awake = false;
body.velocity = pod::Vector3f{}; body.velocity = pod::Vector3f{};
body.angularVelocity = pod::Vector3f{}; body.angularVelocity = pod::Vector3f{};
@ -86,13 +91,14 @@ namespace {
if ( !body.activity.awake ) return; if ( !body.activity.awake ) return;
// check if body is moving // check if body is moving
float linSpeed = uf::vector::norm( body.velocity ); float linSpeedSq = uf::vector::magnitude( body.velocity );
float angSpeed = uf::vector::norm( body.angularVelocity ); float angSpeedSq = uf::vector::magnitude( body.angularVelocity );
// body is nearly still // body is nearly still
if ( linSpeed < pod::Activity::linearSleepEpsilon && angSpeed < pod::Activity::angularSleepEpsilon ) { if ( linSpeedSq < pod::Activity::linearSleepEpsilon && angSpeedSq < pod::Activity::angularSleepEpsilon ) {
body.activity.sleepTimer += dt; body.activity.sleepTimer += dt;
float threshold = pod::Activity::sleepThreshold; float threshold = pod::Activity::sleepThreshold;
if ( wasGrounded ) threshold *= 0.25f; if ( wasGrounded ) threshold *= 0.25f;
if ( body.activity.sleepTimer > threshold ) ::sleepBody( body ); if ( body.activity.sleepTimer > threshold ) ::sleepBody( body );
} }
@ -133,7 +139,7 @@ namespace {
// normalizes the delta between two bodies / contacts by the distance (as it was already computed) if non-zero // normalizes the delta between two bodies / contacts by the distance (as it was already computed) if non-zero
// a lot of collider v colliders use this semantic // a lot of collider v colliders use this semantic
pod::Vector3f normalizeDelta( const pod::Vector3f& delta, float dist, float eps = EPS(1.0e-6), const pod::Vector3f& fallback = pod::Vector3f{0,1,0} ) { pod::Vector3f normalizeDelta( const pod::Vector3f& delta, float dist, float eps = EPS, const pod::Vector3f& fallback = pod::Vector3f{0,1,0} ) {
return ( dist > eps ) ? delta / dist : fallback; return ( dist > eps ) ? delta / dist : fallback;
} }
@ -149,7 +155,7 @@ namespace {
return a + ab * t; return a + ab * t;
} }
std::pair<pod::Vector3f, pod::Vector3f> closestSegmentSegment( const pod::Vector3f& A, const pod::Vector3f& B, const pod::Vector3f& C, const pod::Vector3f& D, float eps = EPS(1e-6f) ) { std::pair<pod::Vector3f, pod::Vector3f> closestSegmentSegment( const pod::Vector3f& A, const pod::Vector3f& B, const pod::Vector3f& C, const pod::Vector3f& D, float eps = EPS ) {
auto u = B - A; auto u = B - A;
auto v = D - C; auto v = D - C;
auto w = A - C; auto w = A - C;
@ -195,7 +201,7 @@ namespace {
return { A + u * sc, C + v * tc }; return { A + u * sc, C + v * tc };
} }
pod::Vector3f closestPointSegmentAabb( const pod::Vector3f& p1, const pod::Vector3f& p2, const pod::AABB& box, float eps = EPS(1e-6f) ) { pod::Vector3f closestPointSegmentAabb( const pod::Vector3f& p1, const pod::Vector3f& p2, const pod::AABB& box, float eps = EPS ) {
const float eps2 = eps * eps; const float eps2 = eps * eps;
// AABB center and half extents // AABB center and half extents
auto c = ( box.min + box.max ) * 0.5f; auto c = ( box.min + box.max ) * 0.5f;
@ -282,11 +288,11 @@ namespace {
return ::interpolateWithBarycentric( bary, points[0], points[1], points[2] ); return ::interpolateWithBarycentric( bary, points[0], points[1], points[2] );
} }
bool pointInTriangle( const pod::Vector3f& p, const pod::Vector3f& a, const pod::Vector3f& b, const pod::Vector3f& c, float eps = EPS(1.0e-6f) ) { bool pointInTriangle( const pod::Vector3f& p, const pod::Vector3f& a, const pod::Vector3f& b, const pod::Vector3f& c, float eps = EPS ) {
auto bary = ::computeBarycentric( p, a, b, c, false ); auto bary = ::computeBarycentric( p, a, b, c, false );
return ( bary.x >= -eps && bary.y >= -eps && bary.z >= -eps ); return ( bary.x >= -eps && bary.y >= -eps && bary.z >= -eps );
} }
bool pointInTriangle( const pod::Vector3f& p, const pod::Triangle& tri, float eps = EPS(1.0e-6f) ) { bool pointInTriangle( const pod::Vector3f& p, const pod::Triangle& tri, float eps = EPS ) {
auto bary = ::computeBarycentric( p, tri, false ); auto bary = ::computeBarycentric( p, tri, false );
return ( bary.x >= -eps && bary.y >= -eps && bary.z >= -eps ); return ( bary.x >= -eps && bary.y >= -eps && bary.z >= -eps );
} }
@ -347,7 +353,7 @@ namespace {
return uf::vector::normalize( uf::vector::dot( n, ::getPosition( b ) - ::getPosition( a ) ) < 0.0f ? -n : n ); return uf::vector::normalize( uf::vector::dot( n, ::getPosition( b ) - ::getPosition( a ) ) < 0.0f ? -n : n );
} }
float segmentTriangleDistanceSq( const pod::Vector3f& p0, const pod::Vector3f& p1, const pod::Triangle& tri, pod::Vector3f& outSeg, pod::Vector3f& outTri, float eps = EPS(1.0e-6f) ) { float segmentTriangleDistanceSq( const pod::Vector3f& p0, const pod::Vector3f& p1, const pod::Triangle& tri, pod::Vector3f& outSeg, pod::Vector3f& outTri, float eps = EPS ) {
float best = std::numeric_limits<float>::max(); float best = std::numeric_limits<float>::max();
auto n = uf::vector::cross( tri.points[1]-tri.points[0], tri.points[2]-tri.points[0] ); auto n = uf::vector::cross( tri.points[1]-tri.points[0], tri.points[2]-tri.points[0] );

View File

@ -4,8 +4,8 @@
#include <uf/utils/mesh/mesh.h> #include <uf/utils/mesh/mesh.h>
#include <uf/utils/memory/stack.h> #include <uf/utils/memory/stack.h>
#define EPS(x) 1.0e-6f #define EPS 1.0e-6f
#define EPS2 (EPS(1.0e-6) * EPS(1.0e-6)) #define EPS2 (EPS * EPS)
#define ASSERT_COLLIDER_TYPES( A, B ) UF_ASSERT( a.collider.type == pod::ShapeType::A && b.collider.type == pod::ShapeType::B ); #define ASSERT_COLLIDER_TYPES( A, B ) UF_ASSERT( a.collider.type == pod::ShapeType::A && b.collider.type == pod::ShapeType::B );
#define UF_PHYSICS_TEST 0 #define UF_PHYSICS_TEST 0
@ -259,7 +259,7 @@ void uf::physics::impl::updateInertia( pod::PhysicsBody& body ) {
pod::Vector3f dims = (body.collider.aabb.max - body.collider.aabb.min); pod::Vector3f dims = (body.collider.aabb.max - body.collider.aabb.min);
pod::Vector3f dimsSq = dims * dims; pod::Vector3f dimsSq = dims * dims;
body.inertiaTensor = pod::Vector3f{ dimsSq.y + dimsSq.z, dimsSq.x + dimsSq.z, dimsSq.x + dimsSq.y } * (body.mass / 12.0f); body.inertiaTensor = pod::Vector3f{ dimsSq.y + dimsSq.z, dimsSq.x + dimsSq.z, dimsSq.x + dimsSq.y } * (body.mass / 12.0f);
body.inertiaTensor = uf::vector::max( body.inertiaTensor, { EPS(1.0e-6f), EPS(1.0e-6f), EPS(1.0e-6f) } ); body.inertiaTensor = uf::vector::max( body.inertiaTensor, { EPS, EPS, EPS } );
body.inverseInertiaTensor = 1.0f / body.inertiaTensor; body.inverseInertiaTensor = 1.0f / body.inertiaTensor;
} break; } break;
case pod::ShapeType::SPHERE: { case pod::ShapeType::SPHERE: {
@ -295,7 +295,7 @@ void uf::physics::impl::updateInertia( pod::PhysicsBody& body ) {
totalVolume += extents.x * extents.y * extents.z; totalVolume += extents.x * extents.y * extents.z;
} }
if ( totalVolume < EPS(1.0e-6) ) { if ( totalVolume < EPS ) {
body.inertiaTensor = { FLT_MAX, FLT_MAX, FLT_MAX }; body.inertiaTensor = { FLT_MAX, FLT_MAX, FLT_MAX };
body.inverseInertiaTensor = { 0.0f, 0.0f, 0.0f }; body.inverseInertiaTensor = { 0.0f, 0.0f, 0.0f };
} else { } else {

View File

@ -17,7 +17,7 @@ namespace {
} }
float result = inverseMass + angularTermA + angularTermB; float result = inverseMass + angularTermA + angularTermB;
if (result < EPS(1e-8f)) result = 1.0f; // prevent divide by zero if (result < EPS) result = 1.0f; // prevent divide by zero
return result; return result;
} }
@ -242,16 +242,17 @@ namespace {
void snapVelocity( pod::PhysicsBody& body, float dt, float threshold = 0.01f ) { void snapVelocity( pod::PhysicsBody& body, float dt, float threshold = 0.01f ) {
if ( !body.activity.grounded || !body.activity.awake ) return; if ( !body.activity.grounded || !body.activity.awake ) return;
float thresholdSq = threshold * threshold;
// snap velocity if body is grounded and nearly still // snap velocity if body is grounded and nearly still
float linSpeed = uf::vector::norm( body.velocity ); float linSpeedSq = uf::vector::magnitude( body.velocity );
float angSpeed = uf::vector::norm( body.angularVelocity ); float angSpeedSq = uf::vector::magnitude( body.angularVelocity );
// cancel out vertical component // cancel out vertical component
if ( fabs(body.velocity.y) < threshold ) body.velocity.y = 0.0f; if ( fabs(body.velocity.y) < thresholdSq ) body.velocity.y = 0.0f;
// cancel out velocity entirely // cancel out velocity entirely
if ( linSpeed < threshold ) body.velocity = {}; if ( linSpeedSq < thresholdSq ) body.velocity = {};
// cancel out rotational velocity entirely // cancel out rotational velocity entirely
if ( angSpeed < threshold ) body.angularVelocity = {}; if ( angSpeedSq < thresholdSq ) body.angularVelocity = {};
} }
// baumgarte position correction // baumgarte position correction
@ -267,7 +268,7 @@ namespace {
float invMassA = ( a.isStatic ? 0.0f : a.inverseMass ); float invMassA = ( a.isStatic ? 0.0f : a.inverseMass );
float invMassB = ( b.isStatic ? 0.0f : b.inverseMass ); float invMassB = ( b.isStatic ? 0.0f : b.inverseMass );
float totalInvMass = invMassA + invMassB; float totalInvMass = invMassA + invMassB;
if ( totalInvMass <= EPS(1e-8f) ) return; if ( totalInvMass <= EPS ) return;
// apply correction vector // apply correction vector
pod::Vector3f correction = contact.normal * (penetration / totalInvMass) * uf::physics::impl::settings.baumgarteCorrectionPercent; pod::Vector3f correction = contact.normal * (penetration / totalInvMass) * uf::physics::impl::settings.baumgarteCorrectionPercent;
@ -278,7 +279,6 @@ namespace {
void integrate( pod::PhysicsBody& body, float dt ) { void integrate( pod::PhysicsBody& body, float dt ) {
// only integrate awake and dynamic bodies // only integrate awake and dynamic bodies
if ( !body.activity.awake || body.isStatic || body.mass == 0 ) return; if ( !body.activity.awake || body.isStatic || body.mass == 0 ) return;

View File

@ -1,5 +1,5 @@
namespace { namespace {
bool rayTriangleIntersect( const pod::Ray& ray, const pod::Triangle& tri, float& t, float& u, float& v, float eps = EPS(1.0e-6f) ) { bool rayTriangleIntersect( const pod::Ray& ray, const pod::Triangle& tri, float& t, float& u, float& v, float eps = EPS ) {
auto edge1 = tri.points[1] - tri.points[0]; auto edge1 = tri.points[1] - tri.points[0];
auto edge2 = tri.points[2] - tri.points[0]; auto edge2 = tri.points[2] - tri.points[0];
auto h = uf::vector::cross( ray.direction, edge2 ); auto h = uf::vector::cross( ray.direction, edge2 );
@ -20,7 +20,7 @@ namespace {
return ( t > eps ); return ( t > eps );
} }
bool rayAabbIntersect( const pod::Ray& ray, const pod::AABB& box, float& tMin, float& tMax, float eps = EPS(1.0e-6f) ) { bool rayAabbIntersect( const pod::Ray& ray, const pod::AABB& box, float& tMin, float& tMax, float eps = EPS ) {
tMin = 0.0f; tMin = 0.0f;
tMax = FLT_MAX; tMax = FLT_MAX;
@ -108,7 +108,7 @@ namespace {
rayHit.contact.penetration = t; rayHit.contact.penetration = t;
return true; return true;
} }
bool rayPlane( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQuery& rayHit, float eps = EPS(1e-6f) ) { bool rayPlane( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQuery& rayHit, float eps = EPS ) {
auto& normal = body.collider.plane.normal; auto& normal = body.collider.plane.normal;
float offset = body.collider.plane.offset; float offset = body.collider.plane.offset;
@ -146,7 +146,7 @@ namespace {
float b = dd * uf::vector::dot(m,n) - nd*md; float b = dd * uf::vector::dot(m,n) - nd*md;
float discr = b*b - a*c; float discr = b*b - a*c;
if ( fabs(a) < EPS(1.0e-6f) || discr < 0.0f ) return false; if ( fabs(a) < EPS || discr < 0.0f ) return false;
float t = (-b - std::sqrt(discr)) / a; // nearer hit float t = (-b - std::sqrt(discr)) / a; // nearer hit

View File

@ -140,6 +140,7 @@ namespace {
pod::Vector3f vB = b.velocity + uf::vector::cross( b.angularVelocity, contact.point - pB ); pod::Vector3f vB = b.velocity + uf::vector::cross( b.angularVelocity, contact.point - pB );
float vRel = uf::vector::dot((vB - vA), contact.normal); float vRel = uf::vector::dot((vB - vA), contact.normal);
/*
// penetration bias with clamp // penetration bias with clamp
float penetrationBias = std::max(contact.penetration - uf::physics::impl::settings.baumgarteCorrectionSlop, 0.0f) * (uf::physics::impl::settings.baumgarteCorrectionPercent / dt); float penetrationBias = std::max(contact.penetration - uf::physics::impl::settings.baumgarteCorrectionSlop, 0.0f) * (uf::physics::impl::settings.baumgarteCorrectionPercent / dt);
penetrationBias = std::min(penetrationBias, 2.0f / dt); // clamp penetrationBias = std::min(penetrationBias, 2.0f / dt); // clamp
@ -148,9 +149,11 @@ namespace {
if ( penetrationBias > maxPenetrationRecovery ) penetrationBias = maxPenetrationRecovery; if ( penetrationBias > maxPenetrationRecovery ) penetrationBias = maxPenetrationRecovery;
float cDot = vRel + penetrationBias; float cDot = vRel + penetrationBias;
rhs[i] = (cDot < 0.0f) ? -cDot : 0.0f; // RHS is magnitude of correction needed rhs[i] = (cDot < 0.0f) ? -cDot : 0.0f; // RHS is magnitude of correction needed
lambda[i] = contact.accumulatedNormalImpulse; lambda[i] = contact.accumulatedNormalImpulse;
*/
rhs[i] = (vRel < 0.0f) ? -vRel : 0.0f;
lambda[i] = contact.accumulatedNormalImpulse;
} }
residual = rhs - uf::matrix::multiply( K, lambda ); residual = rhs - uf::matrix::multiply( K, lambda );
@ -215,7 +218,7 @@ namespace {
// restitution bias + baumgarte // restitution bias + baumgarte
float e = std::min( a.material.restitution, b.material.restitution ); float e = std::min( a.material.restitution, b.material.restitution );
float penetrationBias = std::max( c.penetration - uf::physics::impl::settings.baumgarteCorrectionSlop, 0.0f ) * (uf::physics::impl::settings.baumgarteCorrectionPercent / dt); float penetrationBias = std::max( c.penetration - uf::physics::impl::settings.baumgarteCorrectionSlop, 0.0f ) * (uf::physics::impl::settings.baumgarteCorrectionPercent / dt);
cc.bias = (vn < -1.0f ? -e * vn : 0.0f) + penetrationBias; cc.bias = 0; // (vn < -1.0f ? -e * vn : 0.0f) + penetrationBias;
// effective mass (normal) // effective mass (normal)
pod::Vector3f rnA = uf::vector::cross( cc.rA, cc.normal ); pod::Vector3f rnA = uf::vector::cross( cc.rA, cc.normal );

View File

@ -45,7 +45,7 @@ TEST(SphereSphere_Collision, {
bool collided = sphereSphere(bodyA, bodyB, m); bool collided = sphereSphere(bodyA, bodyB, m);
EXPECT_TRUE(collided); EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty()); EXPECT_TRUE(!m.points.empty());
EXPECT_NEAR(m.points[0].penetration, 0.5f, EPS(1e-4f)); EXPECT_NEAR(m.points[0].penetration, 0.5f, EPS);
}) })
TEST(AabbAabb_Collision, { TEST(AabbAabb_Collision, {
@ -81,7 +81,7 @@ TEST(RaySphere_Hit, {
pod::RayQuery hit = uf::physics::impl::rayCast(ray, world, 100.0f); pod::RayQuery hit = uf::physics::impl::rayCast(ray, world, 100.0f);
EXPECT_TRUE(hit.hit); EXPECT_TRUE(hit.hit);
EXPECT_NEAR(hit.contact.penetration, 4.0f, EPS(1e-4f)); EXPECT_NEAR(hit.contact.penetration, 4.0f, EPS);
}) })
TEST(SphereSphere_NoCollision, { TEST(SphereSphere_NoCollision, {
@ -138,7 +138,7 @@ TEST(SpherePlane_Collision, {
bool collided = planeSphere(bodyB, bodyA, m); bool collided = planeSphere(bodyB, bodyA, m);
EXPECT_TRUE(collided); EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty()); EXPECT_TRUE(!m.points.empty());
EXPECT_NEAR(m.points[0].penetration, 0.5f, EPS(1e-4f)); EXPECT_NEAR(m.points[0].penetration, 0.5f, EPS);
}) })
TEST(SpherePlane_NoCollision, { TEST(SpherePlane_NoCollision, {
@ -308,7 +308,7 @@ TEST(SphereSphere_TouchingButNotOverlapping, {
bool collided = sphereSphere(bodyA, bodyB, m); bool collided = sphereSphere(bodyA, bodyB, m);
EXPECT_TRUE(collided); // should count as a collision EXPECT_TRUE(collided); // should count as a collision
EXPECT_NEAR(m.points[0].penetration, 0.0f, EPS(1e-6f)); EXPECT_NEAR(m.points[0].penetration, 0.0f, EPS);
}) })
// expects being inside the body will set the hit to where the origin is // expects being inside the body will set the hit to where the origin is
@ -326,7 +326,7 @@ TEST(RaySphere_OriginInside, {
auto q = uf::physics::impl::rayCast(ray, world, 100.0f); auto q = uf::physics::impl::rayCast(ray, world, 100.0f);
EXPECT_TRUE(q.hit); EXPECT_TRUE(q.hit);
EXPECT_NEAR(q.contact.penetration, 0.0f, EPS(1e-6f)); EXPECT_NEAR(q.contact.penetration, 0.0f, EPS);
}) })
#endif #endif
@ -944,13 +944,13 @@ TEST(TriangleTriangle_Collision_SimpleOverlap, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::triangleTriangle(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::triangleTriangle(bodyA, bodyB, m, EPS);
EXPECT_TRUE(collided); EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty()); EXPECT_FALSE(m.points.empty());
if ( !m.points.empty() ) { if ( !m.points.empty() ) {
EXPECT_GE(m.points[0].penetration, 0.0f); EXPECT_GE(m.points[0].penetration, 0.0f);
EXPECT_NEAR(uf::vector::norm(m.points[0].normal), 1.0f, EPS(1e-6f)); EXPECT_NEAR(uf::vector::norm(m.points[0].normal), 1.0f, EPS);
} }
}) })
@ -976,7 +976,7 @@ TEST(TriangleTriangle_Collision_CoplanarOverlap, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::triangleTriangle(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::triangleTriangle(bodyA, bodyB, m, EPS);
EXPECT_TRUE(collided); EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty()); EXPECT_FALSE(m.points.empty());
@ -1004,13 +1004,13 @@ TEST(TriangleTriangle_Collision_TouchingEdge, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::triangleTriangle(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::triangleTriangle(bodyA, bodyB, m, EPS);
// Should still report as collision (tangent contact) // Should still report as collision (tangent contact)
EXPECT_TRUE(collided); EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty()); EXPECT_FALSE(m.points.empty());
if(!m.points.empty()) { if(!m.points.empty()) {
EXPECT_NEAR(uf::vector::norm(m.points[0].normal), 1.0f, EPS(1e-6f)); EXPECT_NEAR(uf::vector::norm(m.points[0].normal), 1.0f, EPS);
} }
}) })
@ -1033,7 +1033,7 @@ TEST(TriangleAabb_Collision_OverlapCenter, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::triangleAabb(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::triangleAabb(bodyA, bodyB, m, EPS);
EXPECT_TRUE(collided); EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty()); EXPECT_FALSE(m.points.empty());
@ -1061,7 +1061,7 @@ TEST(TriangleAabb_Collision_NoOverlap, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::triangleAabb(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::triangleAabb(bodyA, bodyB, m, EPS);
EXPECT_FALSE(collided); EXPECT_FALSE(collided);
}) })
@ -1085,7 +1085,7 @@ TEST(TrianglePlane_Collision_BelowPlane, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::trianglePlane(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::trianglePlane(bodyA, bodyB, m, EPS);
EXPECT_TRUE(collided); EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty()); EXPECT_FALSE(m.points.empty());
@ -1113,7 +1113,7 @@ TEST(TrianglePlane_NoCollision_AbovePlane, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::trianglePlane(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::trianglePlane(bodyA, bodyB, m, EPS);
EXPECT_FALSE(collided); EXPECT_FALSE(collided);
}) })
@ -1137,13 +1137,13 @@ TEST(TriangleSphere_Collision_Tangent, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::triangleSphere(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::triangleSphere(bodyA, bodyB, m, EPS);
// At tangency: considered collision // At tangency: considered collision
EXPECT_TRUE(collided); EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty()); EXPECT_FALSE(m.points.empty());
if(!m.points.empty()) { if(!m.points.empty()) {
EXPECT_NEAR(m.points[0].penetration, 0.0f, EPS(1e-6f)); EXPECT_NEAR(m.points[0].penetration, 0.0f, EPS);
} }
}) })
@ -1170,7 +1170,7 @@ TEST(TriangleCapsule_Collision_Overlap, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::triangleCapsule(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::triangleCapsule(bodyA, bodyB, m, EPS);
EXPECT_TRUE(collided); EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty()); EXPECT_FALSE(m.points.empty());
@ -1201,7 +1201,7 @@ TEST(TriangleCapsule_Collision_NoOverlap, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::triangleCapsule(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::triangleCapsule(bodyA, bodyB, m, EPS);
EXPECT_FALSE(collided); EXPECT_FALSE(collided);
}) })
@ -1227,13 +1227,13 @@ TEST(TriangleCapsule_Collision_Tangent, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::triangleCapsule(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::triangleCapsule(bodyA, bodyB, m, EPS);
// At tangency, should still count as collision (penetration ≈ 0) // At tangency, should still count as collision (penetration ≈ 0)
EXPECT_TRUE(collided); EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty()); EXPECT_FALSE(m.points.empty());
if(!m.points.empty()) { if(!m.points.empty()) {
EXPECT_NEAR(m.points[0].penetration, 0.0f, EPS(1e-6f)); EXPECT_NEAR(m.points[0].penetration, 0.0f, EPS);
} }
}) })
@ -1260,7 +1260,7 @@ TEST(TriangleCapsule_Collision_EdgeAlignment, {
bodyB.bounds = ::computeAABB( bodyB ); bodyB.bounds = ::computeAABB( bodyB );
pod::Manifold m; pod::Manifold m;
bool collided = ::triangleCapsule(bodyA, bodyB, m, EPS(1e-6f)); bool collided = ::triangleCapsule(bodyA, bodyB, m, EPS);
EXPECT_TRUE(collided); EXPECT_TRUE(collided);
}) })

View File

@ -15,7 +15,7 @@ namespace {
//return uf::vector::normalize( tri.normals[0] + tri.normals[1] + tri.normals[2] ); //return uf::vector::normalize( tri.normals[0] + tri.normals[1] + tri.normals[2] );
} }
bool triangleTriangleIntersect( const pod::Triangle& a, const pod::Triangle& b, float eps = EPS(1e-6f) ) { bool triangleTriangleIntersect( const pod::Triangle& a, const pod::Triangle& b, float eps = EPS ) {
const float eps2 = eps * eps; const float eps2 = eps * eps;
auto boxA = ::computeTriangleAABB( a ); auto boxA = ::computeTriangleAABB( a );
auto boxB = ::computeTriangleAABB( b ); auto boxB = ::computeTriangleAABB( b );
@ -148,7 +148,7 @@ namespace {
return tri; return tri;
} }
bool computeTriangleTriangleSegment( const pod::TriangleWithNormal& A, const pod::TriangleWithNormal& B, pod::Vector3f& p0, pod::Vector3f& p1, float eps = EPS(1e-6f) ) { bool computeTriangleTriangleSegment( const pod::TriangleWithNormal& A, const pod::TriangleWithNormal& B, pod::Vector3f& p0, pod::Vector3f& p1, float eps = EPS ) {
int intersections = 0; int intersections = 0;
pod::Vector3f intersectionBuffers[6]; pod::Vector3f intersectionBuffers[6];
@ -280,7 +280,7 @@ namespace {
if ( triMin > r || triMax < -r ) return false; // separating axis if ( triMin > r || triMax < -r ) return false; // separating axis
// compute overlap depth // compute overlap depth
float overlap = std::min(triMax, r) - std::max(triMin, -r); float overlap = std::min(triMax + r, r - triMin);
if ( overlap < minOverlap ) { if ( overlap < minOverlap ) {
minOverlap = overlap; minOverlap = overlap;
bestAxis = n; bestAxis = n;