added OBB colliders, painfully trying to resolve issues with rotating bodies

This commit is contained in:
ecker 2026-05-16 23:19:49 -05:00
parent 142f584684
commit 6b07173804
39 changed files with 836 additions and 239 deletions

View File

@ -335,6 +335,15 @@
"ultralight": { "enabled": true, "scale": 1.5 },
"discord": { "enabled": false }
},
"physics": {
"warmup solver": true,
"block solver": true,
"psg solver": true,
"gjk": true,
"fixed step": true,
"substeps": 4,
"solver iterations": 10
},
"audio": {
"mute": false,
"async update": false,

View File

@ -1,5 +1,4 @@
{
"assets": [],
"behaviors": [
"SoundEmitterBehavior"
],
@ -7,10 +6,10 @@
"holdable": true,
"physics": {
"mass": 100,
"inertia": false,
// "type": "bounding box"
// "inertia": false,
"type": "bounding box"
// "type": "mesh"
"type": "hull"
// "type": "hull"
}
}
}

View File

@ -23,8 +23,6 @@ if metadata["normal"] ~= nil then
if metadata["angle"] < 0 then sign = 1 end
normal = Vector3f( metadata["normal"][1] * sign, metadata["normal"][2] * sign, metadata["normal"][3] * sign ):normalize()
end
local starting = Quaternion(transform.orientation)
local ending = transform.orientation:multiply(Quaternion.axisAngle( Vector3f(0,1,0), metadata["angle"] ))
-- local soundEmitter = ent:loadChild("/sound.json",true)
local soundEmitter = ent

View File

@ -15,7 +15,7 @@
"func_door_rotating_5568": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
"func_door_rotating_5584": { "action": "load", "payload": { "import": "/door.json", "metadata": { "angle":-1.570795, "normal": [1,0,0] } } },
// "prop_physics_override_5813": { "action": "load", "payload": { "import": "/physics_prop.json" } },
"prop_physics_override_5813": { "action": "load", "payload": { "import": "/physics_prop.json" } },
// regex matches
"/^prop_physics_[^o]/": { "action": "load", "payload": { "import": "/prop.json" } },

View File

@ -35,6 +35,7 @@
namespace pod {
enum class ShapeType {
AABB,
OBB,
SPHERE,
PLANE,
CAPSULE,
@ -125,16 +126,9 @@ namespace pod {
pod::BVH* bvh;
const uf::Mesh* mesh;
};
// MIGHT contain additional data, so far mirrors the above
struct ConvexHullBVH {
pod::BVH* bvh;
const uf::Mesh* mesh;
};
typedef MeshBVH ConvexHullBVH;
typedef uint32_t CollisionMask;
struct Collider {
// what it is
enum CategoryMask : uint32_t {
@ -166,8 +160,9 @@ namespace pod {
pod::CollisionMask mask = Collider::MASK_ALL;
union {
pod::Sphere sphere;
pod::AABB aabb;
pod::OBB obb;
pod::Sphere sphere;
pod::Plane plane;
pod::Capsule capsule;
pod::TriangleWithNormal triangle;
@ -259,8 +254,8 @@ namespace pod {
};
struct PhysicsSettings {
bool warmupSolver = true; // cache manifold data to warm up the solver
bool blockContactSolver = true; // use BlockNxN solvers (where N = number of contacts for a manifold)
bool warmupSolver = false; // cache manifold data to warm up the solver
bool blockContactSolver = false; // use BlockNxN solvers (where N = number of contacts for a manifold)
bool psgContactSolver = true; // use PSG contact solver
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
@ -362,6 +357,13 @@ namespace uf {
void UF_API applyTorque( pod::PhysicsBody& body, const pod::Vector3f& torque );
void UF_API setVelocity( pod::PhysicsBody& body, const pod::Vector3f& velocity );
void UF_API applyVelocity( pod::PhysicsBody& body, const pod::Vector3f& velocity );
void UF_API setAngularVelocity( pod::PhysicsBody& body, const pod::Vector3f& velocity );
void UF_API setAngularVelocity( pod::PhysicsBody& body, const pod::Quaternion<>& q, float dt = 0 );
void UF_API applyAngularVelocity( pod::PhysicsBody& body, const pod::Vector3f& velocity );
void UF_API applyAngularVelocity( pod::PhysicsBody& body, const pod::Quaternion<>& q, float dt = 0 );
void UF_API applyRotation( pod::PhysicsBody& body, const pod::Quaternion<>& q );
void UF_API applyRotation( pod::PhysicsBody& body, const pod::Vector3f& axis, float angle );

View File

@ -3,6 +3,7 @@
#include "impl.h"
#include "narrowphase/aabb.h"
#include "narrowphase/obb.h"
#include "narrowphase/sphere.h"
#include "narrowphase/plane.h"
#include "narrowphase/capsule.h"

View File

@ -4,6 +4,7 @@
namespace impl {
bool aabbAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool aabbObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool aabbSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool aabbPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool aabbCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );

View File

@ -3,10 +3,11 @@
#include "../impl.h"
namespace impl {
bool capsuleCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool capsuleAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool capsulePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool capsuleObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool capsuleSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool capsulePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool capsuleCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool capsuleMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool capsuleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
}

View File

@ -4,6 +4,7 @@
namespace impl {
bool hullAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool hullObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool hullSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool hullPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool hullCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );

View File

@ -4,6 +4,7 @@
namespace impl {
bool meshAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool meshObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool meshSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool meshPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool meshCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );

View File

@ -0,0 +1,13 @@
#pragma once
#include "../impl.h"
namespace impl {
bool obbAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool obbObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool obbSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool obbPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool obbCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool obbMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool obbHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
}

View File

@ -4,6 +4,7 @@
namespace impl {
bool planeAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool planeObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool planeSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool planePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool planeCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );

View File

@ -5,7 +5,9 @@
namespace impl {
bool rayTriangleIntersect( const pod::Ray& ray, const pod::Triangle& tri, float& t, float& u, float& v );
bool rayAabbIntersect( const pod::Ray& ray, const pod::AABB& box, float& tMin, float& tMax );
bool rayAabb( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQuery& rayHit );
bool rayObb( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQuery& rayHit );
bool raySphere( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQuery& rayHit );
bool rayPlane( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQuery& rayHit );
bool rayCapsule( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQuery& rayHit );

View File

@ -3,8 +3,9 @@
#include "../impl.h"
namespace impl {
bool sphereSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool sphereAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool sphereObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool sphereSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool spherePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool sphereCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool sphereMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );

View File

@ -5,6 +5,7 @@
namespace impl {
bool triangleTriangle( const pod::TriangleWithNormal& a, const pod::TriangleWithNormal& b, pod::Manifold& manifold );
bool triangleAabb( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold );
bool triangleObb( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold );
bool triangleSphere( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold );
bool trianglePlane( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold );
bool triangleCapsule( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold );
@ -12,6 +13,7 @@ namespace impl {
bool triangleTriangle( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool triangleAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool triangleObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool triangleSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool trianglePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool triangleCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );

View File

@ -1,10 +1,3 @@
namespace pod {
// Simple quaterions (designed [to store in arrays] with minimal headaches)
template<typename T = NUM> using Quaternion = Vector4t<T>;
}
// snip header
template<typename T> pod::Quaternion<T> uf::quaternion::identity() {
return pod::Quaternion<T>{ 0, 0, 0, 1 };
}
@ -144,7 +137,7 @@ template<typename T> pod::Matrix3t<T> uf::quaternion::matrix3( const pod::Quater
return pod::Matrix3t<T>({
1 - yy - zz, xy + zw, xz - yw,
xy - zw, 1 - xx - zz, yz + xw,
xz + yw, yz - xw, 1 - xx - yy,
xz + yw, yz - xw, 1 - xx - yy
});
}
template<typename T> pod::Quaternion<T> uf::quaternion::axisAngle( const pod::Vector3t<T>& axis, T angle ) {
@ -218,9 +211,9 @@ template<typename T> T& uf::quaternion::inverse_( T& q ) {
template<typename T> pod::Quaternion<T> uf::quaternion::fromMatrix( const pod::Matrix4t<T>& m ) {
pod::Quaternion<T> q;
T m00 = m[0], m01 = m[1], m02 = m[2];
T m10 = m[4], m11 = m[5], m12 = m[6];
T m20 = m[8], m21 = m[9], m22 = m[10];
T m00 = m(0,0), m01 = m(0,1), m02 = m(0,2);
T m10 = m(1,0), m11 = m(1,1), m12 = m(1,2);
T m20 = m(2,0), m21 = m(2,1), m22 = m(2,2);
T trace = m00 + m11 + m22;
if ( trace > 0 ) {

View File

@ -14,6 +14,8 @@ namespace pod {
alignas(16) pod::Vector3f max;
};
typedef AABB OBB;
struct Sphere {
float radius;
};

View File

@ -23,13 +23,14 @@ pod::Transform<T> /*UF_API*/ uf::transform::initialize() {
template<typename T>
pod::Transform<T>& /*UF_API*/ uf::transform::lookAt( pod::Transform<T>& transform, const pod::Vector3t<T>& at ) {
pod::Vector3t<T> forward = uf::vector::normalize( at - transform.position );
pod::Vector3t<T> right = uf::vector::normalize(uf::vector::cross( forward, transform.up ));
pod::Vector3t<T> up = uf::vector::normalize(uf::vector::cross(at, right));
pod::Vector3t<T> right = uf::vector::normalize(uf::vector::cross( transform.up, forward ));
pod::Vector3t<T> up = uf::vector::normalize(uf::vector::cross( forward, right ));
transform.up = up;
transform.right = right;
transform.forward = forward;
transform.orientation = uf::quaternion::lookAt( at, up );
transform.orientation = uf::quaternion::lookAt( forward, up );
return transform;
}
@ -49,7 +50,7 @@ pod::Transform<T>& /*UF_API*/ uf::transform::move( pod::Transform<T>& transform,
template<typename T>
pod::Transform<T>& /*UF_API*/ uf::transform::reorient( pod::Transform<T>& transform ) {
pod::Quaternion<T> q = transform.orientation;
transform.forward = { 2 * (q.x * q.z + q.w * q.y), 2 * (q.y * q.x - q.w * q.x), 1 - 2 * (q.x * q.x + q.y * q.y) };
transform.forward = { 2 * (q.x * q.z + q.w * q.y), 2 * (q.y * q.x - q.w * q.z), 1 - 2 * (q.x * q.x + q.y * q.y) };
transform.up = { 2 * (q.x * q.y - q.w * q.z), 1 - 2 * (q.x * q.x + q.z * q.z), 2 * (q.y * q.z + q.w * q.x)};
transform.right = { 1 - 2 * (q.y * q.y + q.z * q.z), 2 * (q.x * q.y + q.w * q.z), 2 * (q.x * q.z - q.w * q.y)};
return transform;
@ -94,11 +95,7 @@ pod::Transform<T> /*UF_API*/ uf::transform::flatten( const pod::Transform<T>& tr
const pod::Transform<T>* pointer = transform.reference;
while ( pointer && depth-- > 0 ) {
combined.position = {
combined.position.x + pointer->position.x,
combined.position.y + pointer->position.y,
combined.position.z + pointer->position.z,
};
combined.position = pointer->position + uf::quaternion::rotate(pointer->orientation, combined.position);
combined.orientation = uf::quaternion::multiply( pointer->orientation, combined.orientation );
combined.scale = {
combined.scale.x * pointer->scale.x,

View File

@ -110,152 +110,190 @@ void UF_API uf::load() {
uf::config.readFromFile(uf::io::root+"config.json");
}
void UF_API uf::load( ext::json::Value& json ) {
/*global*/::config.engine.gc.enabled = json["engine"]["debug"]["garbage collection"]["enabled"].as(/*global*/::config.engine.gc.enabled);
/*global*/::config.engine.gc.every = json["engine"]["debug"]["garbage collection"]["every"].as(/*global*/::config.engine.gc.every);
/*global*/::config.engine.gc.mode = json["engine"]["debug"]["garbage collection"]["mode"].as(/*global*/::config.engine.gc.mode);
/*global*/::config.engine.gc.announce = json["engine"]["debug"]["garbage collection"]["announce"].as(/*global*/::config.engine.gc.announce);
/*global*/::config.engine.ext.ultralight.enabled = json["engine"]["ext"]["ultralight"]["enabled"].as(/*global*/::config.engine.ext.ultralight.enabled);
/*global*/::config.engine.ext.discord.enabled = json["engine"]["ext"]["discord"]["enabled"].as(/*global*/::config.engine.ext.discord.enabled);
/*global*/::config.engine.ext.imgui.enabled = json["engine"]["ext"]["imgui"]["enabled"].as(/*global*/::config.engine.ext.imgui.enabled);
/*global*/::config.engine.ext.vall_e.enabled = json["engine"]["ext"]["vall_e"]["enabled"].as(/*global*/::config.engine.ext.vall_e.enabled);
/*global*/::config.engine.ext.vall_e.model_path = json["engine"]["ext"]["vall_e"]["model_path"].as(/*global*/::config.engine.ext.vall_e.model_path);
/*global*/::config.engine.ext.vall_e.encodec_path = json["engine"]["ext"]["vall_e"]["encodec_path"].as(/*global*/::config.engine.ext.vall_e.encodec_path);
/*global*/::config.engine.limiter.print = json["engine"]["debug"]["framerate"]["print"].as(/*global*/::config.engine.limiter.print);
/*global*/::config.engine.fps.print = json["engine"]["debug"]["framerate"]["print"].as(/*global*/::config.engine.fps.print);
/*global*/::config.engine.fps.every = json["engine"]["debug"]["framerate"]["every"].as(/*global*/::config.engine.fps.every);
uf::Mesh::defaultInterleaved = json["engine"]["scenes"]["meshes"]["interleaved"].as( uf::Mesh::defaultInterleaved );
uf::matrix::reverseInfiniteProjection = json["engine"]["scenes"]["matrix"]["reverseInfinite"].as( uf::matrix::reverseInfiniteProjection );
uf::graph::initialBufferElements = json["engine"]["graph"]["initial buffer elements"].as(uf::graph::initialBufferElements);
uf::graph::globalStorage = json["engine"]["graph"]["global storage"].as(uf::graph::globalStorage);
uf::Entity::deleteChildrenOnDestroy = json["engine"]["debug"]["entity"]["delete children on destroy"].as( uf::Entity::deleteChildrenOnDestroy );
uf::Entity::deleteComponentsOnDestroy = json["engine"]["debug"]["entity"]["delete components on destroy"].as( uf::Entity::deleteComponentsOnDestroy );
uf::Object::assertionLoad = json["engine"]["debug"]["loader"]["assert"].as( uf::Object::assertionLoad );
uf::asset::assertionLoad = json["engine"]["debug"]["loader"]["assert"].as( uf::asset::assertionLoad );
uf::asset::asyncQueue = json["engine"]["debug"]["loader"]["async"].as( uf::asset::asyncQueue );
uf::userdata::autoDestruct = json["engine"]["debug"]["userdata"]["auto destruct"].as( uf::userdata::autoDestruct );
uf::userdata::autoValidate = json["engine"]["debug"]["userdata"]["auto validate"].as( uf::userdata::autoValidate );
uf::Object::deferLazyCalls = json["engine"]["debug"]["hooks"]["defer lazy calls"].as( uf::Object::deferLazyCalls );
uf::scene::printTaskCalls = json["engine"]["debug"]["scene"]["print task calls"].as( uf::scene::printTaskCalls );
auto& configEngineLimitersJson = json["engine"]["limiters"];
if ( configEngineLimitersJson["framerate"].as<uf::stl::string>() == "auto" && json["window"]["refresh rate"].is<size_t>() ) {
float scale = 1.0;
size_t refreshRate = json["window"]["refresh rate"].as<size_t>();
configEngineLimitersJson["framerate"] = refreshRate * scale;
UF_MSG_DEBUG("Setting framerate cap to {}", (int) refreshRate * scale);
// Scene settings
{
auto& configEngineSceneJson = json["engine"]["scenes"];
uf::Mesh::defaultInterleaved = configEngineSceneJson["meshes"]["interleaved"].as( uf::Mesh::defaultInterleaved );
uf::matrix::reverseInfiniteProjection = configEngineSceneJson["matrix"]["reverseInfinite"].as( uf::matrix::reverseInfiniteProjection );
}
/* Frame limiter */ {
size_t limit = configEngineLimitersJson["framerate"].as<size_t>();
/*global*/::times.limiter = limit != 0 ? 1.0 / limit : 0;
UF_MSG_DEBUG("Limiter set to {} ms", /*global*/::times.limiter);
}
/* Max delta time */{
size_t limit = configEngineLimitersJson["deltaTime"].as<size_t>();
uf::physics::time::clamp = limit != 0 ? 1.0 / limit : 0;
// Graph settings
{
auto& configEngineGraphJson = json["engine"]["graph"];
uf::graph::initialBufferElements = configEngineGraphJson["initial buffer elements"].as(uf::graph::initialBufferElements);
uf::graph::globalStorage = configEngineGraphJson["global storage"].as(uf::graph::globalStorage);
}
auto& configEngineThreadJson = json["engine"]["threads"];
// Various debug settings
{
auto& configEngineDebugJson = json["engine"]["debug"];
/*global*/::config.engine.gc.enabled = configEngineDebugJson["garbage collection"]["enabled"].as(/*global*/::config.engine.gc.enabled);
/*global*/::config.engine.gc.every = configEngineDebugJson["garbage collection"]["every"].as(/*global*/::config.engine.gc.every);
/*global*/::config.engine.gc.mode = configEngineDebugJson["garbage collection"]["mode"].as(/*global*/::config.engine.gc.mode);
/*global*/::config.engine.gc.announce = configEngineDebugJson["garbage collection"]["announce"].as(/*global*/::config.engine.gc.announce);
if ( configEngineThreadJson["frame limiter"].as<uf::stl::string>() == "auto" && json["window"]["refresh rate"].is<size_t>() ) {
float scale = 2.0;
size_t refreshRate = json["window"]["refresh rate"].as<size_t>();
configEngineThreadJson["frame limiter"] = refreshRate * scale;
UF_MSG_DEBUG("Setting thread frame limiter to {}", (int) refreshRate * scale);
/*global*/::config.engine.limiter.print = configEngineDebugJson["framerate"]["print"].as(/*global*/::config.engine.limiter.print);
/*global*/::config.engine.fps.print = configEngineDebugJson["framerate"]["print"].as(/*global*/::config.engine.fps.print);
/*global*/::config.engine.fps.every = configEngineDebugJson["framerate"]["every"].as(/*global*/::config.engine.fps.every);
uf::Entity::deleteChildrenOnDestroy = configEngineDebugJson["entity"]["delete children on destroy"].as( uf::Entity::deleteChildrenOnDestroy );
uf::Entity::deleteComponentsOnDestroy = configEngineDebugJson["entity"]["delete components on destroy"].as( uf::Entity::deleteComponentsOnDestroy );
uf::Object::assertionLoad = configEngineDebugJson["loader"]["assert"].as( uf::Object::assertionLoad );
uf::asset::assertionLoad = configEngineDebugJson["loader"]["assert"].as( uf::asset::assertionLoad );
uf::asset::asyncQueue = configEngineDebugJson["loader"]["async"].as( uf::asset::asyncQueue );
uf::userdata::autoDestruct = configEngineDebugJson["userdata"]["auto destruct"].as( uf::userdata::autoDestruct );
uf::userdata::autoValidate = configEngineDebugJson["userdata"]["auto validate"].as( uf::userdata::autoValidate );
uf::Object::deferLazyCalls = configEngineDebugJson["hooks"]["defer lazy calls"].as( uf::Object::deferLazyCalls );
uf::scene::printTaskCalls = configEngineDebugJson["scene"]["print task calls"].as( uf::scene::printTaskCalls );
}
// Limiter settings
{
auto& configEngineLimitersJson = json["engine"]["limiters"];
if ( configEngineLimitersJson["framerate"].as<uf::stl::string>() == "auto" && json["window"]["refresh rate"].is<size_t>() ) {
float scale = 1.0;
size_t refreshRate = json["window"]["refresh rate"].as<size_t>();
configEngineLimitersJson["framerate"] = refreshRate * scale;
UF_MSG_DEBUG("Setting framerate cap to {}", (int) refreshRate * scale);
}
/* Frame limiter */ {
size_t limit = configEngineLimitersJson["framerate"].as<size_t>();
/*global*/::times.limiter = limit != 0 ? 1.0 / limit : 0;
UF_MSG_DEBUG("Limiter set to {} ms", /*global*/::times.limiter);
}
/* Max delta time */{
size_t limit = configEngineLimitersJson["deltaTime"].as<size_t>();
uf::physics::time::clamp = limit != 0 ? 1.0 / limit : 0;
}
}
/* Thread frame limiter */ {
size_t limit = configEngineThreadJson["frame limiter"].as<size_t>();
uf::thread::limiter = limit != 0 ? 1.0 / limit : 0;
// Thread settings
{
auto& configEngineThreadJson = json["engine"]["threads"];
if ( configEngineThreadJson["frame limiter"].as<uf::stl::string>() == "auto" && json["window"]["refresh rate"].is<size_t>() ) {
float scale = 2.0;
size_t refreshRate = json["window"]["refresh rate"].as<size_t>();
configEngineThreadJson["frame limiter"] = refreshRate * scale;
UF_MSG_DEBUG("Setting thread frame limiter to {}", (int) refreshRate * scale);
}
/* Thread frame limiter */ {
size_t limit = configEngineThreadJson["frame limiter"].as<size_t>();
uf::thread::limiter = limit != 0 ? 1.0 / limit : 0;
}
// Set worker threads
if ( configEngineThreadJson["workers"].as<uf::stl::string>() == "auto" ) {
auto threads = std::max( 1, (int) std::thread::hardware_concurrency() - 1 ) / 2;
configEngineThreadJson["workers"] = threads;
uf::thread::workers = configEngineThreadJson["workers"].as<size_t>();
UF_MSG_DEBUG("Using {} worker threads", threads);
} else if ( configEngineThreadJson["workers"].is<size_t>() ) {
auto threads = configEngineThreadJson["workers"].as<size_t>();
uf::thread::workers = threads;
UF_MSG_DEBUG("Using {} worker threads", threads);
}
}
// Set worker threads
if ( configEngineThreadJson["workers"].as<uf::stl::string>() == "auto" ) {
auto threads = std::max( 1, (int) std::thread::hardware_concurrency() - 1 ) / 2;
configEngineThreadJson["workers"] = threads;
uf::thread::workers = configEngineThreadJson["workers"].as<size_t>();
UF_MSG_DEBUG("Using {} worker threads", threads);
} else if ( configEngineThreadJson["workers"].is<size_t>() ) {
auto threads = configEngineThreadJson["workers"].as<size_t>();
uf::thread::workers = threads;
UF_MSG_DEBUG("Using {} worker threads", threads);
// Physics settings
{
auto& confingEnginePhysicsJson = json["engine"]["physics"];
uf::physics::settings.warmupSolver = confingEnginePhysicsJson["warmup solver"].as(uf::physics::settings.warmupSolver);
uf::physics::settings.blockContactSolver = confingEnginePhysicsJson["block solver"].as(uf::physics::settings.blockContactSolver);
uf::physics::settings.psgContactSolver = confingEnginePhysicsJson["psg solver"].as(uf::physics::settings.psgContactSolver);
uf::physics::settings.useGjk = confingEnginePhysicsJson["gjk"].as(uf::physics::settings.useGjk);
uf::physics::settings.fixedStep = confingEnginePhysicsJson["fixed step"].as(uf::physics::settings.fixedStep);
uf::physics::settings.substeps = confingEnginePhysicsJson["substeps"].as(uf::physics::settings.substeps);
uf::physics::settings.solverIterations = confingEnginePhysicsJson["solver iterations"].as(uf::physics::settings.solverIterations);
}
// Audio settings
auto& configEngineAudioJson = json["engine"]["audio"];
{
auto& configEngineAudioJson = json["engine"]["audio"];
uf::audio::muted = configEngineAudioJson["mute"].as( uf::audio::muted );
uf::audio::asyncUpdate = configEngineAudioJson["async update"].as( uf::audio::asyncUpdate );
uf::audio::streamsByDefault = configEngineAudioJson["streams by default"].as( uf::audio::streamsByDefault );
uf::audio::bufferSize = configEngineAudioJson["buffers"]["size"].as( uf::audio::bufferSize );
uf::audio::buffers = configEngineAudioJson["buffers"]["count"].as( uf::audio::buffers );
#if UF_AUDIO_MAPPED_VOLUMES
ext::json::forEach( configEngineAudioJson["volumes"], []( const uf::stl::string& key, ext::json::Value& value ){
float volume; volume = value.as(volume);
uf::audio::volumes[key] = volume;
});
#else
if ( ext::json::isObject( configEngineAudioJson["volumes"] ) ) {
uf::audio::volumes::bgm = configEngineAudioJson["volumes"]["bgm"].as(uf::audio::volumes::bgm);
uf::audio::volumes::sfx = configEngineAudioJson["volumes"]["sfx"].as(uf::audio::volumes::sfx);
uf::audio::volumes::voice = configEngineAudioJson["volumes"]["voice"].as(uf::audio::volumes::voice);
}
#endif
}
uf::audio::muted = configEngineAudioJson["mute"].as( uf::audio::muted );
uf::audio::asyncUpdate = configEngineAudioJson["async update"].as( uf::audio::asyncUpdate );
uf::audio::streamsByDefault = configEngineAudioJson["streams by default"].as( uf::audio::streamsByDefault );
uf::audio::bufferSize = configEngineAudioJson["buffers"]["size"].as( uf::audio::bufferSize );
uf::audio::buffers = configEngineAudioJson["buffers"]["count"].as( uf::audio::buffers );
#if UF_AUDIO_MAPPED_VOLUMES
ext::json::forEach( configEngineAudioJson["volumes"], []( const uf::stl::string& key, ext::json::Value& value ){
float volume; volume = value.as(volume);
uf::audio::volumes[key] = volume;
});
#else
if ( ext::json::isObject( configEngineAudioJson["volumes"] ) ) {
uf::audio::volumes::bgm = configEngineAudioJson["volumes"]["bgm"].as(uf::audio::volumes::bgm);
uf::audio::volumes::sfx = configEngineAudioJson["volumes"]["sfx"].as(uf::audio::volumes::sfx);
uf::audio::volumes::voice = configEngineAudioJson["volumes"]["voice"].as(uf::audio::volumes::voice);
// Various external settings
{
/*global*/::config.engine.ext.discord.enabled = json["engine"]["ext"]["discord"]["enabled"].as(/*global*/::config.engine.ext.discord.enabled);
/*global*/::config.engine.ext.imgui.enabled = json["engine"]["ext"]["imgui"]["enabled"].as(/*global*/::config.engine.ext.imgui.enabled);
}
// VALL-E settings
{
auto& configEngineExtValleJson = json["engine"]["ext"]["vall_e"];
/*global*/::config.engine.ext.vall_e.enabled = configEngineExtValleJson["enabled"].as(/*global*/::config.engine.ext.vall_e.enabled);
/*global*/::config.engine.ext.vall_e.model_path = configEngineExtValleJson["model_path"].as(/*global*/::config.engine.ext.vall_e.model_path);
/*global*/::config.engine.ext.vall_e.encodec_path = configEngineExtValleJson["encodec_path"].as(/*global*/::config.engine.ext.vall_e.encodec_path);
}
#if UF_USE_ULTRALIGHT
// Ultralight settings (painfully unused)
{
auto& configEngineExtUltralightJson = json["engine"]["ext"]["ultralight"];
/*global*/::config.engine.ext.ultralight.enabled = configEngineExtUltralightJson["enabled"].as(/*global*/::config.engine.ext.ultralight.enabled);
ext::ultralight::scale = configEngineExtUltralightJson["scale"].as( ext::ultralight::scale );
ext::ultralight::log = configEngineExtUltralightJson["log"].as( ext::ultralight::log );
}
#endif
#if UF_USE_ULTRALIGHT
ext::ultralight::scale = json["engine"]["ext"]["ultralight"]["scale"].as( ext::ultralight::scale );
ext::ultralight::log = json["engine"]["ext"]["ultralight"]["log"].as( ext::ultralight::log );
#endif
// Renderer settings
{
#if UF_USE_VULKAN
auto& configRenderJson = json["engine"]["ext"]["vulkan"];
#elif UF_USE_OPENGL
auto& configRenderJson = json["engine"]["ext"]["opengl"];
#else
auto& configRenderJson = json["engine"]["ext"]["software"];
#endif
auto& configRenderInvariantJson = configRenderJson["invariant"];
auto& configRenderExperimentalJson = configRenderJson["experimental"];
auto& configRenderPipelinesJson = configRenderJson["pipelines"];
#if UF_USE_VULKAN
auto& configRenderJson = json["engine"]["ext"]["vulkan"];
#elif UF_USE_OPENGL
auto& configRenderJson = json["engine"]["ext"]["opengl"];
#else
auto& configRenderJson = json["engine"]["ext"]["software"];
#endif
auto& configRenderInvariantJson = configRenderJson["invariant"];
auto& configRenderExperimentalJson = configRenderJson["experimental"];
auto& configRenderPipelinesJson = configRenderJson["pipelines"];
uf::renderer::settings::validation::messages = configRenderJson["validation"]["messages"].as( uf::renderer::settings::validation::messages );
uf::renderer::settings::validation::checkpoints = configRenderJson["validation"]["checkpoints"].as( uf::renderer::settings::validation::checkpoints );
uf::renderer::settings::experimental::batchQueueSubmissions = configRenderExperimentalJson["batch queue submissions"].as( uf::renderer::settings::experimental::batchQueueSubmissions );
#if UF_USE_VULKAN
uf::renderer::settings::defaultStageBuffers = configRenderInvariantJson["default stage buffers"].as( uf::renderer::settings::defaultStageBuffers );
uf::renderer::settings::defaultDeferBufferDestroy = configRenderInvariantJson["default defer buffer destroy"].as( uf::renderer::settings::defaultDeferBufferDestroy );
#if 0
uf::renderer::settings::defaultCommandBufferImmediate = true;
/*global*/::requestDeferredCommandBufferSubmit = !configRenderInvariantJson["default command buffer immediate"].as( uf::renderer::settings::defaultCommandBufferImmediate );
#else
uf::renderer::settings::defaultCommandBufferImmediate = configRenderInvariantJson["default command buffer immediate"].as( uf::renderer::settings::defaultCommandBufferImmediate );
#endif
uf::renderer::settings::nBufferedUbos = configRenderInvariantJson["n-buffered uniform"].as( uf::renderer::settings::nBufferedUbos );
#endif
#if 1
uf::renderer::settings::experimental::dedicatedThread = false;
/*global*/::requestDedicatedRenderThread = configRenderExperimentalJson["dedicated thread"].as( uf::renderer::settings::experimental::dedicatedThread );
#else
uf::renderer::settings::experimental::dedicatedThread = configRenderExperimentalJson["dedicated thread"].as( uf::renderer::settings::experimental::dedicatedThread );
#endif
uf::renderer::settings::invariant::multithreadedRecording = configRenderInvariantJson["multithreaded recording"].as( uf::renderer::settings::invariant::multithreadedRecording );
uf::renderer::settings::invariant::waitOnRenderEnd = configRenderInvariantJson["wait on render end"].as( uf::renderer::settings::invariant::waitOnRenderEnd );
uf::renderer::settings::invariant::individualPipelines = configRenderInvariantJson["individual pipelines"].as( uf::renderer::settings::invariant::individualPipelines );
uf::renderer::settings::validation::messages = configRenderJson["validation"]["messages"].as( uf::renderer::settings::validation::messages );
uf::renderer::settings::validation::checkpoints = configRenderJson["validation"]["checkpoints"].as( uf::renderer::settings::validation::checkpoints );
uf::renderer::settings::experimental::batchQueueSubmissions = configRenderExperimentalJson["batch queue submissions"].as( uf::renderer::settings::experimental::batchQueueSubmissions );
#if UF_USE_VULKAN
uf::renderer::settings::defaultStageBuffers = configRenderInvariantJson["default stage buffers"].as( uf::renderer::settings::defaultStageBuffers );
uf::renderer::settings::defaultDeferBufferDestroy = configRenderInvariantJson["default defer buffer destroy"].as( uf::renderer::settings::defaultDeferBufferDestroy );
#if 0
uf::renderer::settings::defaultCommandBufferImmediate = true;
/*global*/::requestDeferredCommandBufferSubmit = !configRenderInvariantJson["default command buffer immediate"].as( uf::renderer::settings::defaultCommandBufferImmediate );
#else
uf::renderer::settings::defaultCommandBufferImmediate = configRenderInvariantJson["default command buffer immediate"].as( uf::renderer::settings::defaultCommandBufferImmediate );
#endif
uf::renderer::settings::nBufferedUbos = configRenderInvariantJson["n-buffered uniform"].as( uf::renderer::settings::nBufferedUbos );
#endif
#if 1
uf::renderer::settings::experimental::dedicatedThread = false;
/*global*/::requestDedicatedRenderThread = configRenderExperimentalJson["dedicated thread"].as( uf::renderer::settings::experimental::dedicatedThread );
#else
uf::renderer::settings::experimental::dedicatedThread = configRenderExperimentalJson["dedicated thread"].as( uf::renderer::settings::experimental::dedicatedThread );
#endif
uf::renderer::settings::invariant::multithreadedRecording = configRenderInvariantJson["multithreaded recording"].as( uf::renderer::settings::invariant::multithreadedRecording );
uf::renderer::settings::invariant::waitOnRenderEnd = configRenderInvariantJson["wait on render end"].as( uf::renderer::settings::invariant::waitOnRenderEnd );
uf::renderer::settings::invariant::individualPipelines = configRenderInvariantJson["individual pipelines"].as( uf::renderer::settings::invariant::individualPipelines );
}
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
ext::fsr::preset = json["engine"]["ext"]["fsr"]["preset"].as(ext::fsr::preset);
ext::fsr::jitterScale = json["engine"]["ext"]["fsr"]["jitter scale"].as(ext::fsr::jitterScale);

View File

@ -5,8 +5,13 @@
namespace binds {
bool hasBody( pod::PhysicsBody& self ) { return !!self.object; }
pod::Vector3f& velocity( pod::PhysicsBody& self ) { return self.velocity; }
void setVelocity( pod::PhysicsBody& self, const pod::Vector3f& v ) { self.velocity = v; }
void applyVelocity( pod::PhysicsBody& self, const pod::Vector3f& v ) { self.velocity += v; }
pod::Vector3f& angularVelocity( pod::PhysicsBody& self ) { return self.angularVelocity; }
void setVelocity( pod::PhysicsBody& self, const pod::Vector3f& v ) { uf::physics::setVelocity( self, v ); }
void setAngularVelocity( pod::PhysicsBody& self, const pod::Quaternion<>& q, float dt = 0 ) { uf::physics::setAngularVelocity( self, q, dt ); }
void applyVelocity( pod::PhysicsBody& self, const pod::Vector3f& v ) { uf::physics::applyVelocity( self, v ); }
void applyAngularVelocity( pod::PhysicsBody& self, const pod::Quaternion<>& q, float dt = 0 ) { uf::physics::applyAngularVelocity( self, q, dt ); }
float getMass( const pod::PhysicsBody& self ) { return self.mass; }
void setMass( pod::PhysicsBody& self, float mass ) { self.mass = mass; }
@ -36,9 +41,13 @@ namespace binds {
UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::PhysicsBody,
UF_LUA_REGISTER_USERTYPE_DEFINE( hasBody, UF_LUA_C_FUN(::binds::hasBody) ),
UF_LUA_REGISTER_USERTYPE_DEFINE( velocity, UF_LUA_C_FUN(::binds::velocity) ),
UF_LUA_REGISTER_USERTYPE_DEFINE( angularVelocity, UF_LUA_C_FUN(::binds::angularVelocity) ),
UF_LUA_REGISTER_USERTYPE_DEFINE( setVelocity, UF_LUA_C_FUN(::binds::setVelocity) ),
UF_LUA_REGISTER_USERTYPE_DEFINE( applyVelocity, UF_LUA_C_FUN(::binds::applyVelocity) ),
UF_LUA_REGISTER_USERTYPE_DEFINE( setAngularVelocity, UF_LUA_C_FUN(::binds::setAngularVelocity) ),
UF_LUA_REGISTER_USERTYPE_DEFINE( applyAngularVelocity, UF_LUA_C_FUN(::binds::applyAngularVelocity) ),
UF_LUA_REGISTER_USERTYPE_DEFINE( applyImpulse, UF_LUA_C_FUN(uf::physics::applyImpulse) ),
UF_LUA_REGISTER_USERTYPE_DEFINE( applyRotation, UF_LUA_C_FUN(::binds::applyRotation) ),

View File

@ -2,10 +2,10 @@
namespace impl {
void updateStaticBody( pod::PhysicsBody& body ) {
if ( !body.isStatic ) return;
if ( !body.isStatic ) return;
body.bounds = impl::computeAABB( body );
if ( body.world ) body.world->staticBvh.dirty = true;
body.bounds = impl::computeAABB( body );
if ( body.world ) body.world->staticBvh.dirty = true;
}
}
@ -74,6 +74,8 @@ pod::Transform<> impl::getTransform( const pod::PhysicsBody& body ) {
}
pod::Vector3f impl::getPosition( const pod::PhysicsBody& body, bool useTransform ) {
useTransform = true; // guh
if ( !useTransform ) return impl::aabbCenter( body.bounds );
return impl::getTransform( body ).position;
}
@ -112,7 +114,11 @@ pod::Matrix3f impl::computeWorldInverseInertia( const pod::PhysicsBody& b ) {
pod::Matrix3f invI_local = uf::matrix::diagonal( b.inverseInertiaTensor );
pod::Matrix3f R = uf::quaternion::matrix3(b.transform->orientation);
#if 1
return R * invI_local * uf::matrix::transpose(R);
#else
return uf::matrix::transpose(R) * invI_local * R;
#endif
}
// normalizes the delta between two bodies / contacts by the distance (as it was already computed) if non-zero
@ -423,14 +429,14 @@ bool impl::triangleTriangleIntersect( const pod::Triangle& a, const pod::Triangl
if ( !impl::aabbOverlap( boxA, boxB ) ) return false;
// check vertices of a inside b or vice versa
FOR_EACH(3, {
for ( auto i = 0; i < 3; ++i ) {
auto q = impl::closestPointOnTriangle( a.points[i], b );
if ( uf::vector::magnitude( q - a.points[i] ) < EPS2 ) return true;
});
FOR_EACH(3, {
};
for ( auto i = 0; i < 3; ++i ) {
auto q = impl::closestPointOnTriangle( b.points[i], a );
if ( uf::vector::magnitude( q - b.points[i] ) < EPS2 ) return true;
});
};
return false;
}
@ -547,7 +553,7 @@ pod::TriangleWithNormal impl::fetchTriangle( const uf::Mesh& mesh, size_t triID,
bool impl::computeTriangleTriangleSegment( const pod::TriangleWithNormal& A, const pod::TriangleWithNormal& B, pod::Vector3f& p0, pod::Vector3f& p1 ) {
int intersections = 0;
pod::Vector3f intersectionBuffers[6];
pod::Vector3f intersectionBuffers[6] = {};
auto checkAndPush = [&]( const pod::Vector3f& pt ) {
// avoid duplicates
@ -727,7 +733,8 @@ std::pair<pod::Vector3f, pod::Vector3f> impl::getCapsuleSegment( const pod::Phys
pod::AABB impl::computeAABB( const pod::PhysicsBody& body ) {
const auto transform = impl::getTransform( body );
switch ( body.collider.type ) {
case pod::ShapeType::AABB: {
case pod::ShapeType::AABB:
case pod::ShapeType::OBB: {
return impl::transformAabbToWorld( body.collider.aabb, *body.transform );
/*
return {
@ -817,11 +824,11 @@ bool impl::triAabbOverlap( const pod::Triangle& tri, const pod::AABB& box ) {
if ( !axisTest( {-f2.y, f2.x, 0} ) ) return false;
// test AABB face axes
FOR_EACH(3, {
for ( auto i = 0; i < 3; ++i ) {
float minVal = std::min({v0[i], v1[i], v2[i]});
float maxVal = std::max({v0[i], v1[i], v2[i]});
if ( minVal > e[i] || maxVal < -e[i] ) return false;
});
};
// test triangle normal axis
auto n = uf::vector::cross( f0, f1 );

View File

@ -193,7 +193,7 @@ void uf::physics::step( pod::World& world, float dt ) {
// pass manifolds to solver
impl::solveContacts( manifolds, dt );
// do position correction
// impl::solvePositions( manifolds, dt );
impl::solvePositions( manifolds, dt );
// cache manifold positions
if ( uf::physics::settings.warmupSolver ) {
impl::updateManifoldCache( manifolds, uf::physics::settings.manifoldsCache );
@ -260,7 +260,8 @@ void uf::physics::updateInertia( pod::PhysicsBody& body ) {
}
switch ( body.collider.type ) {
case pod::ShapeType::AABB: {
case pod::ShapeType::AABB:
case pod::ShapeType::OBB: {
pod::Vector3f dims = (body.collider.aabb.max - body.collider.aabb.min);
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);
@ -344,11 +345,13 @@ void uf::physics::updateInertia( pod::PhysicsBody& body ) {
}
}
void uf::physics::applyForce( pod::PhysicsBody& body, const pod::Vector3f& force ) {
if ( body.isStatic ) return; impl::wakeBody( body );
if ( body.isStatic ) return;
impl::wakeBody( body );
body.forceAccumulator += force;
}
void uf::physics::applyForceAtPoint( pod::PhysicsBody& body, const pod::Vector3f& force, const pod::Vector3f& point ) {
if ( body.isStatic ) return; impl::wakeBody( body );
if ( body.isStatic ) return;
impl::wakeBody( body );
// linear force
body.forceAccumulator += force;
// angular force
@ -367,6 +370,50 @@ void uf::physics::setVelocity( pod::PhysicsBody& body, const pod::Vector3f& v )
impl::wakeBody( body );
body.velocity = v;
}
void uf::physics::applyVelocity( pod::PhysicsBody& body, const pod::Vector3f& v ) {
impl::wakeBody( body );
body.velocity += v;
}
void uf::physics::setAngularVelocity( pod::PhysicsBody& body, const pod::Vector3f& v ) {
impl::wakeBody( body );
body.angularVelocity = v;
}
void uf::physics::setAngularVelocity( pod::PhysicsBody& body, const pod::Quaternion<>& q, float dt ) {
if ( !dt ) dt = uf::physics::time::delta;
float angle = 2.0f * std::acos( q.w );
float sinHalfAngle = std::sqrt( 1.0f - q.w * q.w );
pod::Vector3f axis{ 0, 0, 0 };
if ( sinHalfAngle > EPS ) {
axis.x = q.x / sinHalfAngle;
axis.y = q.y / sinHalfAngle;
axis.z = q.z / sinHalfAngle;
}
impl::wakeBody( body );
body.angularVelocity = axis * ( angle / dt );
UF_MSG_DEBUG("axis={}, angle={}, dt={}", uf::vector::toString( axis ), angle, dt );
}
void uf::physics::applyAngularVelocity( pod::PhysicsBody& body, const pod::Vector3f& v ) {
impl::wakeBody( body );
body.angularVelocity += v;
}
void uf::physics::applyAngularVelocity( pod::PhysicsBody& body, const pod::Quaternion<>& q, float dt ) {
if ( !dt ) dt = uf::physics::time::delta;
float angle = 2.0f * std::acos( q.w );
float sinHalfAngle = std::sqrt( 1.0f - q.w * q.w );
pod::Vector3f axis{ 0, 0, 0 };
if ( sinHalfAngle > EPS ) {
axis.x = q.x / sinHalfAngle;
axis.y = q.y / sinHalfAngle;
axis.z = q.z / sinHalfAngle;
}
impl::wakeBody( body );
body.angularVelocity += axis * ( angle / dt );
UF_MSG_DEBUG("axis={}, angle={}, dt={}", uf::vector::toString( axis ), angle, dt );
}
void uf::physics::applyRotation( pod::PhysicsBody& body, const pod::Quaternion<>& q ) {
impl::wakeBody( body );
uf::transform::rotate( *body.transform/*.reference*/, q );
@ -404,7 +451,8 @@ pod::PhysicsBody& uf::physics::create( pod::World& world, uf::Object& object, fl
}
pod::PhysicsBody& uf::physics::create( pod::World& world, uf::Object& object, const pod::AABB& aabb, float mass, const pod::Vector3f& offset ) {
auto& body = uf::physics::create( world, object, mass, offset );
body.collider.type = pod::ShapeType::AABB;
//body.collider.type = pod::ShapeType::AABB;
body.collider.type = pod::ShapeType::OBB;
body.collider.aabb = aabb;
body.bounds = impl::computeAABB( body );
uf::physics::updateInertia( body );
@ -548,6 +596,7 @@ pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world
switch ( b->collider.type ) {
case pod::ShapeType::AABB: impl::rayAabb( ray, *b, rayHit ); break;
case pod::ShapeType::OBB: impl::rayObb( ray, *b, rayHit ); break;
case pod::ShapeType::SPHERE: impl::raySphere( ray, *b, rayHit ); break;
case pod::ShapeType::PLANE: impl::rayPlane( ray, *b, rayHit ); break;
case pod::ShapeType::CAPSULE: impl::rayCapsule( ray, *b, rayHit ); break;

View File

@ -84,13 +84,23 @@ bool impl::generateContacts( pod::PhysicsBody& a, pod::PhysicsBody& b, pod::Mani
if ( a.collider.type == pod::ShapeType::A && b.collider.type == pod::ShapeType::B ) return fun( a, b, manifold );
CHECK_CONTACT( AABB, AABB, impl::aabbAabb );
CHECK_CONTACT( AABB, OBB, impl::aabbObb );
CHECK_CONTACT( AABB, SPHERE, impl::aabbSphere );
CHECK_CONTACT( AABB, PLANE, impl::aabbPlane );
CHECK_CONTACT( AABB, CAPSULE, impl::aabbCapsule );
CHECK_CONTACT( AABB, MESH, impl::aabbMesh );
CHECK_CONTACT( AABB, CONVEX_HULL, impl::aabbHull );
CHECK_CONTACT( OBB, AABB, impl::obbAabb );
CHECK_CONTACT( OBB, OBB, impl::obbObb );
CHECK_CONTACT( OBB, SPHERE, impl::obbSphere );
CHECK_CONTACT( OBB, PLANE, impl::obbPlane );
CHECK_CONTACT( OBB, CAPSULE, impl::obbCapsule );
CHECK_CONTACT( OBB, MESH, impl::obbMesh );
CHECK_CONTACT( OBB, CONVEX_HULL, impl::obbHull );
CHECK_CONTACT( SPHERE, AABB, impl::sphereAabb );
CHECK_CONTACT( SPHERE, OBB, impl::sphereObb );
CHECK_CONTACT( SPHERE, SPHERE, impl::sphereSphere );
CHECK_CONTACT( SPHERE, PLANE, impl::spherePlane );
CHECK_CONTACT( SPHERE, CAPSULE, impl::sphereCapsule );
@ -98,6 +108,7 @@ bool impl::generateContacts( pod::PhysicsBody& a, pod::PhysicsBody& b, pod::Mani
CHECK_CONTACT( SPHERE, CONVEX_HULL, impl::sphereHull );
CHECK_CONTACT( PLANE, AABB, impl::planeAabb );
CHECK_CONTACT( PLANE, OBB, impl::planeObb );
CHECK_CONTACT( PLANE, SPHERE, impl::planeSphere );
CHECK_CONTACT( PLANE, PLANE, impl::planePlane );
CHECK_CONTACT( PLANE, CAPSULE, impl::planeCapsule );
@ -105,6 +116,7 @@ bool impl::generateContacts( pod::PhysicsBody& a, pod::PhysicsBody& b, pod::Mani
CHECK_CONTACT( PLANE, CONVEX_HULL, impl::planeHull );
CHECK_CONTACT( CAPSULE, AABB, impl::capsuleAabb );
CHECK_CONTACT( CAPSULE, OBB, impl::capsuleObb );
CHECK_CONTACT( CAPSULE, SPHERE, impl::capsuleSphere );
CHECK_CONTACT( CAPSULE, PLANE, impl::capsulePlane );
CHECK_CONTACT( CAPSULE, CAPSULE, impl::capsuleCapsule );
@ -112,6 +124,7 @@ bool impl::generateContacts( pod::PhysicsBody& a, pod::PhysicsBody& b, pod::Mani
CHECK_CONTACT( CAPSULE, CONVEX_HULL, impl::capsuleHull );
CHECK_CONTACT( MESH, AABB, impl::meshAabb );
CHECK_CONTACT( MESH, OBB, impl::meshObb );
CHECK_CONTACT( MESH, SPHERE, impl::meshSphere );
CHECK_CONTACT( MESH, PLANE, impl::meshPlane );
CHECK_CONTACT( MESH, CAPSULE, impl::meshCapsule );
@ -119,6 +132,7 @@ bool impl::generateContacts( pod::PhysicsBody& a, pod::PhysicsBody& b, pod::Mani
CHECK_CONTACT( MESH, CONVEX_HULL, impl::meshHull );
CHECK_CONTACT( CONVEX_HULL, AABB, impl::hullAabb );
CHECK_CONTACT( CONVEX_HULL, OBB, impl::hullObb );
CHECK_CONTACT( CONVEX_HULL, SPHERE, impl::hullSphere );
CHECK_CONTACT( CONVEX_HULL, PLANE, impl::hullPlane );
CHECK_CONTACT( CONVEX_HULL, CAPSULE, impl::hullCapsule );
@ -365,10 +379,17 @@ void impl::integrate( pod::PhysicsBody& body, float dt ) {
// angular integration
//body.angularVelocity += body.torqueAccumulator * body.inverseInertiaTensor * dt;
{
#if 1
pod::Matrix3f R = uf::quaternion::matrix3(body.transform->orientation);
pod::Vector3f localTorque = uf::matrix::multiply( uf::matrix::transpose(R), body.torqueAccumulator );
pod::Vector3f localAngAccel = localTorque * body.inverseInertiaTensor; // element-wise
body.angularVelocity += uf::matrix::multiply( R, localAngAccel ) * dt;
#else
pod::Matrix3f R = uf::quaternion::matrix3(body.transform->orientation);
pod::Vector3f localTorque = uf::matrix::multiply( R, body.torqueAccumulator );
pod::Vector3f localAngAccel = localTorque * body.inverseInertiaTensor; // element-wise
body.angularVelocity += uf::matrix::multiply( uf::matrix::transpose(R), localAngAccel ) * dt;
#endif
}
// update position
@ -378,7 +399,7 @@ void impl::integrate( pod::PhysicsBody& body, float dt ) {
float angularSpeed2 = uf::vector::magnitude( body.angularVelocity );
if ( angularSpeed2 > EPS2 ) {
float angularSpeed = std::sqrt( angularSpeed2 );
pod::Quaternion<> dq = uf::quaternion::axisAngle( body.angularVelocity / angularSpeed, angularSpeed * dt);
pod::Quaternion<> dq = uf::quaternion::axisAngle( body.angularVelocity / angularSpeed, -angularSpeed * dt);
uf::transform::rotate( *body.transform/*.reference*/, dq );
}

View File

@ -52,7 +52,10 @@ bool impl::aabbAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
return true;
}
bool impl::aabbObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( AABB, OBB );
REVERSE_COLLIDER( a, b, impl::obbAabb );
}
bool impl::aabbSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( AABB, SPHERE );
REVERSE_COLLIDER( a, b, impl::sphereAabb );

View File

@ -45,6 +45,10 @@ bool impl::capsuleAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, po
manifold.points.emplace_back(pod::Contact{ contact, normal, penetration });
return true;
}
bool impl::capsuleObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( CAPSULE, OBB );
REVERSE_COLLIDER( a, b, impl::obbCapsule );
}
bool impl::capsulePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( CAPSULE, PLANE );
const auto& capsule = a;

View File

@ -50,7 +50,8 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di
outPoly[i] = hasTransform ? uf::transform::apply( transform, body.collider.triangle.points[i] ) : body.collider.triangle.points[i];
});
} break;
case pod::ShapeType::AABB: {
case pod::ShapeType::AABB:
case pod::ShapeType::OBB: {
outCount = 4;
pod::Vector3f n = localDir;
pod::Vector3f absN = { std::fabs(n.x), std::fabs(n.y), std::fabs(n.z) };

View File

@ -4,9 +4,6 @@
pod::Vector3f impl::support( const pod::PhysicsBody& body, const pod::Vector3f& dir ) {
const auto transform = impl::getTransform( body );
switch ( body.collider.type ) {
case pod::ShapeType::SPHERE: {
return transform.position + uf::vector::normalize( dir ) * body.collider.sphere.radius;
} break;
case pod::ShapeType::AABB: {
return {
( dir.x >= 0.0f ) ? body.bounds.max.x : body.bounds.min.x,
@ -14,6 +11,18 @@ pod::Vector3f impl::support( const pod::PhysicsBody& body, const pod::Vector3f&
( dir.z >= 0.0f ) ? body.bounds.max.z : body.bounds.min.z
};
} break;
case pod::ShapeType::OBB: {
pod::Vector3f localDir = uf::quaternion::rotate( uf::quaternion::inverse(transform.orientation), dir );
pod::Vector3f localPt = {
( localDir.x >= 0.0f ) ? body.collider.obb.max.x : body.collider.obb.min.x,
( localDir.y >= 0.0f ) ? body.collider.obb.max.y : body.collider.obb.min.y,
( localDir.z >= 0.0f ) ? body.collider.obb.max.z : body.collider.obb.min.z
};
return uf::transform::apply( transform, localPt );
} break;
case pod::ShapeType::SPHERE: {
return transform.position + uf::vector::normalize( dir ) * body.collider.sphere.radius;
} break;
case pod::ShapeType::PLANE: {
const auto& plane = body.collider.plane;
pod::Vector3f n = plane.normal;

View File

@ -34,6 +34,10 @@ bool impl::hullAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
ASSERT_COLLIDER_TYPES( CONVEX_HULL, AABB );
return hullGeneric(a, b, manifold );
}
bool impl::hullObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( CONVEX_HULL, OBB );
return hullGeneric(a, b, manifold );
}
bool impl::hullSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( CONVEX_HULL, SPHERE );
return hullGeneric(a, b, manifold );

View File

@ -25,6 +25,29 @@ bool impl::meshAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
}
return hit;
}
bool impl::meshObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( MESH, OBB );
const auto& mesh = a;
const auto& obb = b;
const auto& bvh = *mesh.collider.mesh.bvh;
const auto& meshData = *mesh.collider.mesh.mesh;
// transform to local space for BVH query
auto bounds = impl::transformAabbToLocal( obb.bounds, impl::getTransform( mesh ) );
STATIC_THREAD_LOCAL(uf::stl::vector<pod::BVH::index_t>, candidates);
impl::queryBVH( bvh, bounds, candidates );
bool hit = false;
// do collision per triangle
for ( auto triID : candidates ) {
auto tri = impl::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space
if ( !impl::triangleObb( tri, obb, manifold ) ) continue;
hit = true;
}
return hit;
}
bool impl::meshSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( MESH, SPHERE );

View File

@ -0,0 +1,277 @@
#include <uf/utils/math/physics/common.h>
#include <uf/utils/math/physics/narrowphase.h>
bool impl::obbObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( OBB, OBB );
auto tA = impl::getTransform( a );
auto tB = impl::getTransform( b );
pod::Vector3f cA = uf::transform::apply( tA, (a.collider.obb.max + a.collider.obb.min) * 0.5f );
pod::Vector3f cB = uf::transform::apply( tB, (b.collider.obb.max + b.collider.obb.min) * 0.5f );
pod::Vector3f eA = (a.collider.obb.max - a.collider.obb.min) * 0.5f;
pod::Vector3f eB = (b.collider.obb.max - b.collider.obb.min) * 0.5f;
pod::Vector3f axesA[3] = {
uf::quaternion::rotate(tA.orientation, pod::Vector3f{1,0,0}),
uf::quaternion::rotate(tA.orientation, pod::Vector3f{0,1,0}),
uf::quaternion::rotate(tA.orientation, pod::Vector3f{0,0,1})
};
pod::Vector3f axesB[3] = {
uf::quaternion::rotate(tB.orientation, pod::Vector3f{1,0,0}),
uf::quaternion::rotate(tB.orientation, pod::Vector3f{0,1,0}),
uf::quaternion::rotate(tB.orientation, pod::Vector3f{0,0,1})
};
float minOverlap = FLT_MAX;
pod::Vector3f bestAxis;
auto testAxis = [&](const pod::Vector3f& axis) -> bool {
float mag = uf::vector::magnitude(axis);
if (mag < EPS) return true;
pod::Vector3f n = axis / mag;
float pA = uf::vector::dot(cA, n);
float rA = eA.x * std::fabs(uf::vector::dot(axesA[0], n)) +
eA.y * std::fabs(uf::vector::dot(axesA[1], n)) +
eA.z * std::fabs(uf::vector::dot(axesA[2], n));
float pB = uf::vector::dot(cB, n);
float rB = eB.x * std::fabs(uf::vector::dot(axesB[0], n)) +
eB.y * std::fabs(uf::vector::dot(axesB[1], n)) +
eB.z * std::fabs(uf::vector::dot(axesB[2], n));
float dist = std::fabs(pB - pA);
float overlap = (rA + rB) - dist;
if ( overlap < 0) return false;
if ( overlap < minOverlap ) {
minOverlap = overlap;
bestAxis = n;
}
return true;
};
for ( auto i = 0; i < 3; ++i ) if ( !testAxis(axesA[i]) ) return false;
for ( auto i = 0; i < 3; ++i ) if ( !testAxis(axesB[i]) ) return false;
for ( auto i = 0; i < 3; ++i ) {
for ( auto j = 0; j < 3; j++ ) if ( !testAxis(uf::vector::cross(axesA[i], axesB[j])) ) return false;
};
if ( uf::vector::dot(bestAxis, cB - cA) < 0.0f ) bestAxis = -bestAxis;
// to-do: generate contact face
pod::Vector3f contactPoint = cA + bestAxis * (eA.x * std::fabs(uf::vector::dot(axesA[0], bestAxis)) +
eA.y * std::fabs(uf::vector::dot(axesA[1], bestAxis)) +
eA.z * std::fabs(uf::vector::dot(axesA[2], bestAxis)));
manifold.points.emplace_back( pod::Contact{ contactPoint, bestAxis, minOverlap } );
return true;
}
bool impl::obbAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( OBB, AABB );
auto tA = impl::getTransform( a );
pod::Vector3f cA = uf::transform::apply( tA, (a.collider.obb.max + a.collider.obb.min) * 0.5f );
pod::Vector3f eA = (a.collider.obb.max - a.collider.obb.min) * 0.5f;
pod::Vector3f axesA[3] = {
uf::quaternion::rotate(tA.orientation, pod::Vector3f{1,0,0}),
uf::quaternion::rotate(tA.orientation, pod::Vector3f{0,1,0}),
uf::quaternion::rotate(tA.orientation, pod::Vector3f{0,0,1})
};
pod::Vector3f cB = impl::aabbCenter( b.bounds );
pod::Vector3f eB = impl::aabbExtent( b.bounds );
pod::Vector3f axesB[3] = { {1,0,0}, {0,1,0}, {0,0,1} };
float minOverlap = FLT_MAX;
pod::Vector3f bestAxis;
auto testAxis = [&](const pod::Vector3f& axis) -> bool {
float mag = uf::vector::magnitude(axis);
if ( mag < EPS ) return true;
pod::Vector3f n = axis / mag;
float pA = uf::vector::dot(cA, n);
float rA = eA.x * std::fabs(uf::vector::dot(axesA[0], n)) +
eA.y * std::fabs(uf::vector::dot(axesA[1], n)) +
eA.z * std::fabs(uf::vector::dot(axesA[2], n));
float pB = uf::vector::dot(cB, n);
float rB = eB.x * std::fabs(n.x) + eB.y * std::fabs(n.y) + eB.z * std::fabs(n.z);
float dist = std::fabs(pB - pA);
float overlap = (rA + rB) - dist;
if ( overlap < 0 ) return false;
if ( overlap < minOverlap ) {
minOverlap = overlap;
bestAxis = n;
}
return true;
};
for ( auto i = 0; i < 3; ++i ) if ( !testAxis(axesA[i]) ) return false;
for ( auto i = 0; i < 3; ++i ) if ( !testAxis(axesB[i]) ) return false;
for ( auto i = 0; i < 3; ++i ) {
for ( auto j = 0; j < 3; j++ ) if ( !testAxis(uf::vector::cross(axesA[i], axesB[j])) ) return false;
};
if ( uf::vector::dot(bestAxis, cB - cA) < 0.0f ) bestAxis = -bestAxis;
pod::Vector3f contactPoint = cA + bestAxis * (eA.x * std::fabs(uf::vector::dot(axesA[0], bestAxis)) +
eA.y * std::fabs(uf::vector::dot(axesA[1], bestAxis)) +
eA.z * std::fabs(uf::vector::dot(axesA[2], bestAxis)));
manifold.points.emplace_back( pod::Contact{ contactPoint, bestAxis, minOverlap } );
return true;
}
bool impl::obbSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( OBB, SPHERE );
auto tA = impl::getTransform( a );
auto localCenter = ( a.collider.obb.max + a.collider.obb.min ) * 0.5f;
auto extents = ( a.collider.obb.max - a.collider.obb.min ) * 0.5f;
auto sphereCenter = impl::getPosition( b );
float radius = b.collider.sphere.radius;
auto localP = uf::transform::applyInverse( tA, sphereCenter ) - localCenter;
auto closestLocal = uf::vector::clamp( localP, -extents, extents );
auto deltaLocal = localP - closestLocal;
float distSq = uf::vector::dot( deltaLocal, deltaLocal );
if ( distSq > radius * radius ) return false;
auto closestWorld = uf::transform::apply( tA, closestLocal + localCenter );
float dist = std::sqrt( distSq );
pod::Vector3f normal;
float penetration;
if ( dist < EPS ) {
float minDist = FLT_MAX;
int axis = 0;
float sign = 1.0f;
FOR_EACH(3, {
float distToMax = extents[i] - localP[i];
float distToMin = localP[i] - (-extents[i]);
if (distToMax < minDist) { minDist = distToMax; axis = i; sign = 1.0f; }
if (distToMin < minDist) { minDist = distToMin; axis = i; sign = -1.0f; }
});
pod::Vector3f localNormal = {0,0,0};
localNormal[axis] = sign;
normal = uf::quaternion::rotate( tA.orientation, localNormal );
penetration = radius + minDist;
} else {
normal = uf::quaternion::rotate( tA.orientation, deltaLocal / dist );
penetration = radius - dist;
}
manifold.points.emplace_back( pod::Contact{ closestWorld, normal, penetration } );
return true;
}
bool impl::obbPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( OBB, PLANE );
auto tA = impl::getTransform( a );
pod::Vector3f cA = uf::transform::apply( tA, (a.collider.obb.max + a.collider.obb.min) * 0.5f );
pod::Vector3f eA = (a.collider.obb.max - a.collider.obb.min) * 0.5f;
pod::Vector3f axesA[3] = {
uf::quaternion::rotate(tA.orientation, pod::Vector3f{1,0,0}),
uf::quaternion::rotate(tA.orientation, pod::Vector3f{0,1,0}),
uf::quaternion::rotate(tA.orientation, pod::Vector3f{0,0,1})
};
pod::Vector3f normal = b.collider.plane.normal;
float offset = b.collider.plane.offset;
float rA = eA.x * std::fabs(uf::vector::dot(axesA[0], normal)) +
eA.y * std::fabs(uf::vector::dot(axesA[1], normal)) +
eA.z * std::fabs(uf::vector::dot(axesA[2], normal));
float dist = uf::vector::dot(cA, normal) - offset;
if ( dist > rA ) return false; // in front of plane
pod::Vector3f deepestPoint = cA
- axesA[0] * eA.x * (uf::vector::dot(axesA[0], normal) > 0 ? 1.0f : -1.0f)
- axesA[1] * eA.y * (uf::vector::dot(axesA[1], normal) > 0 ? 1.0f : -1.0f)
- axesA[2] * eA.z * (uf::vector::dot(axesA[2], normal) > 0 ? 1.0f : -1.0f);
float penetration = rA - dist;
manifold.points.emplace_back( pod::Contact{ deepestPoint, normal, penetration } );
return true;
}
bool impl::obbCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( OBB, CAPSULE );
auto tA = impl::getTransform( a );
pod::Vector3f cA = uf::transform::apply( tA, (a.collider.obb.max + a.collider.obb.min) * 0.5f );
pod::Vector3f eA = (a.collider.obb.max - a.collider.obb.min) * 0.5f;
pod::Vector3f axesA[3] = {
uf::quaternion::rotate(tA.orientation, pod::Vector3f{1,0,0}),
uf::quaternion::rotate(tA.orientation, pod::Vector3f{0,1,0}),
uf::quaternion::rotate(tA.orientation, pod::Vector3f{0,0,1})
};
auto [p1, p2] = impl::getCapsuleSegment( b );
pod::Vector3f cB = (p1 + p2) * 0.5f;
pod::Vector3f capAxis = uf::vector::normalize(p2 - p1);
float halfHeight = b.collider.capsule.halfHeight;
float radius = b.collider.capsule.radius;
float minOverlap = FLT_MAX;
pod::Vector3f bestAxis;
auto testAxis = [&](const pod::Vector3f& axis) -> bool {
float mag = uf::vector::magnitude(axis);
if (mag < EPS) return true;
pod::Vector3f n = axis / mag;
float pA = uf::vector::dot(cA, n);
float rA = eA.x * std::fabs(uf::vector::dot(axesA[0], n)) +
eA.y * std::fabs(uf::vector::dot(axesA[1], n)) +
eA.z * std::fabs(uf::vector::dot(axesA[2], n));
float pB = uf::vector::dot(cB, n);
float rB = halfHeight * std::fabs(uf::vector::dot(capAxis, n)) + radius;
float dist = std::fabs(pB - pA);
float overlap = (rA + rB) - dist;
if (overlap < 0) return false;
if (overlap < minOverlap) {
minOverlap = overlap;
bestAxis = n;
}
return true;
};
if ( !testAxis(capAxis) ) return false;
for ( auto i = 0; i < 3; ++i ) if ( !testAxis(axesA[i]) ) return false;
for ( auto i = 0; i < 3; ++i ) if ( !testAxis(uf::vector::cross(axesA[i], capAxis)) ) return false;
if ( uf::vector::dot(bestAxis, cB - cA) < 0.0f ) bestAxis = -bestAxis;
pod::Vector3f contactPoint = cB - bestAxis * radius;
manifold.points.emplace_back( pod::Contact{ contactPoint, bestAxis, minOverlap } );
return true;
}
bool impl::obbMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( OBB, MESH );
REVERSE_COLLIDER( a, b, impl::meshObb );
}
bool impl::obbHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( OBB, CONVEX_HULL );
REVERSE_COLLIDER( a, b, impl::hullObb );
}

View File

@ -23,6 +23,10 @@ bool impl::planeAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:
manifold.points.emplace_back(pod::Contact{ contact, normal, penetration });
return true;
}
bool impl::planeObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( PLANE, OBB );
REVERSE_COLLIDER( a, b, impl::obbPlane );
}
bool impl::planeSphere(const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold) {
ASSERT_COLLIDER_TYPES(PLANE, SPHERE);

View File

@ -77,6 +77,40 @@ bool impl::rayAabb( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQ
}
return true;
}
bool impl::rayObb( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQuery& rayHit ) {
auto tA = impl::getTransform( body );
pod::Ray localRay;
localRay.origin = uf::transform::applyInverse( tA, ray.origin );
localRay.direction = uf::quaternion::rotate( uf::quaternion::inverse(tA.orientation), ray.direction );
pod::AABB localBox = { body.collider.obb.min, body.collider.obb.max };
float tMin, tMax;
if ( !impl::rayAabbIntersect( localRay, localBox, tMin, tMax ) ) return false;
if ( tMin < rayHit.contact.penetration ) {
rayHit.hit = true;
rayHit.body = &body;
rayHit.contact.penetration = tMin;
rayHit.contact.point = ray.origin + ray.direction * tMin;
auto localPoint = localRay.origin + localRay.direction * tMin;
auto center = (localBox.max + localBox.min) * 0.5f;
auto extents = (localBox.max - localBox.min) * 0.5f;
auto localDelta = localPoint - center;
auto absDelta = pod::Vector3f{ std::fabs(localDelta.x) / extents.x, std::fabs(localDelta.y) / extents.y, std::fabs(localDelta.z) / extents.z };
pod::Vector3f localNormal = {0,0,0};
if ( absDelta.x > absDelta.y && absDelta.x > absDelta.z ) localNormal.x = localDelta.x > 0 ? 1.0f : -1.0f;
else if ( absDelta.y > absDelta.z ) localNormal.y = localDelta.y > 0 ? 1.0f : -1.0f;
else localNormal.z = localDelta.z > 0 ? 1.0f : -1.0f;
rayHit.contact.normal = uf::quaternion::rotate( tA.orientation, localNormal );
}
return true;
}
bool impl::raySphere( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQuery& rayHit ) {
auto center = impl::getPosition(body);
float r = body.collider.sphere.radius;

View File

@ -46,6 +46,10 @@ bool impl::sphereAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod
manifold.points.emplace_back(pod::Contact{ contact, normal, penetration });
return true;
}
bool impl::sphereObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( SPHERE, OBB );
REVERSE_COLLIDER( a, b, impl::obbSphere );
}
bool impl::spherePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( SPHERE, PLANE );
REVERSE_COLLIDER( a, b, impl::planeSphere );

View File

@ -153,6 +153,77 @@ bool impl::triangleAabb( const pod::TriangleWithNormal& tri, const pod::PhysicsB
manifold.points.emplace_back( pod::Contact{ contact, bestAxis, minOverlap } );
return true;
}
bool impl::triangleObb( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold ) {
auto tB = impl::getTransform( body );
pod::Vector3f cB = uf::transform::apply( tB, (body.collider.obb.max + body.collider.obb.min) * 0.5f );
pod::Vector3f eB = (body.collider.obb.max - body.collider.obb.min) * 0.5f;
pod::Vector3f axesB[3] = {
uf::quaternion::rotate(tB.orientation, pod::Vector3f{1,0,0}),
uf::quaternion::rotate(tB.orientation, pod::Vector3f{0,1,0}),
uf::quaternion::rotate(tB.orientation, pod::Vector3f{0,0,1})
};
pod::Vector3f v0 = tri.points[0];
pod::Vector3f v1 = tri.points[1];
pod::Vector3f v2 = tri.points[2];
pod::Vector3f e0 = v1 - v0;
pod::Vector3f e1 = v2 - v1;
pod::Vector3f e2 = v0 - v2;
pod::Vector3f edges[3] = { e0, e1, e2 };
pod::Vector3f triNormal = tri.normal;
float minOverlap = FLT_MAX;
pod::Vector3f bestAxis;
auto testAxis = [&](const pod::Vector3f& axis) -> bool {
float mag = uf::vector::magnitude(axis);
if (mag < EPS) return true; // degenerate
pod::Vector3f n = axis / mag;
float p0 = uf::vector::dot(v0, n);
float p1 = uf::vector::dot(v1, n);
float p2 = uf::vector::dot(v2, n);
float minT = std::min({p0, p1, p2});
float maxT = std::max({p0, p1, p2});
float pB = uf::vector::dot(cB, n);
float rB = eB.x * std::fabs(uf::vector::dot(axesB[0], n)) +
eB.y * std::fabs(uf::vector::dot(axesB[1], n)) +
eB.z * std::fabs(uf::vector::dot(axesB[2], n));
float minB = pB - rB;
float maxB = pB + rB;
if ( minT > maxB || maxT < minB ) return false;
float overlap = std::min(maxT, maxB) - std::max(minT, minB);
if ( overlap < minOverlap ) {
minOverlap = overlap;
bestAxis = n;
}
return true;
};
if ( !testAxis(triNormal) ) return false;
for ( auto i = 0; i < 3; ++i ) if ( !testAxis(axesB[i]) ) return false;;
for ( auto i = 0; i < 3; ++i ) {
for ( auto j = 0; j < 3; j++ ) if ( !testAxis(uf::vector::cross(edges[i], axesB[j])) ) return false;
};
pod::Vector3f triCenter = (v0 + v1 + v2) / 3.0f;
if ( uf::vector::dot(bestAxis, cB - triCenter) < 0.0f ) bestAxis = -bestAxis;
float rB = eB.x * std::fabs(uf::vector::dot(axesB[0], bestAxis)) +
eB.y * std::fabs(uf::vector::dot(axesB[1], bestAxis)) +
eB.z * std::fabs(uf::vector::dot(axesB[2], bestAxis));
pod::Vector3f contact = cB - bestAxis * rB;
manifold.points.emplace_back( pod::Contact{ contact, bestAxis, minOverlap } );
return true;
}
bool impl::triangleSphere( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold ) {
const auto& sphere = body;
@ -274,6 +345,10 @@ bool impl::triangleAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, p
ASSERT_COLLIDER_TYPES( TRIANGLE, AABB );
return impl::triangleAabb( a.collider.triangle, b, manifold );
}
bool impl::triangleObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( TRIANGLE, OBB );
return impl::triangleObb( a.collider.triangle, b, manifold );
}
bool impl::triangleSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( TRIANGLE, SPHERE );
return impl::triangleSphere( a.collider.triangle, b, manifold );

View File

@ -37,17 +37,28 @@ void impl::blockPGSSolver( pod::PhysicsBody& a, pod::PhysicsBody& b, pod::Manifo
cc.bias = restitutionBias + penetrationBias;
// effective mass (normal)
pod::Matrix3f invIa = impl::computeWorldInverseInertia( a );
pod::Matrix3f invIb = impl::computeWorldInverseInertia( b );
pod::Vector3f rnA = uf::vector::cross( cc.rA, cc.normal );
pod::Vector3f rnB = uf::vector::cross( cc.rB, cc.normal );
pod::Vector3f I_rnA = uf::matrix::multiply( invIa, rnA );
pod::Vector3f I_rnB = uf::matrix::multiply( invIb, rnB );
float Kn = (a.isStatic ? 0.0f : a.inverseMass) + (b.isStatic ? 0.0f : b.inverseMass) +
uf::vector::dot( uf::vector::cross( rnA * a.inverseInertiaTensor, cc.rA ) + uf::vector::cross( rnB * b.inverseInertiaTensor, cc.rB ), cc.normal );
uf::vector::dot( uf::vector::cross( I_rnA, cc.rA ) + uf::vector::cross( I_rnB, cc.rB ), cc.normal );
cc.effectiveMassN = (Kn > 0.0f) ? 1.0f / Kn : 0.0f;
// effective mass (tangent)
pod::Vector3f rtA = uf::vector::cross( cc.rA, cc.tangent );
pod::Vector3f rtB = uf::vector::cross( cc.rB, cc.tangent );
pod::Vector3f I_rtA = uf::matrix::multiply( invIa, rtA );
pod::Vector3f I_rtB = uf::matrix::multiply( invIb, rtB );
float Kt = (a.isStatic ? 0.0f : a.inverseMass) + (b.isStatic ? 0.0f : b.inverseMass) +
uf::vector::dot( uf::vector::cross( rtA * a.inverseInertiaTensor, cc.rA ) + uf::vector::cross( rtB * b.inverseInertiaTensor, cc.rB ), cc.tangent );
uf::vector::dot( uf::vector::cross( I_rtA, cc.rA ) + uf::vector::cross( I_rtB, cc.rB ), cc.tangent );
cc.effectiveMassT = ( Kt > 0.0f ) ? ( 1.0f / Kt ) : 0.0f;
// warm start

View File

@ -5,7 +5,7 @@
#define PHYSICS_STEP(time) for ( int i = 0; i < time * FRAMERATE; i++ ) uf::physics::step(world, INV_FRAMERATE);
namespace {
namespace impl {
uf::Mesh generateMesh( float size = 1 ) {
uf::Mesh mesh;
mesh.bind<pod::Vertex_3F2F, uint16_t>();
@ -43,7 +43,7 @@ TEST(SphereSphere_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = sphereSphere(bodyA, bodyB, m);
bool collided = impl::sphereSphere(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
EXPECT_NEAR(m.points[0].penetration, 0.5f, EPS);
@ -64,7 +64,7 @@ TEST(AabbAabb_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = aabbAabb(bodyA, bodyB, m);
bool collided = impl::aabbAabb(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
})
@ -98,7 +98,7 @@ TEST(SphereSphere_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = sphereSphere(bodyA, bodyB, m);
bool collided = impl::sphereSphere(bodyA, bodyB, m);
EXPECT_TRUE(!collided);
})
@ -116,7 +116,7 @@ TEST(SphereAabb_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = sphereAabb(bodyA, bodyB, m);
bool collided = impl::sphereAabb(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
EXPECT_TRUE(m.points[0].penetration > 0.0f);
@ -136,7 +136,7 @@ TEST(SpherePlane_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = planeSphere(bodyB, bodyA, m);
bool collided = impl::planeSphere(bodyB, bodyA, m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
EXPECT_NEAR(m.points[0].penetration, 0.5f, EPS);
@ -154,7 +154,7 @@ TEST(SpherePlane_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = planeSphere(bodyB, bodyA, m);
bool collided = impl::planeSphere(bodyB, bodyA, m);
EXPECT_TRUE(!collided);
})
@ -171,7 +171,7 @@ TEST(CapsuleCapsule_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = capsuleCapsule(bodyA, bodyB, m);
bool collided = impl::capsuleCapsule(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
EXPECT_TRUE(m.points[0].penetration > 0.0f);
@ -306,7 +306,7 @@ TEST(SphereSphere_TouchingButNotOverlapping, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = sphereSphere(bodyA, bodyB, m);
bool collided = impl::sphereSphere(bodyA, bodyB, m);
EXPECT_TRUE(collided); // should count as a collision
EXPECT_NEAR(m.points[0].penetration, 0.0f, EPS);
@ -516,7 +516,7 @@ TEST(CapsulePlane_ContactNormal, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = capsulePlane(bodyA, bodyB, m);
bool collided = impl::capsulePlane(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
@ -556,7 +556,7 @@ TEST(CapsuleSphere_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = capsuleSphere(bodyA, bodyB, m);
bool collided = impl::capsuleSphere(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
})
@ -574,7 +574,7 @@ TEST(CapsuleSphere_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = capsuleSphere(bodyA, bodyB, m);
bool collided = impl::capsuleSphere(bodyA, bodyB, m);
EXPECT_TRUE(!collided);
})
@ -592,7 +592,7 @@ TEST(AabbSphere_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = aabbSphere( bodyA, bodyB, m );
bool collided = impl::aabbSphere( bodyA, bodyB, m );
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
})
@ -610,7 +610,7 @@ TEST(AabbSphere_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = aabbSphere(bodyA,bodyB,m);
bool collided = impl::aabbSphere(bodyA,bodyB,m);
EXPECT_TRUE(!collided);
})
@ -627,7 +627,7 @@ TEST(AabbPlane_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = aabbPlane(bodyA,bodyB,m);
bool collided = impl::aabbPlane(bodyA,bodyB,m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
})
@ -644,7 +644,7 @@ TEST(AabbPlane_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = aabbPlane(bodyA,bodyB,m);
bool collided = impl::aabbPlane(bodyA,bodyB,m);
EXPECT_TRUE(!collided);
})
@ -662,7 +662,7 @@ TEST(AabbCapsule_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = aabbCapsule(bodyA,bodyB,m);
bool collided = impl::aabbCapsule(bodyA,bodyB,m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
})
@ -680,7 +680,7 @@ TEST(AabbCapsule_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = aabbCapsule(bodyA,bodyB,m);
bool collided = impl::aabbCapsule(bodyA,bodyB,m);
EXPECT_TRUE(!collided);
})
@ -698,7 +698,7 @@ TEST(SphereCapsule_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = sphereCapsule(bodyA,bodyB,m);
bool collided = impl::sphereCapsule(bodyA,bodyB,m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
})
@ -716,7 +716,7 @@ TEST(SphereCapsule_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = sphereCapsule(bodyA,bodyB,m);
bool collided = impl::sphereCapsule(bodyA,bodyB,m);
EXPECT_TRUE(!collided);
})
@ -730,7 +730,7 @@ TEST(PlanePlane_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = planePlane(bodyA,bodyB,m);
bool collided = impl::planePlane(bodyA,bodyB,m);
EXPECT_TRUE(!collided); // always false in your engine
})
@ -747,7 +747,7 @@ TEST(PlaneCapsule_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = planeCapsule(bodyA,bodyB,m);
bool collided = impl::planeCapsule(bodyA,bodyB,m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
})
@ -764,7 +764,7 @@ TEST(PlaneCapsule_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = planeCapsule(bodyA,bodyB,m);
bool collided = impl::planeCapsule(bodyA,bodyB,m);
EXPECT_TRUE(!collided);
})
TEST(MeshSphere_Collision, {
@ -784,7 +784,7 @@ TEST(MeshSphere_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = meshSphere(bodyA, bodyB, m);
bool collided = impl::meshSphere(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
if ( !m.points.empty() ) EXPECT_TRUE(m.points[0].penetration > 0.0f);
@ -805,7 +805,7 @@ TEST(MeshSphere_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = meshSphere(bodyA, bodyB, m);
bool collided = impl::meshSphere(bodyA, bodyB, m);
EXPECT_FALSE(collided);
})
@ -825,7 +825,7 @@ TEST(MeshAabb_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = meshAabb(bodyA, bodyB, m);
bool collided = impl::meshAabb(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_TRUE(!m.points.empty());
})
@ -846,7 +846,7 @@ TEST(MeshAabb_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = meshAabb(bodyA, bodyB, m);
bool collided = impl::meshAabb(bodyA, bodyB, m);
EXPECT_FALSE(collided);
})
@ -899,7 +899,7 @@ TEST(MeshMesh_Collision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = meshMesh(bodyA, bodyB, m);
bool collided = impl::meshMesh(bodyA, bodyB, m);
EXPECT_TRUE(collided);
})
@ -919,7 +919,7 @@ TEST(MeshMesh_NoCollision, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = meshMesh(bodyA, bodyB, m);
bool collided = impl::meshMesh(bodyA, bodyB, m);
EXPECT_FALSE(collided);
})
@ -945,7 +945,7 @@ TEST(TriangleTriangle_Collision_SimpleOverlap, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::triangleTriangle(bodyA, bodyB, m, EPS);
bool collided = impl::triangleTriangle(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty());
@ -977,7 +977,7 @@ TEST(TriangleTriangle_Collision_CoplanarOverlap, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::triangleTriangle(bodyA, bodyB, m, EPS);
bool collided = impl::triangleTriangle(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty());
@ -1005,7 +1005,7 @@ TEST(TriangleTriangle_Collision_TouchingEdge, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::triangleTriangle(bodyA, bodyB, m, EPS);
bool collided = impl::triangleTriangle(bodyA, bodyB, m);
// Should still report as collision (tangent contact)
EXPECT_TRUE(collided);
@ -1034,7 +1034,7 @@ TEST(TriangleAabb_Collision_OverlapCenter, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::triangleAabb(bodyA, bodyB, m, EPS);
bool collided = impl::triangleAabb(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty());
@ -1062,7 +1062,7 @@ TEST(TriangleAabb_Collision_NoOverlap, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::triangleAabb(bodyA, bodyB, m, EPS);
bool collided = impl::triangleAabb(bodyA, bodyB, m);
EXPECT_FALSE(collided);
})
@ -1086,7 +1086,7 @@ TEST(TrianglePlane_Collision_BelowPlane, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::trianglePlane(bodyA, bodyB, m, EPS);
bool collided = impl::trianglePlane(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty());
@ -1114,7 +1114,7 @@ TEST(TrianglePlane_NoCollision_AbovePlane, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::trianglePlane(bodyA, bodyB, m, EPS);
bool collided = impl::trianglePlane(bodyA, bodyB, m);
EXPECT_FALSE(collided);
})
@ -1138,7 +1138,7 @@ TEST(TriangleSphere_Collision_Tangent, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::triangleSphere(bodyA, bodyB, m, EPS);
bool collided = impl::triangleSphere(bodyA, bodyB, m);
// At tangency: considered collision
EXPECT_TRUE(collided);
@ -1171,7 +1171,7 @@ TEST(TriangleCapsule_Collision_Overlap, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::triangleCapsule(bodyA, bodyB, m, EPS);
bool collided = impl::triangleCapsule(bodyA, bodyB, m);
EXPECT_TRUE(collided);
EXPECT_FALSE(m.points.empty());
@ -1202,7 +1202,7 @@ TEST(TriangleCapsule_Collision_NoOverlap, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::triangleCapsule(bodyA, bodyB, m, EPS);
bool collided = impl::triangleCapsule(bodyA, bodyB, m);
EXPECT_FALSE(collided);
})
@ -1228,7 +1228,7 @@ TEST(TriangleCapsule_Collision_Tangent, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::triangleCapsule(bodyA, bodyB, m, EPS);
bool collided = impl::triangleCapsule(bodyA, bodyB, m);
// At tangency, should still count as collision (penetration ≈ 0)
EXPECT_TRUE(collided);
@ -1261,7 +1261,7 @@ TEST(TriangleCapsule_Collision_EdgeAlignment, {
bodyB.bounds = impl::computeAABB( bodyB );
pod::Manifold m;
bool collided = impl::triangleCapsule(bodyA, bodyB, m, EPS);
bool collided = impl::triangleCapsule(bodyA, bodyB, m);
EXPECT_TRUE(collided);
})

View File

@ -1,5 +1,5 @@
FLAGS += -DUF_ENV_DREAMCAST
REQ_DEPS += opengl gldc json:nlohmann zlib lua r:eactphysics simd ctti fmt freetype openal aldc ogg wav png
REQ_DEPS += opengl gldc json:nlohmann zlib lua simd ctti fmt freetype openal aldc ogg wav png
INCS := -I./dep/dreamcast/include $(INCS)

View File

@ -3,7 +3,7 @@ ifneq (,$(findstring -DUF_DEV_ENV,$(FLAGS)))
FLAGS += -march=native -g # -flto # -g
endif
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit r:eactphysics simd ctti gltf imgui fmt freetype openal ogg wav
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit simd ctti gltf imgui fmt freetype openal ogg wav
FLAGS += -DUF_ENV_LINUX -fPIC
DEPS += -pthread -ldl -lX11 -lXrandr
INCS := -I./dep/master/include $(INCS)

View File

@ -3,7 +3,7 @@ ifneq (,$(findstring -DUF_DEV_ENV,$(FLAGS)))
FLAGS += -march=native -g # -flto # -g
endif
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit r:eactphysics simd ctti gltf imgui fmt freetype openal ogg wav
REQ_DEPS += $(RENDERER) json:nlohmann zlib luajit simd ctti gltf imgui fmt freetype openal ogg wav
FLAGS += -DUF_ENV_WINDOWS -DUF_ENV_WIN64 -DWIN32_LEAN_AND_MEAN
DEPS += -lgdi32 -ldwmapi
LINKS += #-Wl,-subsystem,windows