added forward+ rendermode, added debug draw for physics system (revealed my meshAabb/neshObb ocntact points were wrong)

This commit is contained in:
ecker 2026-05-18 21:11:42 -05:00
parent 6b07173804
commit a1f62ef0c3
46 changed files with 684 additions and 69 deletions

View File

@ -8,7 +8,7 @@
"useLightmaps": false,
"max": 32,
"shadows": {
"enabled": true,
"enabled": false,
"update": 4,
"max": 16,
"samples": 2
@ -110,7 +110,7 @@
"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
},
@ -121,7 +121,7 @@
"hdr": true,
"vxgi": false, // to-do: fix issues
"culling": false,
"bloom": true,
"bloom": false,
"dof": false,
"rt": false,
"fsr": false,
@ -336,10 +336,12 @@
"discord": { "enabled": false }
},
"physics": {
"warmup solver": true,
"warmup solver": false,
"block solver": true,
"psg solver": true,
"gjk": true,
"correction percent": 0.0,
"gjk": false,
"debug draw": false,
"fixed step": true,
"substeps": 4,
"solver iterations": 10

View File

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

View File

@ -1,4 +1,5 @@
{
"assets": [],
"behaviors": [
"SoundEmitterBehavior"
],

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

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

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

@ -243,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 };
};
@ -251,6 +252,8 @@ namespace pod {
bool awake = true;
uf::stl::vector<uint32_t> indices;
pod::BVH::pairs_t pairs;
uf::stl::vector<pod::Manifold> manifolds;
};
struct PhysicsSettings {
@ -259,6 +262,7 @@ namespace pod {
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

View File

@ -10,4 +10,6 @@ namespace impl {
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

@ -10,4 +10,6 @@ namespace impl {
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

@ -10,4 +10,6 @@ namespace impl {
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

@ -10,4 +10,6 @@ namespace impl {
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

@ -10,4 +10,6 @@ namespace impl {
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

@ -10,4 +10,6 @@ namespace impl {
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

@ -13,4 +13,6 @@ namespace impl {
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

@ -10,4 +10,6 @@ namespace impl {
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

@ -18,4 +18,6 @@ namespace impl {
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

@ -95,13 +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 = pointer->position + uf::quaternion::rotate(pointer->orientation, combined.position);
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

@ -200,14 +200,16 @@ void UF_API uf::load( ext::json::Value& json ) {
// Physics settings
{
auto& confingEnginePhysicsJson = json["engine"]["physics"];
uf::physics::settings.warmupSolver = confingEnginePhysicsJson["warmup solver"].as(uf::physics::settings.warmupSolver);
uf::physics::settings.blockContactSolver = confingEnginePhysicsJson["block solver"].as(uf::physics::settings.blockContactSolver);
uf::physics::settings.psgContactSolver = confingEnginePhysicsJson["psg solver"].as(uf::physics::settings.psgContactSolver);
uf::physics::settings.useGjk = confingEnginePhysicsJson["gjk"].as(uf::physics::settings.useGjk);
uf::physics::settings.fixedStep = confingEnginePhysicsJson["fixed step"].as(uf::physics::settings.fixedStep);
uf::physics::settings.substeps = confingEnginePhysicsJson["substeps"].as(uf::physics::settings.substeps);
uf::physics::settings.solverIterations = confingEnginePhysicsJson["solver iterations"].as(uf::physics::settings.solverIterations);
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

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

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

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

@ -735,7 +735,7 @@ pod::AABB impl::computeAABB( const pod::PhysicsBody& body ) {
switch ( body.collider.type ) {
case pod::ShapeType::AABB:
case pod::ShapeType::OBB: {
return impl::transformAabbToWorld( body.collider.aabb, *body.transform );
return impl::transformAabbToWorld( body.collider.aabb, transform );
/*
return {
transform.position + body.collider.aabb.min,
@ -756,7 +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 impl::transformAabbToWorld( body.collider.mesh.bvh->bounds[0], *body.transform );
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 );

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;
@ -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 );
@ -290,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;
@ -338,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: {
@ -455,6 +473,7 @@ pod::PhysicsBody& uf::physics::create( pod::World& world, uf::Object& object, co
body.collider.type = pod::ShapeType::OBB;
body.collider.aabb = aabb;
body.bounds = impl::computeAABB( body );
uf::physics::updateInertia( body );
return body;
}
@ -579,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;
@ -604,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

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

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

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

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

@ -274,4 +274,29 @@ bool impl::obbMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::M
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

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

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

@ -65,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
@ -154,7 +190,9 @@ bool impl::triangleAabb( const pod::TriangleWithNormal& tri, const pod::PhysicsB
return true;
}
bool impl::triangleObb( const pod::TriangleWithNormal& tri, const pod::PhysicsBody& body, pod::Manifold& manifold ) {
auto tB = impl::getTransform( body );
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;
@ -225,6 +263,8 @@ bool impl::triangleObb( const pod::TriangleWithNormal& tri, const pod::PhysicsBo
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;
@ -254,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;
@ -300,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;
@ -339,26 +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 ) {
@ -371,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 );
}