From 02d98c1176e064da256f962e44c86d0ded983945 Mon Sep 17 00:00:00 2001 From: ecker Date: Thu, 11 Jun 2026 22:24:16 -0500 Subject: [PATCH] more neurotic changes, triangle IDs are dispatched in collision events, proper footstep sounds (to-do: make use of the physics material), Lua script can handle footsteps too (probably shouldn't because Lua invocations are kinda slow on dreamcast last I checked due to lack of LuaJIT), some other things I forgot already --- bin/data/config.json | 2 +- .../entities/gui/mainmenu/scripts/menu.lua | 1 + bin/data/entities/scripts/player.lua | 156 ++++++++++++++++-- engine/inc/uf/engine/graph/graph.h | 10 ++ engine/inc/uf/ext/lua/lua.inl | 9 + engine/inc/uf/ext/vulkan/device.h | 4 +- engine/inc/uf/utils/math/physics/structs.h | 7 + engine/inc/uf/utils/math/vector/pod.inl | 18 +- engine/inc/uf/utils/mesh/mesh.h | 33 ++-- engine/src/engine/ext/ext.cpp | 5 + engine/src/engine/ext/gui/behavior.cpp | 4 +- engine/src/engine/ext/player/behavior.cpp | 123 ++++++++++++-- engine/src/engine/ext/player/behavior.h | 15 +- engine/src/engine/graph/animation.cpp | 6 +- engine/src/engine/graph/graph.cpp | 39 ++++- engine/src/ext/audio/wav.cpp | 4 +- engine/src/ext/lua/usertypes/graph.cpp | 63 +++++++ engine/src/ext/lua/usertypes/mesh.cpp | 48 +++++- engine/src/ext/lua/usertypes/physics.cpp | 69 +++++++- engine/src/ext/valve/vpk.cpp | 1 + engine/src/ext/vulkan/device.cpp | 4 +- engine/src/ext/vulkan/graphic.cpp | 2 +- engine/src/ext/vulkan/vulkan.cpp | 2 +- engine/src/ext/xatlas/xatlas.cpp | 6 +- .../src/utils/math/physics/broadphase/bvh.cpp | 8 +- engine/src/utils/math/physics/common.cpp | 4 +- .../math/physics/constraints/contact.cpp | 30 +++- .../utils/math/physics/narrowphase/epa.cpp | 4 +- .../utils/math/physics/narrowphase/gjk.cpp | 2 +- .../utils/math/physics/narrowphase/hull.cpp | 7 + .../utils/math/physics/narrowphase/mesh.cpp | 20 +++ .../utils/math/physics/narrowphase/ray.cpp | 2 + engine/src/utils/mesh/mesh.cpp | 37 +++-- 33 files changed, 628 insertions(+), 117 deletions(-) create mode 100644 engine/src/ext/lua/usertypes/graph.cpp diff --git a/bin/data/config.json b/bin/data/config.json index 4abfad31..e1447802 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -355,7 +355,7 @@ "ngs": true, "percent": 0.2, "slop": 0.01, // 0.005 - "max": 0.01 // 0.2 + "max": 0.2 // 0.2 }, "debug draw": { "static": false, diff --git a/bin/data/entities/gui/mainmenu/scripts/menu.lua b/bin/data/entities/gui/mainmenu/scripts/menu.lua index 37ae6f3c..a62a996a 100644 --- a/bin/data/entities/gui/mainmenu/scripts/menu.lua +++ b/bin/data/entities/gui/mainmenu/scripts/menu.lua @@ -31,6 +31,7 @@ if not timer:running() then timer:start() end local playSound = function( key ) local url = "/ui/" .. key .. ".wav" + --local url = "valve://sound/ui/" .. key .. ".wav" soundEmitter:callHook("sound:Emit.%UID%", { filename = url }) -- local assetLoader = scene:getComponent("Asset") -- assetLoader:cache(ent:formatHookName("asset:Load.%UID%"), string.resolveURI(url)) diff --git a/bin/data/entities/scripts/player.lua b/bin/data/entities/scripts/player.lua index e1a4d265..847afeee 100644 --- a/bin/data/entities/scripts/player.lua +++ b/bin/data/entities/scripts/player.lua @@ -1,11 +1,12 @@ local ent = ent local scene = entities.currentScene() +local graph = scene:getComponent("Graph") local metadataJson = ent:getComponent("Metadata") local transform = ent:getComponent("Transform") local physicsBody = ent:getComponent("PhysicsBody") local camera = ent:getComponent("Camera") local cameraTransform = camera:getTransform() - +local metadata = ent:getComponent("PlayerBehavior::Metadata") local fixedCamera = metadataJson["camera"]["settings"]["fixed"] -- setup all timers @@ -54,23 +55,28 @@ light.metadata.power = 0 --light.entity:setComponent("Metadata", { light = { power = 0 } }) -- sound emitter -local playSound = function( key, loop ) +local playSound = function( path, loop ) if not loop then loop = false end - local url = "/ui/" .. key .. ".ogg" + local uri = string.resolveURI(path) ent:callHook("sound:Emit.%UID%", { - filename = string.resolveURI(url, metadataJson["system"]["root"]), + filename = uri, spatial = true, streamed = true, volume = "sfx", loop = loop }, 0) end -local stopSound = function( key ) - local url = "/ui/" .. key .. ".ogg" +local stopSound = function( path ) ent:callHook("sound:Stop.%UID%", { - filename = string.resolveURI(url, metadataJson["system"]["root"]) + filename = string.resolveURI(path, metadataJson["system"]["root"]) }, 0) end +local playUiSound = function( key, loop ) + return playSound("/ui/" .. key .. ".ogg", loop) +end +local stopUiSound = function( key ) + return stopSound("/ui/" .. key .. ".ogg", loop) +end local useDistance = 6 local pullDistance = useDistance * 4 @@ -91,7 +97,7 @@ local function tickFlashlight( transform, axes, inputs ) timers.flashlight:reset() light.enabled = (light.metadata.power ~= light.power) light.metadata.power = light.enabled and light.power or 0 - playSound("flashlight") + playUiSound("flashlight") end end @@ -129,7 +135,7 @@ local function onUse( payload ) heldObject.momentum = Vector3f(0,0,0) end - playSound(validUse and "select" or "deny") + playUiSound(validUse and "select" or "deny") end local function tickUse( transform, axes, inputs ) @@ -169,7 +175,7 @@ local function tickGravGun( transform, axes, inputs ) if timers.physcannon:elapsed() > 1.0 then timers.physcannon:reset() - playSound("phys_tooHeavy") + playUiSound("phys_tooHeavy") end end end @@ -196,7 +202,7 @@ local function tickGravGun( transform, axes, inputs ) heldObjectPhysicsBody:enableGravity(true) heldObjectPhysicsBody:applyImpulse( axes.forward * heldObjectPhysicsBody:getMass() * 50 ) - playSound("phys_launch"..math.random(1,4)) + playUiSound("phys_launch"..math.random(1,4)) else -- update rotation if heldObject.rotate then @@ -228,6 +234,125 @@ local function tickGravGun( transform, axes, inputs ) end end +local footstepTimer = 0.0 +local surfaceTypes = { + "chainlink", + "concrete", + "dirt", + "duct", + "grass", + "gravel", + "hardboot_generic", + "ladder", + "metal", + "metalgrate", + "mud", + "sand", + "slosh", + "snow", + "softshoe_generic", + "tile", + "wade", + "wood", + "woodpanel" +} + +local playFootstepSound = function( surface, isCrouching ) + local variant = math.random(1, 4) + local path = "valve://sound/player/footsteps/" .. surface .. tostring(variant) .. ".wav" + local pitch = 0.95 + (math.random() * 0.10) + local vol = 1.0 + + if isCrouching then vol = 0.5 end + + ent:callHook("sound:Emit.%UID%", { + filename = string.resolveURI(path, metadataJson["system"]["root"]), + spatial = true, + streamed = false, + volume = vol, + pitch = pitch, + loop = false + }, 0) +end + +local tickFootsteps = function() + local isWalking = metadata.states.walking + local isRunning = metadata.states.running + local isCrouching = metadata.states.crouching + local isFloored = metadata.states.floored + local isNoclipped = metadata.states.noclipped + + if not isFloored or isNoclipped then return end + + if not isWalking then + footstepTimer = 0.0 + return + end + + footstepTimer = footstepTimer - time.delta() + if footstepTimer > 0.0 then return end + + local surface = "concrete" + local collisionEvents = physicsBody:getCollisionEvents() + for i, event in ipairs( collisionEvents ) do + if event.normal.y <= -0.7 then + local tri = event.featureA or event.featureB + local other = event.a == ent and event.a or event.b + local collider = other:getCollider() + + if tri ~= nil and collider.type == ShapeType.MESH then + local mesh = collider:asMesh() + local drawCommand = mesh:fetchDrawCommand( tri ) + local instance = graph:getInstance( drawCommand.instanceID ) + local materialName = string.lower( graph:getMaterialName( instance.materialID ) ) + for _, key in ipairs(surfaceTypes) do + if string.find(materialName, key) then + surface = key + break + end + end + + break + end + end + end + + playFootstepSound( surface, isCrouching ) + + + if isRunning then + footstepTimer = 0.3 + elseif isCrouching then + footstepTimer = 0.6 + else + footstepTimer = 0.45 + end +end + +--[[ +local tickCollisionEvents = function() + local collisionEvents = physicsBody:getCollisionEvents() + for i, event in ipairs(collisionEvents) do + -- print( event.state, event.a, event.b, event.point, event.normal, event.impulse, event.featureA ) + local tri = event.featureA or event.featureB + local other = event.a == ent and event.a or event.b + local collider = other:getCollider() + -- technically will always return a triangle ID if there's a mesh collider + if tri ~= nil and collider.type == ShapeType.MESH then + local mesh = collider:asMesh() + local drawCommand = mesh:fetchDrawCommand( tri ) + local instance = graph:getInstance( drawCommand.instanceID ) + local material = graph:getMaterial( instance.materialID ) + local materialName = graph:getMaterialName( instance.materialID ) + if not materialName:find("tools/") and event.normal.y < -0.7 then + local soundKey = getSurfaceSound(materialName) + playSound("valve://sound/" .. soundKey .. ".wav", false) + end + end + end +end +]] + -- on tick ent:bind( "tick", function(self) local inControl = scene:globalFindByName("Gui: Menu"):uid() == 0 @@ -263,13 +388,8 @@ ent:bind( "tick", function(self) -- update HOLP tickGravGun( flattenedTransform, axes, inputs ) - -- get collision events ---[[ - local collisionEvents = physicsBody:getCollisionEvents() - for i, event in ipairs(collisionEvents) do - print( event.state, event.a, event.b, event.point, event.normal, event.impulse ) - end -]] + -- play footsteps + tickFootsteps() end ) -- on use diff --git a/engine/inc/uf/engine/graph/graph.h b/engine/inc/uf/engine/graph/graph.h index 5da77ebe..78eeca73 100644 --- a/engine/inc/uf/engine/graph/graph.h +++ b/engine/inc/uf/engine/graph/graph.h @@ -107,6 +107,9 @@ namespace pod { uf::stl::vector shadow2Ds; uf::stl::vector shadowCubes; + // flattened variants + uf::stl::vector flattenedPrimitives; + struct Buffer { uf::renderer::Buffer camera; uf::renderer::Buffer drawCommands; @@ -207,6 +210,13 @@ namespace uf { void UF_API reload( pod::Graph& graph ); void UF_API reload( pod::Graph& graph, pod::Node& node ); void UF_API reload(); + + // access helpers + // to-do: the other storage values (although I don't foresee ever needing more) + uf::stl::string UF_API getMaterialName( pod::Graph& graph, size_t id ); + pod::Material UF_API getMaterial( pod::Graph& graph, size_t id ); + pod::Primitive UF_API getPrimitive( pod::Graph& graph, size_t id ); + pod::Instance UF_API getInstance( pod::Graph& graph, size_t id ); } } diff --git a/engine/inc/uf/ext/lua/lua.inl b/engine/inc/uf/ext/lua/lua.inl index d9faf04f..9c423624 100644 --- a/engine/inc/uf/ext/lua/lua.inl +++ b/engine/inc/uf/ext/lua/lua.inl @@ -60,5 +60,14 @@ namespace {\ #define UF_LUA_REGISTER_USERTYPE_MEMBER(member) UF_NS_GET_LAST(member), &member #define UF_LUA_REGISTER_USERTYPE_MEMBER_FUN(member) UF_NS_GET_LAST(member), UF_LUA_C_FUN(member) +#define UF_LUA_REGISTER_ENUM(type, ...) \ +namespace {\ + static uf::StaticInitialization TOKEN_PASTE(STATIC_INITIALIZATION_, __LINE__)( []{\ + ext::lua::onInitialization( []{\ + ext::lua::state.new_enum(UF_NS_GET_LAST(type), __VA_ARGS__);\ + });\ + });\ +} + #define UF_LUA_C_FUN(x) &x #define UF_LUA_WRAP_FUN(x) UF_LUA_C_FUN(x) \ No newline at end of file diff --git a/engine/inc/uf/ext/vulkan/device.h b/engine/inc/uf/ext/vulkan/device.h index 5ecd92b4..dfdff751 100644 --- a/engine/inc/uf/ext/vulkan/device.h +++ b/engine/inc/uf/ext/vulkan/device.h @@ -107,8 +107,8 @@ namespace ext { struct { struct CommandBuffer { - std::vector commandBuffers; - std::vector fences; + uf::stl::vector commandBuffers; + uf::stl::vector fences; }; uf::stl::unordered_map> commandBuffers; diff --git a/engine/inc/uf/utils/math/physics/structs.h b/engine/inc/uf/utils/math/physics/structs.h index 629bac51..872958dc 100644 --- a/engine/inc/uf/utils/math/physics/structs.h +++ b/engine/inc/uf/utils/math/physics/structs.h @@ -179,6 +179,9 @@ namespace pod { float accumulatedNormalImpulse = 0.0f; float accumulatedTangentImpulse = 0.0f; float accumulatedPseudoImpulse = 0.0f; + + uint32_t featureA = (uint32_t)(-1); + uint32_t featureB = (uint32_t)(-1); }; struct Manifold { @@ -374,9 +377,13 @@ namespace pod { pod::CollisionState state = {}; pod::PhysicsBody* a = NULL; pod::PhysicsBody* b = NULL; + pod::Vector3f point = {}; pod::Vector3f normal = {}; float impulse = 0; + + uint32_t featureA = (uint32_t)(-1); + uint32_t featureB = (uint32_t)(-1); }; } diff --git a/engine/inc/uf/utils/math/vector/pod.inl b/engine/inc/uf/utils/math/vector/pod.inl index 9bef1664..743a58a9 100644 --- a/engine/inc/uf/utils/math/vector/pod.inl +++ b/engine/inc/uf/utils/math/vector/pod.inl @@ -62,13 +62,12 @@ bool uf::vector::equals( const T& left, const T& right ) { if constexpr ( simd_able_v ) { return uf::simd::all( uf::simd::equals( left, right ) ); } -#else +#endif bool result = true; FOR_EACH(T::size, { if ( !(left[i] == right[i]) ) result = false; }); return result; -#endif } template bool uf::vector::notEquals( const T& left, const T& right ) { @@ -76,13 +75,12 @@ bool uf::vector::notEquals( const T& left, const T& right ) { if constexpr ( simd_able_v ) { return uf::simd::all( uf::simd::notEquals( left, right ) ); } -#else +#endif bool result = true; FOR_EACH(T::size, { if ( !(left[i] != right[i]) ) result = false; }); return result; -#endif } template bool uf::vector::less( const T& left, const T& right ) { @@ -90,13 +88,12 @@ bool uf::vector::less( const T& left, const T& right ) { if constexpr ( simd_able_v ) { return uf::simd::all( uf::simd::less( left, right ) ); } -#else +#endif bool result = true; FOR_EACH(T::size, { if ( !(left[i] < right[i]) ) result = false; }); return result; -#endif } template bool uf::vector::lessEquals( const T& left, const T& right ) { @@ -104,13 +101,12 @@ bool uf::vector::lessEquals( const T& left, const T& right ) { if constexpr ( simd_able_v ) { return uf::simd::all( uf::simd::lessEquals( left, right ) ); } -#else +#endif bool result = true; FOR_EACH(T::size, { if ( !(left[i] <= right[i]) ) result = false; }); return result; -#endif } template bool uf::vector::greater( const T& left, const T& right ) { @@ -118,13 +114,12 @@ bool uf::vector::greater( const T& left, const T& right ) { if constexpr ( simd_able_v ) { return uf::simd::all( uf::simd::greater( left, right ) ); } -#else +#endif bool result = true; FOR_EACH(T::size, { if ( !(left[i] > right[i]) ) result = false; }); return result; -#endif } template bool uf::vector::greaterEquals( const T& left, const T& right ) { @@ -132,13 +127,12 @@ bool uf::vector::greaterEquals( const T& left, const T& right ) { if constexpr ( simd_able_v ) { return uf::simd::all( uf::simd::greaterEquals( left, right ) ); } -#else +#endif bool result = true; FOR_EACH(T::size, { if ( !(left[i] >= right[i]) ) result = false; }); return result; -#endif } template bool uf::vector::isValid( const T& v ) { diff --git a/engine/inc/uf/utils/mesh/mesh.h b/engine/inc/uf/utils/mesh/mesh.h index 55ef26c7..7e72314b 100644 --- a/engine/inc/uf/utils/mesh/mesh.h +++ b/engine/inc/uf/utils/mesh/mesh.h @@ -194,22 +194,15 @@ namespace uf { uf::Mesh::Input index; int32_t indirectIndex = -1; - uf::stl::unordered_map attributes; + uf::stl::unordered_map attributes; - bool has( uint32_t hash ) const { - return attributes.count( hash ) > 0; + bool has( const uf::stl::string& name ) const { + return attributes.count( name ) > 0; } - const AttributeView& operator[]( uint32_t hash ) const { - if ( auto it = attributes.find( hash ); it != attributes.end() ) return it->second; - UF_EXCEPTION("invalid view hash: {}", hash); - } - // support legacy code - bool has( const uf::stl::string_view name ) const { - return has( uf::algo::fnv1a( name ) ); - } - const AttributeView& operator[]( const uf::stl::string_view name ) const { - return operator[]( uf::algo::fnv1a( name ) ); + const AttributeView& operator[]( const uf::stl::string& name ) const { + if ( auto it = attributes.find( name ); it != attributes.end() ) return it->second; + UF_EXCEPTION("invalid view name: {}", name); } }; typedef uf::stl::vector views_t; @@ -615,6 +608,10 @@ namespace uf { pod::Triangle UF_API fetchTriangle( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, const uf::Mesh::AttributeView& positions, size_t triID ); pod::TriangleWithNormal UF_API fetchTriangle( const uf::Mesh& mesh, size_t triID ); + const pod::DrawCommand& UF_API fetchDrawCommand( const uf::Mesh& mesh, const uf::Mesh::View& view ); + const pod::DrawCommand& UF_API fetchDrawCommand( const uf::Mesh& mesh, size_t triID ); + const uf::Mesh::View* UF_API fetchView( const uf::Mesh& mesh, size_t& triID ); + template size_t windingOrder( uf::stl::vector& vertices ); template size_t windingOrder( uf::stl::vector& vertices, uf::stl::vector& indices ); template void normals( uf::stl::vector& vertices ); @@ -637,7 +634,7 @@ namespace uf { return uf::mesh::getIndex( indices.data(view.index.first), index ); } template inline U& getIndex( const uf::Mesh::View& view, size_t index ) { - return uf::mesh::getIndex( view, view["indices"_hash], index ); + return uf::mesh::getIndex( view, view["indices"], index ); } template inline U& getIndex( const uf::Mesh::View& view, const uf::stl::string& indices, size_t index ) { return uf::mesh::getIndex( view, view[indices], index ); @@ -657,13 +654,13 @@ namespace uf { return uf::mesh::fetchIndex( indices.data(view.index.first), indices.stride(), index ); } static inline size_t fetchIndex( const uf::Mesh::View& view, size_t index ) { - return uf::mesh::fetchIndex( view, view["indices"_hash], index ); + return uf::mesh::fetchIndex( view, view["indices"], index ); } static inline size_t fetchIndex( const uf::Mesh::View& view, const uf::stl::string& indices, size_t index ) { return uf::mesh::fetchIndex( view, view[indices], index ); } static inline pod::Vector3f fetchVertex( const uf::Mesh::View& view, size_t index ) { - return uf::mesh::fetchVertex( view, view["positions"_hash], index ); + return uf::mesh::fetchVertex( view, view["positions"], index ); } static inline pod::Vector3f fetchVertex( const uf::Mesh::View& view, const uf::stl::string& positions, size_t index ) { return uf::mesh::fetchVertex( view, view[positions], index ); @@ -672,14 +669,14 @@ namespace uf { return uf::mesh::fetchTriangle( view, view[indices], view[positions], triID ); } static inline pod::Triangle fetchTriangle( const uf::Mesh::View& view, size_t triID ) { - return uf::mesh::fetchTriangle( view, view["index"_hash], view["position"_hash], triID ); + return uf::mesh::fetchTriangle( view, view["index"], view["position"], triID ); } static inline void setIndex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, size_t index, size_t value ) { return uf::mesh::setIndex( const_cast(indices.data(view.index.first)), indices.stride(), index, value ); } static inline void setIndex( const uf::Mesh::View& view, size_t index, size_t value ) { - return uf::mesh::setIndex( view, view["indices"_hash], index, value ); + return uf::mesh::setIndex( view, view["indices"], index, value ); } } } diff --git a/engine/src/engine/ext/ext.cpp b/engine/src/engine/ext/ext.cpp index f6f02a4e..f0ae839b 100644 --- a/engine/src/engine/ext/ext.cpp +++ b/engine/src/engine/ext/ext.cpp @@ -392,6 +392,11 @@ void UF_API uf::initialize() { UF_MSG_DEBUG("Setting JSON implicit preference: {}.{}", ext::json::PREFERRED_ENCODING, ext::json::PREFERRED_COMPRESSION); } + { + uf::stl::string name = "window:Mouse.CursorVisibility"; + UF_MSG_DEBUG( "c_str={}, str={}", uf::algo::fnv1a("window:Mouse.CursorVisibility"), uf::algo::fnv1a(name) ); + } + /* Arguments */ { bool modified = false; auto& arguments = uf::config["arguments"]; diff --git a/engine/src/engine/ext/gui/behavior.cpp b/engine/src/engine/ext/gui/behavior.cpp index 1af1c208..677e8028 100644 --- a/engine/src/engine/ext/gui/behavior.cpp +++ b/engine/src/engine/ext/gui/behavior.cpp @@ -567,8 +567,8 @@ void ext::GuiBehavior::tick( uf::Object& self ) { pod::Vector2f max = { -std::numeric_limits::max(), -std::numeric_limits::max() }; for ( const auto& view : mesh.buffer_views ) { - auto posView = view["position"_hash]; - auto offView = view.has("offset"_hash) ? view["offset"_hash] : uf::Mesh::AttributeView{}; + auto posView = view["position"]; + auto offView = view.has("offset") ? view["offset"] : uf::Mesh::AttributeView{}; for ( auto i = 0; i < view.vertex.count; ++i ) { auto pos = uf::mesh::fetchVertex( view, posView, i ); auto off = offView.valid() ? uf::mesh::fetchVertex( view, offView, i ) : pod::Vector3f{}; diff --git a/engine/src/engine/ext/player/behavior.cpp b/engine/src/engine/ext/player/behavior.cpp index 677d3e93..6890fe40 100644 --- a/engine/src/engine/ext/player/behavior.cpp +++ b/engine/src/engine/ext/player/behavior.cpp @@ -135,6 +135,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { auto axes = uf::transform::axes( transform ); auto& scene = uf::scene::getCurrentScene(); + auto& graph = scene.getComponent(); auto& metadata = this->getComponent(); auto& metadataJson = this->getComponent(); @@ -166,16 +167,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { } keys; - struct { - bool deltaCrouch = false; - bool walking = false; - bool floored = true; - bool noclipped = false; - uf::stl::string menu = ""; - uf::stl::string targetAnimation = ""; - - pod::Matrix4f previous; - } stats; + ext::PlayerBehavior::Metadata::States stats; struct { float move = 4; @@ -447,6 +439,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { physicsBody.velocity *= { speed.friction, 1, speed.friction }; stats.walking = (keys.forward ^ keys.backwards) || (keys.left ^ keys.right); + stats.running = keys.running; if ( stats.walking ) { float factor = stats.floored ? 1.0f : speed.air; @@ -587,21 +580,27 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { UF_MSG_DEBUG("count={}", events.size()); } #endif + + metadata.states = stats; + metadata.states.crouching = metadata.system.crouching; + #if UF_USE_OPENAL - if ( stats.floored && !stats.noclipped ) { + if ( false && stats.floored && !stats.noclipped ) { if ( stats.walking ) { auto& emitter = this->getComponent(); - int cycle = rand() % metadata.audio.footstep.list.size(); - uf::stl::string filename = metadata.audio.footstep.list[cycle]; - uf::Audio& footstep = emitter.has(filename) ? emitter.get(filename) : emitter.load(filename); - + #if 0 bool playing = false; for ( uint i = 0; i < metadata.audio.footstep.list.size(); ++i ) { if ( !emitter.has(metadata.audio.footstep.list[i]) ) continue; uf::Audio& audio = emitter.get( metadata.audio.footstep.list[i] ); if ( audio.playing() ) playing = true; } + if ( !playing ) { + int cycle = rand() % metadata.audio.footstep.list.size(); + uf::stl::string filename = metadata.audio.footstep.list[cycle]; + uf::Audio& footstep = emitter.has(filename) ? emitter.get(filename) : emitter.load(filename); + // [0, 1] float modulation = (rand() % 100) / 100.0; // [0, 0.1] @@ -616,10 +615,84 @@ void ext::PlayerBehavior::tick( uf::Object& self ) { footstep.setTime( 0 ); footstep.play(); } + #else + metadata.audio.footstep.timer -= uf::physics::time::delta; + if ( metadata.audio.footstep.timer <= 0.0f ) { + // player/footsteps + uf::stl::vector surfaces = { + "chainlink", + "concrete", + "dirt", + "duct", + "grass", + "gravel", + "ladder", + "metal", + "metalgrate", + "mud", + "sand", + "slosh", + "tile", + "wade", + "wood", + "woodpanel", + }; + uf::stl::string filename = "concrete"; + + auto events = uf::physics::getCollisionEvents( physicsBody ); + for ( const auto& event : events ) { + if ( event.normal.y > -0.7f ) continue; + auto triID = MIN( event.featureA, event.featureB ); + auto* other = event.a != &physicsBody ? event.a : event.b; + if ( triID == (uint32_t)(-1) ) continue; + if ( other->collider.type != pod::ShapeType::MESH ) continue; + // to-do: sugar it up with a helper function + auto& mesh = *other->collider.mesh.mesh; + auto drawCommand = uf::mesh::fetchDrawCommand( mesh, triID ); + auto instance = uf::graph::getInstance( graph, drawCommand.instanceID ); + auto materialName = uf::graph::getMaterialName( graph, instance.materialID ); + + for ( auto& key : surfaces ) { + if ( !uf::string::contains( materialName, key ) ) continue; + filename = key; + break; + } + break; + } + + filename = ::fmt::format("valve://sound/player/footsteps/{}{}.wav", filename, (rand() % 3) + 1 ); + uf::Audio& footstep = emitter.has(filename) ? emitter.get(filename) : emitter.load(filename); + + float pitch = 0.95f + ((rand() % 11) / 100.0f); + float volume = metadata.audio.footstep.volume; + + if ( metadata.system.crouching ) { + volume *= 0.5f; + } else if ( keys.running ) { + volume *= 1.0f; + } + + footstep.setPitch( pitch ); + footstep.setVolume( volume ); + footstep.setPosition( transform.position ); + + footstep.setTime( 0 ); + footstep.play(); + + if ( keys.running ) { + metadata.audio.footstep.timer = 0.3f; + } else if ( metadata.system.crouching ) { + metadata.audio.footstep.timer = 0.6f; + } else { + metadata.audio.footstep.timer = 0.45f; + } + } + #endif // set animation to walk stats.targetAnimation = "walk"; } else if ( !keys.jump ) { - stats.targetAnimation = "idle_wank"; + stats.targetAnimation = "idle"; + metadata.audio.footstep.timer = 0; } } #endif @@ -766,4 +839,22 @@ void ext::PlayerBehavior::Metadata::deserialize( uf::Object& self, uf::Serialize /*this->*/use.length = serializer["use"]["length"].as(/*this->*/use.length); } + +// yikes +#include +UF_LUA_REGISTER_USERTYPE(ext::PlayerBehavior::Metadata::States, + UF_LUA_REGISTER_USERTYPE_MEMBER(ext::PlayerBehavior::Metadata::States::walking), + UF_LUA_REGISTER_USERTYPE_MEMBER(ext::PlayerBehavior::Metadata::States::running), + UF_LUA_REGISTER_USERTYPE_MEMBER(ext::PlayerBehavior::Metadata::States::crouching), + UF_LUA_REGISTER_USERTYPE_MEMBER(ext::PlayerBehavior::Metadata::States::floored), + UF_LUA_REGISTER_USERTYPE_MEMBER(ext::PlayerBehavior::Metadata::States::noclipped), + UF_LUA_REGISTER_USERTYPE_MEMBER(ext::PlayerBehavior::Metadata::States::deltaCrouch), + UF_LUA_REGISTER_USERTYPE_MEMBER(ext::PlayerBehavior::Metadata::States::menu), + UF_LUA_REGISTER_USERTYPE_MEMBER(ext::PlayerBehavior::Metadata::States::targetAnimation), + UF_LUA_REGISTER_USERTYPE_MEMBER(ext::PlayerBehavior::Metadata::States::previous) +) + +UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(ext::PlayerBehavior::Metadata, + UF_LUA_REGISTER_USERTYPE_MEMBER(ext::PlayerBehavior::Metadata::states) +) #undef this \ No newline at end of file diff --git a/engine/src/engine/ext/player/behavior.h b/engine/src/engine/ext/player/behavior.h index d9d5eed1..6f230014 100644 --- a/engine/src/engine/ext/player/behavior.h +++ b/engine/src/engine/ext/player/behavior.h @@ -57,12 +57,25 @@ namespace ext { struct { struct { uf::stl::vector list; - float volume; + float volume = 1; + float timer = 0; } footstep; } audio; struct { float length = 4.0f; } use; + struct States { + bool walking = false; + bool running = false; + bool crouching = false; + bool floored = true; + bool noclipped = false; + bool deltaCrouch = false; + uf::stl::string menu = ""; + uf::stl::string targetAnimation = ""; + + pod::Matrix4f previous; + } states; ); } } \ No newline at end of file diff --git a/engine/src/engine/graph/animation.cpp b/engine/src/engine/graph/animation.cpp index d7f339ba..a8ed441d 100644 --- a/engine/src/engine/graph/animation.cpp +++ b/engine/src/engine/graph/animation.cpp @@ -310,9 +310,9 @@ uf::stl::vector uf::graph::obbFromSkin( const pod::Graph& graph, const // iterate through mesh to fetch attributes for ( const auto& view : mesh.buffer_views ) { - auto posView = view["position"_hash]; - auto jointsView = view["joints"_hash]; - auto weightView = view["weights"_hash]; + auto posView = view["position"]; + auto jointsView = view["joints"]; + auto weightView = view["weights"]; for ( auto i = 0; i < view.vertex.count; ++i ) { auto pos = uf::mesh::fetchVertex( view, posView, i ); diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index 36da151e..737afbef 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -782,6 +782,7 @@ void uf::graph::process( pod::Graph& graph ) { auto& scene = uf::scene::getCurrentScene(); auto& sceneMetadataJson = scene.getComponent(); auto& graphMetadataJson = graph.metadata; + auto& graphMetadataValve = graphMetadataJson["valve"]; auto& storage = uf::graph::getStorage( graph ); // merge light settings with global settings @@ -997,6 +998,15 @@ void uf::graph::process( pod::Graph& graph ) { UF_DEBUG_TIMER_MULTITRACE("Patching textures/materials"); for ( auto& name : graph.materials ) { auto& material = storage.materials[name]; + + // + if ( ext::json::isObject( graphMetadataValve ) ) { + // nodraw + if ( name.starts_with("tools/") ) { + material.colorBase.w = 0.0f; + } + } + auto tag = ext::json::find( name, graphMetadataJson["tags"] ); if ( ext::json::isObject( tag ) ) { material.colorBase = uf::vector::decode( tag["material"]["base"], material.colorBase); @@ -1264,7 +1274,8 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) float baseMass = graph.metadata["valve"]["models"][meshName]["mass"].as(1.0f); float massScale = metadataValve["massScale"].as(1.0f); // flag as static - if ( node.name.starts_with("prop_static") || motionDisabled ) massScale = 0; + // if ( node.name.starts_with("prop_static") || motionDisabled ) massScale = 0; + massScale = 0; float mass = baseMass * massScale; node.metadata["physics"]["type"] = "mesh"; @@ -1622,6 +1633,8 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) { rebuild = storage.buffers.object.update( (const void*) objects.data(), objects.size() * sizeof(pod::Instance::Object) ) || rebuild; if ( storage.stale ) { + storage.flattenedPrimitives.clear(); + for ( auto& key : storage.primitives.keys ) { auto& primitives = storage.primitives.map[key]; auto& grouped = storage.instances.map[key]; @@ -1651,7 +1664,9 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) { for ( size_t i = 0; i < count; ++i ) { size_t strideIndex = (i * primitives.size()) + drawID; - instances.emplace_back( grouped[strideIndex] ); + + auto& p = storage.flattenedPrimitives.emplace_back( primitive ); + p.instance = instances.emplace_back( grouped[strideIndex] ); addresses.emplace_back( primitive.addresses ); } } @@ -2398,4 +2413,24 @@ void uf::graph::update( pod::Graph& graph, float delta ) { #endif uf::graph::updateAnimation( graph, delta ); +} + +uf::stl::string uf::graph::getMaterialName( pod::Graph& graph, size_t materialID ) { + auto& storage = uf::graph::getStorage( graph ); + if ( !(0 <= materialID && materialID < storage.materials.keys.size() ) ) return ""; + return storage.materials.keys[materialID]; +} +pod::Material uf::graph::getMaterial( pod::Graph& graph, size_t materialID ) { + auto key = uf::graph::getMaterialName( graph, materialID ); + return storage.materials.map[key]; +} + +pod::Primitive uf::graph::getPrimitive( pod::Graph& graph, size_t primitiveID ) { + auto& storage = uf::graph::getStorage( graph ); + if ( !(0 <= primitiveID && primitiveID < storage.flattenedPrimitives.size() ) ) return {}; + return storage.flattenedPrimitives[primitiveID]; +} +pod::Instance uf::graph::getInstance( pod::Graph& graph, size_t instanceID ) { + auto primitive = uf::graph::getPrimitive( graph, instanceID ); + return primitive.instance; } \ No newline at end of file diff --git a/engine/src/ext/audio/wav.cpp b/engine/src/ext/audio/wav.cpp index 6b49b4d9..7a241185 100644 --- a/engine/src/ext/audio/wav.cpp +++ b/engine/src/ext/audio/wav.cpp @@ -47,6 +47,8 @@ namespace { targetOffset = offset; } else if ( (int)origin == 1 ) { targetOffset = (long long)ctx->currentOffset + offset; + } else if ( (int)origin == 2 ) { + targetOffset = (long long)ctx->totalSize + offset; } if (targetOffset < 0) targetOffset = 0; @@ -177,7 +179,7 @@ void ext::wav::load(uf::Audio::Metadata& metadata) { if (metadata.settings.streamed) return ext::wav::stream(metadata); // read all PCM data size_t totalBytes = (size_t) metadata.info.size; - std::vector bytes(totalBytes); + uf::stl::vector bytes(totalBytes); size_t bytesRead = funs::read(bytes.data(), 1, totalBytes, &metadata); if (bytesRead < totalBytes) { diff --git a/engine/src/ext/lua/usertypes/graph.cpp b/engine/src/ext/lua/usertypes/graph.cpp new file mode 100644 index 00000000..aa99a5f0 --- /dev/null +++ b/engine/src/ext/lua/usertypes/graph.cpp @@ -0,0 +1,63 @@ +#include +#if UF_USE_LUA +#include + +namespace binds { + uf::stl::string getMaterialName( pod::Graph& self, size_t id ) { + return uf::graph::getMaterialName( self, id ); + } + pod::Material getMaterial( pod::Graph& self, size_t id ) { + return uf::graph::getMaterial( self, id ); + } + pod::Primitive getPrimitive( pod::Graph& self, size_t id ) { + return uf::graph::getPrimitive( self, id ); + } + pod::Instance getInstance( pod::Graph& self, size_t id ) { + return uf::graph::getInstance( self, id ); + } +} + +#include +UF_LUA_REGISTER_USERTYPE(pod::DrawCommand, + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::DrawCommand::indices), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::DrawCommand::instances), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::DrawCommand::indexID), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::DrawCommand::vertexID), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::DrawCommand::instanceID), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::DrawCommand::materialID) +) + +UF_LUA_REGISTER_USERTYPE(pod::Instance, + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Instance::materialID), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Instance::primitiveID), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Instance::objectID) +) + +UF_LUA_REGISTER_USERTYPE(pod::Primitive, + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Primitive::drawCommand), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Primitive::instance) +) + +UF_LUA_REGISTER_USERTYPE(pod::Material, + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::colorBase), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::colorEmissive), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::factorMetallic), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::factorRoughness), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::factorOcclusion), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::factorAlphaCutoff), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::indexAlbedo), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::indexNormal), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::indexEmissive), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::indexOcclusion), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::indexMetallicRoughness), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::modeCull), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Material::modeAlpha) +) + +UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Graph, + UF_LUA_REGISTER_USERTYPE_DEFINE( getMaterialName, UF_LUA_C_FUN( ::binds::getMaterialName ) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( getMaterial, UF_LUA_C_FUN( ::binds::getMaterial ) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( getPrimitive, UF_LUA_C_FUN( ::binds::getPrimitive ) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( getInstance, UF_LUA_C_FUN( ::binds::getInstance ) ) +) +#endif \ No newline at end of file diff --git a/engine/src/ext/lua/usertypes/mesh.cpp b/engine/src/ext/lua/usertypes/mesh.cpp index cddee4d5..513f9253 100644 --- a/engine/src/ext/lua/usertypes/mesh.cpp +++ b/engine/src/ext/lua/usertypes/mesh.cpp @@ -6,10 +6,56 @@ namespace binds { void updateDescriptor( uf::Mesh& self ) { self.updateDescriptor(); } + + std::tuple fetchView( uf::Mesh& self, size_t triID ) { + const auto* view = uf::mesh::fetchView( self, triID ); + return std::make_tuple( view, triID ); + } + const pod::DrawCommand& fetchDrawCommand( uf::Mesh& mesh, size_t triID ) { + return uf::mesh::fetchDrawCommand( mesh, triID ); + } + + size_t fetchIndex( const uf::Mesh::View& view, const uf::stl::string& name, size_t index ) { + return uf::mesh::fetchIndex( view, name, index ); + } + + uf::stl::vector fetchVertexAttribute( const uf::Mesh::View& view, const uf::stl::string& name, size_t index ) { + uf::stl::vector res; + if ( !view.has( name ) ) return res; + const auto& attr = view[name]; + if ( !attr.valid() ) return res; + + size_t comps = attr.components(); + auto type = attr.type(); + + const uint8_t* ptr = static_cast(attr.data(view.vertex.first + index)); + + for ( size_t i = 0; i < comps; ++i ) { + switch ( type ) { + case uf::renderer::enums::Type::BYTE: res.emplace_back( (float)( reinterpret_cast(ptr)[i] ) ); break; + case uf::renderer::enums::Type::UBYTE: res.emplace_back( (float)( reinterpret_cast(ptr)[i] ) ); break; + case uf::renderer::enums::Type::SHORT: res.emplace_back( (float)( reinterpret_cast(ptr)[i] ) ); break; + case uf::renderer::enums::Type::USHORT: res.emplace_back( (float)( reinterpret_cast(ptr)[i] ) ); break; + case uf::renderer::enums::Type::INT: res.emplace_back( (float)( reinterpret_cast(ptr)[i] ) ); break; + case uf::renderer::enums::Type::UINT: res.emplace_back( (float)( reinterpret_cast(ptr)[i] ) ); break; + case uf::renderer::enums::Type::FLOAT: res.emplace_back( (float)( reinterpret_cast(ptr)[i] ) ); break; + case uf::renderer::enums::Type::DOUBLE: res.emplace_back( (float)( reinterpret_cast(ptr)[i] ) ); break; + default: res.emplace_back( 0.0 ); break; + } + } + + return res; + } } #include +UF_LUA_REGISTER_USERTYPE(uf::Mesh::View, + UF_LUA_REGISTER_USERTYPE_MEMBER(uf::Mesh::View::indirectIndex) +) UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(uf::Mesh, - UF_LUA_REGISTER_USERTYPE_DEFINE( updateDescriptor, UF_LUA_C_FUN( ::binds::updateDescriptor ) ) + UF_LUA_REGISTER_USERTYPE_DEFINE( updateDescriptor, UF_LUA_C_FUN( ::binds::updateDescriptor ) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( fetchView, UF_LUA_C_FUN( ::binds::fetchView ) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( fetchDrawCommand, UF_LUA_C_FUN( ::binds::fetchDrawCommand ) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( fetchIndex, UF_LUA_C_FUN( ::binds::fetchIndex ) ) ) #endif \ No newline at end of file diff --git a/engine/src/ext/lua/usertypes/physics.cpp b/engine/src/ext/lua/usertypes/physics.cpp index e5a22602..dc100c04 100644 --- a/engine/src/ext/lua/usertypes/physics.cpp +++ b/engine/src/ext/lua/usertypes/physics.cpp @@ -5,6 +5,8 @@ namespace binds { namespace body { bool initialized( pod::PhysicsBody& self ) { return !!self.world; } + uf::Object* getObject( pod::PhysicsBody& self ) { return self.object; } + pod::Collider& getCollider( pod::PhysicsBody& self ) { return self.collider; } pod::Vector3f& getVelocity( pod::PhysicsBody& self ) { return self.velocity; } pod::Vector3f& getAngularVelocity( pod::PhysicsBody& self ) { return self.angularVelocity; } @@ -79,6 +81,32 @@ namespace binds { return uf::physics::unconstrain( body ); } } + namespace collider { + const pod::AABB& asAabb( pod::Collider& self ) { + UF_ASSERT( self.type == pod::ShapeType::AABB ); + return self.aabb; + } + const pod::OBB& asObb( pod::Collider& self ) { + UF_ASSERT( self.type == pod::ShapeType::OBB ); + return self.obb; + } + const pod::Sphere& asSphere( pod::Collider& self ) { + UF_ASSERT( self.type == pod::ShapeType::SPHERE ); + return self.sphere; + } + const pod::Plane& asPlane( pod::Collider& self ) { + UF_ASSERT( self.type == pod::ShapeType::PLANE ); + return self.plane; + } + const pod::Capsule& asCapsule( pod::Collider& self ) { + UF_ASSERT( self.type == pod::ShapeType::CAPSULE ); + return self.capsule; + } + const uf::Mesh& asMesh( pod::Collider& self ) { + UF_ASSERT( self.type == pod::ShapeType::MESH ); + return *self.mesh.mesh; + } + } namespace constraint { void setLimits( pod::Constraint& self, float lower, float upper ) { return uf::physics::setConstraintLimits( self, lower, upper ); @@ -112,13 +140,38 @@ namespace binds { #include -UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::CollisionEvent, +UF_LUA_REGISTER_ENUM(pod::ShapeType, + "AABB", pod::ShapeType::AABB, + "OBB", pod::ShapeType::OBB, + "SPHERE", pod::ShapeType::SPHERE, + "PLANE", pod::ShapeType::PLANE, + "CAPSULE", pod::ShapeType::CAPSULE, + "TRIANGLE", pod::ShapeType::TRIANGLE, + "MESH", pod::ShapeType::MESH, + "CONVEX_HULL", pod::ShapeType::CONVEX_HULL +) + +UF_LUA_REGISTER_USERTYPE(pod::Collider, + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Collider::type), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Collider::category), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Collider::mask), + + UF_LUA_REGISTER_USERTYPE_DEFINE( asAabb, UF_LUA_C_FUN(::binds::collider::asAabb) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( asObb, UF_LUA_C_FUN(::binds::collider::asObb) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( asSphere, UF_LUA_C_FUN(::binds::collider::asSphere) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( asPlane, UF_LUA_C_FUN(::binds::collider::asPlane) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( asCapsule, UF_LUA_C_FUN(::binds::collider::asCapsule) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( asMesh, UF_LUA_C_FUN(::binds::collider::asMesh) ) +) +UF_LUA_REGISTER_USERTYPE(pod::CollisionEvent, UF_LUA_REGISTER_USERTYPE_MEMBER(pod::CollisionEvent::state), UF_LUA_REGISTER_USERTYPE_MEMBER(pod::CollisionEvent::a), UF_LUA_REGISTER_USERTYPE_MEMBER(pod::CollisionEvent::b), UF_LUA_REGISTER_USERTYPE_MEMBER(pod::CollisionEvent::point), UF_LUA_REGISTER_USERTYPE_MEMBER(pod::CollisionEvent::normal), - UF_LUA_REGISTER_USERTYPE_MEMBER(pod::CollisionEvent::impulse) + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::CollisionEvent::impulse), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::CollisionEvent::featureA), + UF_LUA_REGISTER_USERTYPE_MEMBER(pod::CollisionEvent::featureB) ) UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::AABB, sol::call_constructor, sol::initializers( @@ -162,7 +215,7 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::OBB, UF_LUA_REGISTER_USERTYPE_MEMBER(pod::OBB::extent), UF_LUA_REGISTER_USERTYPE_MEMBER(pod::OBB::center) ) -UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Sphere, +UF_LUA_REGISTER_USERTYPE(pod::Sphere, sol::call_constructor, sol::initializers( []( pod::Sphere& self ) { return self = {}; @@ -176,7 +229,7 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Sphere, ), UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Sphere::radius) ) -UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Capsule, +UF_LUA_REGISTER_USERTYPE(pod::Capsule, sol::call_constructor, sol::initializers( []( pod::Capsule& self ) { return self = {}; @@ -192,7 +245,7 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Capsule, UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Capsule::radius), UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Capsule::up) ) -UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Plane, +UF_LUA_REGISTER_USERTYPE(pod::Plane, sol::call_constructor, sol::initializers( []( pod::Plane& self ) { return self = {}; @@ -207,7 +260,7 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Plane, UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Plane::normal), UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Plane::offset) ) -UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Ray, +UF_LUA_REGISTER_USERTYPE(pod::Ray, sol::call_constructor, sol::initializers( []( pod::Ray& self ) { return self = {}; @@ -225,6 +278,8 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Ray, UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::PhysicsBody, UF_LUA_REGISTER_USERTYPE_DEFINE( initialized, UF_LUA_C_FUN(::binds::body::initialized) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( getObject, UF_LUA_C_FUN(::binds::body::getObject) ), + UF_LUA_REGISTER_USERTYPE_DEFINE( getCollider, UF_LUA_C_FUN(::binds::body::getCollider) ), UF_LUA_REGISTER_USERTYPE_DEFINE( getVelocity, UF_LUA_C_FUN(::binds::body::getVelocity) ), UF_LUA_REGISTER_USERTYPE_DEFINE( setVelocity, UF_LUA_C_FUN(::binds::body::setVelocity) ), @@ -258,7 +313,7 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::PhysicsBody, UF_LUA_REGISTER_USERTYPE_DEFINE( unconstrain, UF_LUA_C_FUN(::binds::body::unconstrain) ) ) -UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Constraint, +UF_LUA_REGISTER_USERTYPE(pod::Constraint, UF_LUA_REGISTER_USERTYPE_DEFINE( setLimits, UF_LUA_C_FUN(::binds::constraint::setLimits) ), UF_LUA_REGISTER_USERTYPE_DEFINE( asBallSocket, UF_LUA_C_FUN(::binds::constraint::asBallSocket) ), UF_LUA_REGISTER_USERTYPE_DEFINE( asConeTwist, UF_LUA_C_FUN(::binds::constraint::asConeTwist) ), diff --git a/engine/src/ext/valve/vpk.cpp b/engine/src/ext/valve/vpk.cpp index 9d9cefb9..47a9494f 100644 --- a/engine/src/ext/valve/vpk.cpp +++ b/engine/src/ext/valve/vpk.cpp @@ -210,6 +210,7 @@ bool ext::valve::loadVpk( pod::VpkArchive& vpk, const uf::stl::string& path ) { // read data auto& entry = vpk.files[fullPath]; + //UF_MSG_DEBUG("path={}, entry={}", path, fullPath); if ( !readBytes(&entry.metadata, sizeof(pod::VpkData)) ) return false; if ( entry.metadata.preloadBytes > 0 ) { diff --git a/engine/src/ext/vulkan/device.cpp b/engine/src/ext/vulkan/device.cpp index 8a1f7403..8fea4559 100644 --- a/engine/src/ext/vulkan/device.cpp +++ b/engine/src/ext/vulkan/device.cpp @@ -1142,7 +1142,7 @@ void ext::vulkan::Device::initialize() { uf::stl::vector queueCreateInfos{}; // Get queue family indices for the requested queue family types // Note that the indices may overlap depending on the implementation - std::vector> queuePriorities; + uf::stl::vector> queuePriorities; // Graphics queue if ( requestedQueueTypes & VK_QUEUE_GRAPHICS_BIT ) { @@ -1181,7 +1181,7 @@ void ext::vulkan::Device::initialize() { uint32_t maxSupported = queueFamilyProperties[familyIndex].queueCount; uint32_t actualCount = std::min( requestedCount, maxSupported ); - queuePriorities.emplace_back(std::vector(actualCount, 1.0f)); + queuePriorities.emplace_back(uf::stl::vector(actualCount, 1.0f)); VkDeviceQueueCreateInfo queueInfo{}; queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; diff --git a/engine/src/ext/vulkan/graphic.cpp b/engine/src/ext/vulkan/graphic.cpp index 8867a616..33acf56a 100644 --- a/engine/src/ext/vulkan/graphic.cpp +++ b/engine/src/ext/vulkan/graphic.cpp @@ -211,7 +211,7 @@ void ext::vulkan::Pipeline::initialize( const Graphic& graphic, const GraphicDes const uint32_t sbtSize = groupCount * handleSizeAligned; const VkBufferUsageFlags bufferUsageFlags = VK_BUFFER_USAGE_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; - std::vector shaderHandleStorage(sbtSize); + uf::stl::vector shaderHandleStorage(sbtSize); VK_CHECK_RESULT(vkGetRayTracingShaderGroupHandlesKHR(device, pipeline, 0, groupCount, sbtSize, shaderHandleStorage.data())); requestedAlignment = rayTracingPipelineProperties.shaderGroupBaseAlignment; diff --git a/engine/src/ext/vulkan/vulkan.cpp b/engine/src/ext/vulkan/vulkan.cpp index 8130e22a..e65b1508 100644 --- a/engine/src/ext/vulkan/vulkan.cpp +++ b/engine/src/ext/vulkan/vulkan.cpp @@ -213,7 +213,7 @@ uf::stl::string ext::vulkan::retrieveCheckpoint( VkQueue queue ) { uint32_t size = 0; vkGetQueueCheckpointDataNV(queue, &size, 0); - std::vector checkpoints(size, { VK_STRUCTURE_TYPE_CHECKPOINT_DATA_NV }); + uf::stl::vector checkpoints(size, { VK_STRUCTURE_TYPE_CHECKPOINT_DATA_NV }); vkGetQueueCheckpointDataNV(queue, &size, checkpoints.data()); uf::stl::vector strings; diff --git a/engine/src/ext/xatlas/xatlas.cpp b/engine/src/ext/xatlas/xatlas.cpp index b0cc0944..e835d8d2 100644 --- a/engine/src/ext/xatlas/xatlas.cpp +++ b/engine/src/ext/xatlas/xatlas.cpp @@ -68,9 +68,9 @@ size_t ext::xatlas::unwrap( pod::Graph& graph ) { entry.commandID = viewIdx; auto& decl = entry.decl; - auto posView = view["position"_hash]; - auto uvView = view["uv"_hash]; - auto idxView = view["index"_hash]; + auto posView = view["position"]; + auto uvView = view["uv"]; + auto idxView = view["index"]; UF_ASSERT( posView.valid() && uvView.valid() ); diff --git a/engine/src/utils/math/physics/broadphase/bvh.cpp b/engine/src/utils/math/physics/broadphase/bvh.cpp index 397dc2a0..82c4e830 100644 --- a/engine/src/utils/math/physics/broadphase/bvh.cpp +++ b/engine/src/utils/math/physics/broadphase/bvh.cpp @@ -223,8 +223,8 @@ void impl::buildMeshBVH( pod::BVH& bvh, const uf::Mesh& mesh, pod::BVH::index_t // populate initial indices and bounds for ( auto& view : views ) { - auto& indices = view["index"_hash]; - auto& positions = view["position"_hash]; + auto& indices = view["index"]; + auto& positions = view["position"]; auto tris = view.index.count / 3; for ( auto triIndexID = 0; triIndexID < tris; ++triIndexID ) { @@ -441,8 +441,8 @@ void impl::refitBVH( pod::BVH& bvh, const uf::Mesh& mesh ) { // populate initial indices and bounds for ( auto& view : views ) { - auto& indices = view["index"_hash]; - auto& positions = view["position"_hash]; + auto& indices = view["index"]; + auto& positions = view["position"]; auto tris = view.index.count / 3; for ( auto triIndexID = 0; triIndexID < tris; ++triIndexID ) { diff --git a/engine/src/utils/math/physics/common.cpp b/engine/src/utils/math/physics/common.cpp index 736a45ed..352a4e42 100644 --- a/engine/src/utils/math/physics/common.cpp +++ b/engine/src/utils/math/physics/common.cpp @@ -622,7 +622,7 @@ pod::AABB impl::computeConvexHullAABB( const uf::Mesh::View& view, const uf::Mes return bounds; } pod::AABB impl::computeConvexHullAABB( const uf::Mesh::View& view, pod::AABB bounds ) { - return impl::computeConvexHullAABB( view, view["position"_hash], bounds ); + return impl::computeConvexHullAABB( view, view["position"], bounds ); } // combines two AABBs pod::AABB impl::mergeAabb( const pod::AABB& a, const pod::AABB& b ) { @@ -741,7 +741,7 @@ pod::AABB impl::computeAABB( const pod::PhysicsBody& body ) { 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"_hash], bounds ); + for ( const auto& view : meshData.buffer_views ) impl::computeConvexHullAABB( view, view["position"], bounds ); return impl::transformAabbToWorld( bounds, transform ); } default: { diff --git a/engine/src/utils/math/physics/constraints/contact.cpp b/engine/src/utils/math/physics/constraints/contact.cpp index 822c39aa..c03b1c48 100644 --- a/engine/src/utils/math/physics/constraints/contact.cpp +++ b/engine/src/utils/math/physics/constraints/contact.cpp @@ -357,19 +357,37 @@ void impl::solveManifold( uf::stl::vector& manifolds, float dt ) } void impl::dispatchManifold( pod::Manifold& manifold, pod::CollisionEvent::events_t& events, pod::CollisionEvent::map_t& active, const pod::CollisionEvent::map_t& previous ) { - auto pairKey = impl::makePairKey( *manifold.a, *manifold.b ); - // find largest impulse - float maxImpulse = 0.0f; - for ( const auto& c : manifold.points ) maxImpulse = std::max( maxImpulse, c.accumulatedNormalImpulse ); // mark as an active collision + auto pairKey = impl::makePairKey( *manifold.a, *manifold.b ); active[pairKey] = { manifold.a, manifold.b }; + // find largest impulse + size_t primaryID = 0; + float maxImpulse = -1.0f; + for ( auto i = 0; i < manifold.points.size(); ++i ) { + auto& c = manifold.points[i]; + if ( c.accumulatedNormalImpulse <= maxImpulse ) continue; + primaryID = i; + maxImpulse = c.accumulatedNormalImpulse; + } + if ( maxImpulse <= EPS ) { + float maxPen = -FLT_MAX; + for ( int i = 0; i < manifold.points.size(); ++i ) { + auto& c = manifold.points[i]; + if ( c.penetration <= maxPen ) continue; + maxPen = c.penetration; + primaryID = i; + } + } + auto& contact = manifold.points[primaryID]; // dispatch events.emplace_back(pod::CollisionEvent{ .state = previous.count( pairKey ) == 0 ? pod::CollisionState::ENTER : pod::CollisionState::SUSTAIN, .a = manifold.a, .b = manifold.b, - .point = manifold.points[0].point, - .normal = manifold.points[0].normal, + .point = contact.point, + .normal = contact.normal, .impulse = maxImpulse, + .featureA = contact.featureA, + .featureB = contact.featureB, }); } \ No newline at end of file diff --git a/engine/src/utils/math/physics/narrowphase/epa.cpp b/engine/src/utils/math/physics/narrowphase/epa.cpp index be53e2ec..024a6632 100644 --- a/engine/src/utils/math/physics/narrowphase/epa.cpp +++ b/engine/src/utils/math/physics/narrowphase/epa.cpp @@ -151,8 +151,8 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di if ( 0 <= selectedViewIdx && selectedViewIdx != viewIdx ) continue; const auto& view = mesh.buffer_views[viewIdx]; - auto& indices = view["index"_hash]; - auto& positions = view["position"_hash]; + auto& indices = view["index"]; + auto& positions = view["position"]; for ( size_t i = 0; i < view.index.count / 3; ++i ) { pod::Triangle tri = uf::mesh::fetchTriangle( view, indices, positions, i ); pod::Vector3f normal = impl::triangleNormal( tri ); diff --git a/engine/src/utils/math/physics/narrowphase/gjk.cpp b/engine/src/utils/math/physics/narrowphase/gjk.cpp index d60b2418..dfca66c0 100644 --- a/engine/src/utils/math/physics/narrowphase/gjk.cpp +++ b/engine/src/utils/math/physics/narrowphase/gjk.cpp @@ -66,7 +66,7 @@ pod::Vector3f impl::support( const pod::PhysicsBody& body, const pod::Vector3f& for ( auto viewIdx = 0; viewIdx < mesh.buffer_views.size(); ++viewIdx ) { if ( 0 <= selectedViewIdx && selectedViewIdx != viewIdx ) continue; // cringe, but saves code duplication (could just alter the bounds above) const auto& view = mesh.buffer_views[viewIdx]; - auto& positions = view["position"_hash]; + auto& positions = view["position"]; for ( size_t i = 0; i < view.vertex.count; ++i ) { pod::Vector3f v = uf::mesh::fetchVertex( view, positions, i ); float dist = uf::vector::dot( v, localDir ); diff --git a/engine/src/utils/math/physics/narrowphase/hull.cpp b/engine/src/utils/math/physics/narrowphase/hull.cpp index fd8a89a7..f423bb91 100644 --- a/engine/src/utils/math/physics/narrowphase/hull.cpp +++ b/engine/src/utils/math/physics/narrowphase/hull.cpp @@ -18,6 +18,7 @@ namespace impl { bool hit = false; // do collision per hull for ( auto hullID : candidates ) { + size_t previousCount = manifold.points.size(); auto hullView = impl::physicsBodyHullView( hull, hullID ); pod::Simplex simplex; @@ -25,6 +26,7 @@ namespace impl { auto result = impl::epa( hullView, body, simplex ); if ( !impl::generateClippingManifold( hullView, body, result, manifold ) ) continue; hit = true; + for ( size_t i = previousCount; i < manifold.points.size(); ++i ) manifold.points[i].featureA = hullID; } return hit; } @@ -69,6 +71,7 @@ bool impl::hullHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:: bool hit = false; for (auto [ viewIdA, viewIdB ] : pairs ) { + size_t previousCount = manifold.points.size(); auto viewA = impl::physicsBodyHullView( a, viewIdA ); auto viewB = impl::physicsBodyHullView( b, viewIdB ); @@ -78,6 +81,10 @@ bool impl::hullHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:: auto result = impl::epa( viewA, viewB, simplex ); if ( !impl::generateClippingManifold( viewA, viewB, result, manifold ) ) continue; hit = true; + for ( size_t i = previousCount; i < manifold.points.size(); ++i ) { + manifold.points[i].featureA = viewIdA; + manifold.points[i].featureB = viewIdB; + } } return hit; } diff --git a/engine/src/utils/math/physics/narrowphase/mesh.cpp b/engine/src/utils/math/physics/narrowphase/mesh.cpp index 408d0b6c..3b3f6644 100644 --- a/engine/src/utils/math/physics/narrowphase/mesh.cpp +++ b/engine/src/utils/math/physics/narrowphase/mesh.cpp @@ -20,9 +20,11 @@ bool impl::meshAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:: bool hit = false; // do collision per triangle for ( auto triID : candidates ) { + size_t previousCount = manifold.points.size(); auto tri = impl::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space if ( !impl::triangleAabb( tri, aabb, manifold ) ) continue; hit = true; + for ( size_t i = previousCount; i < manifold.points.size(); ++i ) manifold.points[i].featureA = triID; } return hit; } @@ -43,9 +45,11 @@ bool impl::meshObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::M bool hit = false; // do collision per triangle for ( auto triID : candidates ) { + size_t previousCount = manifold.points.size(); auto tri = impl::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space if ( !impl::triangleObb( tri, obb, manifold ) ) continue; hit = true; + for ( size_t i = previousCount; i < manifold.points.size(); ++i ) manifold.points[i].featureA = triID; } return hit; } @@ -66,9 +70,11 @@ bool impl::meshSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod bool hit = false; // do collision per triangle for ( auto triID : candidates ) { + size_t previousCount = manifold.points.size(); auto tri = impl::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space if ( !impl::triangleSphere( tri, sphere, manifold ) ) continue; hit = true; + for ( size_t i = previousCount; i < manifold.points.size(); ++i ) manifold.points[i].featureA = triID; } return hit; @@ -91,9 +97,11 @@ bool impl::meshPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod: bool hit = false; // do collision per triangle for ( auto triID : candidates ) { + size_t previousCount = manifold.points.size(); auto tri = impl::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space if ( !impl::trianglePlane( tri, plane, manifold ) ) continue; hit = true; + for ( size_t i = previousCount; i < manifold.points.size(); ++i ) manifold.points[i].featureA = triID; } return hit; @@ -115,9 +123,11 @@ bool impl::meshCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, po bool hit = false; // do collision per triangle for ( auto triID : candidates ) { + size_t previousCount = manifold.points.size(); auto tri = impl::fetchTriangle( meshData, triID, mesh ); // transform triangle to world space if ( !impl::triangleCapsule( tri, capsule, manifold ) ) continue; hit = true; + for ( size_t i = previousCount; i < manifold.points.size(); ++i ) manifold.points[i].featureA = triID; } return hit; @@ -144,12 +154,17 @@ bool impl::meshMesh( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:: bool hit = false; // do collision per triangle for (auto [idA, idB] : pairs ) { + size_t previousCount = manifold.points.size(); auto triA = impl::fetchTriangle( meshA, idA, a ); // transform triangles to world space auto triB = impl::fetchTriangle( meshB, idB, b ); bool collides = impl::triangleTriangle( triA, triB, manifold ); if ( !collides ) continue; hit = true; + for ( size_t i = previousCount; i < manifold.points.size(); ++i ) { + manifold.points[i].featureA = idA; + manifold.points[i].featureB = idB; + } } return hit; } @@ -177,6 +192,7 @@ bool impl::meshHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:: bool hit = false; // do collision per hull and triangle for (auto [ triID, hullID ] : pairs ) { + size_t previousCount = manifold.points.size(); auto triView = impl::physicsBodyTriView( mesh, triID ); auto hullView = impl::physicsBodyHullView( hull, hullID ); @@ -184,6 +200,10 @@ bool impl::meshHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:: if ( !collides ) continue; hit = true; + for ( size_t i = previousCount; i < manifold.points.size(); ++i ) { + manifold.points[i].featureA = triID; + manifold.points[i].featureB = hullID; + } } return hit; } diff --git a/engine/src/utils/math/physics/narrowphase/ray.cpp b/engine/src/utils/math/physics/narrowphase/ray.cpp index 98acca7f..8a14b2aa 100644 --- a/engine/src/utils/math/physics/narrowphase/ray.cpp +++ b/engine/src/utils/math/physics/narrowphase/ray.cpp @@ -261,6 +261,7 @@ bool impl::rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue rayHit.contact.point = p; rayHit.contact.normal = n; rayHit.contact.penetration = t; + rayHit.contact.featureA = triID; } return rayHit.hit; @@ -295,6 +296,7 @@ bool impl::rayHull( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue rayHit.contact.point = impl::apply( transform, ray.origin + ray.direction * t ); rayHit.contact.normal = uf::quaternion::rotate( transform.orientation, normal ); rayHit.contact.penetration = t; + rayHit.contact.featureA = hullID; } return rayHit.hit; diff --git a/engine/src/utils/mesh/mesh.cpp b/engine/src/utils/mesh/mesh.cpp index 2c69e448..5852ebc7 100644 --- a/engine/src/utils/mesh/mesh.cpp +++ b/engine/src/utils/mesh/mesh.cpp @@ -294,16 +294,16 @@ uf::Mesh::View uf::Mesh::makeView( const uf::stl::vector& wante if ( wanted.size() ) { for ( auto& attr : vertex.attributes ) { if ( std::find(wanted.begin(), wanted.end(), attr.descriptor.name ) == wanted.end() ) continue; - view.attributes[uf::algo::fnv1a(attr.descriptor.name)] = { attr }; + view.attributes[attr.descriptor.name] = { attr }; } } else { for ( auto& attr : vertex.attributes ) { - view.attributes[uf::algo::fnv1a(attr.descriptor.name)] = { attr }; + view.attributes[attr.descriptor.name] = { attr }; } } if ( !index.attributes.empty() ) { - view.attributes["index"_hash] = { index.attributes[lod] }; + view.attributes["index"] = { index.attributes[lod] }; } return view; @@ -317,14 +317,14 @@ uf::Mesh::View uf::Mesh::makeView( size_t i, const uf::stl::vector