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:
parent
78171039a1
commit
d8baa484da
@ -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,
|
||||
|
||||
@ -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": {
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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": {
|
||||
|
||||
17
bin/data/shaders/base/textured/frag.glsl
Normal file
17
bin/data/shaders/base/textured/frag.glsl
Normal 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;
|
||||
}
|
||||
25
bin/data/shaders/base/textured/vert.glsl
Normal file
25
bin/data/shaders/base/textured/vert.glsl
Normal 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);
|
||||
}
|
||||
@ -153,6 +153,8 @@
|
||||
#define UF_DEBUG 1
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if UF_DEBUG
|
||||
#include <uf/utils/io/fmt.h>
|
||||
#endif
|
||||
@ -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
|
||||
|
||||
@ -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 );
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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 };
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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.orientation = uf::quaternion::fromMatrix( matrix );
|
||||
// transform.scale = ...;
|
||||
transform.position = uf::matrix::extractTranslation( matrix );
|
||||
transform.orientation = uf::quaternion::fromMatrix( matrix );
|
||||
transform.scale = uf::matrix::extractScale( matrix );
|
||||
return transform;
|
||||
}
|
||||
|
||||
@ -168,7 +168,7 @@ pod::Transform<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;
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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];
|
||||
});
|
||||
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
|
||||
42
engine/inc/uf/utils/text/glyph_.h
Normal file
42
engine/inc/uf/utils/text/glyph_.h
Normal 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 );
|
||||
}
|
||||
}
|
||||
@ -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 ) ) {
|
||||
|
||||
@ -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 ) {
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 ) {}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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 ) {
|
||||
|
||||
@ -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 );
|
||||
}
|
||||
@ -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_t side = std::sqrt( area ) * padding;
|
||||
size = { std::bit_ceil(side), std::bit_ceil(side) }; // to-do: non-C++20 method
|
||||
queue.Sort();
|
||||
internalAtlas = BinPack2D::UniformCanvasArrayBuilder<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 ) {
|
||||
auto& size = this->m_atlas.getDimensions();
|
||||
for ( auto pair : this->m_tiles ) {
|
||||
auto& tile = pair.second;
|
||||
if ( tile.identifier.hash != hash ) continue;
|
||||
pod::Vector2ui coord = { uv.x * tile.size.x + tile.coord.x, uv.y * tile.size.y + tile.coord.y };
|
||||
pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, const uf::Atlas::hash_t& hash ) const {
|
||||
auto it = this->m_tiles.find(hash);
|
||||
if ( it != this->m_tiles.end() ) {
|
||||
auto& tile = it->second;
|
||||
auto& size = this->m_atlas.getDimensions();
|
||||
pod::Vector2ui coord = {
|
||||
uv.x * tile.size.x + tile.coord.x,
|
||||
uv.y * tile.size.y + tile.coord.y
|
||||
};
|
||||
return pod::Vector2f{ (float) coord.x / (float) size.x, (float) coord.y / (float) size.y };
|
||||
}
|
||||
return uv;
|
||||
}
|
||||
pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, size_t index ) {
|
||||
pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, size_t index ) const {
|
||||
auto& size = this->m_atlas.getDimensions();
|
||||
for ( auto pair : this->m_tiles ) {
|
||||
auto& tile = pair.second;
|
||||
@ -147,7 +119,7 @@ pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, size_t index ) {
|
||||
}
|
||||
return uv;
|
||||
}
|
||||
pod::Vector3f uf::Atlas::mapUv( const pod::Vector3f& uv ) {
|
||||
pod::Vector3f uf::Atlas::mapUv( const pod::Vector3f& uv ) const {
|
||||
pod::Vector2f nuv = mapUv( { uv.x, uv.y }, uv.z );
|
||||
return { nuv.x, nuv.y, uv.z };
|
||||
}
|
||||
|
||||
@ -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 );
|
||||
});
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -17,7 +17,8 @@ void impl::drawManifold( const pod::Manifold& manifold ) {
|
||||
}
|
||||
void impl::drawBody( const pod::PhysicsBody& body ) {
|
||||
if ( !(body.collider.category & uf::physics::settings.debugDraw.mask) ) return;
|
||||
switch( body.collider.type ) {
|
||||
// draw wireframe
|
||||
switch ( body.collider.type ) {
|
||||
case pod::ShapeType::AABB:
|
||||
impl::drawAabb( body );
|
||||
break;
|
||||
@ -43,6 +44,48 @@ void impl::drawBody( const pod::PhysicsBody& body ) {
|
||||
impl::drawHull( body );
|
||||
break;
|
||||
}
|
||||
// draw name
|
||||
auto& scene = uf::scene::getCurrentScene();
|
||||
auto& controller = scene.getController();
|
||||
auto& camera = scene.getCamera( controller );
|
||||
auto& bounds = body.bounds;
|
||||
|
||||
auto transform = impl::getTransform( body );
|
||||
auto cameraTransform = uf::transform::flatten( camera.getTransform() );
|
||||
auto cameraAxes = uf::transform::axes( cameraTransform );
|
||||
auto& projection = camera.getProjection();
|
||||
auto fov = std::atan(1.0f / fabs(projection(1,1)));
|
||||
auto viewThreshold = std::cos(fov * 1.5f);
|
||||
|
||||
#if 1
|
||||
// (an attempt to) continuously pick the closest point on the AABB
|
||||
auto position = impl::closestPointOnAABB( cameraTransform.position, bounds );
|
||||
auto dir = position - cameraTransform.position;
|
||||
auto magSq = uf::vector::magnitude( dir );
|
||||
if ( magSq > EPS2 ) dir /= std::sqrt( magSq );
|
||||
auto dot = uf::vector::dot( cameraAxes.forward, dir );
|
||||
position -= cameraAxes.forward * 0.1f;
|
||||
if ( dot > viewThreshold ) uf::debug::drawText( body.object->getName(), position );
|
||||
#else
|
||||
// picks the closest corner
|
||||
int closestCorner = -1;
|
||||
float closestDistanceSq = FLT_MAX;
|
||||
float closestDot = FLT_MAX; //
|
||||
pod::Vector3f corners[8];
|
||||
impl::getCorners( bounds, corners );
|
||||
for ( auto i = 0; i < 8; ++i ) {
|
||||
auto dir = corners[i] - cameraTransform.position;
|
||||
auto distanceSq = uf::vector::magnitude( dir );
|
||||
if ( distanceSq > EPS2 ) dir /= std::sqrt( distanceSq );
|
||||
auto dot = uf::vector::dot( cameraAxes.forward, dir );
|
||||
if ( dot <= viewThreshold ) continue;
|
||||
if ( distanceSq >= closestDistanceSq ) continue;
|
||||
closestDistanceSq = distanceSq;
|
||||
closestCorner = i;
|
||||
}
|
||||
|
||||
if ( 0 <= closestCorner ) uf::debug::drawText( body.object->getName(), corners[closestCorner] );
|
||||
#endif
|
||||
}
|
||||
void impl::drawConstraint( const pod::Constraint& constraint ) {
|
||||
if ( !uf::physics::settings.debugDraw.constraints ) return;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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] );
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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] );
|
||||
|
||||
@ -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});
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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();
|
||||
|
||||
329
engine/src/utils/text/glyph_.cpp
Normal file
329
engine/src/utils/text/glyph_.cpp
Normal 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);
|
||||
}
|
||||
@ -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 ) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user