Compare commits

...

3 Commits

64 changed files with 1532 additions and 368 deletions

View File

@ -8,7 +8,7 @@
"useLightmaps": false,
"max": 32,
"shadows": {
"enabled": true,
"enabled": false,
"update": 4,
"max": 16,
"samples": 2
@ -110,18 +110,18 @@
"invariant": {
"default stage buffers": true,
"default defer buffer destroy": true,
"default command buffer immediate": false,
"default command buffer immediate": true,
"n-buffered uniform": false,
"multithreaded recording": true
},
"pipelines": {
"deferred": true,
"gui": true,
"vsync": true, // vsync on vulkan side rather than engine-side
"vsync": false, // vsync on vulkan side rather than engine-side
"hdr": true,
"vxgi": true, // to-do: fix issues
"vxgi": false, // to-do: fix issues
"culling": false,
"bloom": true,
"bloom": false,
"dof": false,
"rt": false,
"fsr": false,
@ -335,6 +335,17 @@
"ultralight": { "enabled": true, "scale": 1.5 },
"discord": { "enabled": false }
},
"physics": {
"warmup solver": false,
"block solver": true,
"psg solver": true,
"correction percent": 0.0,
"gjk": false,
"debug draw": false,
"fixed step": true,
"substeps": 4,
"solver iterations": 10
},
"audio": {
"mute": false,
"async update": false,

View File

@ -7,7 +7,7 @@
"physics": {
"mass": 0,
"inertia": [0, 0, 0],
"type": "mesh" // "bounding box"
"type": "bounding box"
}
}
}

View File

@ -7,10 +7,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

@ -99,12 +99,10 @@ ent:bind( "tick", function(self)
local flattenedTransform = nil
if fixedCamera then
flattenedTransform = transform:flatten()
-- flattenedTransform.position.y = flattenedTransform.position.y
else
flattenedTransform = cameraTransform:flatten()
end
flattenedTransform.forward = ( transform.forward + Vector3f( 0, cameraTransform.forward.y, 0 ) ):normalize();
flattenedTransform = transform:flatten()
else
flattenedTransform = cameraTransform:flatten()
end
-- toggle flashlight
if light.enabled then

View File

@ -18,7 +18,7 @@
[ 0, 0, 0, 0 ]
],
"shader": {
"mode": 10,
"mode": 0,
"scalar": 16,
"parameters": [ 0, 0, 0, "time" ]
}

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

@ -1,13 +1,9 @@
#version 450
#pragma shader_stage(fragment)
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec3 inColor;
layout (location = 0) out uvec2 outId;
layout (location = 1) out vec2 outNormals;
layout (location = 2) out vec4 outAlbedo;
layout (location = 0) in vec4 inColor;
layout (location = 0) out vec4 outColor;
void main() {
outAlbedo = vec4(inColor, 1);
outColor = inColor;
}

View File

@ -4,10 +4,10 @@
#include "../../common/macros.h"
#include "../../common/structs.h"
layout (constant_id = 0) const uint PASSES = 6;
layout (constant_id = 0) const uint PASSES = 1;
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inColor;
layout (location = 1) in vec4 inColor;
layout( push_constant ) uniform PushBlock {
uint pass;
@ -18,12 +18,9 @@ layout (binding = 0) uniform Camera {
Viewport viewport[PASSES];
} camera;
layout (location = 0) out vec3 outPosition;
layout (location = 1) out vec3 outColor;
layout (location = 0) out vec4 outColor;
void main() {
outPosition = vec3(camera.viewport[PushConstant.pass].view * vec4(inPos.xyz, 1.0));
outColor = inColor;
gl_Position = camera.viewport[PushConstant.pass].projection * camera.viewport[PushConstant.pass].view * vec4(inPos.xyz, 1.0);
}

View File

@ -3,7 +3,6 @@
// enable if shaderNonUniform is not supported
// Nvidia hardware does not require nonuniformEXT, but AMD does
#endif
// implicit variables
#ifndef MULTISAMPLING
#define MULTISAMPLING 1
@ -20,8 +19,11 @@
#ifndef MAX_SHADOWS
#define MAX_SHADOWS ubo.settings.lighting.maxShadows
#endif
#ifndef USE_CAMERA_VIEWPORT
#define USE_CAMERA_VIEWPORT 0
#endif
#ifndef VIEW_MATRIX
#if VXGI
#if VXGI || !USE_CAMERA_VIEWPORT
#define VIEW_MATRIX ubo.eyes[surface.pass].view
#else
#define VIEW_MATRIX camera.viewport[surface.pass].view
@ -80,7 +82,7 @@
#endif
#if BARYCENTRIC
#ifndef BARYCENTRIC_CALCULATE
#define BARYCENTRIC_CALCULATE 1
#define BARYCENTRIC_CALCULATE 0
#endif
#ifndef BUFFER_REFERENCE
#define BUFFER_REFERENCE 1

View File

@ -199,10 +199,15 @@ void populateSurface() {
{
vec2 inUv = (vec2(gl_GlobalInvocationID.xy) / vec2(renderSize)) * 2.0f - 1.0f;
#if USE_CAMERA_VIEWPORT
const mat4 iProjection = inverse( camera.viewport[surface.pass].projection );
const mat4 iView = inverse( camera.viewport[surface.pass].view );
const mat4 iProjectionView = inverse( camera.viewport[surface.pass].projection * mat4(mat3(camera.viewport[surface.pass].view)) );
const mat4 iProjectionView = /*iProjection * iView;*/ inverse( camera.viewport[surface.pass].projection * mat4(mat3(camera.viewport[surface.pass].view)) );
#else
const mat4 iView = ubo.eyes[surface.pass].iView;
const mat4 iProjection = ubo.eyes[surface.pass].iProjection;
const mat4 iProjectionView = /*iProjection * iView;*/ inverse( ubo.eyes[surface.pass].projection * mat4(mat3(ubo.eyes[surface.pass].view)) );
#endif
const vec4 near4 = iProjectionView * (vec4(inUv, -1.0, 1.0));
const vec4 far4 = iProjectionView * (vec4(inUv, 1.0, 1.0));
const vec3 near3 = near4.xyz / near4.w;

View File

@ -6,7 +6,7 @@
#include <uf/ext/opengl/texture.h>
#include <uf/ext/opengl/shader.h>
#include <uf/utils/mesh/mesh.h>
#include <uf/utils/memory/unordered_map.h>
#define UF_GRAPHIC_POINTERED_USERDATA 1
namespace ext {
@ -103,6 +103,8 @@ namespace ext {
void record( CommandBuffer& commandBuffer, size_t pass = 0, size_t draw = 0 ) const;
void record( CommandBuffer& commandBuffer, const GraphicDescriptor& descriptor, size_t pass = 0, size_t draw = 0 ) const;
};
typedef uf::stl::unordered_map<uf::stl::string, Graphic> Graphics;
}
}

View File

@ -6,6 +6,7 @@
#include <uf/ext/vulkan/texture.h>
#include <uf/ext/vulkan/shader.h>
#include <uf/utils/mesh/mesh.h>
#include <uf/utils/memory/unordered_map.h>
namespace ext {
namespace vulkan {
@ -140,5 +141,7 @@ namespace ext {
void record( VkCommandBuffer commandBuffer, const GraphicDescriptor& descriptor, size_t pass = 0, size_t draw = 0, size_t offset = 0 ) const;
};
typedef uf::stl::unordered_map<uf::stl::string, Graphic> Graphics;
}
}

View File

@ -4,7 +4,9 @@
namespace ext {
namespace vulkan {
struct UF_API DeferredRenderMode : public ext::vulkan::RenderMode {
struct UF_API DeferredRenderMode : public ext::vulkan::RenderMode {
RenderTarget forwardRenderTarget;
virtual const uf::stl::string getType() const;
virtual GraphicDescriptor bindGraphicDescriptor( const GraphicDescriptor&, size_t = 0 );

View File

@ -13,7 +13,7 @@ namespace ext {
bool blend = false;
uint8_t samples = 1;
uint8_t mips = 0;
bool screenshottable = true;
bool screenshottable = false;
bool aliased = false;
uint32_t width = 0;
@ -57,6 +57,7 @@ namespace ext {
void destroy();
void addPass( VkPipelineStageFlags, VkAccessFlags, const uf::stl::vector<size_t>&, const uf::stl::vector<size_t>&, const uf::stl::vector<size_t>&, size_t, size_t = 0, bool = true );
size_t attach( const Attachment::Descriptor& descriptor, Attachment* attachment = NULL );
size_t aliasAttachment( const Attachment& attachment );
};
}
}

View File

@ -101,8 +101,8 @@
#define TYPE_NAME(T) TYPE(T).name()
#endif
#define MIN(X, Y) (X) < (Y) ? (X) : (Y)
#define MAX(X, Y) (X) > (Y) ? (X) : (Y)
#define MIN(X, Y) ((X) < (Y) ? (X) : (Y))
#define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
#define CLAMP(X, LO, HI) MAX(LO, MIN(HI, X))
#define LENGTH_OF(X) *(&X + 1) - X
#define FOR_ARRAY(X) for ( auto i = 0; i < LENGTH_OF(X); ++i )

View File

@ -1,6 +1,7 @@
#pragma once
#include "impl.h"
#include "draw.h"
// to-do: organize this mess
namespace impl {

View File

@ -0,0 +1,19 @@
#pragma once
#include "impl.h"
namespace impl {
struct Vertex {
pod::Vector3f position;
pod::Vector4f color;
static uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
};
/*FORCE_INLINE*/ void addLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color = { 1, 1, 1, 1 } );
/*FORCE_INLINE*/ void addTransientLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color = { 1, 1, 1, 1 }, const pod::PhysicsBody* a = NULL, const pod::PhysicsBody* b = NULL );
void drawManifold( const pod::Manifold& manifold );
void drawBody( const pod::PhysicsBody& body );
void draw( const pod::World& world, float dt = 0 );
}

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;
@ -248,7 +243,8 @@ namespace pod {
struct RayQuery {
bool hit = false;
const pod::PhysicsBody* body;
const pod::PhysicsBody* body = NULL;
const pod::PhysicsBody* invoker = NULL;
pod::Contact contact = { pod::Vector3f{}, pod::Vector3f{}, FLT_MAX };
};
@ -256,14 +252,17 @@ namespace pod {
bool awake = true;
uf::stl::vector<uint32_t> indices;
pod::BVH::pairs_t pairs;
uf::stl::vector<pod::Manifold> manifolds;
};
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
bool debugDraw = false; // draws wireframe of collision bodies
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
@ -362,6 +361,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,9 +4,12 @@
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 );
bool aabbMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool aabbHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
void drawAabb( const pod::PhysicsBody& body );
}

View File

@ -3,10 +3,13 @@
#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 );
void drawCapsule( const pod::PhysicsBody& body );
}

View File

@ -4,9 +4,12 @@
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 );
bool hullMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool hullHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
void drawHull( const pod::PhysicsBody& body );
}

View File

@ -4,9 +4,12 @@
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 );
bool meshMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool meshHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
void drawMesh( const pod::PhysicsBody& body );
}

View File

@ -0,0 +1,15 @@
#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 );
void drawObb( const pod::PhysicsBody& body );
}

View File

@ -4,9 +4,12 @@
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 );
bool planeMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
bool planeHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
void drawPlane( const pod::PhysicsBody& body );
}

View File

@ -5,10 +5,14 @@
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 );
bool rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQuery& rayHit );
bool rayHull( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQuery& rayHit );
void drawRay( const pod::Ray& r, const pod::RayQuery& query );
}

View File

@ -3,10 +3,13 @@
#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 );
bool sphereHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
void drawSphere( const pod::PhysicsBody& body );
}

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,8 +13,11 @@ 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 );
bool triangleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold );
void drawTriangle( const pod::PhysicsBody& body );
}

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,17 +95,9 @@ 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 * pointer->scale);
combined.orientation = uf::quaternion::multiply( pointer->orientation, combined.orientation );
combined.scale = {
combined.scale.x * pointer->scale.x,
combined.scale.y * pointer->scale.y,
combined.scale.z * pointer->scale.z,
};
combined.scale = combined.scale * pointer->scale;
combined.model = pointer->model * combined.model;
if ( pointer == pointer->reference ) break;

View File

@ -16,6 +16,7 @@
#include <uf/ext/json/json.h>
#include <uf/utils/serialize/serializer.h>
#include <uf/utils/math/angle.h>
#include <uf/utils/math/hash.h>
#if UF_USE_BFLOAT16
#include <stdfloat>

View File

@ -743,7 +743,7 @@ template<typename T>
size_t uf::vector::hash( const T& v ) {
size_t hash = 0;
FOR_EACH(T::size, {
hash ^= v[i];
uf::hash( hash, v[i] );
});
return hash;
}

View File

@ -110,152 +110,192 @@ 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& configEnginePhysicsJson = json["engine"]["physics"];
uf::physics::settings.warmupSolver = configEnginePhysicsJson["warmup solver"].as(uf::physics::settings.warmupSolver);
uf::physics::settings.blockContactSolver = configEnginePhysicsJson["block solver"].as(uf::physics::settings.blockContactSolver);
uf::physics::settings.psgContactSolver = configEnginePhysicsJson["psg solver"].as(uf::physics::settings.psgContactSolver);
uf::physics::settings.useGjk = configEnginePhysicsJson["gjk"].as(uf::physics::settings.useGjk);
uf::physics::settings.debugDraw = configEnginePhysicsJson["debug draw"].as(uf::physics::settings.debugDraw);
uf::physics::settings.fixedStep = configEnginePhysicsJson["fixed step"].as(uf::physics::settings.fixedStep);
uf::physics::settings.substeps = configEnginePhysicsJson["substeps"].as(uf::physics::settings.substeps);
uf::physics::settings.solverIterations = configEnginePhysicsJson["solver iterations"].as(uf::physics::settings.solverIterations);
uf::physics::settings.baumgarteCorrectionPercent = configEnginePhysicsJson["correction percent"].as(uf::physics::settings.baumgarteCorrectionPercent);
}
// 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

@ -134,12 +134,25 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
auto type = metadataJsonPhysics["type"].as<uf::stl::string>();
float mass = metadataJsonPhysics["mass"].as<float>();
bool recenter = metadataJsonPhysics["recenter"].as<bool>();
pod::Vector3f offset = uf::vector::decode( metadataJsonPhysics["offset"], pod::Vector3f{} );
if ( offset == pod::Vector3f{} ) recenter = true;
if ( type == "bounding box" || type == "aabb" ) {
pod::Vector3f min = uf::vector::decode( metadataJsonPhysics["min"], pod::Vector3f{-0.5f, -0.5f, -0.5f} );
pod::Vector3f max = uf::vector::decode( metadataJsonPhysics["max"], pod::Vector3f{0.5f, 0.5f, 0.5f} );
// recenter
if ( recenter ) {
pod::Vector3f center = (max + min) * 0.5f;
pod::Vector3f extents = (max - min) * 0.5f;
min = -extents;
max = extents;
offset = center;
}
uf::physics::create( self, pod::AABB{ .min = min, .max = max }, mass, offset );
} else if ( type == "plane" ) {
pod::Vector3f direction = uf::vector::decode( metadataJsonPhysics["direction"], pod::Vector3f{} );

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

@ -381,11 +381,20 @@ void ext::opengl::tick(){
auto& scene = uf::scene::getCurrentScene();
auto& graph = scene.getGraph();
for ( auto entity : graph ) {
if ( !entity->hasComponent<uf::Graphic>() ) continue;
ext::opengl::Graphic& graphic = entity->getComponent<uf::Graphic>();
if ( graphic.initialized || !graphic.process || graphic.initialized ) continue;
graphic.initializePipeline();
ext::opengl::states::rebuild = true;
if ( entity->hasComponent<ext::opengl::Graphics>() ) {
auto& graphics = entity->getComponent<ext::opengl::Graphics>();
for ( auto& [ _, graphic ] : graphics ) {
if ( graphic.initialized || !graphic.process ) continue;
graphic.initializePipeline();
ext::opengl::states::rebuild = true;
}
}
if ( entity->hasComponent<ext::opengl::Graphic>() ) {
auto& graphic = entity->getComponent<ext::opengl::Graphic>();
if ( graphic.initialized || !graphic.process ) continue;
graphic.initializePipeline();
ext::opengl::states::rebuild = true;
}
}
for ( auto& renderMode : renderModes ) {
if ( !renderMode ) continue;

View File

@ -60,10 +60,18 @@ void ext::opengl::RenderMode::createCommandBuffers() {
auto& scene = uf::scene::getCurrentScene();
auto/*&*/ graph = scene.getGraph();
for ( auto entity : graph ) {
if ( !entity->hasComponent<uf::Graphic>() ) continue;
ext::opengl::Graphic& graphic = entity->getComponent<uf::Graphic>();
if ( !graphic.initialized || !graphic.process ) continue;
graphics.push_back(&graphic);
if ( entity->hasComponent<ext::opengl::Graphics>() ) {
auto& g = entity->getComponent<ext::opengl::Graphics>();
for ( auto& [ _, graphic ] : g ) {
if ( !graphic.initialized || !graphic.process ) continue;
graphics.emplace_back(&graphic);
}
}
if ( entity->hasComponent<ext::opengl::Graphic>() ) {
auto& graphic = entity->getComponent<ext::opengl::Graphic>();
if ( !graphic.initialized || !graphic.process ) continue;
graphics.emplace_back(&graphic);
}
}
this->synchronize();

View File

@ -226,10 +226,18 @@ void ext::vulkan::RenderMode::createCommandBuffers() {
auto& scene = uf::scene::getCurrentScene();
auto/*&*/ graph = scene.getGraph();
for ( auto entity : graph ) {
if ( !entity->hasComponent<uf::Graphic>() ) continue;
ext::vulkan::Graphic& graphic = entity->getComponent<uf::Graphic>();
if ( !graphic.initialized || !graphic.process ) continue;
graphics.emplace_back(&graphic);
if ( entity->hasComponent<ext::vulkan::Graphics>() ) {
auto& g = entity->getComponent<ext::vulkan::Graphics>();
for ( auto& [ _, graphic ] : g ) {
if ( !graphic.initialized || !graphic.process ) continue;
graphics.emplace_back(&graphic);
}
}
if ( entity->hasComponent<ext::vulkan::Graphic>() ) {
auto& graphic = entity->getComponent<ext::vulkan::Graphic>();
if ( !graphic.initialized || !graphic.process ) continue;
graphics.emplace_back(&graphic);
}
}
// this->synchronize();

View File

@ -21,7 +21,7 @@
#if BARYCENTRIC
// 0 keeps a buffer for barycentric coordinates, 1 will reconstruct in the deferred pass
#ifndef BARYCENTRIC_CALCULATE
#define BARYCENTRIC_CALCULATE 1
#define BARYCENTRIC_CALCULATE 0
#endif
#endif
@ -159,7 +159,7 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
/*.usage = */VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT,
/*.blend = */false,
/*.samples = */msaa,
//*.mips = */1,
/*.mips = */1,
});
// output buffers
attachments.color = renderTarget.attach(RenderTarget::Attachment::Descriptor{
@ -245,6 +245,39 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
// metadata.outputs.emplace_back(metadata.attachments["output"]);
renderTarget.initialize( device );
// initialize forward+ renderTarget
{
forwardRenderTarget.device = &device;
forwardRenderTarget.views = metadata.eyes;
forwardRenderTarget.width = renderTarget.width;
forwardRenderTarget.height = renderTarget.height;
forwardRenderTarget.scale = renderTarget.scale;
size_t msaa = ext::vulkan::settings::msaa;
struct {
size_t color, depth;
} attachmentsPlus = {};
attachmentsPlus.color = forwardRenderTarget.aliasAttachment(this->getAttachment("color"));
attachmentsPlus.depth = forwardRenderTarget.aliasAttachment(this->getAttachment("depth"));
metadata.attachments["color+"] = attachmentsPlus.color;
metadata.attachments["depth+"] = attachmentsPlus.depth;
forwardRenderTarget.addPass(
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
{ attachmentsPlus.color },
{},
{},
attachmentsPlus.depth,
0,
true
);
forwardRenderTarget.initialize( device );
}
{
uf::Mesh mesh;
mesh.vertex.count = 3;
@ -599,7 +632,17 @@ void ext::vulkan::DeferredRenderMode::tick() {
if ( resized ) {
this->resized = false;
rebuild = true;
renderTarget.initialize( *renderTarget.device );
forwardRenderTarget.width = renderTarget.width;
forwardRenderTarget.height = renderTarget.height;
forwardRenderTarget.scale = renderTarget.scale;
forwardRenderTarget.attachments.clear();
forwardRenderTarget.aliasAttachment(this->getAttachment("color"));
forwardRenderTarget.aliasAttachment(this->getAttachment("depth"));
forwardRenderTarget.initialize( *forwardRenderTarget.device );
}
// update blitter descriptor set
@ -660,6 +703,8 @@ void ext::vulkan::DeferredRenderMode::render() {
//unlockMutex( this->mostRecentCommandPoolId );
}
void ext::vulkan::DeferredRenderMode::destroy() {
forwardRenderTarget.destroy();
// cleanup
::postprocesses::depthPyramid.atomicCounter.destroy(false);
::postprocesses::bloom.atomicCounter.destroy(false);
@ -819,6 +864,7 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
for ( auto graphic : graphics ) {
// only draw graphics that are assigned to this type of render mode
if ( graphic->descriptor.renderMode != this->getName() ) continue;
if ( graphic->descriptor.renderTarget != 0 /*this->getName()*/ ) continue;
ext::vulkan::GraphicDescriptor descriptor = bindGraphicDescriptor(graphic->descriptor, currentSubpass);
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[{}]", currentDraw) );
graphic->record( commandBuffer, descriptor, 0, currentDraw++, frame );
@ -875,6 +921,65 @@ void ext::vulkan::DeferredRenderMode::createCommandBuffers( const uf::stl::vecto
::transitionAttachmentsFrom( this, shader, commandBuffer );
}
// forward+
{
{
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, "forward:setImageLayout" );
// Transition Color
VkImageSubresourceRange colorRange = {};
colorRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
colorRange.baseMipLevel = 0;
colorRange.levelCount = 1;
colorRange.baseArrayLayer = 0;
colorRange.layerCount = metadata.eyes; // Or this->views
uf::renderer::Texture::setImageLayout(
commandBuffer,
forwardRenderTarget.attachments[0].image,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
colorRange
);
// Transition Depth
VkImageSubresourceRange depthRange = colorRange;
depthRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; // Depth aspect!
uf::renderer::Texture::setImageLayout(
commandBuffer,
forwardRenderTarget.attachments[1].image,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
depthRange
);
}
renderPassBeginInfo.clearValueCount = 0;
renderPassBeginInfo.pClearValues = NULL;
renderPassBeginInfo.renderPass = forwardRenderTarget.renderPass;
renderPassBeginInfo.framebuffer = forwardRenderTarget.framebuffers[frame];
vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdSetViewport(commandBuffer, 0, 1, &viewport);
vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
// render to geometry buffers
{
size_t currentPass = 0;
size_t currentDraw = 0;
for ( auto graphic : graphics ) {
// only draw graphics that are assigned to this type of render mode
if ( graphic->descriptor.renderTarget != 1 /*"forward"*/ ) continue;
//if ( graphic->descriptor.renderMode != this->getName() ) continue;
//if ( graphic->descriptor.pipeline != "forward" ) continue;
ext::vulkan::GraphicDescriptor descriptor = graphic->descriptor; // bindGraphicDescriptor(graphic->descriptor, currentSubpass);
device->UF_CHECKPOINT_MARK( commandBuffer, pod::Checkpoint::GENERIC, ::fmt::format("graphic[{}]", currentDraw) );
graphic->record( commandBuffer, descriptor, 0, currentDraw++, frame );
}
}
vkCmdEndRenderPass(commandBuffer);
}
if ( settings::pipelines::bloom && blitter.material.hasShader("compute", "bloom-down") ) {
auto& shader = blitter.material.getShader("compute", "bloom-down");
auto mips = uf::vector::mips( pod::Vector2ui{ width, height } );

View File

@ -218,6 +218,34 @@ size_t ext::vulkan::RenderTarget::attach( const Attachment::Descriptor& descript
return index;
}
size_t ext::vulkan::RenderTarget::aliasAttachment( const Attachment& source ) {
if ( this->views == 0 ) this->views = source.views.size(); // Keep view count consistent
size_t index = attachments.size();
auto& attachment = attachments.emplace_back();
// 1. Copy the descriptor and mark it as aliased!
// This prevents RenderTarget::destroy from double-freeing the memory,
// and prevents RenderTarget::initialize from trying to re-allocate it.
attachment.descriptor = source.descriptor;
attachment.descriptor.aliased = true;
// 2. Copy the Vulkan resource handles
attachment.image = source.image;
attachment.view = source.view;
attachment.framebufferView = source.framebufferView;
attachment.views = source.views; // Copy the vector of image views
// 3. Copy the pipeline states
attachment.blendState = source.blendState;
// 4. Copy memory references (safe because aliased == true)
attachment.mem = source.mem;
attachment.allocation = source.allocation;
attachment.allocationInfo = source.allocationInfo;
return index;
}
void ext::vulkan::RenderTarget::initialize( Device& device ) {
// Bind
this->device = &device;
@ -254,6 +282,11 @@ void ext::vulkan::RenderTarget::initialize( Device& device ) {
description.finalLayout = ext::vulkan::Texture::remapRenderpassLayout( attachment.descriptor.layout );
description.flags = 0;
if ( attachment.descriptor.aliased && !isSwapchain ) {
description.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
description.initialLayout = attachment.descriptor.layout;
}
attachments.emplace_back(description);
}
@ -438,6 +471,7 @@ void ext::vulkan::RenderTarget::initialize( Device& device ) {
frameBufferCreateInfo.width = width;
frameBufferCreateInfo.height = height;
frameBufferCreateInfo.layers = 1;
// Create the framebuffer
VK_CHECK_RESULT(vkCreateFramebuffer( device, &frameBufferCreateInfo, nullptr, &framebuffers[frame]));
VK_REGISTER_HANDLE( framebuffers[frame] );

View File

@ -520,9 +520,17 @@ void ext::vulkan::tick() {
auto& scene = uf::scene::getCurrentScene();
auto/*&*/ graph = scene.getGraph();
for ( auto entity : graph ) {
if ( entity->hasComponent<uf::Graphic>() ) {
ext::vulkan::Graphic& graphic = entity->getComponent<uf::Graphic>();
if ( graphic.initialized || !graphic.process || graphic.initialized ) continue;
if ( entity->hasComponent<ext::vulkan::Graphics>() ) {
auto& graphics = entity->getComponent<ext::vulkan::Graphics>();
for ( auto& [ _, graphic ] : graphics ) {
if ( graphic.initialized || !graphic.process ) continue;
graphic.update();
ext::vulkan::states::rebuild = true;
}
}
if ( entity->hasComponent<ext::vulkan::Graphic>() ) {
auto& graphic = entity->getComponent<ext::vulkan::Graphic>();
if ( graphic.initialized || !graphic.process ) continue;
graphic.update();
ext::vulkan::states::rebuild = true;
}

View File

@ -1,64 +0,0 @@
#if UF_USE_UNUSED
#include <uf/utils/math/collision.h>
#include <uf/utils/math/collision/gjk.h>
#include <uf/utils/math/collision/boundingbox.h>
#include <uf/utils/math/collision/sphere.h>
#include <uf/utils/math/collision/mesh.h>
#include <uf/utils/math/collision/modular.h>
#include <iostream>
uf::Collider::~Collider() {
this->clear();
}
void uf::Collider::clear() {
for ( pod::Collider* pointer : this->m_container ) delete pointer;
this->m_container.clear();
}
void uf::Collider::add( pod::Collider* pointer ) {
this->m_container.push_back(pointer);
}
uf::Collider::container_t& uf::Collider::getContainer() {
return this->m_container;
}
const uf::Collider::container_t& uf::Collider::getContainer() const {
return this->m_container;
}
std::size_t uf::Collider::getSize() const {
return this->m_container.size();
}
uf::stl::vector<pod::Collider::Manifold> uf::Collider::intersects( const uf::Collider& body, bool smart ) const {
uf::stl::vector<pod::Collider::Manifold> manifolds;
manifolds.reserve( this->m_container.size() * body.m_container.size() );
for ( const pod::Collider* pointer : body.m_container ) {
uf::stl::vector<pod::Collider::Manifold> result = this->intersects( *pointer, smart );
manifolds.insert( manifolds.end(), result.begin(), result.end() );
}
return manifolds;
}
uf::stl::vector<pod::Collider::Manifold> uf::Collider::intersects( const pod::Collider& body, bool smart ) const {
// smart = false;
uf::stl::vector<pod::Collider::Manifold> manifolds;
for ( const pod::Collider* pointer : this->m_container ) {
pod::Collider::Manifold& manifold = manifolds.emplace_back();
if ( !pointer ) continue;
if ( smart && pointer->type() == body.type() ) {
if ( pointer->type() == "BoundingBox" ) {
const uf::BoundingBox& a = *((const uf::BoundingBox*) pointer);
const uf::BoundingBox& b = *((const uf::BoundingBox*) &body);
manifold = a.intersects(b);
} else if ( pointer->type() == "Sphere" ) {
const uf::SphereCollider& a = *((const uf::SphereCollider*) pointer);
const uf::SphereCollider& b = *((const uf::SphereCollider*) &body);
manifold = a.intersects(b);
}
else
manifold = pointer->intersects( body );
} else manifold = pointer->intersects( body );
}
return manifolds;
}
#endif

View File

@ -1,5 +1,14 @@
#include <uf/utils/math/physics/common.h>
namespace impl {
void updateStaticBody( pod::PhysicsBody& body ) {
if ( !body.isStatic ) return;
body.bounds = impl::computeAABB( body );
if ( body.world ) body.world->staticBvh.dirty = true;
}
}
// create ID from pointers
uint64_t impl::makePairKey( const pod::PhysicsBody& a, const pod::PhysicsBody& b ) {
uint64_t lhs = reinterpret_cast<uint64_t>(&a);
@ -19,6 +28,7 @@ void impl::wakeBody( pod::PhysicsBody& body ) {
}
body.activity.awake = true;
if ( body.isStatic ) impl::updateStaticBody( body );
}
void impl::sleepBody( pod::PhysicsBody& body ) {
bool wasAsleep = !body.activity.awake;
@ -64,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;
}
@ -102,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
@ -413,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;
}
@ -537,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
@ -717,12 +733,15 @@ 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: {
// return impl::transformAabbToWorld( body.collider.aabb, *body.transform );
case pod::ShapeType::AABB:
case pod::ShapeType::OBB: {
return impl::transformAabbToWorld( body.collider.aabb, transform );
/*
return {
transform.position + body.collider.aabb.min,
transform.position + body.collider.aabb.max,
};
*/
} break;
case pod::ShapeType::SPHERE: {
return {
@ -737,10 +756,7 @@ pod::AABB impl::computeAABB( const pod::PhysicsBody& body ) {
case pod::ShapeType::MESH:
case pod::ShapeType::CONVEX_HULL: {
if ( body.collider.mesh.bvh && !body.collider.mesh.bvh->bounds.empty() )
return {
transform.position + body.collider.mesh.bvh->bounds[0].min,
transform.position + body.collider.mesh.bvh->bounds[0].max,
};
return impl::transformAabbToWorld( body.collider.mesh.bvh->bounds[0], transform );
const auto& meshData = *body.collider.mesh.mesh;
pod::AABB bounds = { { FLT_MAX, FLT_MAX, FLT_MAX }, { -FLT_MAX, -FLT_MAX, -FLT_MAX } };
for ( const auto& view : meshData.buffer_views ) impl::computeConvexHullAABB( view, view["position"], bounds );
@ -808,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

@ -0,0 +1,141 @@
#include <uf/utils/math/physics/common.h>
#include <uf/utils/math/physics/narrowphase.h>
#include <uf/engine/scene/scene.h>
#include <uf/engine/graph/graph.h>
namespace {
// define a struct for a line because I hate hate hate tuple syntax
struct Line {
pod::Vector3f start = {};
pod::Vector3f end = {};
pod::Vector4f color = { 1, 1, 1, 1 };
float ttl = 0;
};
uf::stl::vector<impl::Vertex> lines;
uf::stl::unordered_map<size_t, Line> transientLines;
size_t getHash( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color, const pod::PhysicsBody* a, const pod::PhysicsBody* b ) {
size_t hash = 0;
/*
int qx = static_cast<int>(start.x * 10.0f);
int qy = static_cast<int>(start.y * 10.0f);
int qz = static_cast<int>(start.z * 10.0f);
uf::hash(hash, a, b, qx, qy, qz);
*/
uf::hash(hash, start, end, color);
return hash;
}
}
UF_VERTEX_DESCRIPTOR(impl::Vertex,
UF_VERTEX_DESCRIPTION(impl::Vertex, R32G32B32_SFLOAT, position)
UF_VERTEX_DESCRIPTION(impl::Vertex, R32G32B32A32_SFLOAT, color)
)
void impl::addLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color ) {
::lines.emplace_back( impl::Vertex{ start, color } );
::lines.emplace_back( impl::Vertex{ end, color } );
}
void impl::addTransientLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color, const pod::PhysicsBody* a, const pod::PhysicsBody* b ) {
::transientLines[::getHash( start, end, color, a, b )] = Line{ start, end, color, 1.0f };
}
void impl::drawManifold( const pod::Manifold& manifold ) {
for ( auto& contact : manifold.points ) {
auto& start = contact.point;
auto end = contact.point + (contact.normal * MIN(contact.penetration, 0.1f));
impl::addTransientLine( start, end, pod::Vector4f{ 1, 0, 0, 1 }, manifold.a, manifold.b );
}
}
void impl::drawBody( const pod::PhysicsBody& body ) {
switch( body.collider.type ) {
case pod::ShapeType::AABB:
impl::drawAabb( body );
break;
case pod::ShapeType::OBB:
impl::drawObb( body );
break;
case pod::ShapeType::SPHERE:
impl::drawSphere( body );
break;
case pod::ShapeType::CAPSULE:
impl::drawCapsule( body );
break;
case pod::ShapeType::PLANE:
impl::drawPlane( body );
break;
case pod::ShapeType::TRIANGLE:
impl::drawTriangle( body );
break;
case pod::ShapeType::MESH:
impl::drawMesh( body );
break;
case pod::ShapeType::CONVEX_HULL:
impl::drawHull( body );
break;
}
}
void impl::draw( const pod::World& world, float dt ) {
if ( world.bodies.empty() ) return;
::lines.clear();
for ( auto* body : world.bodies ) impl::drawBody( *body );
for ( auto it = ::transientLines.begin(); it != ::transientLines.end(); ) {
auto& line = it->second;
if ( line.ttl <= 0 ) it = ::transientLines.erase( it );
else {
impl::addLine( line.start, line.end, line.color * pod::Vector4f{ 1, 1, 1, line.ttl } );
line.ttl -= dt;
++it;
}
}
if ( ::lines.empty() ) return;
uf::Mesh mesh;
mesh.bind<impl::Vertex>();
mesh.insertVertices<impl::Vertex>(::lines);
auto& scene = uf::scene::getCurrentScene();
auto& graphics = scene.getComponent<uf::renderer::Graphics>();
auto& graphic = graphics["physics"];
if ( !graphic.initialized ) {
graphic.device = &uf::renderer::device;
graphic.material.device = &uf::renderer::device;
graphic.descriptor.depth.test = false;
graphic.descriptor.depth.write = false;
graphic.descriptor.renderTarget = 1; // "forward";
graphic.descriptor.topology = uf::renderer::enums::PrimitiveTopology::LINE_LIST;
graphic.descriptor.fill = uf::renderer::enums::PolygonMode::LINE;
graphic.descriptor.lineWidth = 4;
uf::stl::string vertexShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/base/line/vert.spv");
uf::stl::string fragmentShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/base/line/frag.spv");
graphic.material.metadata.autoInitializeUniformBuffers = false;
graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX);
graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT);
graphic.material.metadata.autoInitializeUniformBuffers = true;
auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent<pod::Graph::Storage>();
// vertex shader
{
auto& shader = graphic.material.getShader("vertex");
shader.aliasBuffer( storage.buffers.camera );
}
graphic.initialize();
graphic.initializeMesh( mesh );
UF_MSG_DEBUG("Initialized graphic");
} else {
bool rebuild = graphic.updateMesh( mesh );
if ( rebuild ) uf::renderer::states::rebuild = true;
}
}

View File

@ -68,6 +68,8 @@ void uf::physics::tick( pod::World& world, float dt ) {
else uf::physics::step( world, uf::physics::timescale );
accumulator -= uf::physics::timescale;
}
if ( uf::physics::settings.debugDraw ) impl::draw( world, dt );
}
void uf::physics::terminate() {
uf::physics::terminate( uf::scene::getCurrentScene() );
@ -125,7 +127,7 @@ void uf::physics::step( pod::World& world, float dt ) {
}
// build islands from overlaps
uf::stl::vector<pod::Island> islands;
STATIC_THREAD_LOCAL(uf::stl::vector<pod::Island>, islands);
impl::buildIslands( pairs, bodies, islands );
if ( uf::physics::settings.warmupSolver ) impl::prepareManifoldCache( uf::physics::settings.manifoldsCache, islands, bodies );
@ -134,8 +136,8 @@ void uf::physics::step( pod::World& world, float dt ) {
//#pragma omp parallel for schedule(dynamic)
auto tasks = uf::thread::schedule(true);
for ( auto& island : islands ) tasks.queue([&]{
STATIC_THREAD_LOCAL(uf::stl::vector<pod::Manifold>, manifolds);
manifolds.reserve(uf::physics::settings.reserveCount);
auto& manifolds = island.manifolds;
manifolds.clear();
// sleeping island, skip (asleep islands shouldn't ever be in here)
if ( !island.awake ) return;
@ -193,7 +195,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 );
@ -203,6 +205,14 @@ void uf::physics::step( pod::World& world, float dt ) {
if ( uf::physics::settings.warmupSolver ) impl::pruneManifoldCache( uf::physics::settings.manifoldsCache );
if ( uf::physics::settings.debugDraw ) {
for ( auto& island : islands ) {
for ( auto& manifold : island.manifolds ) {
impl::drawManifold( manifold );
}
}
}
for ( auto* b : bodies ) {
if ( b->isStatic ) continue;
impl::snapVelocity( *b, dt );
@ -260,7 +270,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);
@ -289,6 +300,13 @@ void uf::physics::updateInertia( pod::PhysicsBody& body ) {
case pod::ShapeType::CONVEX_HULL: {
const auto& bvh = *body.collider.mesh.bvh;
#if 1
pod::Vector3f dims = (body.bounds.max - body.bounds.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);
body.inertiaTensor = uf::vector::max( body.inertiaTensor, { EPS, EPS, EPS } );
body.inverseInertiaTensor = 1.0f / body.inertiaTensor;
#else
pod::Matrix3f inertia = {};
float totalVolume = 0.0f;
@ -337,6 +355,7 @@ void uf::physics::updateInertia( pod::PhysicsBody& body ) {
body.inertiaTensor = { inertia(0,0), inertia(1,1), inertia(2,2) };
body.inverseInertiaTensor = 1.0f / body.inertiaTensor;
}
#endif
} break;
// to-do: add others
default: {
@ -344,11 +363,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 +388,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,9 +469,11 @@ 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 );
return body;
}
@ -531,6 +598,7 @@ pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world
}
pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world, const pod::PhysicsBody* body, float maxDistance ) {
pod::RayQuery rayHit;
rayHit.invoker = body;
rayHit.contact.penetration = maxDistance;
auto& dynamicBvh = world.dynamicBvh;
@ -548,6 +616,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;
@ -555,6 +624,8 @@ pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world
case pod::ShapeType::CONVEX_HULL: impl::rayHull( ray, *b, rayHit ); break;
}
}
if ( uf::physics::settings.debugDraw ) impl::drawRay( ray, rayHit );
return rayHit;
}

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 );
@ -72,4 +75,25 @@ bool impl::aabbMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
bool impl::aabbHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( AABB, CONVEX_HULL );
REVERSE_COLLIDER( a, b, impl::hullAabb );
}
void impl::drawAabb( const pod::PhysicsBody& body ) {
auto& aabb = body.bounds;
pod::Vector3f corners[8] = {
{aabb.min.x, aabb.min.y, aabb.min.z}, {aabb.max.x, aabb.min.y, aabb.min.z},
{aabb.max.x, aabb.max.y, aabb.min.z}, {aabb.min.x, aabb.max.y, aabb.min.z},
{aabb.min.x, aabb.min.y, aabb.max.z}, {aabb.max.x, aabb.min.y, aabb.max.z},
{aabb.max.x, aabb.max.y, aabb.max.z}, {aabb.min.x, aabb.max.y, aabb.max.z}
};
// bottom face
impl::addLine( corners[0], corners[1] ); impl::addLine( corners[1], corners[2] );
impl::addLine( corners[2], corners[3] ); impl::addLine( corners[3], corners[0] );
// top face
impl::addLine( corners[4], corners[5] ); impl::addLine( corners[5], corners[6] );
impl::addLine( corners[6], corners[7] ); impl::addLine( corners[7], corners[4] );
// vertical edges
impl::addLine( corners[0], corners[4] ); impl::addLine( corners[1], corners[5] );
impl::addLine( corners[2], corners[6] ); impl::addLine( corners[3], corners[7] );
}

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;
@ -99,4 +103,27 @@ bool impl::capsuleMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, po
bool impl::capsuleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( CAPSULE, CONVEX_HULL );
REVERSE_COLLIDER( a, b, impl::hullCapsule );
}
void impl::drawCapsule( const pod::PhysicsBody& body ) {
float radius = body.collider.capsule.radius;
auto transform = impl::getTransform(body);
auto [p1, p2] = impl::getCapsuleSegment(body);
pod::Vector3f up = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0, 1, 0});
pod::Vector3f right = uf::vector::normalize(impl::computeTangent(up));
pod::Vector3f forward = uf::vector::cross(up, right);
pod::Vector3f rightOffset = right * radius;
pod::Vector3f forwardOffset = forward * radius;
impl::addLine( p1 + rightOffset, p2 + rightOffset );
impl::addLine( p1 - rightOffset, p2 - rightOffset );
impl::addLine( p1 + forwardOffset, p2 + forwardOffset );
impl::addLine( p1 - forwardOffset, p2 - forwardOffset );
impl::addLine( p1 + rightOffset, p1 - rightOffset );
impl::addLine( p1 + forwardOffset, p1 - forwardOffset );
impl::addLine( p2 + rightOffset, p2 - rightOffset );
impl::addLine( p2 + forwardOffset, p2 - forwardOffset );
}

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 );
@ -76,4 +80,20 @@ bool impl::hullHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
hit = true;
}
return hit;
}
void impl::drawHull( const pod::PhysicsBody& body ) {
const uf::Mesh* meshData = body.collider.convexHull.mesh;
if ( !meshData ) return;
size_t totalTriangles = 0;
for ( const auto& view : meshData->buffer_views ) totalTriangles += view.index.count / 3;
for ( size_t i = 0; i < totalTriangles; ++i ) {
auto tri = impl::fetchTriangle(*meshData, i, body);
impl::addLine( tri.points[0], tri.points[1] );
impl::addLine( tri.points[1], tri.points[2] );
impl::addLine( tri.points[2], tri.points[0] );
}
}

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 );
@ -162,4 +185,20 @@ bool impl::meshHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::
hit = true;
}
return hit;
}
void impl::drawMesh( const pod::PhysicsBody& body ) {
const uf::Mesh* meshData = body.collider.mesh.mesh;
if ( !meshData ) return;
size_t totalTriangles = 0;
for ( const auto& view : meshData->buffer_views ) totalTriangles += view.index.count / 3;
for ( size_t i = 0; i < totalTriangles; ++i ) {
auto tri = impl::fetchTriangle(*meshData, i, body);
impl::addLine( tri.points[0], tri.points[1] );
impl::addLine( tri.points[1], tri.points[2] );
impl::addLine( tri.points[2], tri.points[0] );
}
}

View File

@ -0,0 +1,302 @@
#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 );
}
void impl::drawObb( const pod::PhysicsBody& body ) {
auto& aabb = body.collider.aabb;
pod::Vector3f corners[8] = {
{aabb.min.x, aabb.min.y, aabb.min.z}, {aabb.max.x, aabb.min.y, aabb.min.z},
{aabb.max.x, aabb.max.y, aabb.min.z}, {aabb.min.x, aabb.max.y, aabb.min.z},
{aabb.min.x, aabb.min.y, aabb.max.z}, {aabb.max.x, aabb.min.y, aabb.max.z},
{aabb.max.x, aabb.max.y, aabb.max.z}, {aabb.min.x, aabb.max.y, aabb.max.z}
};
auto transform = impl::getTransform(body);
FOR_EACH( 8, {
corners[i] = uf::transform::apply(transform, corners[i]);
});
// bottom face
impl::addLine( corners[0], corners[1] ); impl::addLine( corners[1], corners[2] );
impl::addLine( corners[2], corners[3] ); impl::addLine( corners[3], corners[0] );
// top face
impl::addLine( corners[4], corners[5] ); impl::addLine( corners[5], corners[6] );
impl::addLine( corners[6], corners[7] ); impl::addLine( corners[7], corners[4] );
// vertical edges
impl::addLine( corners[0], corners[4] ); impl::addLine( corners[1], corners[5] );
impl::addLine( corners[2], corners[6] ); impl::addLine( corners[3], corners[7] );
}

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);
@ -59,4 +63,25 @@ bool impl::planeMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:
bool impl::planeHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( PLANE, CONVEX_HULL );
REVERSE_COLLIDER( a, b, impl::hullPlane );
}
void impl::drawPlane( const pod::PhysicsBody& body ) {
auto transform = impl::getTransform(body);
pod::Vector3f right = uf::quaternion::rotate(transform.orientation, pod::Vector3f{1, 0, 0});
pod::Vector3f forward = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0, 0, 1});
float size = 10.0f;
pod::Vector3f p0 = transform.position + (right * size) + (forward * size);
pod::Vector3f p1 = transform.position - (right * size) + (forward * size);
pod::Vector3f p2 = transform.position - (right * size) - (forward * size);
pod::Vector3f p3 = transform.position + (right * size) - (forward * size);
impl::addLine( p0, p1 );
impl::addLine( p1, p2 );
impl::addLine( p2, p3 );
impl::addLine( p3, p0 );
impl::addLine( p0, p2 );
impl::addLine( p1, p3 );
}

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;
@ -265,4 +299,11 @@ bool impl::rayHull( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue
}
return rayHit.hit;
}
void impl::drawRay( const pod::Ray& ray, const pod::RayQuery& query ) {
auto& start = ray.origin;
auto end = ray.origin + ray.direction * query.contact.penetration;
impl::addTransientLine( start, end, query.hit ? pod::Vector4f{ 0, 1, 0, 1 } : pod::Vector4f{ 1, 0, 0, 1 }, query.invoker, query.body );
}

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 );
@ -61,4 +65,36 @@ bool impl::sphereMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod
bool impl::sphereHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( SPHERE, CONVEX_HULL );
REVERSE_COLLIDER( a, b, impl::hullSphere );
}
void impl::drawSphere( const pod::PhysicsBody& body ) {
float radius = body.collider.sphere.radius;
auto transform = impl::getTransform(body);
const int segments = 16;
const float PI = 3.14159265359f;
const float angleIncrement = (2.0f * PI) / segments;
for ( auto i = 0; i < segments; ++i ) {
float theta1 = i * angleIncrement;
float theta2 = (i + 1) * angleIncrement;
float c1 = std::cos(theta1) * radius;
float s1 = std::sin(theta1) * radius;
float c2 = std::cos(theta2) * radius;
float s2 = std::sin(theta2) * radius;
pod::Vector3f xy1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, s1, 0.0f});
pod::Vector3f xy2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, s2, 0.0f});
pod::Vector3f xz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, 0.0f, s1});
pod::Vector3f xz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, 0.0f, s2});
pod::Vector3f yz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c1, s1});
pod::Vector3f yz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c2, s2});
impl::addLine( transform.position + xy1, transform.position + xy2 );
impl::addLine( transform.position + xz1, transform.position + xz2 );
impl::addLine( transform.position + yz1, transform.position + yz2 );
}
}

View File

@ -1,7 +1,41 @@
#include <uf/utils/math/physics/common.h>
#include <uf/utils/math/physics/narrowphase.h>
namespace impl {
bool triangleGeneric( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
const auto& tri = a;
const auto& body = b;
pod::Simplex simplex;
if ( !impl::gjk( tri, body, simplex ) ) return false;
auto result = impl::epa( tri, body, simplex );
if ( !impl::generateClippingManifold( tri, body, result, manifold ) ) return false;
return true;
}
bool triangleGeneric( const pod::TriangleWithNormal& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
pod::PhysicsBody tri = {};
tri.collider.type = pod::ShapeType::TRIANGLE;
tri.collider.triangle = a;
const auto& body = b;
return triangleGeneric( tri, body, manifold );
}
}
bool impl::triangleTriangle( const pod::TriangleWithNormal& a, const pod::TriangleWithNormal& b, pod::Manifold& manifold ) {
if ( uf::physics::settings.useGjk ) {
pod::PhysicsBody A = {};
A.collider.type = pod::ShapeType::TRIANGLE;
A.collider.triangle = a;
pod::PhysicsBody B = {};
B.collider.type = pod::ShapeType::TRIANGLE;
B.collider.triangle = b;
return impl::triangleGeneric( A, B, manifold );
}
size_t axes = 0;
pod::Vector3f axesBuffer[12];
axesBuffer[axes++] = impl::triangleNormal(a);
@ -88,6 +122,8 @@ bool impl::triangleTriangle( const pod::TriangleWithNormal& a, const pod::Triang
}
bool impl::triangleAabb( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold ) {
if ( uf::physics::settings.useGjk ) return impl::triangleGeneric( tri, body, manifold );
const auto& aabb = body.bounds;
// box center and half extents
@ -153,7 +189,82 @@ 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 ) {
if ( uf::physics::settings.useGjk ) return impl::triangleGeneric( tri, body, 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 ) {
if ( uf::physics::settings.useGjk ) return impl::triangleGeneric( tri, body, manifold );
const auto& sphere = body;
float r = sphere.collider.sphere.radius;
@ -183,6 +294,8 @@ bool impl::triangleSphere( const pod::TriangleWithNormal& tri, const pod::Physic
}
// to-do: implement
bool impl::trianglePlane( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold ) {
if ( uf::physics::settings.useGjk ) return impl::triangleGeneric( tri, body, manifold );
const auto& plane = body;
auto normal = plane.collider.plane.normal;
float d = plane.collider.plane.offset;
@ -229,6 +342,8 @@ bool impl::trianglePlane( const pod::TriangleWithNormal& tri, const pod::Physics
return hit;
}
bool impl::triangleCapsule( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold ) {
if ( uf::physics::settings.useGjk ) return impl::triangleGeneric( tri, body, manifold );
const auto& capsule = body;
float r = capsule.collider.capsule.radius;
@ -268,22 +383,32 @@ bool impl::triangleHull( const pod::TriangleWithNormal& tri, const pod::PhysicsB
bool impl::triangleTriangle( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( TRIANGLE, TRIANGLE );
if ( uf::physics::settings.useGjk ) return impl::triangleGeneric( a, b, manifold );
return impl::triangleTriangle( a.collider.triangle, b.collider.triangle, manifold );
}
bool impl::triangleAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( TRIANGLE, AABB );
if ( uf::physics::settings.useGjk ) return impl::triangleGeneric( a, b, manifold );
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 );
if ( uf::physics::settings.useGjk ) return impl::triangleGeneric( a, b, manifold );
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 );
if ( uf::physics::settings.useGjk ) return impl::triangleGeneric( a, b, manifold );
return impl::triangleSphere( a.collider.triangle, b, manifold );
}
bool impl::trianglePlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( TRIANGLE, PLANE );
if ( uf::physics::settings.useGjk ) return impl::triangleGeneric( a, b, manifold );
return impl::trianglePlane( a.collider.triangle, b, manifold );
}
bool impl::triangleCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( TRIANGLE, CAPSULE );
if ( uf::physics::settings.useGjk ) return impl::triangleGeneric( a, b, manifold );
return impl::triangleCapsule( a.collider.triangle, b, manifold );
}
bool impl::triangleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
@ -296,4 +421,17 @@ bool impl::triangleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, p
auto result = impl::epa( tri, hull, simplex );
if ( !impl::generateClippingManifold( tri, hull, result, manifold ) ) return false;
return true;
}
void impl::drawTriangle( const pod::PhysicsBody& body ) {
const auto& tri = body.collider.triangle;
auto transform = impl::getTransform(body);
pod::Vector3f v0 = uf::transform::apply(transform, tri.points[0]);
pod::Vector3f v1 = uf::transform::apply(transform, tri.points[1]);
pod::Vector3f v2 = uf::transform::apply(transform, tri.points[2]);
impl::addLine( v0, v1 );
impl::addLine( v1, v2 );
impl::addLine( v2, v0 );
}

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