diff --git a/bin/data/config.json b/bin/data/config.json index 05da5645..b3dc5ec1 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -61,7 +61,7 @@ }, "graph": { "initial buffer elements": 128, - "global storage": false + "global storage": true // required for animations to work? }, "ext": { "vulkan": { @@ -350,15 +350,16 @@ "max": 0.01 // 0.2 }, "debug draw": { - "static": false, - "dynamic": false, + "static": true, + "dynamic": true, "contacts": false, "constraints": true, "rays": false, - "depthTest": false + "depthTest": true }, "fixed step": true, - "substeps": 4 + "substeps": 4, + "flatten transforms": true }, "audio": { "mute": false, diff --git a/bin/data/entities/cesiumMan.json b/bin/data/entities/cesiumMan.json index 2b862d6e..30baf636 100644 --- a/bin/data/entities/cesiumMan.json +++ b/bin/data/entities/cesiumMan.json @@ -14,7 +14,7 @@ "axis": [ 0, 1, 0 ], "angle": 0 }, - "scale": [ 10.0, 10.0, 10.0 ] + "scale": [ 1.0, 1.0, 1.0 ] }, "system": { "hot reload": { @@ -29,7 +29,7 @@ "animations": true }, "draw": { - "armature": true + "armature": false } }, "exporter": { diff --git a/bin/data/entities/craeture.json b/bin/data/entities/craeture.json index 2ca8465f..4c9cdd63 100644 --- a/bin/data/entities/craeture.json +++ b/bin/data/entities/craeture.json @@ -11,11 +11,11 @@ "SoundEmitterBehavior" ], "transform": { - "position": [ -7, 0, -44 ], + "position": [ -7, -3.5, -44 ], //"position": [ 0, 1.5, 21 ], //"position": [ 16.3489, 1.37972, -68.1571 ], - "scale": [ 0.09, 0.09, 0.09 ] - //"scale": [ 1, 1, 1 ] + //"scale": [ 0.09, 0.09, 0.09 ] + "scale": [ 5, 5, 5 ] }, "system": { "hot reload": { @@ -32,7 +32,7 @@ } }, "physics": { - "ragdoll": false + "ragdoll": true } } /* diff --git a/bin/data/entities/craetureModel.json b/bin/data/entities/craetureModel.json index 1fa91f17..7886e477 100644 --- a/bin/data/entities/craetureModel.json +++ b/bin/data/entities/craetureModel.json @@ -5,7 +5,7 @@ "import": "/model.json", "assets": [ // "/player/pbear.glb" - { "filename": "/player/bear/graph.json" } + { "filename": "/player/pbear/graph.json" } ], "behaviors": [], "transform": { @@ -16,7 +16,7 @@ "axis": [ 0, 1, 0 ], "angle": 0 }, - "scale": [ 0.09, 0.09, 0.09 ], + "scale": [ 1, 1, 1 ], "reference": "parent" }, "system": { @@ -43,6 +43,9 @@ "lights": { "lightmap": false }, + "stream": { + "animations": false + }, "renderer": { "cull mode": "front", "filter": "linear", @@ -52,7 +55,7 @@ "skinned": true }, "animations": { - "animation": "wank" + // "animation": "idle" } } } diff --git a/bin/data/entities/playerModel.json b/bin/data/entities/playerModel.json index bcccdf3d..a29aa9be 100644 --- a/bin/data/entities/playerModel.json +++ b/bin/data/entities/playerModel.json @@ -1,11 +1,11 @@ { "type": "Object", "name": "Player: Model", - "ignore": false, + "ignore": true, "import": "/model.json", "assets": [ - // "/player/bear.glb" - { "filename": "/player/bear/graph.json" } + // "/player/pbear.glb" + { "filename": "/player/pbear/graph.json" } ], "behaviors": [ "PlayerModelBehavior" @@ -18,7 +18,7 @@ "axis": [ 0, 1, 0 ], "angle": 0 }, - "scale": [ 0.09, 0.09, 0.09 ] + "scale": [ 1, 1, 1 ] }, "metadata": { "track": true, @@ -49,7 +49,7 @@ }, "animations": { "animation": "wank", - "speed": 2.0 + "speed": 8.0 } } } diff --git a/bin/data/scenes/sourceengine/mds_mcdonalds.json b/bin/data/scenes/sourceengine/mds_mcdonalds.json index 3b535c79..4d98cb4d 100644 --- a/bin/data/scenes/sourceengine/mds_mcdonalds.json +++ b/bin/data/scenes/sourceengine/mds_mcdonalds.json @@ -4,7 +4,7 @@ // { "filename": "./models/mds_mcdonalds.glb" } { "filename": "./models/mds_mcdonalds/graph.json" } // ,{ "filename": "/ragdoll.json", "delay": 1 } - ,{ "filename": "/craeture.json", "delay": 2.0 } + // ,{ "filename": "/craeture.json", "delay": 2.0 } // ,{ "filename": "/cesiumMan.json", "delay": 2.0 } ], "metadata": { diff --git a/bin/data/shaders/base/textured/frag.glsl b/bin/data/shaders/base/textured/frag.glsl new file mode 100644 index 00000000..0422dd4d --- /dev/null +++ b/bin/data/shaders/base/textured/frag.glsl @@ -0,0 +1,17 @@ +#version 450 +#pragma shader_stage(fragment) + +layout (binding = 1) uniform sampler2D samplerTexture; + +layout (location = 0) in vec2 inUv; +layout (location = 1) in vec4 inColor; + +layout (location = 0) out vec4 outColor; + +void main() { + vec4 color = texture( samplerTexture, inUv ) * inColor; + if ( color.a == 0.0f ) discard; + + outColor.rgb = color.rgb; + outColor.a = color.a; +} \ No newline at end of file diff --git a/bin/data/shaders/base/textured/vert.glsl b/bin/data/shaders/base/textured/vert.glsl new file mode 100644 index 00000000..0752103d --- /dev/null +++ b/bin/data/shaders/base/textured/vert.glsl @@ -0,0 +1,25 @@ +#version 450 +#pragma shader_stage(vertex) + +#include "../../common/macros.h" +#include "../../common/structs.h" + +layout (constant_id = 0) const uint PASSES = 1; + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec2 inUv; +layout (location = 2) in vec4 inColor; + +layout( push_constant ) uniform PushBlock { + uint pass; + uint draw; +} PushConstant; + +layout (location = 0) out vec2 outUv; +layout (location = 1) out vec4 outColor; + +void main() { + outUv = inUv; + outColor = inColor; + gl_Position = vec4(inPos.xyz, 1.0); +} \ No newline at end of file diff --git a/engine/inc/uf/macros.h b/engine/inc/uf/macros.h index 20778074..704b242d 100644 --- a/engine/inc/uf/macros.h +++ b/engine/inc/uf/macros.h @@ -153,6 +153,8 @@ #define UF_DEBUG 1 #endif + + #if UF_DEBUG #include #endif \ No newline at end of file diff --git a/engine/inc/uf/simd.h b/engine/inc/uf/simd.h index 0839ae25..246602ee 100644 --- a/engine/inc/uf/simd.h +++ b/engine/inc/uf/simd.h @@ -4,6 +4,14 @@ #define __x86_64__ 1 #endif +#if __clang__ + // clang produces multiple symbols or something because the anonymous namespace trick doesn't work + // also crashes when producing debug symbols for multi-versioned functions + #define SIMD_MV 0 +#else + #define SIMD_MV 0 // off for now because there MIGHT be a runtime cost +#endif + // Find sse instruction set from compiler macros if SSE_INSTR_SET not defined // Note: Not all compilers define these macros automatically #ifndef SSE_INSTR_SET diff --git a/engine/inc/uf/utils/debug/draw.h b/engine/inc/uf/utils/debug/draw.h index 3f246b47..a84478bb 100644 --- a/engine/inc/uf/utils/debug/draw.h +++ b/engine/inc/uf/utils/debug/draw.h @@ -8,6 +8,7 @@ namespace uf { namespace debug { void UF_API drawLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color = { 1, 1, 1, 1 } ); + void UF_API drawShape( const pod::AABB& aabb, const pod::Transform<>& transform = {} ); void UF_API drawShape( const pod::OBB& obb, const pod::Transform<>& transform = {} ); void UF_API drawShape( const pod::Sphere& sphere, const pod::Transform<>& transform = {} ); @@ -16,6 +17,10 @@ namespace uf { void UF_API drawShape( const pod::Triangle& tri, const pod::Transform<>& transform = {} ); void UF_API addLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color = { 1, 1, 1, 1 }, float ttl = 1.0f ); + void UF_API drawLines( float dt = 0 ); + + void UF_API drawText( const uf::stl::string& string, const pod::Vector3f& position, const pod::Vector4f& color = { 1, 1, 1, 1 } ); + void UF_API drawTexts( float dt = 0 ); void UF_API draw( float dt = 0 ); } } \ No newline at end of file diff --git a/engine/inc/uf/utils/image/atlas.h b/engine/inc/uf/utils/image/atlas.h index 9dfe8e70..41ddef7c 100644 --- a/engine/inc/uf/utils/image/atlas.h +++ b/engine/inc/uf/utils/image/atlas.h @@ -23,18 +23,18 @@ namespace uf { uf::Image m_atlas; atlas_t m_tiles; public: - hash_t addImage( const uf::Image&, bool = false ); - hash_t addImage( uf::Image&&, bool = false ); - hash_t addImage( const uint8_t*, const pod::Vector2ui&, std::size_t, std::size_t, bool = false, bool = false ); + hash_t addImage( const uf::Image&, const hash_t& hash ); + hash_t addImage( const uf::Image& ); void generate(float padding = 1); void generate( const uf::stl::vector&, float padding = 1); void clear(bool = true); bool generated() const; + bool has( const hash_t& ) const; - pod::Vector2f mapUv( const pod::Vector2f&, const hash_t& ); - pod::Vector2f mapUv( const pod::Vector2f&, size_t ); - pod::Vector3f mapUv( const pod::Vector3f& ); + pod::Vector2f mapUv( const pod::Vector2f&, const hash_t& ) const; + pod::Vector2f mapUv( const pod::Vector2f&, size_t ) const; + pod::Vector3f mapUv( const pod::Vector3f& ) const; uf::Image& getAtlas(); const uf::Image& getAtlas() const; diff --git a/engine/inc/uf/utils/math/matrix/pod.inl b/engine/inc/uf/utils/math/matrix/pod.inl index f8943919..ee3cd59e 100644 --- a/engine/inc/uf/utils/math/matrix/pod.inl +++ b/engine/inc/uf/utils/math/matrix/pod.inl @@ -381,7 +381,10 @@ pod::Vector3t uf::matrix::extractScale( const T& matrix ) { type_t det = matrix(0,0) * ( matrix(1,1) * matrix(2,2) - matrix(2,1) * matrix(1,2)) - matrix(0,1) * ( matrix(1,0) * matrix(2,2) - matrix(1,2) * matrix(2,0)) + matrix(0,2) * ( matrix(1,0) * matrix(2,1) - matrix(1,1) * matrix(2,0)); - if ( det < 0 ) { sx = -sx; sy = -sy; sz = -sz; } + + if ( det < 0 ) { + sx = -sx; + } return { sx, sy, sz }; } diff --git a/engine/inc/uf/utils/math/matrix/simd.inl b/engine/inc/uf/utils/math/matrix/simd.inl index de498703..72e66c97 100644 --- a/engine/inc/uf/utils/math/matrix/simd.inl +++ b/engine/inc/uf/utils/math/matrix/simd.inl @@ -22,12 +22,13 @@ namespace uf { } } -namespace { +/***/ namespace { +#if SIMD_MV __attribute__((target("default"))) - uf::simd::matrix_value matMult_impl(const uf::simd::matrix_value& A, const uf::simd::matrix_value& B) { + uf::simd::matrix_value _impl_matMult(const uf::simd::matrix_value& A, const uf::simd::matrix_value& B) { uf::simd::matrix_value R; uf::simd::matrix_value Bt = uf::simd::matTranspose(B); - FOR_EACH(4, { + FOR_EACH_(4, { __m128 bcol = B.m[i]; __m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx @@ -48,13 +49,12 @@ namespace { return R; } - #if 1 __attribute__((target("sse4.1"))) - uf::simd::matrix_value matMult_impl(const uf::simd::matrix_value& A, const uf::simd::matrix_value& B) { + uf::simd::matrix_value _impl_matMult(const uf::simd::matrix_value& A, const uf::simd::matrix_value& B) { uf::simd::matrix_value R; uf::simd::matrix_value Bt = uf::simd::matTranspose(B); - FOR_EACH(4, { + FOR_EACH_(4, { __m128 bcol = B.m[i]; __m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx @@ -74,14 +74,12 @@ namespace { return R; } - #endif - #if 1 __attribute__((target("avx2,fma"))) - uf::simd::matrix_value matMult_impl(const uf::simd::matrix_value& A, const uf::simd::matrix_value& B) { + uf::simd::matrix_value _impl_matMult(const uf::simd::matrix_value& A, const uf::simd::matrix_value& B) { uf::simd::matrix_value R; uf::simd::matrix_value Bt = uf::simd::matTranspose(B); - FOR_EACH(4, { + FOR_EACH_(4, { __m128 bcol = B.m[i]; __m256 vx = _mm256_broadcastss_ps(bcol); // xxxx @@ -109,10 +107,8 @@ namespace { return R; } - #endif - #if 1 __attribute__((target("avx512f"))) - uf::simd::matrix_value matMult_impl( const uf::simd::matrix_value& A, const uf::simd::matrix_value& B) { + uf::simd::matrix_value _impl_matMult( const uf::simd::matrix_value& A, const uf::simd::matrix_value& B) { uf::simd::matrix_value R; uf::simd::matrix_value Bt = uf::simd::matTranspose(B); @@ -148,10 +144,9 @@ namespace { return R; } - #endif __attribute__((target("default"))) - uf::simd::vector matMult_impl( const uf::simd::matrix_value& M, uf::simd::vector v ) { + uf::simd::vector _impl_matMult( const uf::simd::matrix_value& M, uf::simd::vector v ) { __m128 vx = _mm_shuffle_ps(v, v, 0x00); __m128 vy = _mm_shuffle_ps(v, v, 0x55); __m128 vz = _mm_shuffle_ps(v, v, 0xAA); @@ -164,9 +159,8 @@ namespace { return _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3)); } - #if 1 __attribute__((target("fma"))) - uf::simd::vector matMult_impl( const uf::simd::matrix_value& M, uf::simd::vector v ) { + uf::simd::vector _impl_matMult( const uf::simd::matrix_value& M, uf::simd::vector v ) { __m128 vx = _mm_shuffle_ps(v, v, 0x00); __m128 vy = _mm_shuffle_ps(v, v, 0x55); __m128 vz = _mm_shuffle_ps(v, v, 0xAA); @@ -177,8 +171,134 @@ namespace { _mm_fmadd_ps(M.m[2], vz, _mm_mul_ps(M.m[3], vw)))); } +#else + uf::simd::matrix_value _impl_matMult( const uf::simd::matrix_value& A, const uf::simd::matrix_value& B) { + uf::simd::matrix_value R; + uf::simd::matrix_value Bt = uf::simd::matTranspose(B); + + #if 0 && SSE_INSTR_SET >= 9 // AVX512F + FOR_EACH(4, { + __m128 bcol = B.m[i]; + + __m512 vx = _mm512_set1_ps(((const float*)&bcol)[0]); // xxxx + __m512 vy = _mm512_set1_ps(((const float*)&bcol)[1]); // yyyy + __m512 vz = _mm512_set1_ps(((const float*)&bcol)[2]); // zzzz + __m512 vw = _mm512_set1_ps(((const float*)&bcol)[3]); // wwww + + __m512 a0 = _mm512_castps128_ps512(A.m[0]); + __m512 a1 = _mm512_castps128_ps512(A.m[1]); + __m512 a2 = _mm512_castps128_ps512(A.m[2]); + __m512 a3 = _mm512_castps128_ps512(A.m[3]); + + __m512 r = _mm512_fmadd_ps(a0, vx, + _mm512_fmadd_ps(a1, vy, + _mm512_fmadd_ps(a2, vz, + _mm512_mul_ps(a3, vw)))); + + __m128 r128 = _mm_add_ps( + _mm_add_ps( + _mm512_castps512_ps128(r), // low 128 + _mm512_extractf32x4_ps(r, 1)), // next 128 + _mm_add_ps( + _mm512_extractf32x4_ps(r, 2), // next 128 + _mm512_extractf32x4_ps(r, 3)) // high 128 + ); + + R.m[i] = r128; + }); + #elif 0 && SSE_INSTR_SET >= 8 && __FMA__ // AVX2,FMA + FOR_EACH_(4, { + __m128 bcol = B.m[i]; + + __m256 vx = _mm256_broadcastss_ps(bcol); // xxxx + __m256 vy = _mm256_broadcastss_ps(_mm_shuffle_ps(bcol, bcol, 0x55)); // yyyy + __m256 vz = _mm256_broadcastss_ps(_mm_shuffle_ps(bcol, bcol, 0xAA)); // zzzz + __m256 vw = _mm256_broadcastss_ps(_mm_shuffle_ps(bcol, bcol, 0xFF)); // wwww + + __m256 a0 = _mm256_castps128_ps256(A.m[0]); + __m256 a1 = _mm256_castps128_ps256(A.m[1]); + __m256 a2 = _mm256_castps128_ps256(A.m[2]); + __m256 a3 = _mm256_castps128_ps256(A.m[3]); + + __m256 r = _mm256_fmadd_ps(a0, vx, + _mm256_fmadd_ps(a1, vy, + _mm256_fmadd_ps(a2, vz, + _mm256_mul_ps(a3, vw)))); + + __m128 r128 = _mm_add_ps( + _mm256_castps256_ps128(r), + _mm256_extractf128_ps(r, 1) + ); + + R.m[i] = r128; + }); + #elif 0 && SSE_INSTR_SET >= 5 // SSE4.1 + FOR_EACH_(4, { + __m128 bcol = B.m[i]; + + __m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx + __m128 vy = _mm_shuffle_ps(bcol, bcol, 0x55); // yyyy + __m128 vz = _mm_shuffle_ps(bcol, bcol, 0xAA); // zzzz + __m128 vw = _mm_shuffle_ps(bcol, bcol, 0xFF); // wwww + + R.m[i] = _mm_add_ps( + _mm_add_ps( + _mm_mul_ps(A.m[0], vx), + _mm_mul_ps(A.m[1], vy)), + _mm_add_ps( + _mm_mul_ps(A.m[2], vz), + _mm_mul_ps(A.m[3], vw)) + ); + }); + #else + FOR_EACH_(4, { + __m128 bcol = B.m[i]; + + __m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx + __m128 vy = _mm_shuffle_ps(bcol, bcol, 0x55); // yyyy + __m128 vz = _mm_shuffle_ps(bcol, bcol, 0xAA); // zzzz + __m128 vw = _mm_shuffle_ps(bcol, bcol, 0xFF); // wwww + + R.m[i] = _mm_add_ps( + _mm_add_ps( + _mm_mul_ps(A.m[0], vx), + _mm_mul_ps(A.m[1], vy)), + _mm_add_ps( + _mm_mul_ps(A.m[2], vz), + _mm_mul_ps(A.m[3], vw)) + ); + }); #endif -} + return R; + } + + FORCE_INLINE uf::simd::vector _impl_matMult( const uf::simd::matrix_value& M, uf::simd::vector v ) { + #if __FMA__ + __m128 vx = _mm_shuffle_ps(v, v, 0x00); + __m128 vy = _mm_shuffle_ps(v, v, 0x55); + __m128 vz = _mm_shuffle_ps(v, v, 0xAA); + __m128 vw = _mm_shuffle_ps(v, v, 0xFF); + + return _mm_fmadd_ps(M.m[0], vx, + _mm_fmadd_ps(M.m[1], vy, + _mm_fmadd_ps(M.m[2], vz, + _mm_mul_ps(M.m[3], vw)))); + #else + __m128 vx = _mm_shuffle_ps(v, v, 0x00); + __m128 vy = _mm_shuffle_ps(v, v, 0x55); + __m128 vz = _mm_shuffle_ps(v, v, 0xAA); + __m128 vw = _mm_shuffle_ps(v, v, 0xFF); + + __m128 r0 = _mm_mul_ps(M.m[0], vx); + __m128 r1 = _mm_mul_ps(M.m[1], vy); + __m128 r2 = _mm_mul_ps(M.m[2], vz); + __m128 r3 = _mm_mul_ps(M.m[3], vw); + + return _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3)); + #endif + } +#endif +/**/} template FORCE_INLINE uf::simd::matrix_value::matrix_value() {} @@ -204,10 +324,10 @@ FORCE_INLINE uf::simd::matrix_value::operator pod::Matrix() const { } FORCE_INLINE uf::simd::matrix_value uf::simd::matMult( const uf::simd::matrix_value& A, const uf::simd::matrix_value& B ) { - return ::matMult_impl( A, B ); + return _impl_matMult( A, B ); } FORCE_INLINE uf::simd::vector uf::simd::matMult( const uf::simd::matrix_value& M, uf::simd::vector vec ) { - return ::matMult_impl( M, vec ); + return _impl_matMult( M, vec ); } FORCE_INLINE uf::simd::matrix_value uf::simd::matTranspose( const uf::simd::matrix_value& M ) { uf::simd::matrix_value R = M; diff --git a/engine/inc/uf/utils/math/physics/common.h b/engine/inc/uf/utils/math/physics/common.h index 908db8c5..a3774658 100644 --- a/engine/inc/uf/utils/math/physics/common.h +++ b/engine/inc/uf/utils/math/physics/common.h @@ -67,6 +67,9 @@ namespace impl { /*FORCE_INLINE*/ void boxAxes( pod::Vector3f axes[3], const pod::Transform<>& transform ); /*FORCE_INLINE*/ pod::Vector3f extentFromAxes( const pod::OBB& box, const pod::Vector3f axes[3] ); /*FORCE_INLINE*/ float projectExtents( const pod::OBB& box, const pod::Vector3f& normal, const pod::Vector3f axes[3] ); + + void getCorners( const pod::AABB& aabb, pod::Vector3f corners[8] ); + void getCorners( const pod::AABB& aabb, const pod::Transform<>& transform, pod::Vector3f corners[8] ); pod::AABB transformAabbToWorld( const pod::AABB& localBox, const pod::Transform<>& transform ); std::pair getCapsuleSegment( const pod::PhysicsBody& body ); pod::AABB computeAABB( const pod::PhysicsBody& body ); diff --git a/engine/inc/uf/utils/math/quaternion/pod.inl b/engine/inc/uf/utils/math/quaternion/pod.inl index 303ff95d..fd567c21 100644 --- a/engine/inc/uf/utils/math/quaternion/pod.inl +++ b/engine/inc/uf/utils/math/quaternion/pod.inl @@ -183,7 +183,7 @@ template pod::Quaternion uf::quaternion::lookAt( const pod::Vecto right.z, up.z, forward.z, 0, 0, 0, 0, 1 }); - return uf::quaternion::normalize( uf::quaternion::fromMatrix( m ) ); + return uf::quaternion::fromMatrix( m ); } diff --git a/engine/inc/uf/utils/math/transform.h b/engine/inc/uf/utils/math/transform.h index 7e3178bd..f66d8c8b 100644 --- a/engine/inc/uf/utils/math/transform.h +++ b/engine/inc/uf/utils/math/transform.h @@ -20,9 +20,9 @@ namespace pod { struct /*UF_API*/ Transform { typedef T type_t; - pod::Vector3t position = {0, 0,0 }; - pod::Quaternion orientation = {0, 0, 0, 1}; - pod::Vector3t scale = {1, 1, 1}; + alignas(16) pod::Vector3t position = {0, 0, 0 }; + alignas(16) pod::Quaternion orientation = {0, 0, 0, 1}; + alignas(16) pod::Vector3t scale = {1, 1, 1}; pod::Transform* reference = NULL; }; @@ -52,8 +52,9 @@ namespace uf { template pod::Transform& /*UF_API*/ reference( pod::Transform& transform, const pod::Transform& parent, bool reorient = true ); template pod::Transform /*UF_API*/ interpolate( const pod::Transform& from, const pod::Transform& to, float factor ); template pod::Transform /*UF_API*/ inverse(const pod::Transform& t); - template pod::Vector3t /*UF_API*/ apply( const pod::Transform& transform, const pod::Vector3t& point ); - template pod::Vector3t /*UF_API*/ applyInverse(const pod::Transform& t, const pod::Vector3t& worldPoint); + // forced inline because of a GCC quirk + template pod::Vector3t FORCE_INLINE /*UF_API*/ apply( const pod::Transform& transform, const pod::Vector3t& point ); + template pod::Vector3t FORCE_INLINE /*UF_API*/ applyInverse(const pod::Transform& t, const pod::Vector3t& worldPoint); template pod::Transform /*UF_API*/ relative(const pod::Transform& a, const pod::Transform& b); template uf::stl::string /*UF_API*/ toString( const pod::Transform&, bool flatten = true ); diff --git a/engine/inc/uf/utils/math/transform/transform.inl b/engine/inc/uf/utils/math/transform/transform.inl index 914c99e8..5546f197 100644 --- a/engine/inc/uf/utils/math/transform/transform.inl +++ b/engine/inc/uf/utils/math/transform/transform.inl @@ -121,9 +121,9 @@ pod::Matrix4t /*UF_API*/ uf::transform::model( const pod::Transform& trans template pod::Transform uf::transform::fromMatrix( const pod::Matrix4t& matrix ) { pod::Transform transform; - transform.position = uf::matrix::multiply( matrix, pod::Vector4f{ 0, 0, 0, 1 } ); - transform.orientation = uf::quaternion::fromMatrix( matrix ); - // transform.scale = ...; + transform.position = uf::matrix::extractTranslation( matrix ); + transform.orientation = uf::quaternion::fromMatrix( matrix ); + transform.scale = uf::matrix::extractScale( matrix ); return transform; } @@ -168,7 +168,7 @@ pod::Transform uf::transform::inverse(const pod::Transform& t) { } template -pod::Vector3t /*UF_API*/ uf::transform::apply( const pod::Transform& t, const pod::Vector3t& p ) { +pod::Vector3t uf::transform::apply( const pod::Transform& t, const pod::Vector3t& p ) { return uf::quaternion::rotate( t.orientation, p * t.scale ) + t.position; } diff --git a/engine/inc/uf/utils/math/vector.h b/engine/inc/uf/utils/math/vector.h index a28c2c19..627271e1 100644 --- a/engine/inc/uf/utils/math/vector.h +++ b/engine/inc/uf/utils/math/vector.h @@ -166,7 +166,7 @@ namespace uf { template /*FORCE_INLINE*/ typename T::type_t /*UF_API*/ magnitude( const T& vector ); // gets the magnitude of the vector template /*FORCE_INLINE*/ typename T::type_t /*UF_API*/ norm( const T& vector ); // compute the norm (length) of the vector template /*FORCE_INLINE*/ T /*UF_API*/ normalize( const T& vector ); // normalizes a vector - template /*FORCE_INLINE*/ T /*UF_API*/ clampMagnitude( const T& vector ); // clamps the magnitude of a vector + template /*FORCE_INLINE*/ T /*UF_API*/ clampMagnitude( const T& vector, float maxMag ); // clamps the magnitude of a vector template /*FORCE_INLINE*/ void /*UF_API*/ orthonormalize( T& x, T& y ); // orthonormalizes a vector against another vector template /*FORCE_INLINE*/ T /*UF_API*/ orthonormalize( const T& x, const T& y ); // orthonormalizes a vector against another vector diff --git a/engine/inc/uf/utils/math/vector/pod.inl b/engine/inc/uf/utils/math/vector/pod.inl index f8b43ae4..5d5928dd 100644 --- a/engine/inc/uf/utils/math/vector/pod.inl +++ b/engine/inc/uf/utils/math/vector/pod.inl @@ -12,8 +12,8 @@ constexpr void for_each_index(F&& f) { #include "simd.h" #endif -// #define FOR_EACH( N, F ) for ( auto i = 0; i < N; ++i ) F; #define FOR_EACH( N, F ) for_each_index([&](auto i) F ) +#define FOR_EACH_( N, F ) for ( auto i = 0; i < N; ++i ) F; template T elementwise( const T& left, const T& right, Op&& op ) { @@ -309,7 +309,7 @@ T uf::vector::divide( typename T::type_t scalar, const T& vector ) { } template typename T::type_t uf::vector::sum( const T& vector ) { - auto res = 0; + typename T::type_t res = 0; FOR_EACH(T::size, { res += vector[i]; }); @@ -317,7 +317,7 @@ typename T::type_t uf::vector::sum( const T& vector ) { } template typename T::type_t uf::vector::product( const T& vector ) { - auto res = 1; + typename T::type_t res = 1; FOR_EACH(T::size, { res *= vector[i]; }); diff --git a/engine/inc/uf/utils/math/vector/simd.inl b/engine/inc/uf/utils/math/vector/simd.inl index a86b2147..4fff354f 100644 --- a/engine/inc/uf/utils/math/vector/simd.inl +++ b/engine/inc/uf/utils/math/vector/simd.inl @@ -1,15 +1,15 @@ #include -namespace { - FORCE_INLINE __m128i bias_unsigned(__m128i v) { +/**/namespace { + FORCE_INLINE __m128i _impl_bias_unsigned(__m128i v) { const __m128i signbit = _mm_set1_epi32(0x80000000); return _mm_xor_si128(v, signbit); } - FORCE_INLINE int32_t boolMask(bool b) { + FORCE_INLINE int32_t _impl_bool_mask(bool b) { return b ? -1 : 0; // 0xFFFFFFFF for true, 0x00000000 for false } -} +/**/} #define MV_INSTR_SET_DEFAULT __attribute__((target("default"))) #define MV_INSTR_SET_2 __attribute__((target("sse2"))) @@ -202,13 +202,18 @@ FORCE_INLINE uf::simd::vector uf::simd::sqrt( uf::simd::vector v ) return _mm_sqrt_ps( v ); } -namespace { +/**/namespace { +#if SIMD_MV MV_INSTR_SET_DEFAULT - uf::simd::vector dot_impl( uf::simd::vector x, uf::simd::vector y ) { - return uf::simd::mul( x, y ); + uf::simd::vector _impl_dot( uf::simd::vector x, uf::simd::vector y ) { + __m128 mul = _mm_mul_ps(x, y); + __m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 3, 0, 1)); + __m128 sums = _mm_add_ps(mul, shuf); + shuf = _mm_movehl_ps(shuf, sums); + return _mm_add_ss(sums, shuf); } MV_INSTR_SET_3 - uf::simd::vector dot_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_dot( uf::simd::vector x, uf::simd::vector y ) { __m128 mulRes = _mm_mul_ps(x, y); __m128 shufReg = _mm_movehdup_ps(mulRes); __m128 sumsReg = _mm_add_ps(mulRes, shufReg); @@ -216,47 +221,93 @@ namespace { return _mm_add_ss(sumsReg, shufReg); } MV_INSTR_SET_5 - uf::simd::vector dot_impl( uf::simd::vector x, uf::simd::vector y ) { - return _mm_dp_ps(x, y, 0xF1); + uf::simd::vector _impl_dot( uf::simd::vector x, uf::simd::vector y ) { + return _mm_dp_ps(x, y, 0xFF); } -} +#else + FORCE_INLINE uf::simd::vector _impl_dot( uf::simd::vector x, uf::simd::vector y ) { + #if SSE_INSTR_SET >= 5 + return _mm_dp_ps(x, y, 0xFF); + #elif SSE_INSTR_SET >= 3 + __m128 mulRes = _mm_mul_ps(x, y); + __m128 shufReg = _mm_movehdup_ps(mulRes); + __m128 sumsReg = _mm_add_ps(mulRes, shufReg); + shufReg = _mm_movehl_ps(shufReg, sumsReg); + return _mm_add_ss(sumsReg, shufReg); + #else + __m128 mul = _mm_mul_ps(x, y); + __m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 3, 0, 1)); + __m128 sums = _mm_add_ps(mul, shuf); + shuf = _mm_movehl_ps(shuf, sums); + return _mm_add_ss(sums, shuf); + #endif + } +#endif +/**/} FORCE_INLINE float uf::simd::dot( uf::simd::vector x, uf::simd::vector y ) { - return _mm_cvtss_f32( ::dot_impl( x, y ) ); + return _mm_cvtss_f32( _impl_dot( x, y ) ); } -namespace { +/**/namespace { +#if SIMD_MV MV_INSTR_SET_DEFAULT - uf::simd::vector load_impl( const int32_t* f ) { + uf::simd::vector _impl_load( const int32_t* f ) { return uf::simd::vector( f[0], f[1], f[2], f[3] ); } MV_INSTR_SET_3 - uf::simd::vector load_impl( const int32_t* f ) { + uf::simd::vector _impl_load( const int32_t* f ) { // if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast(f)); - return _mm_loadu_si128(reinterpret_cast(f)); + return _mm_loadu_si128(reinterpret_cast(f));// } -} +#else + FORCE_INLINE uf::simd::vector _impl_load( const int32_t* f ) { + // if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast(f)); + #if SSE_INSTR_SET >= 3 + return _mm_loadu_si128(reinterpret_cast(f));// + #else + return uf::simd::vector( f[0], f[1], f[2], f[3] ); + #endif + } +#endif +/**/} FORCE_INLINE uf::simd::vector uf::simd::load( const int32_t* f ) { - return ::load_impl( f ); + return _impl_load( f ); } -namespace { +/**/namespace { +#if SIMD_MV MV_INSTR_SET_DEFAULT - void store_impl( uf::simd::vector v, int32_t* f ) { + void _impl_store( uf::simd::vector v, int32_t* f ) { union { __m128i x; int32_t y[4]; } kludge; kludge.x = v; f[0] = kludge.y[0]; f[1] = kludge.y[1]; f[2] = kludge.y[2]; - f[3] = kludge.y[3]; + f[3] = kludge.y[3];// } MV_INSTR_SET_3 - void store_impl( uf::simd::vector v, int32_t* f ) { + void _impl_store( uf::simd::vector v, int32_t* f ) { /*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v); else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v); } -} +#else + FORCE_INLINE void _impl_store( uf::simd::vector v, int32_t* f ) { + #if SSE_INSTR_SET >= 3 + /*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v); + else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v); + #else + union { __m128i x; int32_t y[4]; } kludge; + kludge.x = v; + f[0] = kludge.y[0]; + f[1] = kludge.y[1]; + f[2] = kludge.y[2]; + f[3] = kludge.y[3];// + #endif + } +#endif +/**/} FORCE_INLINE void uf::simd::store( uf::simd::vector v, int32_t* f ) { - return ::store_impl( v, f ); + return _impl_store( v, f ); } @@ -273,20 +324,32 @@ FORCE_INLINE uf::simd::vector uf::simd::sub( uf::simd::vector return _mm_sub_epi32(x, y); } -namespace { +/**/namespace { +#if SIMD_MV MV_INSTR_SET_DEFAULT - uf::simd::vector mul_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_mul( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast(x); auto Y = uf::simd::cast(y); return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]); } MV_INSTR_SET_4 - uf::simd::vector mul_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_mul( uf::simd::vector x, uf::simd::vector y ) {// return _mm_mullo_epi32(x, y); } -} +#else + FORCE_INLINE uf::simd::vector _impl_mul( uf::simd::vector x, uf::simd::vector y ) {// + #if SSE_INSTR_SET >= 4 + return _mm_mullo_epi32(x, y); + #else + auto X = uf::simd::cast(x); + auto Y = uf::simd::cast(y); + return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]); + #endif + } +#endif +/**/} FORCE_INLINE uf::simd::vector uf::simd::mul( uf::simd::vector x, uf::simd::vector y ) { - return ::mul_impl( x, y ); + return _impl_mul( x, y ); } @@ -302,34 +365,56 @@ FORCE_INLINE uf::simd::vector uf::simd::hadd( uf::simd::vector return uf::simd::set( X[0] + Y[0], X[1] + Y[1], X[2] + Y[2], X[3] + Y[3] ); } */ -namespace { +/**/namespace { +#if SIMD_MV MV_INSTR_SET_DEFAULT - uf::simd::vector min_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_min( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast(x); auto Y = uf::simd::cast(y); return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3])); } MV_INSTR_SET_4 - uf::simd::vector min_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_min( uf::simd::vector x, uf::simd::vector y ) {// return _mm_min_epi32(x, y); } MV_INSTR_SET_DEFAULT - uf::simd::vector max_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_max( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast(x); auto Y = uf::simd::cast(y); return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3])); } MV_INSTR_SET_4 - uf::simd::vector max_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_max( uf::simd::vector x, uf::simd::vector y ) { return _mm_max_epi32(x, y); } -} +#else + FORCE_INLINE uf::simd::vector _impl_min( uf::simd::vector x, uf::simd::vector y ) {// + #if SSE_INSTR_SET >= 4 + return _mm_min_epi32(x, y); + #else + auto X = uf::simd::cast(x); + auto Y = uf::simd::cast(y); + return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3])); + #endif + } + + FORCE_INLINE uf::simd::vector _impl_max( uf::simd::vector x, uf::simd::vector y ) { + #if SSE_INSTR_SET >= 4 + return _mm_max_epi32(x, y); + #else + auto X = uf::simd::cast(x); + auto Y = uf::simd::cast(y); + return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3])); + #endif + } +#endif +/**/} FORCE_INLINE uf::simd::vector uf::simd::min( uf::simd::vector x, uf::simd::vector y ) { - return ::min_impl(x, y); + return _impl_min(x, y); } FORCE_INLINE uf::simd::vector uf::simd::max( uf::simd::vector x, uf::simd::vector y ) { - return ::max_impl(x, y); + return _impl_max(x, y); } @@ -340,60 +425,100 @@ FORCE_INLINE bool uf::simd::any( uf::simd::vector mask) { return _mm_movemask_epi8( mask ) != 0x0; // any bit set } -namespace { +/**/namespace { +#if SIMD_MV MV_INSTR_SET_DEFAULT - uf::simd::vector less_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_less( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); return uf::simd::set_i(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]); } MV_INSTR_SET_4 - uf::simd::vector less_impl( uf::simd::vector x, uf::simd::vector y ) { - return _mm_cmplt_epi32( x, y ); + uf::simd::vector _impl_less( uf::simd::vector x, uf::simd::vector y ) { + return _mm_cmplt_epi32( x, y );// } MV_INSTR_SET_DEFAULT - uf::simd::vector lessEquals_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_lessEquals( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); return uf::simd::set_i(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]); } MV_INSTR_SET_4 - uf::simd::vector lessEquals_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_lessEquals( uf::simd::vector x, uf::simd::vector y ) { __m128i gt = _mm_cmpgt_epi32(x, y); return _mm_xor_si128(gt, _mm_set1_epi32(-1)); } MV_INSTR_SET_DEFAULT - uf::simd::vector greater_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_greater( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); return uf::simd::set_i(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]); } MV_INSTR_SET_4 - uf::simd::vector greater_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_greater( uf::simd::vector x, uf::simd::vector y ) { return _mm_cmpgt_epi32(x, y); } MV_INSTR_SET_DEFAULT - uf::simd::vector greaterEquals_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_greaterEquals( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); return uf::simd::set_i(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]); } MV_INSTR_SET_4 - uf::simd::vector greaterEquals_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_greaterEquals( uf::simd::vector x, uf::simd::vector y ) { __m128i gt = _mm_cmplt_epi32(x, y); return _mm_xor_si128(gt, _mm_set1_epi32(-1)); } -} +#else + FORCE_INLINE uf::simd::vector _impl_less( uf::simd::vector x, uf::simd::vector y ) { + #if SSE_INSTR_SET >= 4 + return _mm_cmplt_epi32( x, y );// + #else + auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); + return uf::simd::set_i(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]); + #endif + } + + FORCE_INLINE uf::simd::vector _impl_lessEquals( uf::simd::vector x, uf::simd::vector y ) { + #if SSE_INSTR_SET >= 4 + __m128i gt = _mm_cmpgt_epi32(x, y); + return _mm_xor_si128(gt, _mm_set1_epi32(-1)); + #else + auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); + return uf::simd::set_i(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]); + #endif + } + + FORCE_INLINE uf::simd::vector _impl_greater( uf::simd::vector x, uf::simd::vector y ) { + #if SSE_INSTR_SET >= 4 + return _mm_cmpgt_epi32(x, y); + #else + auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); + return uf::simd::set_i(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]); + #endif + } + + FORCE_INLINE uf::simd::vector _impl_greaterEquals( uf::simd::vector x, uf::simd::vector y ) { + #if SSE_INSTR_SET >= 4 + __m128i gt = _mm_cmplt_epi32(x, y); + return _mm_xor_si128(gt, _mm_set1_epi32(-1)); + #else + auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); + return uf::simd::set_i(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]); + #endif + } +#endif +/**/} FORCE_INLINE uf::simd::vector uf::simd::less( uf::simd::vector x, uf::simd::vector y ) { - return ::less_impl( x, y ); + return _impl_less( x, y ); } FORCE_INLINE uf::simd::vector uf::simd::lessEquals( uf::simd::vector x, uf::simd::vector y ) { - return ::lessEquals_impl( x, y ); + return _impl_lessEquals( x, y ); } FORCE_INLINE uf::simd::vector uf::simd::greater( uf::simd::vector x, uf::simd::vector y ) { - return ::greater_impl( x, y ); + return _impl_greater( x, y ); } FORCE_INLINE uf::simd::vector uf::simd::greaterEquals( uf::simd::vector x, uf::simd::vector y ) { - return ::greaterEquals_impl( x, y ); + return _impl_greaterEquals( x, y ); } FORCE_INLINE uf::simd::vector uf::simd::equals( uf::simd::vector x, uf::simd::vector y ) { @@ -412,19 +537,20 @@ FORCE_INLINE int32_t uf::simd::dot( uf::simd::vector x, uf::simd::vecto return X[0] * Y[0] + X[1] * Y[1] + X[2] * Y[2] + X[3] * Y[3]; } -namespace { +/**/namespace { +#if SIMD_MV MV_INSTR_SET_DEFAULT - uf::simd::vector load_impl( const uint32_t* f ) { + uf::simd::vector _impl_load( const uint32_t* f ) { return uf::simd::vector( f[0], f[1], f[2], f[3] ); } MV_INSTR_SET_3 - uf::simd::vector load_impl( const uint32_t* f ) { + uf::simd::vector _impl_load( const uint32_t* f ) { // if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast(f)); - return _mm_loadu_si128(reinterpret_cast(f)); + return _mm_loadu_si128(reinterpret_cast(f));// } MV_INSTR_SET_DEFAULT - void store_impl( uf::simd::vector v, uint32_t* f ) { + void _impl_store( uf::simd::vector v, uint32_t* f ) { union { __m128i x; uint32_t y[4]; } kludge; kludge.x = v; f[0] = kludge.y[0]; @@ -433,16 +559,40 @@ namespace { f[3] = kludge.y[3]; } MV_INSTR_SET_3 - void store_impl( uf::simd::vector v, uint32_t* f ) { + void _impl_store( uf::simd::vector v, uint32_t* f ) { /*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v); else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v); } -} +#else + FORCE_INLINE uf::simd::vector _impl_load( const uint32_t* f ) { + #if SSE_INSTR_SET >= 3 + // if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast(f)); + return _mm_loadu_si128(reinterpret_cast(f));// + #else + return uf::simd::vector( f[0], f[1], f[2], f[3] ); + #endif + } + + FORCE_INLINE void _impl_store( uf::simd::vector v, uint32_t* f ) { + #if SSE_INSTR_SET >= 3 + /*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v); + else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v); + #else + union { __m128i x; uint32_t y[4]; } kludge; + kludge.x = v; + f[0] = kludge.y[0]; + f[1] = kludge.y[1]; + f[2] = kludge.y[2]; + f[3] = kludge.y[3]; + #endif + } +#endif +/**/} FORCE_INLINE uf::simd::vector uf::simd::load( const uint32_t* f ) { - return ::load_impl( f ); + return _impl_load( f ); } FORCE_INLINE void uf::simd::store( uf::simd::vector v, uint32_t* f ) { - return ::store_impl( v, f ); + return _impl_store( v, f ); } FORCE_INLINE uf::simd::vector uf::simd::set( uint32_t f ) { @@ -458,20 +608,32 @@ FORCE_INLINE uf::simd::vector uf::simd::sub( uf::simd::vector mul_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_mul( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast(x); auto Y = uf::simd::cast(y); return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]); } MV_INSTR_SET_4 - uf::simd::vector mul_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_mul( uf::simd::vector x, uf::simd::vector y ) {// return _mm_mullo_epi32(x, y); } -} +#else + FORCE_INLINE uf::simd::vector _impl_mul( uf::simd::vector x, uf::simd::vector y ) {// + #if SSE_INSTR_SET >= 4 + return _mm_mullo_epi32(x, y); + #else + auto X = uf::simd::cast(x); + auto Y = uf::simd::cast(y); + return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]); + #endif + } +#endif +/**/} FORCE_INLINE uf::simd::vector uf::simd::mul( uf::simd::vector x, uf::simd::vector y ) { - return ::mul_impl( x, y ); + return _impl_mul( x, y ); } FORCE_INLINE uf::simd::vector uf::simd::div( uf::simd::vector x, uf::simd::vector y ) { @@ -487,34 +649,56 @@ FORCE_INLINE uf::simd::vector uf::simd::hadd( uf::simd::vector min_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_min( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast(x); auto Y = uf::simd::cast(y); return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3])); } MV_INSTR_SET_4 - uf::simd::vector min_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_min( uf::simd::vector x, uf::simd::vector y ) {// return _mm_min_epu32(x, y); // unsigned min } MV_INSTR_SET_DEFAULT - uf::simd::vector max_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_max( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast(x); auto Y = uf::simd::cast(y); return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3])); } MV_INSTR_SET_4 - uf::simd::vector max_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_max( uf::simd::vector x, uf::simd::vector y ) { return _mm_max_epu32(x, y); // unsigned max } -} +#else + FORCE_INLINE uf::simd::vector _impl_min( uf::simd::vector x, uf::simd::vector y ) {// + #if SSE_INSTR_SET >= 4 + return _mm_min_epu32(x, y); // unsigned min + #else + auto X = uf::simd::cast(x); + auto Y = uf::simd::cast(y); + return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3])); + #endif + } + + FORCE_INLINE uf::simd::vector _impl_max( uf::simd::vector x, uf::simd::vector y ) { + #if SSE_INSTR_SET >= 4 + return _mm_max_epu32(x, y); // unsigned max + #else + auto X = uf::simd::cast(x); + auto Y = uf::simd::cast(y); + return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3])); + #endif + } +#endif +/**/} FORCE_INLINE uf::simd::vector uf::simd::min( uf::simd::vector x, uf::simd::vector y ) { - return ::min_impl(x, y); + return _impl_min(x, y); } FORCE_INLINE uf::simd::vector uf::simd::max( uf::simd::vector x, uf::simd::vector y ) { - return ::max_impl(x, y); + return _impl_max(x, y); } FORCE_INLINE bool uf::simd::all( uf::simd::vector mask) { @@ -524,66 +708,112 @@ FORCE_INLINE bool uf::simd::any( uf::simd::vector mask) { return _mm_movemask_epi8( mask ) != 0x0; // any bit set } -namespace { +/**/namespace { +#if SIMD_MV MV_INSTR_SET_DEFAULT - uf::simd::vector less_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_less( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); return uf::simd::set_ui(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]); } MV_INSTR_SET_4 - uf::simd::vector less_impl( uf::simd::vector x, uf::simd::vector y ) { - return _mm_cmplt_epi32( ::bias_unsigned(x), ::bias_unsigned(y) ); + uf::simd::vector _impl_less( uf::simd::vector x, uf::simd::vector y ) { + return _mm_cmplt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );// } MV_INSTR_SET_DEFAULT - uf::simd::vector lessEquals_impl( uf::simd::vector x, uf::simd::vector y) { + uf::simd::vector _impl_lessEquals( uf::simd::vector x, uf::simd::vector y) { auto X = uf::simd::cast(x), Y = uf::simd::cast(y); return uf::simd::set_ui(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]); } MV_INSTR_SET_2 - uf::simd::vector lessEquals_impl( uf::simd::vector x, uf::simd::vector y) { + uf::simd::vector _impl_lessEquals( uf::simd::vector x, uf::simd::vector y) { // a <= b <=> !(a > b) - __m128i bx = ::bias_unsigned(x); - __m128i by = ::bias_unsigned(y); + __m128i bx = _impl_bias_unsigned(x); + __m128i by = _impl_bias_unsigned(y); __m128i gt = _mm_cmpgt_epi32(bx, by); // signed compare return _mm_xor_si128(gt, _mm_set1_epi32(-1)); // invert mask } MV_INSTR_SET_DEFAULT - uf::simd::vector greater_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_greater( uf::simd::vector x, uf::simd::vector y ) { auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); return uf::simd::set_ui(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]); } MV_INSTR_SET_4 - uf::simd::vector greater_impl( uf::simd::vector x, uf::simd::vector y ) { - return _mm_cmpgt_epi32( ::bias_unsigned(x), ::bias_unsigned(y) ); + uf::simd::vector _impl_greater( uf::simd::vector x, uf::simd::vector y ) { + return _mm_cmpgt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) ); } MV_INSTR_SET_DEFAULT - uf::simd::vector greaterEquals_impl( uf::simd::vector x, uf::simd::vector y) { + uf::simd::vector _impl_greaterEquals( uf::simd::vector x, uf::simd::vector y) { auto X = uf::simd::cast(x), Y = uf::simd::cast(y); return uf::simd::set_ui(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]); } MV_INSTR_SET_2 - uf::simd::vector greaterEquals_impl( uf::simd::vector x, uf::simd::vector y) { + uf::simd::vector _impl_greaterEquals( uf::simd::vector x, uf::simd::vector y) { // a >= b <=> !(a < b) - __m128i bx = ::bias_unsigned(x); - __m128i by = ::bias_unsigned(y); + __m128i bx = _impl_bias_unsigned(x); + __m128i by = _impl_bias_unsigned(y); __m128i lt = _mm_cmplt_epi32(bx, by); // signed compare return _mm_xor_si128(lt, _mm_set1_epi32(-1)); // invert mask } -} +#else + FORCE_INLINE uf::simd::vector _impl_less( uf::simd::vector x, uf::simd::vector y ) { + #if SSE_INSTR_SET >= 4 + return _mm_cmplt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );// + #else + auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); + return uf::simd::set_ui(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]); + #endif + } + + FORCE_INLINE uf::simd::vector _impl_lessEquals( uf::simd::vector x, uf::simd::vector y) { + #if SSE_INSTR_SET >= 2 + // a <= b <=> !(a > b) + __m128i bx = _impl_bias_unsigned(x); + __m128i by = _impl_bias_unsigned(y); + __m128i gt = _mm_cmpgt_epi32(bx, by); // signed compare + return _mm_xor_si128(gt, _mm_set1_epi32(-1)); // invert mask + #else + auto X = uf::simd::cast(x), Y = uf::simd::cast(y); + return uf::simd::set_ui(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]); + #endif + } + + FORCE_INLINE uf::simd::vector _impl_greater( uf::simd::vector x, uf::simd::vector y ) { + #if SSE_INSTR_SET >= 4 + return _mm_cmpgt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) ); + #else + auto X = uf::simd::cast( x ), Y = uf::simd::cast( y ); + return uf::simd::set_ui(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]); + #endif + } + + FORCE_INLINE uf::simd::vector _impl_greaterEquals( uf::simd::vector x, uf::simd::vector y) { + #if SSE_INSTR_SET >= 2 + // a >= b <=> !(a < b) + __m128i bx = _impl_bias_unsigned(x); + __m128i by = _impl_bias_unsigned(y); + __m128i lt = _mm_cmplt_epi32(bx, by); // signed compare + return _mm_xor_si128(lt, _mm_set1_epi32(-1)); // invert mask + #else + auto X = uf::simd::cast(x), Y = uf::simd::cast(y); + return uf::simd::set_ui(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]); + #endif + } +#endif +/**/} FORCE_INLINE uf::simd::vector uf::simd::less( uf::simd::vector x, uf::simd::vector y ) { - return ::less_impl( x, y ); + return _impl_less( x, y ); } FORCE_INLINE uf::simd::vector uf::simd::lessEquals( uf::simd::vector x, uf::simd::vector y ) { - return ::lessEquals_impl( x, y ); + return _impl_lessEquals( x, y ); } FORCE_INLINE uf::simd::vector uf::simd::greater( uf::simd::vector x, uf::simd::vector y ) { - return ::greater_impl( x, y ); + return _impl_greater( x, y ); } FORCE_INLINE uf::simd::vector uf::simd::greaterEquals( uf::simd::vector x, uf::simd::vector y ) { - return ::greaterEquals_impl( x, y ); + return _impl_greaterEquals( x, y ); } @@ -604,28 +834,29 @@ FORCE_INLINE uint32_t uf::simd::dot( uf::simd::vector x, uf::simd::vec } FORCE_INLINE uf::simd::vector uf::simd::set_f( bool x, bool y, bool z, bool w ) { - return _mm_castsi128_ps(_mm_setr_epi32(::boolMask(x), ::boolMask(y), ::boolMask(z), ::boolMask(w))); + return _mm_castsi128_ps(_mm_setr_epi32(_impl_bool_mask(x), _impl_bool_mask(y), _impl_bool_mask(z), _impl_bool_mask(w))); } FORCE_INLINE uf::simd::vector uf::simd::set_i( bool x, bool y, bool z, bool w ) { - return _mm_setr_epi32(::boolMask(x), ::boolMask(y), ::boolMask(z), ::boolMask(w)); + return _mm_setr_epi32(_impl_bool_mask(x), _impl_bool_mask(y), _impl_bool_mask(z), _impl_bool_mask(w)); } FORCE_INLINE uf::simd::vector uf::simd::set_ui( bool x, bool y, bool z, bool w ) { - return _mm_setr_epi32(::boolMask(x), ::boolMask(y), ::boolMask(z), ::boolMask(w)); + return _mm_setr_epi32(_impl_bool_mask(x), _impl_bool_mask(y), _impl_bool_mask(z), _impl_bool_mask(w)); } -namespace { +/**/namespace { +#if SIMD_MV MV_INSTR_SET_DEFAULT - uf::simd::vector cross_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_cross( uf::simd::vector x, uf::simd::vector y ) { __m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1)); __m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1)); tmp0 = _mm_mul_ps(tmp0,x); tmp1 = _mm_mul_ps(tmp1,y); __m128 tmp2 = _mm_sub_ps(tmp0,tmp1); - __m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1)); + __m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));// return res; } MV_INSTR_SET_7 - uf::simd::vector cross_impl( uf::simd::vector x, uf::simd::vector y ) { + uf::simd::vector _impl_cross( uf::simd::vector x, uf::simd::vector y ) { __m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1)); __m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1)); tmp1 = _mm_mul_ps(tmp1,y); @@ -633,17 +864,37 @@ namespace { __m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1)); return res; } -} +#else + uf::simd::vector _impl_cross( uf::simd::vector x, uf::simd::vector y ) { + #if SSE_INSTR_SET >= 7 + __m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1)); + __m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1)); + tmp1 = _mm_mul_ps(tmp1,y); + __m128 tmp2 = _mm_fmsub_ps( tmp0,x, tmp1 ); + __m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1)); + return res; + #else + __m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1)); + __m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1)); + tmp0 = _mm_mul_ps(tmp0,x); + tmp1 = _mm_mul_ps(tmp1,y); + __m128 tmp2 = _mm_sub_ps(tmp0,tmp1); + __m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));// + return res; + #endif + } +#endif +/**/} FORCE_INLINE uf::simd::vector uf::simd::cross( uf::simd::vector x, uf::simd::vector y ) { - return ::cross_impl( x, y ); + return _impl_cross( x, y ); } FORCE_INLINE uf::simd::vector uf::simd::normalize( uf::simd::vector v ) { - __m128 len = _mm_sqrt_ss( ::dot_impl( v,v ) ); + __m128 len = _mm_sqrt_ss( _impl_dot( v,v ) ); len = _mm_shuffle_ps(len, len, 0x00); return _mm_div_ps(v, len); } FORCE_INLINE uf::simd::vector uf::simd::normalize_fast( uf::simd::vector v ) { - __m128 invLen = _mm_rsqrt_ps(::dot_impl(v, v)); + __m128 invLen = _mm_rsqrt_ps(_impl_dot(v, v)); return _mm_mul_ps(v, invLen); } \ No newline at end of file diff --git a/engine/inc/uf/utils/mesh/mesh.h b/engine/inc/uf/utils/mesh/mesh.h index 962a041a..a77e8c99 100644 --- a/engine/inc/uf/utils/mesh/mesh.h +++ b/engine/inc/uf/utils/mesh/mesh.h @@ -311,8 +311,9 @@ namespace uf { const buffer_t& getBuffer( const uf::Mesh::Input&, size_t = 0 ) const; const buffer_t& getBuffer( const uf::Mesh::Input&, const uf::Mesh::Attribute& ) const; - void eraseAttribute( uf::Mesh::Input&, const uf::Mesh::Attribute& ); - void eraseAttribute( uf::Mesh::Input&, size_t ); + void clearAttribute( uf::Mesh::Input&, const uf::Mesh::Attribute& ); + void clearAttribute( uf::Mesh::Input&, size_t ); + void clear(); uf::Mesh::Input remapInput( const uf::Mesh::Input&, size_t = 0, size_t = 0 ) const; uf::Mesh::Input remapVertexInput( size_t i = 0, size_t = 0 ) const; diff --git a/engine/inc/uf/utils/text/glyph_.h b/engine/inc/uf/utils/text/glyph_.h new file mode 100644 index 00000000..40a2463b --- /dev/null +++ b/engine/inc/uf/utils/text/glyph_.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include +#include +#include + + +namespace pod { + struct GlyphBox { + struct { + float x, y, w, h, z; + } box; + + pod::Vector4f color; + uint64_t code; + }; + struct TextToken { + uf::stl::string text; + pod::Vector4f color; + }; + struct GlyphSettings { + uf::stl::string alignment = ""; + uf::stl::string font = "Coolvetica.ttf"; + float size = 96; + bool sdf = true; + float spread = 8; + pod::Vector2ui padding = { 8, 8 }; + }; +} + +namespace uf { + namespace glyph { + size_t UF_API hashSettings( uint64_t c, const pod::GlyphSettings& metadata ); + size_t UF_API hashSettings( const uf::stl::string& c, const pod::GlyphSettings& metadata ); + + uf::stl::vector UF_API parseTextTokens( const uf::stl::string& text, const pod::Vector4f& color = {1,1,1,1} ); + uf::stl::vector UF_API calculateLayout( const uf::stl::vector& tokens, const pod::GlyphSettings& metadata ); + bool UF_API generateAtlas( const uf::stl::vector& layout, const pod::GlyphSettings& metadata, uf::Atlas& atlas ); + void UF_API generateMesh( const uf::stl::vector& layout, const pod::GlyphSettings& metadata, const uf::Atlas& atlas, uf::Mesh& mesh ); + } +} \ No newline at end of file diff --git a/engine/src/engine/ext/ext.cpp b/engine/src/engine/ext/ext.cpp index 2b0008a1..120b8d4b 100644 --- a/engine/src/engine/ext/ext.cpp +++ b/engine/src/engine/ext/ext.cpp @@ -205,6 +205,7 @@ void UF_API uf::load( ext::json::Value& json ) { uf::physics::settings.timestep = configEnginePhysicsJson["timestep"].as(uf::physics::settings.timestep); 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.flattenTransforms = configEnginePhysicsJson["flatten transforms"].as(uf::physics::settings.flattenTransforms); auto& configEnginePhysicsSolverJson = configEnginePhysicsJson["solvers"]; if ( ext::json::isObject( configEnginePhysicsSolverJson ) ) { diff --git a/engine/src/engine/ext/gui/glyph/behavior.cpp b/engine/src/engine/ext/gui/glyph/behavior.cpp index a2423a41..e73668de 100644 --- a/engine/src/engine/ext/gui/glyph/behavior.cpp +++ b/engine/src/engine/ext/gui/glyph/behavior.cpp @@ -2,6 +2,8 @@ #include #include +#include + #include #include #include @@ -12,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -20,359 +23,16 @@ #include #include #include +#include #include "../payload.h" #include "../behavior.h" #include "../manager/behavior.h" -#if UF_USE_OPENGL -#define EXT_COLOR_FLOATS 0 -#else -#define EXT_COLOR_FLOATS 1 -#endif - -namespace { - struct Mesh { - pod::Vector3f position; - pod::Vector2f uv; - - #if EXT_COLOR_FLOATS - pod::Vector4f color; - #else - pod::Vector4b color; - #endif - - static uf::stl::vector descriptor; - }; -} - -UF_VERTEX_DESCRIPTOR(Mesh, - UF_VERTEX_DESCRIPTION(Mesh, R32G32B32_SFLOAT, position) - UF_VERTEX_DESCRIPTION(Mesh, R32G32_SFLOAT, uv) -#if EXT_COLOR_FLOATS - UF_VERTEX_DESCRIPTION(Mesh, R32G32B32A32_SFLOAT, color) -#else - UF_VERTEX_DESCRIPTION(Mesh, R8G8B8A8_UNORM, color) -#endif -) - -#include - -#include - -namespace { - const uint64_t COLOR_CTRL = 0x7F; - - struct GlyphBox { - struct { - float x, y, w, h; - } box; - - pod::Vector3f color; - uint64_t code; - }; - - struct { - #if UF_USE_FREETYPE - ext::freetype::Glyph glyph; - uf::stl::unordered_map> cache; - #else - char glyph; - uf::stl::unordered_map> cache; - #endif - } glyphs; - - struct { - uf::Serializer settings; - #if UF_ENV_DREAMCAST - pod::Vector2ui size = { 640, 480 }; - #else - pod::Vector2ui size = { 1920, 1080 }; - #endif - } defaults; -} - UF_BEHAVIOR_REGISTER_CPP(ext::GuiGlyphBehavior) UF_BEHAVIOR_TRAITS_CPP(ext::GuiGlyphBehavior, ticks = true, renders = false, thread = "") #define this (&self) -namespace { - size_t hashGlyphSettings( uint64_t c, const ext::GuiGlyphBehavior::Metadata& metadata ) { - size_t seed{}; - uf::hash( seed, c, metadata.padding[0], metadata.padding[1], metadata.spread, metadata.size, metadata.font, metadata.sdf ); - return seed; - } - size_t hashGlyphSettings( const uf::stl::string& c, const ext::GuiGlyphBehavior::Metadata& metadata ) { - size_t seed{}; - uf::hash( seed, c, metadata.padding[0], metadata.padding[1], metadata.spread, metadata.size, metadata.font, metadata.sdf ); - return seed; - } - - // default to left - pod::Vector2f parseAnchor( const uf::stl::string& anchor, const pod::Vector2f& def = {0.0f, 0.0f} ) { - if ( anchor == "top-center" || anchor == "top" ) return {0.5f, 0.0f}; - if ( anchor == "top-left" ) return {0.0f, 0.0f}; - if ( anchor == "top-right" ) return {1.0f, 0.0f}; - - if ( anchor == "center" ) return {0.5f, 0.5f}; - if ( anchor == "center-left" || anchor == "left" ) return {0.0f, 0.5f}; - if ( anchor == "center-right" || anchor == "right" ) return {1.0f, 0.5f}; - - if ( anchor == "bottom-center" || anchor == "bottom" ) return {0.5f, 1.0f}; - if ( anchor == "bottom-left" ) return {0.0f, 1.0f}; - if ( anchor == "bottom-right" ) return {1.0f, 1.0f}; - - return def; - } - - struct TextToken { - uf::stl::string text; - pod::Vector3f color; - }; - - float hexToFloat( const uf::stl::string& str ) { - int value; - uf::stl::stringstream stream; - stream << str; - stream >> std::hex >> value; - return value / 255.0f; - } - - // parses a text for special tokens, associating strings with color - // should maybe be utf8, but in theory it shouldn't matter since tags are ASCII - uf::stl::vector parseTextTokens( const uf::stl::string& text, pod::Vector3f color ) { - uf::stl::vector tokens; - - auto tagLength = 10; // hard-coded cringe - bool colorChanged = false; - size_t currentPos = 0; - size_t textLength = text.length(); - - TextToken currentToken; - currentToken.color = color; - - while ( currentPos < textLength ) { - size_t tagStart = text.find("${#", currentPos); - - if ( tagStart == uf::stl::string::npos ) { - currentToken.text += text.substr(currentPos); - if ( !currentToken.text.empty() || colorChanged ) { - tokens.emplace_back(currentToken); - } - break; - } - - if ( tagStart > currentPos ) { - currentToken.text += text.substr( currentPos, tagStart - currentPos ); // append everything up to the tag - tokens.emplace_back( currentToken ); // commit token - currentToken.text = ""; // reset text - } - - // validate tag - if ( tagStart + tagLength <= textLength && text[tagStart + tagLength - 1] == '}' ) { - uf::stl::string rHex = text.substr(tagStart + 3, 2); - uf::stl::string gHex = text.substr(tagStart + 5, 2); - uf::stl::string bHex = text.substr(tagStart + 7, 2); - - // change color - currentToken.color = { hexToFloat(rHex), hexToFloat(gHex), hexToFloat(bHex) }; - colorChanged = true; - - // advance past the tag - currentPos = tagStart + tagLength; - } else { - currentToken.text += "${#"; // treat it as normal text - currentPos = tagStart + 3; - } - } - - return tokens; - } - - // compute the boxes for a given string and settings - uf::stl::vector calculateGlyphLayout( uf::Object& self, const uf::stl::vector& tokens, const ext::GuiGlyphBehavior::Metadata& metadata, const ext::GuiBehavior::Metadata& metadataGui, const uf::stl::string& font ) { - uf::stl::vector layout; - auto& glyphsCache = ::glyphs.cache[font]; - - if ( glyphsCache.empty() ) { - ext::freetype::initialize( ::glyphs.glyph, font ); - } - - pod::Vector2f anchor = ::parseAnchor( metadataGui.alignment ); - pod::Vector2f cursor = { 0.0f, 0.0f }; - float maxTextWidth = 0.0f; - float maxTextHeight = 0.0f; - float tallestGlyphY = 0.0f; - float averageTabWidth = 0.0f; - float totalWidth = 0.0f; - int charCount = 0; - - // generate glyph and line height - for ( const auto& token : tokens ) { - std::u8string str(token.text.begin(), token.text.end()); - for ( uint64_t c : str ) { - if ( c == '\n' || c == '\t' ) continue; // special characters - - auto key = hashGlyphSettings(c, metadata); - auto& glyph = glyphsCache[key]; - - // generate glyph - if ( !glyph.generated() ) { - glyph.setPadding({ metadata.padding[0], metadata.padding[1] }); - glyph.setSpread(metadata.spread); - glyph.useSdf(metadata.sdf); - glyph.generate(::glyphs.glyph, c, metadata.size); - } - - tallestGlyphY = std::max(tallestGlyphY, (float) glyph.getSize().y); - totalWidth += glyph.getSize().x; // should probably be reset on new-line to find the widest line - charCount++; - } - } - - if ( charCount > 0 ) averageTabWidth = (totalWidth / charCount) * 4.0f; - cursor.y = tallestGlyphY; - - // calculate positions - for ( const auto& token : tokens ) { - std::u8string str( token.text.begin(), token.text.end() ); - for ( uint64_t c : str ) { - // advance cursor on special characters - if ( c == '\n' ) { - cursor.y += tallestGlyphY; - cursor.x = 0; - continue; - } else if ( c == '\t' ) { - cursor.x = ((int)(cursor.x / averageTabWidth) + 1) * averageTabWidth; - continue; - } else if ( c == ' ' ) { - cursor.x += averageTabWidth / 4.0f; - continue; - } - - // retrieve glyph - auto key = hashGlyphSettings(c, metadata); - auto& glyph = glyphsCache[key]; - auto& g = layout.emplace_back(GlyphBox{ - .box = { - .x = cursor.x + glyph.getBearing().x, - .y = cursor.y - glyph.getBearing().y, - .w = glyph.getSize().x, - .h = glyph.getSize().y, - }, - .color = token.color, - .code = c, - }); - - // advance cursor - cursor.x += glyph.getAdvance().x; - - // advance bounding box - maxTextWidth = std::max(maxTextWidth, g.box.x + g.box.w); - maxTextHeight = std::max(maxTextHeight, g.box.y + g.box.h); - } - } - - // calculate offset based on anchor - float offsetX = maxTextWidth * anchor.x; - float offsetY = maxTextHeight * anchor.y; - - // adjust all glyphs for our offset - for ( auto& g : layout ) { - g.box.x -= offsetX; - g.box.y -= offsetY; - - // normalize - g.box.x /= ::defaults.size.x; - g.box.w /= ::defaults.size.x; - g.box.y /= ::defaults.size.y; - g.box.h /= ::defaults.size.y; - } - - return layout; - } - - // generate the mesh and texture atlas - void generateAtlasAndMesh( const uf::stl::vector& layout, const ext::GuiGlyphBehavior::Metadata& metadata, const uf::stl::string& font, uf::Atlas& atlas, uf::Mesh& mesh ) { - auto& glyphs_cache = ::glyphs.cache[font]; - uf::stl::unordered_map glyph_atlas_map; - - #if UF_USE_FREETYPE - // generate atlas - { - atlas.clear(); - - // generate atlas - for ( const auto& g : layout ) { - auto key = hashGlyphSettings(g.code, metadata); - auto& glyph = glyphs_cache[key]; - - // already in atlas map - if ( glyph_atlas_map.find(key) != glyph_atlas_map.end() ) continue; - - const uint8_t* buffer = glyph.getBuffer(); - uf::Image::container_t pixels; - size_t len = glyph.getSize().x * glyph.getSize().y; - - if ( metadata.sdf ) { - glyph_atlas_map[key] = atlas.addImage(glyph.getBuffer(), glyph.getSize(), 8, 1, true); - } else { - pixels.reserve(len * 4); - for ( size_t i = 0; i < len; ++i ) { - pixels.emplace_back(buffer[i]); // R - pixels.emplace_back(buffer[i]); // G - pixels.emplace_back(buffer[i]); // B - pixels.emplace_back(buffer[i]); // A - } - glyph_atlas_map[key] = atlas.addImage(&pixels[0], glyph.getSize(), 8, 4, true); - } - } - - atlas.generate(); - atlas.clear(false); - } - #endif - // generate mesh - { - mesh.destroy(); - mesh.bind<::Mesh, uint16_t>(); - - uf::stl::vector<::Mesh> vertices; - uf::stl::vector indices; - vertices.reserve(layout.size() * 4); - indices.reserve(layout.size() * 6); - - for ( const auto& g : layout ) { - auto key = hashGlyphSettings(g.code, metadata); - auto hash = glyph_atlas_map[key]; - - #if EXT_COLOR_FLOATS - auto& color = g.color; - #else - pod::Vector4b color = { - (uint8_t)(g.color[0] * 255), - (uint8_t)(g.color[1] * 255), - (uint8_t)(g.color[2] * 255), - (uint8_t)(g.color[3] * 255) - }; - #endif - // insert indices - uint16_t idx = (uint16_t) vertices.size(); - indices.insert( indices.end(), { idx, idx + 1, idx + 2, idx, idx + 2, idx + 3 }); - - // insert vertices - vertices.emplace_back(::Mesh{pod::Vector3f{ g.box.x, g.box.y + g.box.h, 0 }, atlas.mapUv(pod::Vector2f{ 0.0f, 0.0f }, hash), color}); - vertices.emplace_back(::Mesh{pod::Vector3f{ g.box.x, g.box.y , 0 }, atlas.mapUv(pod::Vector2f{ 0.0f, 1.0f }, hash), color}); - vertices.emplace_back(::Mesh{pod::Vector3f{ g.box.x + g.box.w, g.box.y , 0 }, atlas.mapUv(pod::Vector2f{ 1.0f, 1.0f }, hash), color}); - vertices.emplace_back(::Mesh{pod::Vector3f{ g.box.x + g.box.w, g.box.y + g.box.h, 0 }, atlas.mapUv(pod::Vector2f{ 1.0f, 0.0f }, hash), color}); - } - - mesh.insertVertices(vertices); - mesh.insertIndices(indices); - } - } -} - void ext::GuiGlyphBehavior::initialize( uf::Object& self ) { auto& metadata = this->getComponent(); auto& metadataGui = this->getComponent(); @@ -380,7 +40,7 @@ void ext::GuiGlyphBehavior::initialize( uf::Object& self ) { this->addHook( "gui:UpdateText.%UID%", [&](ext::json::Value& payload){ auto string = payload["string"].as(metadata.string); - auto font = uf::io::root+"/fonts/" + payload["font"].as(metadata.font); + auto font = payload["font"].as(metadata.font); bool forced = payload["force"].as(false); // override @@ -393,14 +53,21 @@ void ext::GuiGlyphBehavior::initialize( uf::Object& self ) { auto& mesh = this->getComponent(); auto& atlas = this->getComponent(); auto& images = atlas.getImages(); - auto& glyphs_cache = ::glyphs.cache; uf::stl::unordered_map glyph_atlas_map; - - auto tokens = ::parseTextTokens(string, metadataGui.color); - auto layout = ::calculateGlyphLayout(self, tokens, metadata, metadataGui, font); - ::generateAtlasAndMesh( layout, metadata, font, atlas, mesh ); + auto settings = pod::GlyphSettings{ + .alignment = metadataGui.alignment, + .font = font, + .size = metadata.size, + .sdf = metadata.sdf, + .spread = metadata.spread, + .padding = metadata.padding, + }; + auto tokens = uf::glyph::parseTextTokens( string, metadataGui.color ); + auto layout = uf::glyph::calculateLayout( tokens, settings ); + uf::glyph::generateAtlas( layout, settings, atlas ); + uf::glyph::generateMesh( layout, settings, atlas, mesh ); // set proper shaders if ( metadata.sdf ) { @@ -487,7 +154,14 @@ void ext::GuiGlyphBehavior::Metadata::serialize( uf::Object& self, uf::Serialize serializer["range"] = uf::vector::encode( /*this->*/shader.range); } void ext::GuiGlyphBehavior::Metadata::deserialize( uf::Object& self, uf::Serializer& serializer ){ - size_t oldHash = ::hashGlyphSettings( string, *this ); + size_t oldHash = uf::glyph::hashSettings( string, pod::GlyphSettings{ + .alignment = "", + .font = /*this->*/font, + .size = /*this->*/size, + .sdf = /*this->*/sdf, + .spread = /*this->*/spread, + .padding = /*this->*/padding, + }); /*this->*/string = serializer["string"].as(/*this->*/string); /*this->*/font = serializer["font"].as(/*this->*/font); @@ -501,7 +175,14 @@ void ext::GuiGlyphBehavior::Metadata::deserialize( uf::Object& self, uf::Seriali /*this->*/shader.stroke = uf::vector::decode(serializer["stroke"], /*this->*/shader.stroke); /*this->*/shader.range = uf::vector::decode(serializer["range"], /*this->*/shader.range); - size_t newHash = ::hashGlyphSettings( string, *this ); + size_t newHash = uf::glyph::hashSettings( string, pod::GlyphSettings{ + .alignment = "", + .font = /*this->*/font, + .size = /*this->*/size, + .sdf = /*this->*/sdf, + .spread = /*this->*/spread, + .padding = /*this->*/padding, + }); // fire text update if ( oldHash != newHash ) { diff --git a/engine/src/engine/graph/animation.cpp b/engine/src/engine/graph/animation.cpp index b979fee1..fa4e9c1e 100644 --- a/engine/src/engine/graph/animation.cpp +++ b/engine/src/engine/graph/animation.cpp @@ -127,7 +127,7 @@ void uf::graph::override( pod::Graph& graph ) { pod::Animation& animation = storage.animations.map[name]; // load animation data - if ( animation.channels.empty() || animation.samplers.empty() ) ::loadAnimation( name ); + // if ( animation.channels.empty() || animation.samplers.empty() ) ::loadAnimation( name ); for ( auto& channel : animation.channels ) { auto& override = graph.settings.animations.override.map[channel.node]; @@ -152,7 +152,12 @@ void uf::graph::animate( pod::Graph& graph, const uf::stl::string& _name, float auto& storage = ::getGraphStorage( scene ); if ( !(graph.metadata["renderer"]["skinned"].as()) ) return; - const uf::stl::string name = _name; + uf::stl::string key = graph.metadata["key"].as(""); + if ( key != "" ) key += ":"; + uf::stl::string name = key + _name; + + if ( storage.animations.map.count( name ) == 0 ) ::loadAnimation( name ); + //UF_MSG_DEBUG("name={}, count={}", name, storage.animations.map.count( name ) ); if ( storage.animations.map.count( name ) > 0 ) { // if already playing, ignore it if ( !graph.sequence.empty() && graph.sequence.front() == name ) return; @@ -179,15 +184,12 @@ void uf::graph::updateAnimation( pod::Graph& graph, float delta ) { return; } - if ( graph.sequence.empty() ) goto UPDATE; if ( graph.settings.animations.override.a >= 0 ) goto OVERRIDE; { uf::stl::string name = graph.sequence.front(); pod::Animation* animation = &storage.animations.map[name]; // &graph.animations[name]; animation->cur += delta * graph.settings.animations.speed; // * graph.settings.animations.override.speed; - - // UF_MSG_DEBUG("entity={}, name={}, cur={}", graph.root.entity->getParent().getName(), name, animation->cur); if ( animation->end < animation->cur ) { animation->cur = graph.settings.animations.loop ? animation->cur - animation->end : 0; // go-to next animation @@ -207,7 +209,7 @@ void uf::graph::updateAnimation( pod::Graph& graph, float delta ) { } // load animation data - if ( animation->channels.empty() || animation->samplers.empty() ) ::loadAnimation( name ); + // if ( animation->channels.empty() || animation->samplers.empty() ) ::loadAnimation( name ); for ( auto& channel : animation->channels ) { auto& sampler = animation->samplers[channel.sampler]; @@ -245,24 +247,6 @@ void uf::graph::updateAnimation( pod::Graph& graph, pod::Node& node ) { auto& scene = uf::scene::getCurrentScene(); auto& storage = ::getGraphStorage( scene ); -#if 1 - if ( 0 <= node.skin && node.skin < graph.skins.size() ) { - pod::Matrix4f nodeMatrix = ::worldMatrix( graph, node.index ); - pod::Matrix4f inverseTransform = uf::matrix::inverse( nodeMatrix ); - - auto& name = graph.skins[node.skin]; - auto& skin = storage.skins[name]; - auto& joints = storage.joints[name]; - joints.resize( skin.joints.size() ); - for ( size_t i = 0; i < skin.joints.size(); ++i ) joints[i] = uf::matrix::identity(); - - if ( graph.settings.animations.override.a >= 0 || !graph.sequence.empty() ) { - for ( size_t i = 0; i < skin.joints.size(); ++i ) { - joints[i] = inverseTransform * (::worldMatrix(graph, skin.joints[i]) * skin.inverseBindMatrices[i]); - } - } - } -#else if ( 0 <= node.skin && node.skin < graph.skins.size() ) { pod::Matrix4f nodeMatrix = ::worldMatrix( graph, node.index ); pod::Matrix4f inverseTransform = uf::matrix::inverse( nodeMatrix ); @@ -296,7 +280,6 @@ void uf::graph::updateAnimation( pod::Graph& graph, pod::Node& node ) { joints[i] = inverseTransform * matrix * skin.inverseBindMatrices[i]; } } -#endif } // separate function in the event something later might need it @@ -374,13 +357,6 @@ uf::stl::vector uf::graph::obbFromSkin( const pod::Graph& graph, const .center = (aabb.max + aabb.min) * 0.5f, .extent = (aabb.max - aabb.min) * 0.5f, }; - - // transform back from bone => mesh space - auto bindMatrix = uf::matrix::inverse( skin.inverseBindMatrices[i] ); - auto scale = uf::matrix::extractScale( bindMatrix ); - bounds[i].center = uf::matrix::multiply( bindMatrix, bounds[i].center, 1.0f ); - bounds[i].extent *= scale; - // UF_MSG_DEBUG("name={}, scale={}", graph.nodes[skin.joints[i]].name, uf::vector::toString( scale )); } return bounds; } @@ -415,58 +391,43 @@ void uf::graph::rigRagdoll( pod::Graph& graph, pod::Node& node ) { // skip leaf bones if they aren't used in the mesh if ( !useBone && node.children.empty() ) continue; // useBone = false; // disable for now - bool offsetByBodyTransform = true; // offset via physics body rather than in the shape itself auto shapeType = pod::ShapeType::CAPSULE; // default to capsules // transform bone into world space bone.start = uf::transform::apply( armatureTransform, bone.start ); bone.end = uf::transform::apply( armatureTransform, bone.end ); float length = uf::vector::distance( bone.start, bone.end ); // bone length in world-space - float thickness = /*useBone ? MAX( obb.extent.x, obb.extent.z ) :*/ length * 0.15f; // limb thickness + float thickness = useBone ? MAX( obb.extent.x, obb.extent.z ) : length * 0.15f; // limb thickness // transform into node space auto start = uf::transform::applyInverse( transform, bone.start ); auto end = uf::transform::applyInverse( transform, bone.end ); auto dir = uf::vector::normalize( end - start ); - auto up = pod::Vector3f{0, 1, 0}; - float mass = 0.0f; - - // valid bone, use boxes - if ( useBone ) shapeType = pod::ShapeType::OBB; - - // physics body transform offsets auto offset = pod::Vector3f{}; auto orientation = uf::quaternion::identity(); - // offset position - if ( offsetByBodyTransform || shapeType != pod::ShapeType::OBB ) { - offset = /*useBone ? obb.center :*/ uf::vector::lerp( start, end, 0.5f ); - obb.center = {}; - } - // offset orientation - if ( offsetByBodyTransform || shapeType != pod::ShapeType::CAPSULE ) { - //orientation = uf::quaternion::unitVectors( up, dir ); - orientation = uf::matrix::extractRotation( uf::matrix::inverse( skin.inverseBindMatrices[nodeID] ) ); + + float mass = 0.0f; + if ( false && useBone ) { + shapeType = pod::ShapeType::OBB; + // volume = obb.extent.x * obb.extent.y * obb.extent.z * 8.0f; } else { - up = dir; // set up direction to bone direction (for capsule) + offset = uf::vector::lerp( start, end, 0.5f ); + // auto up = pod::Vector3f{0, 1, 0}; + // orientation = uf::quaternion::unitVectors( up, dir ); + // dir = up; } + // create body auto& body = uf::physics::create( entity, mass, offset, orientation ); bodies[nodeID] = &body; - // "root" bone will try and parent to local origin - /* - if ( !isJoint[node.parent] ) { - // mark it as non-colliding - uf::physics::setColliderCategory( body, pod::Collider::CATEGORY_NONE ); - // make it heavier - uf::physics::setMass( body, 1000.0f ); - } - */ + // bone unused, just mark it as non-colliding + if ( !useBone ) uf::physics::setColliderCategory( body, pod::Collider::CATEGORY_NONE ); switch ( shapeType ) { case pod::ShapeType::CAPSULE: { float height = length - (thickness * 2); // subtract end-caps - uf::physics::initialize( body, pod::Capsule{ thickness, up * height * 0.5f } ); + uf::physics::initialize( body, pod::Capsule{ thickness, dir * height * 0.5f } ); } break; case pod::ShapeType::AABB: case pod::ShapeType::OBB: { diff --git a/engine/src/engine/graph/decode.cpp b/engine/src/engine/graph/decode.cpp index b78a8ec7..3b17b01a 100644 --- a/engine/src/engine/graph/decode.cpp +++ b/engine/src/engine/graph/decode.cpp @@ -440,9 +440,7 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const } uf::stl::string key = graph.metadata["key"].as(""); - if ( key != "" ) { - key += ":"; - } + if ( key != "" ) key += ":"; tasks.queue([&]{ // load images diff --git a/engine/src/engine/object/behaviors/graph.cpp b/engine/src/engine/object/behaviors/graph.cpp index efba84bc..d5f581cb 100644 --- a/engine/src/engine/object/behaviors/graph.cpp +++ b/engine/src/engine/object/behaviors/graph.cpp @@ -59,8 +59,8 @@ void uf::GraphBehavior::initialize( uf::Object& self ) { uf::graph::initialize( graph ); if ( graph.metadata["renderer"]["skinned"].as() ) { - if ( metadata["graph"]["animation"].is() ) { - uf::graph::animate( graph, metadata["graph"]["animation"].as(), metadata["graph"]["speed"].as( 1.0f ) ); + if ( ext::json::isObject( metadata["graph"]["animations"] ) ) { + uf::graph::animate( graph, metadata["graph"]["animations"]["animation"].as(), metadata["graph"]["animations"]["speed"].as( 1.0f ) ); } } @@ -87,18 +87,17 @@ void uf::GraphBehavior::tick( uf::Object& self ) { uf::debug::drawLine( bone.start, bone.end, pod::Vector4f{ 0, 1, 0, 1 } ); } - for ( auto obb : bounds ) { - uf::debug::drawShape( obb, transform ); - } - /* for ( auto i = 0; i < skin.joints.size(); ++i ) { auto nodeID = skin.joints[i]; - auto obb = bounds[i]; - auto bone = bones[nodeID]; - - uf::debug::drawShape( obb, transform ); + auto& obb = bounds[i]; + auto& bone = bones[nodeID]; + if ( obb.extent.x < 0 ) continue; + auto bindMatrix = uf::matrix::inverse( skin.inverseBindMatrices[i] ); + auto modelMatrix = uf::transform::model( transform ); + auto finalMatrix = modelMatrix * bindMatrix; + auto t = uf::transform::fromMatrix( finalMatrix ); + uf::debug::drawShape( obb, t ); } - */ } } void uf::GraphBehavior::render( uf::Object& self ) {} diff --git a/engine/src/ext/vulkan/rendermodes/deferred.cpp b/engine/src/ext/vulkan/rendermodes/deferred.cpp index f13dea7f..91a36d2a 100644 --- a/engine/src/ext/vulkan/rendermodes/deferred.cpp +++ b/engine/src/ext/vulkan/rendermodes/deferred.cpp @@ -312,7 +312,10 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) { }); auto& shader = blitter.material.getShader("fragment"); - if ( !settings::pipelines::fsr || !ext::fsr::frameUpscale ) { + #if UF_USE_FFX_FSR || UF_USE_FFX_SDK + if ( !settings::pipelines::fsr || !ext::fsr::frameUpscale ) + #endif + { shader.aliasAttachment("output", this); } } diff --git a/engine/src/ext/vulkan/swapchain.cpp b/engine/src/ext/vulkan/swapchain.cpp index 0339fa4b..b5d0f0c0 100644 --- a/engine/src/ext/vulkan/swapchain.cpp +++ b/engine/src/ext/vulkan/swapchain.cpp @@ -7,7 +7,7 @@ #include VkResult ext::vulkan::Swapchain::acquireNextImage( uint32_t* imageIndex, VkSemaphore presentCompleteSemaphore, VkFence acquireFence ) { -#if UF_USE_FFX_SDK +#if UF_USE_FFX_FSR || UF_USE_FFX_SDK if ( ext::fsr::frameInterpolation ) { return ext::fsr::acquireNextImage( imageIndex, presentCompleteSemaphore, acquireFence ); } @@ -17,7 +17,7 @@ VkResult ext::vulkan::Swapchain::acquireNextImage( uint32_t* imageIndex, VkSemap } VkResult ext::vulkan::Swapchain::queuePresent( VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore ) { -#if UF_USE_FFX_SDK +#if UF_USE_FFX_FSR || UF_USE_FFX_SDK if ( ext::fsr::frameInterpolation ) { return ext::fsr::queuePresent( queue, imageIndex, waitSemaphore ); } @@ -178,7 +178,7 @@ void ext::vulkan::Swapchain::initialize( Device& device ) { swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; } - #if UF_USE_FFX_SDK + #if UF_USE_FFX_FSR || UF_USE_FFX_SDK VK_CHECK_RESULT(ext::fsr::createSwapchain( device.logicalDevice, &swapchainCI, nullptr, &swapChain )); #else VK_CHECK_RESULT(vkCreateSwapchainKHR( device.logicalDevice, &swapchainCI, nullptr, &swapChain )); @@ -188,14 +188,14 @@ void ext::vulkan::Swapchain::initialize( Device& device ) { // If an existing swap chain is re-created, destroy the old swap chain // This also cleans up all the presentable images if ( oldSwapchain != VK_NULL_HANDLE ) { - #if UF_USE_FFX_SDK + #if UF_USE_FFX_FSR || UF_USE_FFX_SDK ext::fsr::destroySwapchain( device.logicalDevice, oldSwapchain, nullptr); #else vkDestroySwapchainKHR( device.logicalDevice, oldSwapchain, nullptr); #endif // VK_UNREGISTER_HANDLE( oldSwapchain ); } - #if UF_USE_FFX_SDK + #if UF_USE_FFX_FSR || UF_USE_FFX_SDK VK_CHECK_RESULT(ext::fsr::getSwapchainImages( device.logicalDevice, swapChain, &buffers, NULL)); #else VK_CHECK_RESULT(vkGetSwapchainImagesKHR( device.logicalDevice, swapChain, &buffers, NULL)); @@ -204,7 +204,7 @@ void ext::vulkan::Swapchain::initialize( Device& device ) { // Bind images { images.resize( buffers ); - #if UF_USE_FFX_SDK + #if UF_USE_FFX_FSR || UF_USE_FFX_SDK VK_CHECK_RESULT(ext::fsr::getSwapchainImages( device, swapChain, &buffers, images.data())); #else VK_CHECK_RESULT(vkGetSwapchainImagesKHR( device, swapChain, &buffers, images.data())); @@ -222,7 +222,7 @@ void ext::vulkan::Swapchain::destroy() { } swapchain.images.clear(); if ( swapChain != VK_NULL_HANDLE ) { - #if UF_USE_FFX_SDK + #if UF_USE_FFX_FSR || UF_USE_FFX_SDK ext::fsr::destroySwapchain( *device, swapChain, nullptr ); #else vkDestroySwapchainKHR( *device, swapChain, nullptr); diff --git a/engine/src/ext/vulkan/vulkan.cpp b/engine/src/ext/vulkan/vulkan.cpp index b4b1eefc..6959e8f9 100644 --- a/engine/src/ext/vulkan/vulkan.cpp +++ b/engine/src/ext/vulkan/vulkan.cpp @@ -504,7 +504,9 @@ void ext::vulkan::tick() { ext::vulkan::states::rebuild = true; } +#if UF_USE_FFX_FSR || UF_USE_FFX_SDK ext::fsr::tick(); +#endif auto renderModes = ::fetchRenderModes(); for ( auto& renderMode : renderModes ) { diff --git a/engine/src/utils/debug/draw.cpp b/engine/src/utils/debug/draw.cpp index 4a8816eb..cab2dc87 100644 --- a/engine/src/utils/debug/draw.cpp +++ b/engine/src/utils/debug/draw.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace impl { struct Vertex { @@ -19,15 +20,29 @@ namespace impl { float ttl = 0; }; - float decayRate = 0.25f; - uf::stl::vector lines; - uf::stl::unordered_map transientLines; - size_t getHash( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color ) { - size_t hash = 0; - uf::hash(hash, start, end, color); - return hash; - } + float decayRate = 1.0f; + uf::stl::vector lines; + uf::stl::vector transientLines; + uf::Mesh lineMesh; +} + +namespace impl { + struct Text { + uf::stl::string string = ""; + pod::Vector3f position = {}; + pod::Vector4f color = { 1, 1, 1, 1 }; + }; + uf::stl::vector texts; + uf::Mesh textMesh; + uf::Atlas textAtlas; + + pod::GlyphSettings textSettings = { + .alignment = "center", + .font = "FragmentMono.ttf", + .size = 96, + .sdf = false, + }; } UF_VERTEX_DESCRIPTOR(impl::Vertex, @@ -39,7 +54,7 @@ void uf::debug::drawLine( const pod::Vector3f& start, const pod::Vector3f& end, impl::lines.emplace_back( impl::Vertex{ start, color } ); impl::lines.emplace_back( impl::Vertex{ end, color } ); } -// for some reason compiling these two and not even using them causes physics to break + void uf::debug::drawShape( const pod::AABB& aabb, const pod::Transform<>& transform ) { pod::Vector3f corners[8] = { {aabb.min.x, aabb.min.y, aabb.min.z}, {aabb.max.x, aabb.min.y, aabb.min.z}, @@ -189,31 +204,36 @@ void uf::debug::drawShape( const pod::Triangle& tri, const pod::Transform<>& tra uf::debug::drawLine( v2, v0 ); } void uf::debug::addLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color, float ttl ) { - impl::transientLines[impl::getHash( start, end, color )] = impl::Line{ start, end, color, ttl }; + impl::transientLines.emplace_back(impl::Line{ start, end, color, ttl }); } -void uf::debug::draw( float dt ) { - for ( auto it = impl::transientLines.begin(); it != impl::transientLines.end(); ) { - auto& line = it->second; - - if ( line.ttl <= 0 ) it = impl::transientLines.erase( it ); - else { +void uf::debug::drawLines( float dt ) { + for ( auto i = 0; i < impl::transientLines.size(); ) { + auto& line = impl::transientLines[i]; + if ( line.ttl <= 0 ) { + line = impl::transientLines.back(); + impl::transientLines.pop_back(); + } else { uf::debug::drawLine( line.start, line.end, line.color * pod::Vector4f{ 1, 1, 1, CLAMP( line.ttl, 0, 1 ) } ); line.ttl -= dt * impl::decayRate; - ++it; + ++i; } } if ( impl::lines.empty() ) return; + // double buffer + STATIC_THREAD_LOCAL(uf::stl::vector, lines); + std::swap( impl::lines, lines ); - // to-do: make this static to avoid additional allocations - uf::Mesh mesh; - mesh.bind(); - mesh.insertVertices(impl::lines); + impl::lineMesh.clear(); + impl::lineMesh.bind(); + impl::lineMesh.insertVertices(lines); + impl::lineMesh.generateIndirect(); auto& scene = uf::scene::getCurrentScene(); auto& graphics = scene.getComponent(); - auto& graphic = graphics["immediate"]; + + auto& graphic = graphics["immediate:lines"]; if ( !graphic.initialized ) { graphic.device = &uf::renderer::device; graphic.material.device = &uf::renderer::device; @@ -243,12 +263,123 @@ void uf::debug::draw( float dt ) { } graphic.initialize(); - graphic.initializeMesh( mesh ); - UF_MSG_DEBUG("Initialized graphic"); + graphic.initializeMesh( impl::lineMesh ); } else { - bool rebuild = graphic.updateMesh( mesh ); + bool rebuild = graphic.updateMesh( impl::lineMesh ); if ( rebuild ) uf::renderer::states::rebuild = true; // to-do: rebuild the defer mode only } +} - impl::lines.clear(); +void uf::debug::drawText( const uf::stl::string& string, const pod::Vector3f& position, const pod::Vector4f& color ) { + impl::texts.emplace_back( impl::Text{ string, position, color } ); +} + +void uf::debug::drawTexts( float dt ) { + if ( impl::texts.empty() ) return; + // double buffer + STATIC_THREAD_LOCAL(uf::stl::vector, texts); + std::swap( impl::texts, texts ); + + auto& scene = uf::scene::getCurrentScene(); + auto& controller = scene.getController(); + auto& camera = scene.getCamera( controller ); + auto& transform = camera.getTransform(); + + uf::stl::vector textLayout; + // pre-init with ASCII characters + if ( !impl::textAtlas.generated() ) { + uf::stl::string ascii = ""; + for ( char c = 32; c < 127; ++c ) ascii += c; + auto tokens = uf::glyph::parseTextTokens( ascii, {0,0,0,0} ); + auto layout = uf::glyph::calculateLayout( tokens, impl::textSettings ); + for ( auto& g : layout ) { + g.box.x = 0; + g.box.y = 0; + g.box.w = 0; + g.box.h = 0; + + textLayout.emplace_back( g ); + } + } + + for ( auto& text : texts ) { + auto tokens = uf::glyph::parseTextTokens( text.string, text.color ); + auto layout = uf::glyph::calculateLayout( tokens, impl::textSettings ); + + auto world = pod::Vector4f{ text.position.x, text.position.y, text.position.z, 1.0f }; + auto view = camera.getProjection() * camera.getView(); + auto clip = uf::matrix::multiply( view, world ); + + if ( clip.w <= 0.0f ) continue; + + auto ndc = pod::Vector3f{ clip.x, clip.y, clip.z } / clip.w; + float scale = 2.0f / clip.w; + + if ( ndc.z < 0.0f || ndc.z > 1.0f ) continue; + + for ( auto& g : layout ) { + g.box.x = g.box.x * scale + ndc.x; + g.box.y = g.box.y * scale + ndc.y; + g.box.z = ndc.z; + g.box.w *= scale; + g.box.h *= scale; + + if ( (g.box.x + g.box.w < -1.0f) || (g.box.x > 1.0f) || (g.box.y + g.box.h < -1.0f) || (g.box.y > 1.0f) ) continue; + + textLayout.emplace_back( g ); + } + } + + bool dirty = uf::glyph::generateAtlas( textLayout, impl::textSettings, impl::textAtlas ); + uf::glyph::generateMesh( textLayout, impl::textSettings, impl::textAtlas, impl::textMesh ); + impl::textMesh.generateIndirect(); + + auto& graphics = scene.getComponent(); + auto& graphic = graphics["immediate:texts"]; + if ( !graphic.initialized ) { + graphic.device = &uf::renderer::device; + graphic.material.device = &uf::renderer::device; + + graphic.descriptor.depth.test = uf::physics::settings.debugDraw.depthTest; + graphic.descriptor.depth.write = false; + graphic.descriptor.renderTarget = 1; // "forward"; + graphic.descriptor.blend.enabled = true; + graphic.descriptor.cullMode = uf::renderer::enums::CullMode::NONE; + + uf::stl::string vertexShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/base/textured/vert.spv"); + uf::stl::string fragmentShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/base/textured/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(); + + // vertex shader + { + auto& shader = graphic.material.getShader("vertex"); + shader.aliasBuffer( storage.buffers.camera ); + } + // fragment shader + auto& texture = graphic.material.textures.emplace_back(); + texture.loadFromImage( impl::textAtlas.getAtlas() ); + + graphic.initialize(); + graphic.initializeMesh( impl::textMesh ); + } else { + if ( dirty ) { + graphic.material.textures.clear(); + + auto& texture = graphic.material.textures.emplace_back(); + texture.loadFromImage( impl::textAtlas.getAtlas() ); + } + + bool rebuild = graphic.updateMesh( impl::textMesh ); + if ( rebuild ) uf::renderer::states::rebuild = true; // to-do: rebuild the defer mode only + } +} +void uf::debug::draw( float dt ) { + uf::debug::drawLines( dt ); + uf::debug::drawTexts( dt ); } \ No newline at end of file diff --git a/engine/src/utils/image/atlas.cpp b/engine/src/utils/image/atlas.cpp index d0bd70f4..1e7dacf4 100644 --- a/engine/src/utils/image/atlas.cpp +++ b/engine/src/utils/image/atlas.cpp @@ -1,38 +1,23 @@ #include #include #include +#include -uf::Atlas::hash_t uf::Atlas::addImage( const uf::Image& _image, bool regenerate ) { +uf::Atlas::hash_t uf::Atlas::addImage( const uf::Image& image, const uf::Atlas::hash_t& hash ) { size_t index = this->m_tiles.size(); - auto hash = _image.getHash(); if ( this->m_tiles.count( hash ) > 0 ) return hash; auto& tile = this->m_tiles[hash]; - tile.image = _image; + tile.image = image; tile.identifier.hash = hash; tile.identifier.index = index; - if ( regenerate ) this->generate(); return hash; } -uf::Atlas::hash_t uf::Atlas::addImage( uf::Image&& _image, bool regenerate ) { - size_t index = this->m_tiles.size(); - auto hash = _image.getHash(); - if ( this->m_tiles.count( hash ) > 0 ) return hash; - - auto& tile = this->m_tiles[hash]; - tile.image = _image; - tile.identifier.hash = hash; - tile.identifier.index = index; - if ( regenerate ) this->generate(); - return hash; -} -uf::Atlas::hash_t uf::Atlas::addImage( const uint8_t* pointer, const pod::Vector2ui& size, std::size_t bpp, std::size_t channels, bool flip, bool regenerate ) { - uf::Image image; - image.loadFromBuffer( pointer, size, bpp, channels, flip ); - return this->addImage( std::move( image ), regenerate ); +uf::Atlas::hash_t uf::Atlas::addImage( const uf::Image& image ) { + return this->addImage( image, image.getHash() ); } void uf::Atlas::generate( const uf::Atlas::images_t& images, float padding ) { - for ( auto& image : images ) this->addImage( image, true ); + for ( auto& image : images ) this->addImage( image ); generate( padding ); } void uf::Atlas::generate( float padding ) { @@ -59,27 +44,8 @@ void uf::Atlas::generate( float padding ) { } size_t tries = 16; do { - { - // size_t area = largest.x * largest.y * this->m_tiles.size(); - size_t side = std::sqrt( area ) * padding; - size = { side, side }; - { - size.x--; - size.x |= size.x >> 1; - size.x |= size.x >> 2; - size.x |= size.x >> 4; - size.x |= size.x >> 8; - size.x |= size.x >> 16; - size.x++; - size.y--; - size.y |= size.y >> 1; - size.y |= size.y >> 2; - size.y |= size.y >> 4; - size.y |= size.y >> 8; - size.y |= size.y >> 16; - size.y++; - } - } + size_t side = std::sqrt( area ) * padding; + size = { std::bit_ceil(side), std::bit_ceil(side) }; // to-do: non-C++20 method queue.Sort(); internalAtlas = BinPack2D::UniformCanvasArrayBuilder(size.x, size.y, 1).Build(); bool success = internalAtlas.Place( queue, remainder ); @@ -116,6 +82,9 @@ void uf::Atlas::generate( float padding ) { } } } +bool uf::Atlas::has( const uf::Atlas::hash_t& hash ) const { + return this->m_tiles.count( hash ) > 0; +} bool uf::Atlas::generated() const { return !this->m_atlas.getPixels().empty(); } @@ -127,17 +96,20 @@ void uf::Atlas::clear( bool full ) { this->m_tiles.clear(); this->m_atlas.clear(); } -pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, const uf::Atlas::hash_t& hash ) { - auto& size = this->m_atlas.getDimensions(); - for ( auto pair : this->m_tiles ) { - auto& tile = pair.second; - if ( tile.identifier.hash != hash ) continue; - pod::Vector2ui coord = { uv.x * tile.size.x + tile.coord.x, uv.y * tile.size.y + tile.coord.y }; +pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, const uf::Atlas::hash_t& hash ) const { + auto it = this->m_tiles.find(hash); + if ( it != this->m_tiles.end() ) { + auto& tile = it->second; + auto& size = this->m_atlas.getDimensions(); + pod::Vector2ui coord = { + uv.x * tile.size.x + tile.coord.x, + uv.y * tile.size.y + tile.coord.y + }; return pod::Vector2f{ (float) coord.x / (float) size.x, (float) coord.y / (float) size.y }; } return uv; } -pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, size_t index ) { +pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, size_t index ) const { auto& size = this->m_atlas.getDimensions(); for ( auto pair : this->m_tiles ) { auto& tile = pair.second; @@ -147,7 +119,7 @@ pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, size_t index ) { } return uv; } -pod::Vector3f uf::Atlas::mapUv( const pod::Vector3f& uv ) { +pod::Vector3f uf::Atlas::mapUv( const pod::Vector3f& uv ) const { pod::Vector2f nuv = mapUv( { uv.x, uv.y }, uv.z ); return { nuv.x, nuv.y, uv.z }; } diff --git a/engine/src/utils/math/physics/common.cpp b/engine/src/utils/math/physics/common.cpp index 5b88548f..391c3962 100644 --- a/engine/src/utils/math/physics/common.cpp +++ b/engine/src/utils/math/physics/common.cpp @@ -64,10 +64,12 @@ void impl::updateActivity( pod::PhysicsBody& body, float dt ) { // returns an absolute transform while also allowing offsetting the collision body // to-do: find a succinct way to explain this madness pod::Transform<> impl::getTransform( const pod::PhysicsBody& body ) { - pod::Transform<> t; - t.position = body.offsetPosition; - t.orientation = body.offsetOrientation; - t.reference = body.transform; + pod::Transform<> t = { + .position = body.offsetPosition, + .orientation = body.offsetOrientation, + .scale = {1, 1, 1}, + .reference = body.transform, + }; return uf::transform::flatten( t ); } // get position of a body, uses bounds center or transform's position @@ -78,6 +80,7 @@ pod::Vector3f impl::getPosition( const pod::PhysicsBody& body, bool useTransform // applies a transform pod::Vector3f impl::apply( const pod::Transform<>& t, const pod::Vector3f& p ) { return uf::transform::apply( t, p ); +// return uf::quaternion::rotate( t.orientation, p * t.scale ) + t.position; // explicitly needed to copy or GCC breaks } // applies an inverse transform pod::Vector3f impl::applyInverse( const pod::Transform<>& t, const pod::Vector3f& p ) { @@ -528,7 +531,7 @@ pod::TriangleWithNormal impl::fetchTriangle( const uf::Mesh& mesh, size_t triID, if ( body.collider.type == pod::ShapeType::MESH || body.collider.type == pod::ShapeType::CONVEX_HULL ) { FOR_EACH(3, { - tri.points[i] = uf::transform::apply( transform, tri.points[i] ); + tri.points[i] = impl::apply( transform, tri.points[i] ); }); tri.normal = uf::quaternion::rotate( transform.orientation, tri.normal ); } @@ -662,7 +665,7 @@ pod::AABB impl::transformAabbToWorld( const pod::AABB& aabb, const pod::Transfor pod::Vector3f axes[3]; impl::boxAxes( axes, transform ); - pod::Vector3f center = uf::transform::apply( transform, box.center ); + pod::Vector3f center = impl::apply( transform, box.center ); pod::Vector3f extent = impl::extentFromAxes( box, axes ); return { center - extent, center + extent }; @@ -713,11 +716,25 @@ pod::AABB impl::computeAABB( const pod::PhysicsBody& body ) { return {}; } +// gets the corners of an AABB +void impl::getCorners( const pod::AABB& aabb, pod::Vector3f corners[8] ) { + corners[0] = {aabb.min.x, aabb.min.y, aabb.min.z}; + corners[1] = {aabb.max.x, aabb.min.y, aabb.min.z}; + corners[2] = {aabb.max.x, aabb.max.y, aabb.min.z}; + corners[3] = {aabb.min.x, aabb.max.y, aabb.min.z}; + corners[4] = {aabb.min.x, aabb.min.y, aabb.max.z}; + corners[5] = {aabb.max.x, aabb.min.y, aabb.max.z}; + corners[6] = {aabb.max.x, aabb.max.y, aabb.max.z}; + corners[7] = {aabb.min.x, aabb.max.y, aabb.max.z}; +} +void impl::getCorners( const pod::AABB& aabb, const pod::Transform<>& transform, pod::Vector3f corners[8] ) { + impl::getCorners( aabb, corners ); + FOR_EACH( 8, { + corners[i] = impl::apply( transform, corners[i] ); + }); +} // transforms an AABB into local space pod::AABB impl::transformAabbToLocal( const pod::AABB& box, const pod::Transform<>& transform ) { - auto inv = uf::transform::inverse( transform ); - - // transform all 8 corners pod::Vector3f corners[8] = { { box.min.x, box.min.y, box.min.z }, { box.max.x, box.min.y, box.min.z }, @@ -735,7 +752,7 @@ pod::AABB impl::transformAabbToLocal( const pod::AABB& box, const pod::Transform }; FOR_EACH(8, { - auto local = uf::transform::apply( inv, corners[i] ); + auto local = impl::applyInverse( transform, corners[i] ); out.min = uf::vector::min( out.min, local ); out.max = uf::vector::max( out.max, local ); }); diff --git a/engine/src/utils/math/physics/constraints/ballSocket.cpp b/engine/src/utils/math/physics/constraints/ballSocket.cpp index 00940b93..644d26a3 100644 --- a/engine/src/utils/math/physics/constraints/ballSocket.cpp +++ b/engine/src/utils/math/physics/constraints/ballSocket.cpp @@ -53,8 +53,8 @@ pod::Constraint& uf::physics::constrainBallSocket( pod::Constraint& constraint, auto tB = impl::getTransform( *constraint.b ); constraint.type = pod::ConstraintType::BALL_AND_SOCKET; - joint.localAnchorA = uf::transform::applyInverse( tA, p ); - joint.localAnchorB = uf::transform::applyInverse( tB, p ); + joint.localAnchorA = impl::applyInverse( tA, p ); + joint.localAnchorB = impl::applyInverse( tB, p ); joint.accumulatedImpulse = {}; return constraint; @@ -67,8 +67,8 @@ void impl::drawBallSocket( const pod::Constraint& constraint ) { auto tA = impl::getTransform( *constraint.a ); auto tB = impl::getTransform( *constraint.b ); - auto pA = uf::transform::apply( tA, joint.localAnchorA ); - auto pB = uf::transform::apply( tB, joint.localAnchorB ); + auto pA = impl::apply( tA, joint.localAnchorA ); + auto pB = impl::apply( tB, joint.localAnchorB ); uf::debug::drawLine( tA.position, pA ); uf::debug::drawLine( tB.position, pB ); diff --git a/engine/src/utils/math/physics/constraints/coneTwist.cpp b/engine/src/utils/math/physics/constraints/coneTwist.cpp index 0159f53c..565ae245 100644 --- a/engine/src/utils/math/physics/constraints/coneTwist.cpp +++ b/engine/src/utils/math/physics/constraints/coneTwist.cpp @@ -150,8 +150,8 @@ pod::Constraint& uf::physics::constrainConeTwist( pod::Constraint& constraint, c auto& joint = constraint.coneTwist; constraint.type = pod::ConstraintType::CONE_TWIST; - joint.localAnchorA = uf::transform::applyInverse( tA, p ); - joint.localAnchorB = uf::transform::applyInverse( tB, p ); + joint.localAnchorA = impl::applyInverse( tA, p ); + joint.localAnchorB = impl::applyInverse( tB, p ); joint.accumulatedImpulse = {}; joint.accumulatedAngularImpulse = {}; @@ -176,8 +176,8 @@ void impl::drawConeTwist( const pod::Constraint& constraint ) { auto tA = impl::getTransform( *constraint.a ); auto tB = impl::getTransform( *constraint.b ); - auto pA = uf::transform::apply( tA, joint.localAnchorA ); - auto pB = uf::transform::apply( tB, joint.localAnchorB ); + auto pA = impl::apply( tA, joint.localAnchorA ); + auto pB = impl::apply( tB, joint.localAnchorB ); auto taA = uf::quaternion::rotate( tA.orientation, joint.localTwistAxisA ); auto taB = uf::quaternion::rotate( tB.orientation, joint.localTwistAxisB ); diff --git a/engine/src/utils/math/physics/constraints/contact.cpp b/engine/src/utils/math/physics/constraints/contact.cpp index ea7d7a65..5dc75215 100644 --- a/engine/src/utils/math/physics/constraints/contact.cpp +++ b/engine/src/utils/math/physics/constraints/contact.cpp @@ -104,8 +104,8 @@ void impl::computeLocalManifold( pod::Manifold& manifold ) { auto tB = impl::getTransform( b ); for ( auto& c : manifold.points ) { - c.localA = uf::transform::applyInverse( tA, c.point - c.normal * (c.penetration * 0.5f) ); - c.localB = uf::transform::applyInverse( tB, c.point + c.normal * (c.penetration * 0.5f) ); + c.localA = impl::applyInverse( tA, c.point - c.normal * (c.penetration * 0.5f) ); + c.localB = impl::applyInverse( tB, c.point + c.normal * (c.penetration * 0.5f) ); } } @@ -243,8 +243,8 @@ void impl::retrieveManifold( pod::Manifold& current, const pod::Manifold& previo float distSqThresh = distanceThreshold * distanceThreshold; for ( const auto& oldContact : previous.points ) { // reproject point according to current transform - auto worldA = uf::transform::apply( tA, oldContact.localA ); - auto worldB = uf::transform::apply( tB, oldContact.localB ); + auto worldA = impl::apply( tA, oldContact.localA ); + auto worldB = impl::apply( tB, oldContact.localB ); auto delta = worldB - worldA; auto normal = current.points.empty() ? oldContact.normal : current.points[0].normal; diff --git a/engine/src/utils/math/physics/constraints/distance.cpp b/engine/src/utils/math/physics/constraints/distance.cpp index bc530972..2b240eb4 100644 --- a/engine/src/utils/math/physics/constraints/distance.cpp +++ b/engine/src/utils/math/physics/constraints/distance.cpp @@ -45,8 +45,8 @@ pod::Constraint& uf::physics::constrainDistance( pod::Constraint& constraint, co auto& joint = constraint.distance; constraint.type = pod::ConstraintType::DISTANCE; - joint.localAnchorA = uf::transform::applyInverse( tA, pA ); - joint.localAnchorB = uf::transform::applyInverse( tB, pB ); + joint.localAnchorA = impl::applyInverse( tA, pA ); + joint.localAnchorB = impl::applyInverse( tB, pB ); joint.targetDistance = uf::vector::distance( pB, pA ); joint.accumulatedImpulse = 0.0f; @@ -62,8 +62,8 @@ void impl::drawDistance( const pod::Constraint& constraint ) { auto tA = impl::getTransform( *constraint.a ); auto tB = impl::getTransform( *constraint.b ); - auto pA = uf::transform::apply( tA, joint.localAnchorA ); - auto pB = uf::transform::apply( tB, joint.localAnchorB ); + auto pA = impl::apply( tA, joint.localAnchorA ); + auto pB = impl::apply( tB, joint.localAnchorB ); uf::debug::drawLine( tA.position, pA ); uf::debug::drawLine( tB.position, pB ); diff --git a/engine/src/utils/math/physics/constraints/hinge.cpp b/engine/src/utils/math/physics/constraints/hinge.cpp index 09c0fe0e..e6778586 100644 --- a/engine/src/utils/math/physics/constraints/hinge.cpp +++ b/engine/src/utils/math/physics/constraints/hinge.cpp @@ -63,8 +63,8 @@ pod::Constraint& uf::physics::constrainHinge( pod::Constraint& constraint, const auto& joint = constraint.hinge; constraint.type = pod::ConstraintType::HINGE; - joint.localAnchorA = uf::transform::applyInverse( tA, p ); - joint.localAnchorB = uf::transform::applyInverse( tB, p ); + joint.localAnchorA = impl::applyInverse( tA, p ); + joint.localAnchorB = impl::applyInverse( tB, p ); joint.accumulatedImpulse = {}; joint.localAxisA = uf::quaternion::rotate( uf::quaternion::inverse( tA.orientation ), axis ); joint.localAxisB = uf::quaternion::rotate( uf::quaternion::inverse( tB.orientation ), axis ); @@ -80,8 +80,8 @@ void impl::drawHinge( const pod::Constraint& constraint ) { auto tA = impl::getTransform( *constraint.a ); auto tB = impl::getTransform( *constraint.b ); - auto pA = uf::transform::apply( tA, joint.localAnchorA ); - auto pB = uf::transform::apply( tB, joint.localAnchorB ); + auto pA = impl::apply( tA, joint.localAnchorA ); + auto pB = impl::apply( tB, joint.localAnchorB ); auto aA = uf::quaternion::rotate( tA.orientation, joint.localAxisA ); auto aB = uf::quaternion::rotate( tB.orientation, joint.localAxisB ); diff --git a/engine/src/utils/math/physics/constraints/slider.cpp b/engine/src/utils/math/physics/constraints/slider.cpp index 0ff986e9..b0a8ba99 100644 --- a/engine/src/utils/math/physics/constraints/slider.cpp +++ b/engine/src/utils/math/physics/constraints/slider.cpp @@ -115,8 +115,8 @@ pod::Constraint& uf::physics::constrainSlider( pod::Constraint& constraint, cons auto& joint = constraint.slider; constraint.type = pod::ConstraintType::SLIDER; - joint.localAnchorA = uf::transform::applyInverse( tA, p ); - joint.localAnchorB = uf::transform::applyInverse( tB, p ); + joint.localAnchorA = impl::applyInverse( tA, p ); + joint.localAnchorB = impl::applyInverse( tB, p ); joint.localAxisA = uf::quaternion::rotate( invqA, axis ); joint.localAxisB = uf::quaternion::rotate( invqB, axis ); diff --git a/engine/src/utils/math/physics/constraints/spring.cpp b/engine/src/utils/math/physics/constraints/spring.cpp index 6008f75d..a2a0766a 100644 --- a/engine/src/utils/math/physics/constraints/spring.cpp +++ b/engine/src/utils/math/physics/constraints/spring.cpp @@ -49,8 +49,8 @@ pod::Constraint& uf::physics::constrainSpring( pod::Constraint& constraint, cons auto& joint = constraint.spring; constraint.type = pod::ConstraintType::SPRING; - joint.localAnchorA = uf::transform::applyInverse( tA, pA ); - joint.localAnchorB = uf::transform::applyInverse( tB, pB ); + joint.localAnchorA = impl::applyInverse( tA, pA ); + joint.localAnchorB = impl::applyInverse( tB, pB ); joint.restLength = uf::vector::distance( pB, pA ); joint.stiffness = stiffness; diff --git a/engine/src/utils/math/physics/constraints/weld.cpp b/engine/src/utils/math/physics/constraints/weld.cpp index 61be3f2e..9ab92589 100644 --- a/engine/src/utils/math/physics/constraints/weld.cpp +++ b/engine/src/utils/math/physics/constraints/weld.cpp @@ -58,8 +58,8 @@ pod::Constraint& uf::physics::constrainWeld( pod::Constraint& constraint, const auto& joint = constraint.weld; constraint.type = pod::ConstraintType::WELD; - joint.localAnchorA = uf::transform::applyInverse( tA, p ); - joint.localAnchorB = uf::transform::applyInverse( tB, p ); + joint.localAnchorA = impl::applyInverse( tA, p ); + joint.localAnchorB = impl::applyInverse( tB, p ); joint.localAxisA = uf::quaternion::rotate( invqA, axis ); joint.localAxisB = uf::quaternion::rotate( invqB, axis ); diff --git a/engine/src/utils/math/physics/draw.cpp b/engine/src/utils/math/physics/draw.cpp index 3a197784..d9b6d651 100644 --- a/engine/src/utils/math/physics/draw.cpp +++ b/engine/src/utils/math/physics/draw.cpp @@ -17,7 +17,8 @@ void impl::drawManifold( const pod::Manifold& manifold ) { } void impl::drawBody( const pod::PhysicsBody& body ) { if ( !(body.collider.category & uf::physics::settings.debugDraw.mask) ) return; - switch( body.collider.type ) { + // draw wireframe + switch ( body.collider.type ) { case pod::ShapeType::AABB: impl::drawAabb( body ); break; @@ -43,6 +44,48 @@ void impl::drawBody( const pod::PhysicsBody& body ) { impl::drawHull( body ); break; } + // draw name + auto& scene = uf::scene::getCurrentScene(); + auto& controller = scene.getController(); + auto& camera = scene.getCamera( controller ); + auto& bounds = body.bounds; + + auto transform = impl::getTransform( body ); + auto cameraTransform = uf::transform::flatten( camera.getTransform() ); + auto cameraAxes = uf::transform::axes( cameraTransform ); + auto& projection = camera.getProjection(); + auto fov = std::atan(1.0f / fabs(projection(1,1))); + auto viewThreshold = std::cos(fov * 1.5f); + +#if 1 + // (an attempt to) continuously pick the closest point on the AABB + auto position = impl::closestPointOnAABB( cameraTransform.position, bounds ); + auto dir = position - cameraTransform.position; + auto magSq = uf::vector::magnitude( dir ); + if ( magSq > EPS2 ) dir /= std::sqrt( magSq ); + auto dot = uf::vector::dot( cameraAxes.forward, dir ); + position -= cameraAxes.forward * 0.1f; + if ( dot > viewThreshold ) uf::debug::drawText( body.object->getName(), position ); +#else + // picks the closest corner + int closestCorner = -1; + float closestDistanceSq = FLT_MAX; + float closestDot = FLT_MAX; // + pod::Vector3f corners[8]; + impl::getCorners( bounds, corners ); + for ( auto i = 0; i < 8; ++i ) { + auto dir = corners[i] - cameraTransform.position; + auto distanceSq = uf::vector::magnitude( dir ); + if ( distanceSq > EPS2 ) dir /= std::sqrt( distanceSq ); + auto dot = uf::vector::dot( cameraAxes.forward, dir ); + if ( dot <= viewThreshold ) continue; + if ( distanceSq >= closestDistanceSq ) continue; + closestDistanceSq = distanceSq; + closestCorner = i; + } + + if ( 0 <= closestCorner ) uf::debug::drawText( body.object->getName(), corners[closestCorner] ); +#endif } void impl::drawConstraint( const pod::Constraint& constraint ) { if ( !uf::physics::settings.debugDraw.constraints ) return; diff --git a/engine/src/utils/math/physics/impl.cpp b/engine/src/utils/math/physics/impl.cpp index 6f9585a5..c1c88c09 100644 --- a/engine/src/utils/math/physics/impl.cpp +++ b/engine/src/utils/math/physics/impl.cpp @@ -295,7 +295,7 @@ void uf::physics::step( pod::World& world, float dt ) { parentTransform = *transformsMap[originalTransform->reference]; } - transform.position = uf::transform::applyInverse( parentTransform, flattenedTransform.position ); + transform.position = impl::applyInverse( parentTransform, flattenedTransform.position ); transform.orientation = uf::quaternion::multiply( uf::quaternion::inverse( parentTransform.orientation ), flattenedTransform.orientation diff --git a/engine/src/utils/math/physics/narrowphase/aabb.cpp b/engine/src/utils/math/physics/narrowphase/aabb.cpp index 7df5a53f..7b87d2da 100644 --- a/engine/src/utils/math/physics/narrowphase/aabb.cpp +++ b/engine/src/utils/math/physics/narrowphase/aabb.cpp @@ -81,15 +81,11 @@ bool impl::aabbHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:: void impl::drawAabb( const pod::PhysicsBody& body ) { const auto& aabb = body.bounds; auto transform = impl::getTransform( body ); -#if 0 - uf::debug::drawAabb( aabb, transform ); +#if 1 + uf::debug::drawShape( aabb, transform ); #else - 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} - }; + pod::Vector3f corners[8]; + impl::getCorners( aabb, corners ); uf::debug::drawLine( corners[0], corners[1] ); uf::debug::drawLine( corners[1], corners[2] ); uf::debug::drawLine( corners[2], corners[3] ); uf::debug::drawLine( corners[3], corners[0] ); diff --git a/engine/src/utils/math/physics/narrowphase/capsule.cpp b/engine/src/utils/math/physics/narrowphase/capsule.cpp index ec15ca61..0aad79ac 100644 --- a/engine/src/utils/math/physics/narrowphase/capsule.cpp +++ b/engine/src/utils/math/physics/narrowphase/capsule.cpp @@ -109,8 +109,8 @@ bool impl::capsuleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, po void impl::drawCapsule( const pod::PhysicsBody& body ) { const auto& capsule = body.collider.capsule; auto transform = impl::getTransform(body); -#if 0 - uf::debug::drawCapsule( capsule, transform ); +#if 1 + uf::debug::drawShape( capsule, transform ); #else auto [p1, p2] = impl::getCapsuleSegment(body); const int segments = 16; diff --git a/engine/src/utils/math/physics/narrowphase/epa.cpp b/engine/src/utils/math/physics/narrowphase/epa.cpp index e57ea020..024a6632 100644 --- a/engine/src/utils/math/physics/narrowphase/epa.cpp +++ b/engine/src/utils/math/physics/narrowphase/epa.cpp @@ -47,7 +47,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di outCount = 3; bool hasTransform = ( body.transform != nullptr ); FOR_EACH(3, { - outPoly[i] = hasTransform ? uf::transform::apply( transform, body.collider.triangle.points[i] ) : body.collider.triangle.points[i]; + outPoly[i] = hasTransform ? impl::apply( transform, body.collider.triangle.points[i] ) : body.collider.triangle.points[i]; }); } break; case pod::ShapeType::AABB: { @@ -81,7 +81,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di if (n.z < 0) std::swap(outPoly[1], outPoly[3]); } FOR_EACH(4, { - outPoly[i] = uf::transform::apply(transform, outPoly[i]); + outPoly[i] = impl::apply(transform, outPoly[i]); }); } break; case pod::ShapeType::OBB: { @@ -115,7 +115,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di if (n.z < 0) std::swap(outPoly[1], outPoly[3]); } FOR_EACH(4, { - outPoly[i] = uf::transform::apply(transform, outPoly[i]); + outPoly[i] = impl::apply(transform, outPoly[i]); }); } break; case pod::ShapeType::SPHERE: { @@ -165,7 +165,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di } outCount = 3; FOR_EACH(3, { - outPoly[i] = uf::transform::apply(transform, bestTri.points[i]); + outPoly[i] = impl::apply(transform, bestTri.points[i]); }); } break; // unsupported, fallback to single contact point diff --git a/engine/src/utils/math/physics/narrowphase/gjk.cpp b/engine/src/utils/math/physics/narrowphase/gjk.cpp index 94aa88e7..dfca66c0 100644 --- a/engine/src/utils/math/physics/narrowphase/gjk.cpp +++ b/engine/src/utils/math/physics/narrowphase/gjk.cpp @@ -19,7 +19,7 @@ pod::Vector3f impl::support( const pod::PhysicsBody& body, const pod::Vector3f& ( localDir.y >= 0.0f ) ? box.max.y : box.min.y, ( localDir.z >= 0.0f ) ? box.max.z : box.min.z }; - return uf::transform::apply( transform, localPt ); + return impl::apply( transform, localPt ); } break; case pod::ShapeType::SPHERE: { return transform.position + uf::vector::normalize( dir ) * body.collider.sphere.radius; @@ -77,7 +77,7 @@ pod::Vector3f impl::support( const pod::PhysicsBody& body, const pod::Vector3f& } } - return uf::transform::apply( transform, furthestVertex ); + return impl::apply( transform, furthestVertex ); } break; default: { diff --git a/engine/src/utils/math/physics/narrowphase/hull.cpp b/engine/src/utils/math/physics/narrowphase/hull.cpp index 6332e6bd..fd8a89a7 100644 --- a/engine/src/utils/math/physics/narrowphase/hull.cpp +++ b/engine/src/utils/math/physics/narrowphase/hull.cpp @@ -92,12 +92,12 @@ void impl::drawHull( const pod::PhysicsBody& body ) { for ( size_t i = 0; i < totalTriangles; ++i ) { auto tri = uf::mesh::fetchTriangle( *meshData, i ); - #if 0 - uf::debug::drawTriangle( tri, transform ); + #if 1 + uf::debug::drawShape( tri, transform ); #else - auto v0 = uf::transform::apply( transform, tri.points[0] ); - auto v1 = uf::transform::apply( transform, tri.points[1] ); - auto v2 = uf::transform::apply( transform, tri.points[2] ); + auto v0 = impl::apply( transform, tri.points[0] ); + auto v1 = impl::apply( transform, tri.points[1] ); + auto v2 = impl::apply( transform, tri.points[2] ); uf::debug::drawLine( v0, v1 ); uf::debug::drawLine( v1, v2 ); diff --git a/engine/src/utils/math/physics/narrowphase/mesh.cpp b/engine/src/utils/math/physics/narrowphase/mesh.cpp index b0a1bdc6..408d0b6c 100644 --- a/engine/src/utils/math/physics/narrowphase/mesh.cpp +++ b/engine/src/utils/math/physics/narrowphase/mesh.cpp @@ -200,12 +200,12 @@ void impl::drawMesh( const pod::PhysicsBody& body ) { for ( size_t i = 0; i < totalTriangles; ++i ) { auto tri = uf::mesh::fetchTriangle( *meshData, i ); - #if 0 - uf::debug::drawTriangle( tri, transform ); + #if 1 + uf::debug::drawShape( tri, transform ); #else - auto v0 = uf::transform::apply( transform, tri.points[0] ); - auto v1 = uf::transform::apply( transform, tri.points[1] ); - auto v2 = uf::transform::apply( transform, tri.points[2] ); + auto v0 = impl::apply( transform, tri.points[0] ); + auto v1 = impl::apply( transform, tri.points[1] ); + auto v2 = impl::apply( transform, tri.points[2] ); uf::debug::drawLine( v0, v1 ); uf::debug::drawLine( v1, v2 ); diff --git a/engine/src/utils/math/physics/narrowphase/obb.cpp b/engine/src/utils/math/physics/narrowphase/obb.cpp index 09e1dd5c..39b8dbf7 100644 --- a/engine/src/utils/math/physics/narrowphase/obb.cpp +++ b/engine/src/utils/math/physics/narrowphase/obb.cpp @@ -129,8 +129,8 @@ bool impl::obbObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Ma auto boxA = a.collider.obb; auto boxB = b.collider.obb; - boxA.center = uf::transform::apply( tA, boxA.center ); - boxB.center = uf::transform::apply( tB, boxB.center ); + boxA.center = impl::apply( tA, boxA.center ); + boxB.center = impl::apply( tB, boxB.center ); pod::Vector3f axesA[3]; pod::Vector3f axesB[3]; @@ -149,7 +149,7 @@ bool impl::obbAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::M auto boxA = a.collider.obb; auto boxB = impl::aabbToObb( b.bounds ); - boxA.center = uf::transform::apply( tA, boxA.center ); + boxA.center = impl::apply( tA, boxA.center ); pod::Vector3f axesA[3]; pod::Vector3f axesB[3]; @@ -164,12 +164,12 @@ bool impl::obbSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod: auto tA = impl::getTransform( a ); auto box = a.collider.obb; - box.center = uf::transform::apply( tA, box.center ); + box.center = impl::apply( tA, box.center ); auto sphereCenter = impl::getPosition( b ); float radius = b.collider.sphere.radius; - auto localP = uf::transform::applyInverse( tA, sphereCenter ) - box.center; + auto localP = impl::applyInverse( tA, sphereCenter ) - box.center; auto closestLocal = uf::vector::clamp( localP, -box.extent, box.extent ); auto deltaLocal = localP - closestLocal; @@ -177,7 +177,7 @@ bool impl::obbSphere( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod: if ( distSq > radius * radius ) return false; - auto closestWorld = uf::transform::apply( tA, closestLocal + box.center ); + auto closestWorld = impl::apply( tA, closestLocal + box.center ); float dist = std::sqrt( distSq ); pod::Vector3f normal; @@ -212,7 +212,7 @@ bool impl::obbPlane( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod:: auto tA = impl::getTransform( a ); auto box = a.collider.obb; - box.center = uf::transform::apply( tA, box.center ); + box.center = impl::apply( tA, box.center ); pod::Vector3f axesA[3]; impl::boxAxes( axesA, tA ); @@ -242,7 +242,7 @@ bool impl::obbCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod auto tA = impl::getTransform( a ); auto box = a.collider.obb; - box.center = uf::transform::apply( tA, box.center ); + box.center = impl::apply( tA, box.center ); pod::Vector3f axesA[3]; impl::boxAxes( axesA, tA ); @@ -307,23 +307,10 @@ bool impl::obbHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::M void impl::drawObb( const pod::PhysicsBody& body ) { const auto& obb = body.collider.obb; auto transform = impl::getTransform(body); -#if 0 - uf::debug::drawObb( obb, transform ); +#if 1 + uf::debug::drawShape( obb, transform ); #else - auto aabb = pod::AABB{ - .min = obb.center - obb.extent, - .max = obb.center + obb.extent, - }; - 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} - }; - - FOR_EACH( 8, { - corners[i] = uf::transform::apply(transform, corners[i]); - }); + impl::getCorners( pod::aabbToObb( obb ), transform, corners ); uf::debug::drawLine( corners[0], corners[1] ); uf::debug::drawLine( corners[1], corners[2] ); uf::debug::drawLine( corners[2], corners[3] ); uf::debug::drawLine( corners[3], corners[0] ); diff --git a/engine/src/utils/math/physics/narrowphase/plane.cpp b/engine/src/utils/math/physics/narrowphase/plane.cpp index cfa51aee..dfd1354c 100644 --- a/engine/src/utils/math/physics/narrowphase/plane.cpp +++ b/engine/src/utils/math/physics/narrowphase/plane.cpp @@ -70,8 +70,8 @@ void impl::drawPlane( const pod::PhysicsBody& body ) { const auto& plane = body.collider.plane; auto transform = impl::getTransform(body); -#if 0 - uf::debug::drawPlane( plane, transform ); +#if 1 + uf::debug::drawShape( plane, transform ); #else 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}); diff --git a/engine/src/utils/math/physics/narrowphase/ray.cpp b/engine/src/utils/math/physics/narrowphase/ray.cpp index 11598336..98acca7f 100644 --- a/engine/src/utils/math/physics/narrowphase/ray.cpp +++ b/engine/src/utils/math/physics/narrowphase/ray.cpp @@ -82,7 +82,7 @@ bool impl::rayObb( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQu auto tA = impl::getTransform( body ); pod::Ray localRay; - localRay.origin = uf::transform::applyInverse( tA, ray.origin ); + localRay.origin = impl::applyInverse( tA, ray.origin ); localRay.direction = uf::quaternion::rotate( uf::quaternion::inverse(tA.orientation), ray.direction ); auto box = impl::obbToAabb( body.collider.obb ); @@ -235,7 +235,7 @@ bool impl::rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue const auto transform = impl::getTransform( body ); pod::Ray ray; - ray.origin = uf::transform::applyInverse( transform, r.origin ); + ray.origin = impl::applyInverse( transform, r.origin ); ray.direction = uf::quaternion::rotate( uf::quaternion::inverse( transform.orientation ), r.direction ); thread_local uf::stl::vector candidates; @@ -253,7 +253,7 @@ bool impl::rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue auto n = impl::triangleNormal( tri ); // push back to world - auto p = uf::transform::apply( transform, l); + auto p = impl::apply( transform, l); n = uf::quaternion::rotate( transform.orientation, n ); rayHit.hit = true; @@ -273,7 +273,7 @@ bool impl::rayHull( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue const auto transform = impl::getTransform( body ); pod::Ray ray; - ray.origin = uf::transform::applyInverse( transform, r.origin ); + ray.origin = impl::applyInverse( transform, r.origin ); ray.direction = uf::quaternion::rotate( uf::quaternion::inverse( transform.orientation ), r.direction ); thread_local uf::stl::vector candidates; @@ -292,7 +292,7 @@ bool impl::rayHull( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue rayHit.hit = true; rayHit.body = &body; - rayHit.contact.point = uf::transform::apply( transform, ray.origin + ray.direction * t ); + rayHit.contact.point = impl::apply( transform, ray.origin + ray.direction * t ); rayHit.contact.normal = uf::quaternion::rotate( transform.orientation, normal ); rayHit.contact.penetration = t; } diff --git a/engine/src/utils/math/physics/narrowphase/sphere.cpp b/engine/src/utils/math/physics/narrowphase/sphere.cpp index b90c2b68..f7fc2d24 100644 --- a/engine/src/utils/math/physics/narrowphase/sphere.cpp +++ b/engine/src/utils/math/physics/narrowphase/sphere.cpp @@ -71,8 +71,8 @@ bool impl::sphereHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod void impl::drawSphere( const pod::PhysicsBody& body ) { const auto& sphere = body.collider.sphere; auto transform = impl::getTransform(body); -#if 0 - uf::debug::drawSphere( sphere, transform ); +#if 1 + uf::debug::drawShape( sphere, transform ); #else const int segments = 16; const float angleIncrement = (2.0f * M_PI) / segments; diff --git a/engine/src/utils/math/physics/narrowphase/triangle.cpp b/engine/src/utils/math/physics/narrowphase/triangle.cpp index ef6e8646..c98ea3fc 100644 --- a/engine/src/utils/math/physics/narrowphase/triangle.cpp +++ b/engine/src/utils/math/physics/narrowphase/triangle.cpp @@ -190,7 +190,7 @@ bool impl::triangleObb( const pod::TriangleWithNormal& tri, const pod::PhysicsBo auto transform = impl::getTransform( body ); auto box = body.collider.obb; - box.center = uf::transform::apply( transform, box.center ); + box.center = impl::apply( transform, box.center ); pod::Vector3f axes[3]; impl::boxAxes( axes, transform ); @@ -358,12 +358,12 @@ bool impl::triangleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, p void impl::drawTriangle( const pod::PhysicsBody& body ) { const auto& tri = body.collider.triangle; auto transform = impl::getTransform(body); -#if 0 - uf::debug::drawTriangle( tri, transform ); +#if 1 + uf::debug::drawShape( tri, transform ); #else - auto v0 = uf::transform::apply( transform, tri.points[0] ); - auto v1 = uf::transform::apply( transform, tri.points[1] ); - auto v2 = uf::transform::apply( transform, tri.points[2] ); + auto v0 = impl::apply( transform, tri.points[0] ); + auto v1 = impl::apply( transform, tri.points[1] ); + auto v2 = impl::apply( transform, tri.points[2] ); uf::debug::drawLine( v0, v1 ); uf::debug::drawLine( v1, v2 ); diff --git a/engine/src/utils/mesh/mesh.cpp b/engine/src/utils/mesh/mesh.cpp index 15d1ce69..96221038 100644 --- a/engine/src/utils/mesh/mesh.cpp +++ b/engine/src/utils/mesh/mesh.cpp @@ -228,15 +228,26 @@ uf::Mesh uf::Mesh::expand( bool interleaved ) { return res; } -void uf::Mesh::eraseAttribute( uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute ) { - for ( size_t i = 0; i < input.attributes.size(); ++i ) if ( input.attributes[i].descriptor == attribute.descriptor ) return eraseAttribute( input, i ); +void uf::Mesh::clearAttribute( uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute ) { + for ( size_t i = 0; i < input.attributes.size(); ++i ) if ( input.attributes[i].descriptor == attribute.descriptor ) return clearAttribute( input, i ); } -void uf::Mesh::eraseAttribute( uf::Mesh::Input& input, size_t i ) { +void uf::Mesh::clearAttribute( uf::Mesh::Input& input, size_t i ) { UF_ASSERT( !isInterleaved( input ) ); // can't be assed to de-interleave, erase, and then interleave again auto attribute = input.attributes[i]; buffers[attribute.buffer].clear(); } +void uf::Mesh::clear() { + for ( size_t i = 0; i < vertex.attributes.size(); ++i ) clearAttribute( vertex, i ); + for ( size_t i = 0; i < index.attributes.size(); ++i ) clearAttribute( index, i ); + for ( size_t i = 0; i < instance.attributes.size(); ++i ) clearAttribute( instance, i ); + for ( size_t i = 0; i < indirect.attributes.size(); ++i ) clearAttribute( indirect, i ); + + vertex.count = 0; + index.count = 0; + instance.count = 0; + indirect.count = 0; +} void uf::Mesh::generateIndirect() { if ( index.count == 0 ) generateIndices(); diff --git a/engine/src/utils/text/glyph_.cpp b/engine/src/utils/text/glyph_.cpp new file mode 100644 index 00000000..d6c13780 --- /dev/null +++ b/engine/src/utils/text/glyph_.cpp @@ -0,0 +1,329 @@ +#include +#include + +#if UF_USE_OPENGL +#define EXT_COLOR_FLOATS 0 +#else +#define EXT_COLOR_FLOATS 1 +#endif + +namespace { + struct { + #if UF_ENV_DREAMCAST + pod::Vector2ui size = { 640, 480 }; + #else + pod::Vector2ui size = { 1920, 1080 }; + #endif + } defaults; +} + +namespace { + struct GlyphVertex { + pod::Vector3f position; + pod::Vector2f uv; + + #if EXT_COLOR_FLOATS + pod::Vector4f color; + #else + pod::Vector4b color; + #endif + + static uf::stl::vector descriptor; + }; +} + +UF_VERTEX_DESCRIPTOR(GlyphVertex, + UF_VERTEX_DESCRIPTION(GlyphVertex, R32G32B32_SFLOAT, position) + UF_VERTEX_DESCRIPTION(GlyphVertex, R32G32_SFLOAT, uv) +#if EXT_COLOR_FLOATS + UF_VERTEX_DESCRIPTION(GlyphVertex, R32G32B32A32_SFLOAT, color) +#else + UF_VERTEX_DESCRIPTION(GlyphVertex, R8G8B8A8_UNORM, color) +#endif +) + +namespace { + struct { + #if UF_USE_FREETYPE + ext::freetype::Glyph glyph; + uf::stl::unordered_map> cache; + #else + char glyph; + uf::stl::unordered_map> cache; + #endif + } glyphs; + + float hexToFloat( const uf::stl::string& str ) { + int value; + uf::stl::stringstream stream; + stream << str; + stream >> std::hex >> value; + return value / 255.0f; + } + + // default to left + pod::Vector2f parseAnchor( const uf::stl::string& anchor, const pod::Vector2f& def = {0.0f, 0.0f} ) { + if ( anchor == "top-center" || anchor == "top" ) return {0.5f, 0.0f}; + if ( anchor == "top-left" ) return {0.0f, 0.0f}; + if ( anchor == "top-right" ) return {1.0f, 0.0f}; + + if ( anchor == "center" ) return {0.5f, 0.5f}; + if ( anchor == "center-left" || anchor == "left" ) return {0.0f, 0.5f}; + if ( anchor == "center-right" || anchor == "right" ) return {1.0f, 0.5f}; + + if ( anchor == "bottom-center" || anchor == "bottom" ) return {0.5f, 1.0f}; + if ( anchor == "bottom-left" ) return {0.0f, 1.0f}; + if ( anchor == "bottom-right" ) return {1.0f, 1.0f}; + + return def; + } +} + +size_t uf::glyph::hashSettings( uint64_t c, const pod::GlyphSettings& metadata ) { + size_t seed{}; + uf::hash( seed, c, metadata.padding[0], metadata.padding[1], metadata.spread, metadata.size, metadata.font, metadata.sdf ); + return seed; +} +size_t uf::glyph::hashSettings( const uf::stl::string& c, const pod::GlyphSettings& metadata ) { + size_t seed{}; + uf::hash( seed, c, metadata.padding[0], metadata.padding[1], metadata.spread, metadata.size, metadata.font, metadata.sdf ); + return seed; +} + + +// parses a text for special tokens, associating strings with color +// should maybe be utf8, but in theory it shouldn't matter since tags are ASCII +uf::stl::vector uf::glyph::parseTextTokens( const uf::stl::string& text, const pod::Vector4f& color ) { + uf::stl::vector tokens; + + auto tagLength = 12; // hard-coded cringe + bool colorChanged = false; + size_t currentPos = 0; + size_t textLength = text.length(); + + pod::TextToken currentToken; + currentToken.color = color; + + while ( currentPos < textLength ) { + size_t tagStart = text.find("${#", currentPos); + + if ( tagStart == uf::stl::string::npos ) { + currentToken.text += text.substr(currentPos); + if ( !currentToken.text.empty() || colorChanged ) { + tokens.emplace_back(currentToken); + } + break; + } + + if ( tagStart > currentPos ) { + currentToken.text += text.substr( currentPos, tagStart - currentPos ); // append everything up to the tag + tokens.emplace_back( currentToken ); // commit token + currentToken.text = ""; // reset text + } + + // validate tag + if ( tagStart + tagLength <= textLength && text[tagStart + tagLength - 1] == '}' ) { + uf::stl::string rHex = text.substr(tagStart + 3, 2); + uf::stl::string gHex = text.substr(tagStart + 5, 2); + uf::stl::string bHex = text.substr(tagStart + 7, 2); + uf::stl::string aHex = text.substr(tagStart + 9, 2); + + // change color + currentToken.color = { hexToFloat(rHex), hexToFloat(gHex), hexToFloat(bHex), hexToFloat(aHex) }; + colorChanged = true; + + // advance past the tag + currentPos = tagStart + tagLength; + } else { + currentToken.text += "${#"; // treat it as normal text + currentPos = tagStart + 3; + } + } + + return tokens; +} + +// compute the boxes for a given string and settings +uf::stl::vector uf::glyph::calculateLayout( const uf::stl::vector& tokens, const pod::GlyphSettings& metadata ) { + uf::stl::vector layout; + auto& glyphsCache = ::glyphs.cache[metadata.font]; + + if ( glyphsCache.empty() ) { + ext::freetype::initialize( ::glyphs.glyph, uf::io::root + "/fonts/" + metadata.font ); + } + + pod::Vector2f anchor = ::parseAnchor( metadata.alignment ); + pod::Vector2f cursor = { 0.0f, 0.0f }; + float maxTextWidth = 0.0f; + float maxTextHeight = 0.0f; + float tallestGlyphY = 0.0f; + float averageTabWidth = 0.0f; + float totalWidth = 0.0f; + int charCount = 0; + + // generate glyph and line height + for ( const auto& token : tokens ) { + std::u8string str(token.text.begin(), token.text.end()); + for ( uint64_t c : str ) { + if ( c == '\n' || c == '\t' ) continue; // special characters + + auto key = uf::glyph::hashSettings(c, metadata); + auto& glyph = glyphsCache[key]; + + // generate glyph + if ( !glyph.generated() ) { + glyph.setPadding({ metadata.padding[0], metadata.padding[1] }); + glyph.setSpread(metadata.spread); + glyph.useSdf(metadata.sdf); + glyph.generate(::glyphs.glyph, c, metadata.size); + } + + tallestGlyphY = std::max(tallestGlyphY, (float) glyph.getSize().y); + totalWidth += glyph.getSize().x; // should probably be reset on new-line to find the widest line + charCount++; + } + } + + if ( charCount > 0 ) averageTabWidth = (totalWidth / charCount) * 4.0f; + cursor.y = tallestGlyphY; + + // calculate positions + for ( const auto& token : tokens ) { + std::u8string str( token.text.begin(), token.text.end() ); + for ( uint64_t c : str ) { + // advance cursor on special characters + if ( c == '\n' ) { + cursor.y += tallestGlyphY; + cursor.x = 0; + continue; + } else if ( c == '\t' ) { + cursor.x = ((int)(cursor.x / averageTabWidth) + 1) * averageTabWidth; + continue; + } else if ( c == ' ' ) { + cursor.x += averageTabWidth / 4.0f; + continue; + } + + // retrieve glyph + auto key = uf::glyph::hashSettings(c, metadata); + auto& glyph = glyphsCache[key]; + auto& g = layout.emplace_back(pod::GlyphBox{ + .box = { + .x = cursor.x + glyph.getBearing().x, + .y = cursor.y - glyph.getBearing().y, + .w = glyph.getSize().x, + .h = glyph.getSize().y, + .z = 0, + }, + .color = token.color, + .code = c, + }); + + // advance cursor + cursor.x += glyph.getAdvance().x; + + // advance bounding box + maxTextWidth = std::max(maxTextWidth, g.box.x + g.box.w); + maxTextHeight = std::max(maxTextHeight, g.box.y + g.box.h); + } + } + + // calculate offset based on anchor + float offsetX = maxTextWidth * anchor.x; + float offsetY = maxTextHeight * anchor.y; + + // adjust all glyphs for our offset + for ( auto& g : layout ) { + g.box.x -= offsetX; + g.box.y -= offsetY; + + // normalize + g.box.x /= ::defaults.size.x; + g.box.w /= ::defaults.size.x; + g.box.y /= ::defaults.size.y; + g.box.h /= ::defaults.size.y; + } + + return layout; +} + +// generate the mesh and texture atlas +bool uf::glyph::generateAtlas( const uf::stl::vector& layout, const pod::GlyphSettings& metadata, uf::Atlas& atlas ) { + bool dirty = false; + auto& cache = ::glyphs.cache[metadata.font]; + +#if UF_USE_FREETYPE + // generate atlas + for ( const auto& g : layout ) { + auto key = uf::glyph::hashSettings( g.code, metadata ); + auto hash = std::to_string( key ); + auto& glyph = cache[key]; + + // already in atlas map + if ( atlas.has( hash ) ) continue; + dirty = true; + + uf::Image image; + const uint8_t* buffer = glyph.getBuffer(); + + if ( metadata.sdf ) { + image.loadFromBuffer( glyph.getBuffer(), glyph.getSize(), 8, 1, true ); + } else { + uf::Image::container_t pixels; + size_t len = glyph.getSize().x * glyph.getSize().y; + pixels.resize(len * 4); + for ( auto i = 0; i < len; ++i ) { + pixels[i * 4 + 0] = buffer[i]; // R + pixels[i * 4 + 1] = buffer[i]; // G + pixels[i * 4 + 2] = buffer[i]; // B + pixels[i * 4 + 3] = buffer[i]; // A + } + image.loadFromBuffer( &pixels[0], glyph.getSize(), 8, 4, true ); + } + atlas.addImage( image, hash ); + } + + atlas.generate(); +#endif + return dirty; +} +void uf::glyph::generateMesh( const uf::stl::vector& layout, const pod::GlyphSettings& metadata, const uf::Atlas& atlas, uf::Mesh& mesh ) { + // generate mesh + mesh.clear(); + mesh.bind<::GlyphVertex, uint16_t>(); + + uf::stl::vector<::GlyphVertex> vertices; + uf::stl::vector indices; + vertices.reserve(layout.size() * 4); + indices.reserve(layout.size() * 6); + + for ( const auto& g : layout ) { + auto hash = std::to_string( uf::glyph::hashSettings(g.code, metadata) ); + + // zero-width + if ( g.box.w == 0 || g.box.h == 0 || g.color.w == 0.0f ) continue; + + #if EXT_COLOR_FLOATS + auto& color = g.color; + #else + pod::Vector4b color = { + (uint8_t)(g.color[0] * 255), + (uint8_t)(g.color[1] * 255), + (uint8_t)(g.color[2] * 255), + (uint8_t)(g.color[3] * 255) + }; + #endif + // insert indices + uint16_t idx = (uint16_t) vertices.size(); + indices.insert( indices.end(), { idx, idx + 1, idx + 2, idx, idx + 2, idx + 3 }); + + // insert vertices + vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x, g.box.y + g.box.h, g.box.z }, atlas.mapUv(pod::Vector2f{ 0.0f, 0.0f }, hash), color}); + vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x, g.box.y, g.box.z }, atlas.mapUv(pod::Vector2f{ 0.0f, 1.0f }, hash), color}); + vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x + g.box.w, g.box.y, g.box.z }, atlas.mapUv(pod::Vector2f{ 1.0f, 1.0f }, hash), color}); + vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x + g.box.w, g.box.y + g.box.h, g.box.z }, atlas.mapUv(pod::Vector2f{ 1.0f, 0.0f }, hash), color}); + } + + mesh.insertVertices(vertices); + mesh.insertIndices(indices); +} \ No newline at end of file diff --git a/ext/behaviors/craeture/behavior.cpp b/ext/behaviors/craeture/behavior.cpp index 3a30dba0..0e3b7a0c 100644 --- a/ext/behaviors/craeture/behavior.cpp +++ b/ext/behaviors/craeture/behavior.cpp @@ -20,7 +20,7 @@ UF_BEHAVIOR_REGISTER_CPP(ext::CraetureBehavior) -UF_BEHAVIOR_TRAITS_CPP(ext::CraetureBehavior, ticks = true, renders = false, thread = "") +UF_BEHAVIOR_TRAITS_CPP(ext::CraetureBehavior, ticks = false, renders = false, thread = "") #define this (&self) namespace { void load( uf::Object& self, const uf::Image& image ) { diff --git a/makefiles/platforms/win64.clang.mk b/makefiles/platforms/win64.clang.mk index 3ef2fede..c3edafb7 100644 --- a/makefiles/platforms/win64.clang.mk +++ b/makefiles/platforms/win64.clang.mk @@ -3,8 +3,8 @@ CDIR = CC = clang CXX = clang++ OPTIMIZATIONS = -O3 -fstrict-aliasing -DUF_NO_EXCEPTIONS # -flto # -march=native -WARNINGS = -Wall -Wno-deprecated-literal-operator -Wno-pointer-arith -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder-ctor -Wno-ignored-attributes -Wno-c++11-narrowing -Wno-unknown-pragmas -Wno-nullability-completeness -Wno-defaulted-function-deleted -Wno-mismatched-tags -SANITIZE = -fsanitize=address # -fuse-ld=lld -fno-omit-frame-pointer +WARNINGS = -Wall -Wno-missing-braces -Wno-deprecated-literal-operator -Wno-pointer-arith -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder-ctor -Wno-ignored-attributes -Wno-c++11-narrowing -Wno-unknown-pragmas -Wno-nullability-completeness -Wno-defaulted-function-deleted -Wno-mismatched-tags +SANITIZE = -fsanitize=address,undefined # -fuse-ld=lld -fno-omit-frame-pointer FLAGS += -std=c++2b $(OPTIMIZATIONS) $(WARNINGS) $(SANITIZE) -fcolor-diagnostics -fansi-escape-codes # MSYS2 diff --git a/makefiles/platforms/win64.mk b/makefiles/platforms/win64.mk index 4cdfc549..8780c04f 100644 --- a/makefiles/platforms/win64.mk +++ b/makefiles/platforms/win64.mk @@ -1,5 +1,5 @@ ifneq (,$(findstring -DUF_DEV_ENV,$(FLAGS))) - REQ_DEPS += meshoptimizer toml xatlas curl ffx:sdk dc:texconv # vall_e cpptrace # openvr # ncurses draco discord bullet ultralight-ux + REQ_DEPS += meshoptimizer toml xatlas curl dc:texconv # ffx:sdk vall_e cpptrace # openvr # ncurses draco discord bullet ultralight-ux FLAGS += -march=native -g # -flto # -g endif