mucho texto (hunted down a weird GCC v13 bug that required uf::transform::apply to be inlined, working clang again, more attempts to fix ragdolls, debug draw text, a bunch of other things)

This commit is contained in:
ecker 2026-05-30 21:44:18 -05:00
parent 78171039a1
commit d8baa484da
60 changed files with 1402 additions and 789 deletions

View File

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

View File

@ -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": {

View File

@ -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
}
}
/*

View File

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

View File

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

View File

@ -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": {

View File

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

View File

@ -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);
}

View File

@ -153,6 +153,8 @@
#define UF_DEBUG 1
#endif
#if UF_DEBUG
#include <uf/utils/io/fmt.h>
#endif

View File

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

View File

@ -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 );
}
}

View File

@ -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<uf::Image>&, 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;

View File

@ -381,7 +381,10 @@ pod::Vector3t<typename T::type_t> 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 };
}

View File

@ -22,12 +22,13 @@ namespace uf {
}
}
namespace {
/***/ namespace {
#if SIMD_MV
__attribute__((target("default")))
uf::simd::matrix_value<float> matMult_impl(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> _impl_matMult(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> R;
uf::simd::matrix_value<float> 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<float> matMult_impl(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> _impl_matMult(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> R;
uf::simd::matrix_value<float> 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<float> matMult_impl(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> _impl_matMult(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> R;
uf::simd::matrix_value<float> 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<float> matMult_impl( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> _impl_matMult( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> R;
uf::simd::matrix_value<float> Bt = uf::simd::matTranspose(B);
@ -148,10 +144,9 @@ namespace {
return R;
}
#endif
__attribute__((target("default")))
uf::simd::vector<float> matMult_impl( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
uf::simd::vector<float> _impl_matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> 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<float> matMult_impl( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
uf::simd::vector<float> _impl_matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> 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<float> _impl_matMult( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> R;
uf::simd::matrix_value<float> 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<float> _impl_matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> 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<typename T>
FORCE_INLINE uf::simd::matrix_value<T>::matrix_value() {}
@ -204,10 +324,10 @@ FORCE_INLINE uf::simd::matrix_value<T>::operator pod::Matrix<T,4>() const {
}
FORCE_INLINE uf::simd::matrix_value<float> uf::simd::matMult( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B ) {
return ::matMult_impl( A, B );
return _impl_matMult( A, B );
}
FORCE_INLINE uf::simd::vector<float> uf::simd::matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> vec ) {
return ::matMult_impl( M, vec );
return _impl_matMult( M, vec );
}
FORCE_INLINE uf::simd::matrix_value<float> uf::simd::matTranspose( const uf::simd::matrix_value<float>& M ) {
uf::simd::matrix_value<float> R = M;

View File

@ -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<pod::Vector3f, pod::Vector3f> getCapsuleSegment( const pod::PhysicsBody& body );
pod::AABB computeAABB( const pod::PhysicsBody& body );

View File

@ -183,7 +183,7 @@ template<typename T> pod::Quaternion<T> 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 );
}

View File

@ -20,9 +20,9 @@ namespace pod {
struct /*UF_API*/ Transform {
typedef T type_t;
pod::Vector3t<T> position = {0, 0,0 };
pod::Quaternion<T> orientation = {0, 0, 0, 1};
pod::Vector3t<T> scale = {1, 1, 1};
alignas(16) pod::Vector3t<T> position = {0, 0, 0 };
alignas(16) pod::Quaternion<T> orientation = {0, 0, 0, 1};
alignas(16) pod::Vector3t<T> scale = {1, 1, 1};
pod::Transform<T>* reference = NULL;
};
@ -52,8 +52,9 @@ namespace uf {
template<typename T> pod::Transform<T>& /*UF_API*/ reference( pod::Transform<T>& transform, const pod::Transform<T>& parent, bool reorient = true );
template<typename T> pod::Transform<T> /*UF_API*/ interpolate( const pod::Transform<T>& from, const pod::Transform<T>& to, float factor );
template<typename T> pod::Transform<T> /*UF_API*/ inverse(const pod::Transform<T>& t);
template<typename T> pod::Vector3t<T> /*UF_API*/ apply( const pod::Transform<T>& transform, const pod::Vector3t<T>& point );
template<typename T> pod::Vector3t<T> /*UF_API*/ applyInverse(const pod::Transform<T>& t, const pod::Vector3t<T>& worldPoint);
// forced inline because of a GCC quirk
template<typename T> pod::Vector3t<T> FORCE_INLINE /*UF_API*/ apply( const pod::Transform<T>& transform, const pod::Vector3t<T>& point );
template<typename T> pod::Vector3t<T> FORCE_INLINE /*UF_API*/ applyInverse(const pod::Transform<T>& t, const pod::Vector3t<T>& worldPoint);
template<typename T> pod::Transform<T> /*UF_API*/ relative(const pod::Transform<T>& a, const pod::Transform<T>& b);
template<typename T> uf::stl::string /*UF_API*/ toString( const pod::Transform<T>&, bool flatten = true );

View File

@ -121,9 +121,9 @@ pod::Matrix4t<T> /*UF_API*/ uf::transform::model( const pod::Transform<T>& trans
template<typename T>
pod::Transform<T> uf::transform::fromMatrix( const pod::Matrix4t<T>& matrix ) {
pod::Transform<T> transform;
transform.position = uf::matrix::multiply( matrix, pod::Vector4f{ 0, 0, 0, 1 } );
transform.position = uf::matrix::extractTranslation( matrix );
transform.orientation = uf::quaternion::fromMatrix( matrix );
// transform.scale = ...;
transform.scale = uf::matrix::extractScale( matrix );
return transform;
}
@ -168,7 +168,7 @@ pod::Transform<T> uf::transform::inverse(const pod::Transform<T>& t) {
}
template<typename T>
pod::Vector3t<T> /*UF_API*/ uf::transform::apply( const pod::Transform<T>& t, const pod::Vector3t<T>& p ) {
pod::Vector3t<T> uf::transform::apply( const pod::Transform<T>& t, const pod::Vector3t<T>& p ) {
return uf::quaternion::rotate( t.orientation, p * t.scale ) + t.position;
}

View File

@ -166,7 +166,7 @@ namespace uf {
template<typename T> /*FORCE_INLINE*/ typename T::type_t /*UF_API*/ magnitude( const T& vector ); // gets the magnitude of the vector
template<typename T> /*FORCE_INLINE*/ typename T::type_t /*UF_API*/ norm( const T& vector ); // compute the norm (length) of the vector
template<typename T> /*FORCE_INLINE*/ T /*UF_API*/ normalize( const T& vector ); // normalizes a vector
template<typename T> /*FORCE_INLINE*/ T /*UF_API*/ clampMagnitude( const T& vector ); // clamps the magnitude of a vector
template<typename T> /*FORCE_INLINE*/ T /*UF_API*/ clampMagnitude( const T& vector, float maxMag ); // clamps the magnitude of a vector
template<typename T> /*FORCE_INLINE*/ void /*UF_API*/ orthonormalize( T& x, T& y ); // orthonormalizes a vector against another vector
template<typename T> /*FORCE_INLINE*/ T /*UF_API*/ orthonormalize( const T& x, const T& y ); // orthonormalizes a vector against another vector

View File

@ -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<N>([&](auto i) F )
#define FOR_EACH_( N, F ) for ( auto i = 0; i < N; ++i ) F;
template<typename T, typename Op>
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>
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>
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];
});

View File

@ -1,15 +1,15 @@
#include <uf/utils/memory/alignment.h>
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<float> uf::simd::sqrt( uf::simd::vector<float> v )
return _mm_sqrt_ps( v );
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<float> dot_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
return uf::simd::mul( x, y );
uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> 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<float> dot_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> 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<float> dot_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
return _mm_dp_ps(x, y, 0xF1);
uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
return _mm_dp_ps(x, y, 0xFF);
}
#else
FORCE_INLINE uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> 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<float> x, uf::simd::vector<float> 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<int32_t> load_impl( const int32_t* f ) {
uf::simd::vector<int32_t> _impl_load( const int32_t* f ) {
return uf::simd::vector<int32_t>( f[0], f[1], f[2], f[3] );
}
MV_INSTR_SET_3
uf::simd::vector<int32_t> load_impl( const int32_t* f ) {
uf::simd::vector<int32_t> _impl_load( const int32_t* f ) {
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
}
#else
FORCE_INLINE uf::simd::vector<int32_t> _impl_load( const int32_t* f ) {
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
#if SSE_INSTR_SET >= 3
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
#else
return uf::simd::vector<int32_t>( f[0], f[1], f[2], f[3] );
#endif
}
#endif
/**/}
FORCE_INLINE uf::simd::vector<int32_t> 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<int32_t> v, int32_t* f ) {
void _impl_store( uf::simd::vector<int32_t> 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<int32_t> v, int32_t* f ) {
void _impl_store( uf::simd::vector<int32_t> 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<int32_t> 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<int32_t> v, int32_t* f ) {
return ::store_impl( v, f );
return _impl_store( v, f );
}
@ -273,20 +324,32 @@ FORCE_INLINE uf::simd::vector<int32_t> uf::simd::sub( uf::simd::vector<int32_t>
return _mm_sub_epi32(x, y);
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> mul_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> mul_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {//
return _mm_mullo_epi32(x, y);
}
#else
FORCE_INLINE uf::simd::vector<int32_t> _impl_mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> uf::simd::mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::mul_impl( x, y );
return _impl_mul( x, y );
}
@ -302,34 +365,56 @@ FORCE_INLINE uf::simd::vector<int32_t> uf::simd::hadd( uf::simd::vector<int32_t>
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<int32_t> min_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> min_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {//
return _mm_min_epi32(x, y);
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> max_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> max_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return _mm_max_epi32(x, y);
}
#else
FORCE_INLINE uf::simd::vector<int32_t> _impl_min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> _impl_max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> uf::simd::min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::min_impl(x, y);
return _impl_min(x, y);
}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::max_impl(x, y);
return _impl_max(x, y);
}
@ -340,60 +425,100 @@ FORCE_INLINE bool uf::simd::any( uf::simd::vector<int32_t> mask) {
return _mm_movemask_epi8( mask ) != 0x0; // any bit set
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> less_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> less_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return _mm_cmplt_epi32( x, y );
uf::simd::vector<int32_t> _impl_less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return _mm_cmplt_epi32( x, y );//
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> lessEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> lessEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
__m128i gt = _mm_cmpgt_epi32(x, y);
return _mm_xor_si128(gt, _mm_set1_epi32(-1));
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> greater_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> greater_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return _mm_cmpgt_epi32(x, y);
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> greaterEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> greaterEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
__m128i gt = _mm_cmplt_epi32(x, y);
return _mm_xor_si128(gt, _mm_set1_epi32(-1));
}
#else
FORCE_INLINE uf::simd::vector<int32_t> _impl_less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> _impl_lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> _impl_greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> _impl_greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> 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<int32_t> uf::simd::less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::less_impl( x, y );
return _impl_less( x, y );
}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::lessEquals_impl( x, y );
return _impl_lessEquals( x, y );
}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::greater_impl( x, y );
return _impl_greater( x, y );
}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::greaterEquals_impl( x, y );
return _impl_greaterEquals( x, y );
}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::equals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
@ -412,19 +537,20 @@ FORCE_INLINE int32_t uf::simd::dot( uf::simd::vector<int32_t> 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<uint32_t> load_impl( const uint32_t* f ) {
uf::simd::vector<uint32_t> _impl_load( const uint32_t* f ) {
return uf::simd::vector<uint32_t>( f[0], f[1], f[2], f[3] );
}
MV_INSTR_SET_3
uf::simd::vector<uint32_t> load_impl( const uint32_t* f ) {
uf::simd::vector<uint32_t> _impl_load( const uint32_t* f ) {
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
}
MV_INSTR_SET_DEFAULT
void store_impl( uf::simd::vector<uint32_t> v, uint32_t* f ) {
void _impl_store( uf::simd::vector<uint32_t> 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<uint32_t> v, uint32_t* f ) {
void _impl_store( uf::simd::vector<uint32_t> 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<uint32_t> _impl_load( const uint32_t* f ) {
#if SSE_INSTR_SET >= 3
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
#else
return uf::simd::vector<uint32_t>( f[0], f[1], f[2], f[3] );
#endif
}
FORCE_INLINE void _impl_store( uf::simd::vector<uint32_t> 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<uint32_t> uf::simd::load( const uint32_t* f ) {
return ::load_impl( f );
return _impl_load( f );
}
FORCE_INLINE void uf::simd::store( uf::simd::vector<uint32_t> v, uint32_t* f ) {
return ::store_impl( v, f );
return _impl_store( v, f );
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::set( uint32_t f ) {
@ -458,20 +608,32 @@ FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::sub( uf::simd::vector<uint32_t
return _mm_sub_epi32(x, y);
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> mul_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> mul_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {//
return _mm_mullo_epi32(x, y);
}
#else
FORCE_INLINE uf::simd::vector<uint32_t> _impl_mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> uf::simd::mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::mul_impl( x, y );
return _impl_mul( x, y );
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::div( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
@ -487,34 +649,56 @@ FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::hadd( uf::simd::vector<uint32_
}
*/
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> min_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> min_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {//
return _mm_min_epu32(x, y); // unsigned min
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> max_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> max_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return _mm_max_epu32(x, y); // unsigned max
}
#else
FORCE_INLINE uf::simd::vector<uint32_t> _impl_min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> _impl_max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> uf::simd::min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::min_impl(x, y);
return _impl_min(x, y);
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::max_impl(x, y);
return _impl_max(x, y);
}
FORCE_INLINE bool uf::simd::all( uf::simd::vector<uint32_t> mask) {
@ -524,66 +708,112 @@ FORCE_INLINE bool uf::simd::any( uf::simd::vector<uint32_t> mask) {
return _mm_movemask_epi8( mask ) != 0x0; // any bit set
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> less_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> less_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return _mm_cmplt_epi32( ::bias_unsigned(x), ::bias_unsigned(y) );
uf::simd::vector<uint32_t> _impl_less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return _mm_cmplt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );//
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> lessEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
uf::simd::vector<uint32_t> _impl_lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> lessEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
uf::simd::vector<uint32_t> _impl_lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> greater_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> greater_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return _mm_cmpgt_epi32( ::bias_unsigned(x), ::bias_unsigned(y) );
uf::simd::vector<uint32_t> _impl_greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return _mm_cmpgt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> greaterEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
uf::simd::vector<uint32_t> _impl_greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> greaterEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
uf::simd::vector<uint32_t> _impl_greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> _impl_less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> _impl_lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> _impl_greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> _impl_greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> uf::simd::less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::less_impl( x, y );
return _impl_less( x, y );
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::lessEquals_impl( x, y );
return _impl_lessEquals( x, y );
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::greater_impl( x, y );
return _impl_greater( x, y );
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> 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<uint32_t> x, uf::simd::vec
}
FORCE_INLINE uf::simd::vector<float> 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<int32_t> 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<uint32_t> 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<float> cross_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
uf::simd::vector<float> _impl_cross( uf::simd::vector<float> x, uf::simd::vector<float> 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<float> cross_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
uf::simd::vector<float> _impl_cross( uf::simd::vector<float> x, uf::simd::vector<float> 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<float> _impl_cross( uf::simd::vector<float> x, uf::simd::vector<float> 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<float> uf::simd::cross( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
return ::cross_impl( x, y );
return _impl_cross( x, y );
}
FORCE_INLINE uf::simd::vector<float> uf::simd::normalize( uf::simd::vector<float> 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<float> uf::simd::normalize_fast( uf::simd::vector<float> 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);
}

View File

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

View File

@ -0,0 +1,42 @@
#pragma once
#include <uf/config.h>
#include <uf/utils/memory/vector.h>
#include <uf/utils/image/atlas.h>
#include <uf/utils/mesh/mesh.h>
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<pod::TextToken> UF_API parseTextTokens( const uf::stl::string& text, const pod::Vector4f& color = {1,1,1,1} );
uf::stl::vector<pod::GlyphBox> UF_API calculateLayout( const uf::stl::vector<pod::TextToken>& tokens, const pod::GlyphSettings& metadata );
bool UF_API generateAtlas( const uf::stl::vector<pod::GlyphBox>& layout, const pod::GlyphSettings& metadata, uf::Atlas& atlas );
void UF_API generateMesh( const uf::stl::vector<pod::GlyphBox>& layout, const pod::GlyphSettings& metadata, const uf::Atlas& atlas, uf::Mesh& mesh );
}
}

View File

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

View File

@ -2,6 +2,8 @@
#include <uf/utils/hook/hook.h>
#include <uf/utils/image/atlas.h>
#include <uf/utils/mesh/mesh.h>
#include <uf/utils/time/time.h>
#include <uf/utils/serialize/serializer.h>
#include <uf/utils/userdata/userdata.h>
@ -12,6 +14,7 @@
#include <uf/utils/string/ext.h>
#include <uf/utils/math/physics.h>
#include <uf/utils/text/glyph.h>
#include <uf/utils/text/glyph_.h>
#include <uf/engine/asset/asset.h>
#include <uf/engine/scene/scene.h>
#include <uf/utils/memory/unordered_map.h>
@ -20,359 +23,16 @@
#include <uf/utils/http/http.h>
#include <uf/utils/audio/audio.h>
#include <uf/utils/window/payloads.h>
#include <uf/utils/string/hash.h>
#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<uf::renderer::AttributeDescriptor> 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 <regex>
#include <uf/utils/string/hash.h>
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<uf::stl::string, uf::stl::unordered_map<size_t, uf::Glyph>> cache;
#else
char glyph;
uf::stl::unordered_map<uf::stl::string, uf::stl::unordered_map<size_t, char>> 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<TextToken> parseTextTokens( const uf::stl::string& text, pod::Vector3f color ) {
uf::stl::vector<TextToken> 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<GlyphBox> calculateGlyphLayout( uf::Object& self, const uf::stl::vector<TextToken>& tokens, const ext::GuiGlyphBehavior::Metadata& metadata, const ext::GuiBehavior::Metadata& metadataGui, const uf::stl::string& font ) {
uf::stl::vector<GlyphBox> 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<GlyphBox>& 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<size_t, uf::stl::string> 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<uint16_t> 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<ext::GuiGlyphBehavior::Metadata>();
auto& metadataGui = this->getComponent<ext::GuiBehavior::Metadata>();
@ -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<uf::Mesh>();
auto& atlas = this->getComponent<uf::Atlas>();
auto& images = atlas.getImages();
auto& glyphs_cache = ::glyphs.cache;
uf::stl::unordered_map<size_t, uf::stl::string> 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 ) {

View File

@ -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<bool>()) ) return;
const uf::stl::string name = _name;
uf::stl::string key = graph.metadata["key"].as<uf::stl::string>("");
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<pod::OBB> 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: {

View File

@ -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<uf::stl::string>("");
if ( key != "" ) {
key += ":";
}
if ( key != "" ) key += ":";
tasks.queue([&]{
// load images

View File

@ -59,8 +59,8 @@ void uf::GraphBehavior::initialize( uf::Object& self ) {
uf::graph::initialize( graph );
if ( graph.metadata["renderer"]["skinned"].as<bool>() ) {
if ( metadata["graph"]["animation"].is<uf::stl::string>() ) {
uf::graph::animate( graph, metadata["graph"]["animation"].as<uf::stl::string>(), metadata["graph"]["speed"].as<float>( 1.0f ) );
if ( ext::json::isObject( metadata["graph"]["animations"] ) ) {
uf::graph::animate( graph, metadata["graph"]["animations"]["animation"].as<uf::stl::string>(), metadata["graph"]["animations"]["speed"].as<float>( 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 ) {}

View File

@ -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);
}
}

View File

@ -7,7 +7,7 @@
#include <uf/ext/ffx/fsr.h>
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);

View File

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

View File

@ -3,6 +3,7 @@
#include <uf/engine/scene/scene.h>
#include <uf/engine/graph/graph.h>
#include <uf/utils/math/physics.h>
#include <uf/utils/text/glyph_.h>
namespace impl {
struct Vertex {
@ -19,15 +20,29 @@ namespace impl {
float ttl = 0;
};
float decayRate = 0.25f;
uf::stl::vector<impl::Vertex> lines;
uf::stl::unordered_map<size_t, impl::Line> 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<impl::Vertex> lines;
uf::stl::vector<impl::Line> 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<impl::Text> 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<impl::Vertex>, lines);
std::swap( impl::lines, lines );
// to-do: make this static to avoid additional allocations
uf::Mesh mesh;
mesh.bind<impl::Vertex>();
mesh.insertVertices<impl::Vertex>(impl::lines);
impl::lineMesh.clear();
impl::lineMesh.bind<impl::Vertex>();
impl::lineMesh.insertVertices<impl::Vertex>(lines);
impl::lineMesh.generateIndirect();
auto& scene = uf::scene::getCurrentScene();
auto& graphics = scene.getComponent<uf::renderer::Graphics>();
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<impl::Text>, 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<pod::GlyphBox> 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<uf::renderer::Graphics>();
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<pod::Graph::Storage>();
// 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 );
}

View File

@ -1,38 +1,23 @@
#include <uf/utils/image/atlas.h>
#include <binpack2d/binpack2d.hpp>
#include <iostream>
#include <bit>
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 = { std::bit_ceil(side), std::bit_ceil(side) }; // to-do: non-C++20 method
queue.Sort();
internalAtlas = BinPack2D::UniformCanvasArrayBuilder<uf::Atlas::Identifier>(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 ) {
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();
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::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 };
}

View File

@ -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 );
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,6 +17,7 @@ void impl::drawManifold( const pod::Manifold& manifold ) {
}
void impl::drawBody( const pod::PhysicsBody& body ) {
if ( !(body.collider.category & uf::physics::settings.debugDraw.mask) ) return;
// draw wireframe
switch ( body.collider.type ) {
case pod::ShapeType::AABB:
impl::drawAabb( body );
@ -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;

View File

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

View File

@ -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] );

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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] );

View File

@ -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});

View File

@ -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<pod::BVH::index_t> 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<pod::BVH::index_t> 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;
}

View File

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

View File

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

View File

@ -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();

View File

@ -0,0 +1,329 @@
#include <uf/utils/text/glyph.h>
#include <uf/utils/text/glyph_.h>
#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<uf::renderer::AttributeDescriptor> 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<uf::stl::string, uf::stl::unordered_map<size_t, uf::Glyph>> cache;
#else
char glyph;
uf::stl::unordered_map<uf::stl::string, uf::stl::unordered_map<size_t, char>> 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<pod::TextToken> uf::glyph::parseTextTokens( const uf::stl::string& text, const pod::Vector4f& color ) {
uf::stl::vector<pod::TextToken> 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<pod::GlyphBox> uf::glyph::calculateLayout( const uf::stl::vector<pod::TextToken>& tokens, const pod::GlyphSettings& metadata ) {
uf::stl::vector<pod::GlyphBox> 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<pod::GlyphBox>& 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<pod::GlyphBox>& 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<uint16_t> 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);
}

View File

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

View File

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

View File

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