Compare commits

...

2 Commits

75 changed files with 1756 additions and 975 deletions

View File

@ -61,7 +61,7 @@
},
"graph": {
"initial buffer elements": 128,
"global storage": false
"global storage": true // required for animations to work?
},
"ext": {
"vulkan": {
@ -354,10 +354,12 @@
"dynamic": true,
"contacts": false,
"constraints": true,
"rays": false
"rays": false,
"depthTest": true
},
"fixed step": true,
"substeps": 4
"substeps": 4,
"flatten transforms": true
},
"audio": {
"mute": false,

View File

@ -14,7 +14,7 @@
"axis": [ 0, 1, 0 ],
"angle": 0
},
"scale": [ 10.0, 10.0, 10.0 ]
"scale": [ 1.0, 1.0, 1.0 ]
},
"system": {
"hot reload": {
@ -29,7 +29,7 @@
"animations": true
},
"draw": {
"armature": true
"armature": false
}
},
"exporter": {

View File

@ -11,10 +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.3, 0.3, 0.3 ]
//"scale": [ 0.09, 0.09, 0.09 ]
"scale": [ 5, 5, 5 ]
},
"system": {
"hot reload": {

View File

@ -4,8 +4,8 @@
"ignore": false,
"import": "/model.json",
"assets": [
// "/player/bear.glb"
{ "filename": "/player/bear/graph.json" }
// "/player/pbear.glb"
{ "filename": "/player/pbear/graph.json" }
],
"behaviors": [],
"transform": {
@ -16,7 +16,7 @@
"axis": [ 0, 1, 0 ],
"angle": 0
},
// "scale": [ 0.35, 0.35, 0.35 ],
"scale": [ 1, 1, 1 ],
"reference": "parent"
},
"system": {
@ -33,7 +33,7 @@
}
},
"exporter": {
"enabled": false,
"enabled": true,
"unwrap": false,
"optimize": false
},
@ -43,6 +43,9 @@
"lights": {
"lightmap": false
},
"stream": {
"animations": false
},
"renderer": {
"cull mode": "front",
"filter": "linear",
@ -52,7 +55,7 @@
"skinned": true
},
"animations": {
"animation": "idle"
// "animation": "idle"
}
}
}

View File

@ -163,7 +163,7 @@ ent:bind( "tick", function(self)
for k, obj in pairs(children) do
local transform = obj:getComponent("Transform")
transform.orientation = orientation
transform.model = controllerCamera:getProjection() * Matrix4f.translate( transform.position ) * transform.orientation:matrix() * Matrix4f.scale( transform.scale ) --Matrix4f.scale( Vector3f( 1.7776 * 2, 2, 2 ) )
-- controllerCamera:getProjection() * Matrix4f.translate( transform.position ) * transform.orientation:matrix() * Matrix4f.scale( transform.scale ) --Matrix4f.scale( Vector3f( 1.7776 * 2, 2, 2 ) )
end
end )

View File

@ -1,11 +1,11 @@
{
"type": "Object",
"name": "Player: Model",
"ignore": false,
"ignore": true,
"import": "/model.json",
"assets": [
// "/player/ben.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.16, 0.16, 0.16 ]
"scale": [ 1, 1, 1 ]
},
"metadata": {
"track": true,
@ -49,7 +49,7 @@
},
"animations": {
"animation": "wank",
"speed": 2.0
"speed": 8.0
}
}
}

View File

@ -16,14 +16,15 @@ local target_transform = nil
local soundEmitter = ent
-- on tick
ent:bind( "tick", function(self)
axes = transform:axes()
-- rotate to target
if target_transform ~= nil then
local target = (target_transform.position - transform.position):normalize()
local dot = transform.forward:dot( target )
local dot = axes.forward:dot( target )
if dot < 1.0 then
local cross = Vector3f.cross( transform.forward, target ):normalize()
local axis = transform.up
local angle = Vector3f.signedAngle( transform.forward, target, axis )
local cross = Vector3f.cross( axes.forward, target ):normalize()
local axis = axes.up
local angle = Vector3f.signedAngle( axes.forward, target, axis )
local rot = Quaternion.axisAngle( axis, angle * time.delta() * 4 )
if physicsBody:initialized() then

View File

@ -75,11 +75,11 @@ end
local useDistance = 6
local pullDistance = useDistance * 4
local function tickFlashlight( transform, inputs )
local function tickFlashlight( transform, axes, inputs )
-- update light position
if light.enabled then
local center = transform.position
local direction = transform.forward * 8
local direction = axes.forward * 8
local offset = 0.25
local _, depth = physicsBody:rayCast(center, direction)
depth = math.clamp(depth, 0, 0.5)
@ -132,12 +132,12 @@ local function onUse( payload )
playSound(validUse and "select" or "deny")
end
local function tickUse( transform, inputs )
local function tickUse( transform, axes, inputs )
-- trigger use
if timers.use:elapsed() > 0.5 and inputs["E"] then
timers.use:reset()
local center = transform.position
local direction = transform.forward * useDistance
local direction = axes.forward * useDistance
local prop, depth = physicsBody:rayCast(center, direction)
local payload = {
@ -150,13 +150,13 @@ local function tickUse( transform, inputs )
end
end
local function tickGravGun( transform, inputs )
local function tickGravGun( transform, axes, inputs )
-- not holding anything
if heldObject.uid == 0 then
-- try and launch object in sights
if inputs["mouse2"] then
local center = transform.position
local direction = transform.forward * pullDistance
local direction = axes.forward * pullDistance
local prop, depth = physicsBody:rayCast( center, direction )
if depth >= 0 and prop and not string.matched( prop:name(), "/^worldspawn/" ) then
local heldObjectTransform = prop:getComponent("Transform")
@ -165,7 +165,7 @@ local function tickGravGun( transform, inputs )
local strength = 500
local distanceSquared = (heldObjectTransform.position - transform.position):magnitude()
heldObjectPhysicsBody:applyImpulse( transform.forward * -heldObjectPhysicsBody:getMass() * strength / distanceSquared )
heldObjectPhysicsBody:applyImpulse( axes.forward * -heldObjectPhysicsBody:getMass() * strength / distanceSquared )
if timers.physcannon:elapsed() > 1.0 then
timers.physcannon:reset()
@ -194,18 +194,18 @@ local function tickGravGun( transform, inputs )
heldObject.uid = 0
heldObjectPhysicsBody:enableGravity(true)
heldObjectPhysicsBody:applyImpulse( transform.forward * heldObjectPhysicsBody:getMass() * 50 )
heldObjectPhysicsBody:applyImpulse( axes.forward * heldObjectPhysicsBody:getMass() * 50 )
playSound("phys_launch"..math.random(1,4))
else
-- update rotation
if heldObject.rotate then
--heldObjectTransform.orientation = Quaternion.lookAt( (heldObjectTransform.position - transform.position):normalize(), transform.up )
--heldObjectTransform.orientation = Quaternion.lookAt( (heldObjectTransform.position - transform.position):normalize(), axes.up )
heldObjectTransform.orientation = cameraTransform:flatten().orientation
end
-- move held object
local forward = transform.forward * heldObject.distance
local forward = axes.forward * heldObject.distance
if heldObject.smoothSpeed ~= 0 then
local heldObjectFlattened = heldObjectTransform:flatten()
@ -252,15 +252,16 @@ ent:bind( "tick", function(self)
-- eye transform
local flattenedTransform = fixedCamera and transform:flatten() or cameraTransform:flatten()
local axes = flattenedTransform:axes()
-- update flashlight
tickFlashlight( flattenedTransform, inputs )
tickFlashlight( flattenedTransform, axes, inputs )
-- update use
tickUse( flattenedTransform, inputs )
tickUse( flattenedTransform, axes, inputs )
-- update HOLP
tickGravGun( flattenedTransform, inputs )
tickGravGun( flattenedTransform, axes, inputs )
-- get collision events
--[[

View File

@ -4,7 +4,7 @@
// { "filename": "./models/mds_mcdonalds.glb" }
{ "filename": "./models/mds_mcdonalds/graph.json" }
// ,{ "filename": "/ragdoll.json", "delay": 1 }
,{ "filename": "/craeture.json", "delay": 2.0 }
// ,{ "filename": "/craeture.json", "delay": 2.0 }
// ,{ "filename": "/cesiumMan.json", "delay": 2.0 }
],
"metadata": {

View File

@ -0,0 +1,17 @@
#version 450
#pragma shader_stage(fragment)
layout (binding = 1) uniform sampler2D samplerTexture;
layout (location = 0) in vec2 inUv;
layout (location = 1) in vec4 inColor;
layout (location = 0) out vec4 outColor;
void main() {
vec4 color = texture( samplerTexture, inUv ) * inColor;
if ( color.a == 0.0f ) discard;
outColor.rgb = color.rgb;
outColor.a = color.a;
}

View File

@ -0,0 +1,25 @@
#version 450
#pragma shader_stage(vertex)
#include "../../common/macros.h"
#include "../../common/structs.h"
layout (constant_id = 0) const uint PASSES = 1;
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec2 inUv;
layout (location = 2) in vec4 inColor;
layout( push_constant ) uniform PushBlock {
uint pass;
uint draw;
} PushConstant;
layout (location = 0) out vec2 outUv;
layout (location = 1) out vec4 outColor;
void main() {
outUv = inUv;
outColor = inColor;
gl_Position = vec4(inPos.xyz, 1.0);
}

View File

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

View File

@ -4,6 +4,14 @@
#define __x86_64__ 1
#endif
#if __clang__
// clang produces multiple symbols or something because the anonymous namespace trick doesn't work
// also crashes when producing debug symbols for multi-versioned functions
#define SIMD_MV 0
#else
#define SIMD_MV 0 // off for now because there MIGHT be a runtime cost
#endif
// Find sse instruction set from compiler macros if SSE_INSTR_SET not defined
// Note: Not all compilers define these macros automatically
#ifndef SSE_INSTR_SET

View File

@ -8,14 +8,19 @@
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 drawAabb( pod::AABB aabb, pod::Transform<> transform = {} );
void UF_API drawObb( pod::OBB obb, pod::Transform<> transform = {} );
void UF_API drawSphere( pod::Sphere sphere, pod::Transform<> transform = {} );
void UF_API drawCapsule( pod::Capsule capsule, pod::Transform<> transform = {} );
void UF_API drawPlane( pod::Plane plane, pod::Transform<> transform = {} );
void UF_API drawTriangle( pod::Triangle tri, pod::Transform<> transform = {} );
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 = {} );
void UF_API drawShape( const pod::Capsule& capsule, const pod::Transform<>& transform = {} );
void UF_API drawShape( const pod::Plane& plane, const pod::Transform<>& transform = {} );
void UF_API drawShape( const pod::Triangle& tri, const pod::Transform<>& transform = {} );
void UF_API addLine( const pod::Vector3f& start, const pod::Vector3f& end, const pod::Vector4f& color = { 1, 1, 1, 1 }, float ttl = 1.0f );
void UF_API drawLines( float dt = 0 );
void UF_API drawText( const uf::stl::string& string, const pod::Vector3f& position, const pod::Vector4f& color = { 1, 1, 1, 1 } );
void UF_API drawTexts( float dt = 0 );
void UF_API draw( float dt = 0 );
}
}

View File

@ -23,18 +23,18 @@ namespace uf {
uf::Image m_atlas;
atlas_t m_tiles;
public:
hash_t addImage( const uf::Image&, bool = false );
hash_t addImage( uf::Image&&, bool = false );
hash_t addImage( const uint8_t*, const pod::Vector2ui&, std::size_t, std::size_t, bool = false, bool = false );
hash_t addImage( const uf::Image&, const hash_t& hash );
hash_t addImage( const uf::Image& );
void generate(float padding = 1);
void generate( const uf::stl::vector<uf::Image>&, float padding = 1);
void clear(bool = true);
bool generated() const;
bool has( const hash_t& ) const;
pod::Vector2f mapUv( const pod::Vector2f&, const hash_t& );
pod::Vector2f mapUv( const pod::Vector2f&, size_t );
pod::Vector3f mapUv( const pod::Vector3f& );
pod::Vector2f mapUv( const pod::Vector2f&, const hash_t& ) const;
pod::Vector2f mapUv( const pod::Vector2f&, size_t ) const;
pod::Vector3f mapUv( const pod::Vector3f& ) const;
uf::Image& getAtlas();
const uf::Image& getAtlas() const;

View File

@ -381,7 +381,10 @@ pod::Vector3t<typename T::type_t> uf::matrix::extractScale( const T& matrix ) {
type_t det = matrix(0,0) * ( matrix(1,1) * matrix(2,2) - matrix(2,1) * matrix(1,2))
- matrix(0,1) * ( matrix(1,0) * matrix(2,2) - matrix(1,2) * matrix(2,0))
+ matrix(0,2) * ( matrix(1,0) * matrix(2,1) - matrix(1,1) * matrix(2,0));
if ( det < 0 ) { sx = -sx; sy = -sy; sz = -sz; }
if ( det < 0 ) {
sx = -sx;
}
return { sx, sy, sz };
}

View File

@ -22,12 +22,13 @@ namespace uf {
}
}
namespace {
/***/ namespace {
#if SIMD_MV
__attribute__((target("default")))
uf::simd::matrix_value<float> matMult_impl(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> _impl_matMult(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> R;
uf::simd::matrix_value<float> Bt = uf::simd::matTranspose(B);
FOR_EACH(4, {
FOR_EACH_(4, {
__m128 bcol = B.m[i];
__m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx
@ -48,13 +49,12 @@ namespace {
return R;
}
#if 1
__attribute__((target("sse4.1")))
uf::simd::matrix_value<float> matMult_impl(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> _impl_matMult(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> R;
uf::simd::matrix_value<float> Bt = uf::simd::matTranspose(B);
FOR_EACH(4, {
FOR_EACH_(4, {
__m128 bcol = B.m[i];
__m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx
@ -74,14 +74,12 @@ namespace {
return R;
}
#endif
#if 1
__attribute__((target("avx2,fma")))
uf::simd::matrix_value<float> matMult_impl(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> _impl_matMult(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> R;
uf::simd::matrix_value<float> Bt = uf::simd::matTranspose(B);
FOR_EACH(4, {
FOR_EACH_(4, {
__m128 bcol = B.m[i];
__m256 vx = _mm256_broadcastss_ps(bcol); // xxxx
@ -109,10 +107,8 @@ namespace {
return R;
}
#endif
#if 1
__attribute__((target("avx512f")))
uf::simd::matrix_value<float> matMult_impl( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> _impl_matMult( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> R;
uf::simd::matrix_value<float> Bt = uf::simd::matTranspose(B);
@ -148,10 +144,9 @@ namespace {
return R;
}
#endif
__attribute__((target("default")))
uf::simd::vector<float> matMult_impl( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
uf::simd::vector<float> _impl_matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
__m128 vx = _mm_shuffle_ps(v, v, 0x00);
__m128 vy = _mm_shuffle_ps(v, v, 0x55);
__m128 vz = _mm_shuffle_ps(v, v, 0xAA);
@ -164,9 +159,8 @@ namespace {
return _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3));
}
#if 1
__attribute__((target("fma")))
uf::simd::vector<float> matMult_impl( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
uf::simd::vector<float> _impl_matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
__m128 vx = _mm_shuffle_ps(v, v, 0x00);
__m128 vy = _mm_shuffle_ps(v, v, 0x55);
__m128 vz = _mm_shuffle_ps(v, v, 0xAA);
@ -177,8 +171,134 @@ namespace {
_mm_fmadd_ps(M.m[2], vz,
_mm_mul_ps(M.m[3], vw))));
}
#else
uf::simd::matrix_value<float> _impl_matMult( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
uf::simd::matrix_value<float> R;
uf::simd::matrix_value<float> Bt = uf::simd::matTranspose(B);
#if 0 && SSE_INSTR_SET >= 9 // AVX512F
FOR_EACH(4, {
__m128 bcol = B.m[i];
__m512 vx = _mm512_set1_ps(((const float*)&bcol)[0]); // xxxx
__m512 vy = _mm512_set1_ps(((const float*)&bcol)[1]); // yyyy
__m512 vz = _mm512_set1_ps(((const float*)&bcol)[2]); // zzzz
__m512 vw = _mm512_set1_ps(((const float*)&bcol)[3]); // wwww
__m512 a0 = _mm512_castps128_ps512(A.m[0]);
__m512 a1 = _mm512_castps128_ps512(A.m[1]);
__m512 a2 = _mm512_castps128_ps512(A.m[2]);
__m512 a3 = _mm512_castps128_ps512(A.m[3]);
__m512 r = _mm512_fmadd_ps(a0, vx,
_mm512_fmadd_ps(a1, vy,
_mm512_fmadd_ps(a2, vz,
_mm512_mul_ps(a3, vw))));
__m128 r128 = _mm_add_ps(
_mm_add_ps(
_mm512_castps512_ps128(r), // low 128
_mm512_extractf32x4_ps(r, 1)), // next 128
_mm_add_ps(
_mm512_extractf32x4_ps(r, 2), // next 128
_mm512_extractf32x4_ps(r, 3)) // high 128
);
R.m[i] = r128;
});
#elif 0 && SSE_INSTR_SET >= 8 && __FMA__ // AVX2,FMA
FOR_EACH_(4, {
__m128 bcol = B.m[i];
__m256 vx = _mm256_broadcastss_ps(bcol); // xxxx
__m256 vy = _mm256_broadcastss_ps(_mm_shuffle_ps(bcol, bcol, 0x55)); // yyyy
__m256 vz = _mm256_broadcastss_ps(_mm_shuffle_ps(bcol, bcol, 0xAA)); // zzzz
__m256 vw = _mm256_broadcastss_ps(_mm_shuffle_ps(bcol, bcol, 0xFF)); // wwww
__m256 a0 = _mm256_castps128_ps256(A.m[0]);
__m256 a1 = _mm256_castps128_ps256(A.m[1]);
__m256 a2 = _mm256_castps128_ps256(A.m[2]);
__m256 a3 = _mm256_castps128_ps256(A.m[3]);
__m256 r = _mm256_fmadd_ps(a0, vx,
_mm256_fmadd_ps(a1, vy,
_mm256_fmadd_ps(a2, vz,
_mm256_mul_ps(a3, vw))));
__m128 r128 = _mm_add_ps(
_mm256_castps256_ps128(r),
_mm256_extractf128_ps(r, 1)
);
R.m[i] = r128;
});
#elif 0 && SSE_INSTR_SET >= 5 // SSE4.1
FOR_EACH_(4, {
__m128 bcol = B.m[i];
__m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx
__m128 vy = _mm_shuffle_ps(bcol, bcol, 0x55); // yyyy
__m128 vz = _mm_shuffle_ps(bcol, bcol, 0xAA); // zzzz
__m128 vw = _mm_shuffle_ps(bcol, bcol, 0xFF); // wwww
R.m[i] = _mm_add_ps(
_mm_add_ps(
_mm_mul_ps(A.m[0], vx),
_mm_mul_ps(A.m[1], vy)),
_mm_add_ps(
_mm_mul_ps(A.m[2], vz),
_mm_mul_ps(A.m[3], vw))
);
});
#else
FOR_EACH_(4, {
__m128 bcol = B.m[i];
__m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx
__m128 vy = _mm_shuffle_ps(bcol, bcol, 0x55); // yyyy
__m128 vz = _mm_shuffle_ps(bcol, bcol, 0xAA); // zzzz
__m128 vw = _mm_shuffle_ps(bcol, bcol, 0xFF); // wwww
R.m[i] = _mm_add_ps(
_mm_add_ps(
_mm_mul_ps(A.m[0], vx),
_mm_mul_ps(A.m[1], vy)),
_mm_add_ps(
_mm_mul_ps(A.m[2], vz),
_mm_mul_ps(A.m[3], vw))
);
});
#endif
}
return R;
}
FORCE_INLINE uf::simd::vector<float> _impl_matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
#if __FMA__
__m128 vx = _mm_shuffle_ps(v, v, 0x00);
__m128 vy = _mm_shuffle_ps(v, v, 0x55);
__m128 vz = _mm_shuffle_ps(v, v, 0xAA);
__m128 vw = _mm_shuffle_ps(v, v, 0xFF);
return _mm_fmadd_ps(M.m[0], vx,
_mm_fmadd_ps(M.m[1], vy,
_mm_fmadd_ps(M.m[2], vz,
_mm_mul_ps(M.m[3], vw))));
#else
__m128 vx = _mm_shuffle_ps(v, v, 0x00);
__m128 vy = _mm_shuffle_ps(v, v, 0x55);
__m128 vz = _mm_shuffle_ps(v, v, 0xAA);
__m128 vw = _mm_shuffle_ps(v, v, 0xFF);
__m128 r0 = _mm_mul_ps(M.m[0], vx);
__m128 r1 = _mm_mul_ps(M.m[1], vy);
__m128 r2 = _mm_mul_ps(M.m[2], vz);
__m128 r3 = _mm_mul_ps(M.m[3], vw);
return _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3));
#endif
}
#endif
/**/}
template<typename T>
FORCE_INLINE uf::simd::matrix_value<T>::matrix_value() {}
@ -204,10 +324,10 @@ FORCE_INLINE uf::simd::matrix_value<T>::operator pod::Matrix<T,4>() const {
}
FORCE_INLINE uf::simd::matrix_value<float> uf::simd::matMult( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B ) {
return ::matMult_impl( A, B );
return _impl_matMult( A, B );
}
FORCE_INLINE uf::simd::vector<float> uf::simd::matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> vec ) {
return ::matMult_impl( M, vec );
return _impl_matMult( M, vec );
}
FORCE_INLINE uf::simd::matrix_value<float> uf::simd::matTranspose( const uf::simd::matrix_value<float>& M ) {
uf::simd::matrix_value<float> R = M;

View File

@ -67,6 +67,9 @@ namespace impl {
/*FORCE_INLINE*/ void boxAxes( pod::Vector3f axes[3], const pod::Transform<>& transform );
/*FORCE_INLINE*/ pod::Vector3f extentFromAxes( const pod::OBB& box, const pod::Vector3f axes[3] );
/*FORCE_INLINE*/ float projectExtents( const pod::OBB& box, const pod::Vector3f& normal, const pod::Vector3f axes[3] );
void getCorners( const pod::AABB& aabb, pod::Vector3f corners[8] );
void getCorners( const pod::AABB& aabb, const pod::Transform<>& transform, pod::Vector3f corners[8] );
pod::AABB transformAabbToWorld( const pod::AABB& localBox, const pod::Transform<>& transform );
std::pair<pod::Vector3f, pod::Vector3f> getCapsuleSegment( const pod::PhysicsBody& body );
pod::AABB computeAABB( const pod::PhysicsBody& body );

View File

@ -491,6 +491,7 @@ namespace pod {
bool contacts = false;
bool constraints = false;
bool rays = false;
bool depthTest = true;
} debugDraw;
};

View File

@ -166,7 +166,7 @@ template<typename T> pod::Quaternion<T> uf::quaternion::unitVectors( const pod::
pod::Vector3t<T> cross = uf::vector::cross(uNorm, vNorm);
T s = sqrt((1 + dot) * 2);
return uf::quaternion::normalize({
return uf::quaternion::normalize(pod::Quaternion<>{
.x = cross.x / s,
.y = cross.y / s,
.z = cross.z / s,
@ -183,7 +183,7 @@ template<typename T> pod::Quaternion<T> uf::quaternion::lookAt( const pod::Vecto
right.z, up.z, forward.z, 0,
0, 0, 0, 1
});
return uf::quaternion::normalize( uf::quaternion::fromMatrix( m ) );
return uf::quaternion::fromMatrix( m );
}

View File

@ -3,22 +3,27 @@
#include <uf/config.h>
#include <uf/utils/math/vector.h>
#define OBB_EXTENT_CENTER 0
namespace pod {
struct Plane {
alignas(16) pod::Vector3f normal;
pod::Vector3f normal;
float offset;
};
struct AABB {
alignas(16) pod::Vector3f min;
alignas(16) pod::Vector3f max;
// operator OBB() const { return OBB{ (min + max) * 0.5f, (min - max) * 0.5f }; }
pod::Vector3f min;
pod::Vector3f max;
};
struct OBB {
alignas(16) pod::Vector3f center;
alignas(16) pod::Vector3f extent;
// operator AABB() const { return AABB{ center - extent, center + extent }; }
#if OBB_EXTENT_CENTER
pod::Vector3f extent;
pod::Vector3f center;
#else
pod::Vector3f center;
pod::Vector3f extent;
#endif
};
struct Sphere {
@ -27,7 +32,7 @@ namespace pod {
struct Capsule {
float radius;
float halfHeight;
pod::Vector3f up;
};
struct Ray {

View File

@ -20,30 +20,28 @@ namespace pod {
struct /*UF_API*/ Transform {
typedef T type_t;
pod::Vector3t<T> position = {0, 0,0 };
pod::Vector3t<T> scale = {1, 1, 1};
pod::Vector3t<T> up = {0, 1, 0};
pod::Vector3t<T> right = {1, 0, 0};
pod::Vector3t<T> forward = {0, 0, 1};
pod::Quaternion<T> orientation = {0, 0, 0, 1};
pod::Matrix4t<T> model = uf::matrix::identity();
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;
};
struct Axes {
pod::Vector3f right = { 1, 0, 0 };
pod::Vector3f up = { 0, 1, 0 };
pod::Vector3f forward = { 0, 0, 1 };
};
}
namespace uf {
namespace transform {
template<typename T> pod::Transform<T>& /*UF_API*/ initialize( pod::Transform<T>& transform );
template<typename T> pod::Transform<T> /*UF_API*/ initialize();
template<typename T> pod::Axes /*UF_API*/ axes( const pod::Transform<T>& transform, const pod::Vector3t<T>& at = {} );
template<typename T> pod::Transform<T>& /*UF_API*/ lookAt( pod::Transform<T>& transform, const pod::Vector3t<T>& at );
template<typename T> pod::Transform<T>& /*UF_API*/ move( pod::Transform<T>& transform, const pod::Vector3t<T>& axis, pod::Math::num_t delta );
template<typename T> pod::Transform<T>& /*UF_API*/ move( pod::Transform<T>& transform, const pod::Vector3t<T>& delta );
template<typename T> pod::Transform<T>& /*UF_API*/ reorient( pod::Transform<T>& transform );
template<typename T> pod::Transform<T> /*UF_API*/ reorient( const pod::Transform<T>& transform );
template<typename T> pod::Transform<T>& /*UF_API*/ rotate( pod::Transform<T>& transform, const pod::Vector3t<T>& axis, pod::Math::num_t delta );
template<typename T> pod::Transform<T>& /*UF_API*/ rotate( pod::Transform<T>& transform, const pod::Quaternion<T>& quat );
template<typename T> pod::Transform<T>& /*UF_API*/ scale( pod::Transform<T>& transform, const pod::Vector3t<T>& factor );
@ -52,10 +50,11 @@ namespace uf {
template<typename T> pod::Matrix4t<T> /*UF_API*/ model( const pod::Transform<T>& transform, bool flatten = false, size_t depth = SIZE_MAX );
template<typename T> pod::Transform<T> /*UF_API*/ fromMatrix( const pod::Matrix4t<T>& matrix );
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, bool reorient = true );
template<typename T> pod::Transform<T> /*UF_API*/ interpolate( const pod::Transform<T>& from, const pod::Transform<T>& to, float factor );
template<typename T> pod::Transform<T> /*UF_API*/ inverse(const pod::Transform<T>& t);
template<typename T> pod::Vector3t<T> /*UF_API*/ apply( const pod::Transform<T>& transform, const pod::Vector3t<T>& point );
template<typename T> pod::Vector3t<T> /*UF_API*/ applyInverse(const pod::Transform<T>& t, const pod::Vector3t<T>& worldPoint);
// forced inline because of a GCC quirk
template<typename T> pod::Vector3t<T> FORCE_INLINE /*UF_API*/ apply( const pod::Transform<T>& transform, const pod::Vector3t<T>& point );
template<typename T> pod::Vector3t<T> FORCE_INLINE /*UF_API*/ applyInverse(const pod::Transform<T>& t, const pod::Vector3t<T>& worldPoint);
template<typename T> pod::Transform<T> /*UF_API*/ relative(const pod::Transform<T>& a, const pod::Transform<T>& b);
template<typename T> uf::stl::string /*UF_API*/ toString( const pod::Transform<T>&, bool flatten = true );

View File

@ -2,13 +2,7 @@ template<typename T>
pod::Transform<T>& /*UF_API*/ uf::transform::initialize( pod::Transform<T>& transform ) {
transform.position = {0, 0, 0};
transform.scale = {1, 1, 1};
transform.up = {0, 1, 0};
transform.right = {1, 0, 0};
transform.forward = {0, 0, 1};
transform.orientation = {0, 0, 0, 1};
transform.model = uf::matrix::identity();
transform.reference = nullptr;
return transform;
@ -20,18 +14,27 @@ pod::Transform<T> /*UF_API*/ uf::transform::initialize() {
return uf::transform::initialize(transform);
}
template<typename T>
pod::Axes /*UF_API*/ uf::transform::axes( const pod::Transform<T>& transform, const pod::Vector3t<T>& at ) {
pod::Axes axes;
auto& q = transform.orientation;
axes.forward = { 2 * (q.x * q.z + q.w * q.y), 2 * (q.y * q.z - q.w * q.x), 1 - 2 * (q.x * q.x + q.y * q.y) };
axes.up = { 2 * (q.x * q.y - q.w * q.z), 1 - 2 * (q.x * q.x + q.z * q.z), 2 * (q.y * q.z + q.w * q.x)};
axes.right = { 1 - 2 * (q.y * q.y + q.z * q.z), 2 * (q.x * q.y + q.w * q.z), 2 * (q.x * q.z - q.w * q.y)};
if ( at != pod::Vector3f{} ) {
axes.forward = uf::vector::normalize( at - transform.position );
axes.right = uf::vector::normalize(uf::vector::cross( axes.up, axes.forward ));
axes.up = uf::vector::normalize(uf::vector::cross( axes.forward, axes.right ));
}
return axes;
}
template<typename T>
pod::Transform<T>& /*UF_API*/ uf::transform::lookAt( pod::Transform<T>& transform, const pod::Vector3t<T>& at ) {
pod::Vector3t<T> forward = uf::vector::normalize( at - transform.position );
pod::Vector3t<T> right = uf::vector::normalize(uf::vector::cross( transform.up, forward ));
pod::Vector3t<T> up = uf::vector::normalize(uf::vector::cross( forward, right ));
transform.up = up;
transform.right = right;
transform.forward = forward;
transform.orientation = uf::quaternion::lookAt( forward, up );
auto axes = uf::transform::axes( transform, at );
transform.orientation = uf::quaternion::lookAt( axes.forward, axes.up );
return transform;
}
@ -47,32 +50,17 @@ pod::Transform<T>& /*UF_API*/ uf::transform::move( pod::Transform<T>& transform,
return transform;
}
template<typename T>
pod::Transform<T>& /*UF_API*/ uf::transform::reorient( pod::Transform<T>& transform ) {
pod::Quaternion<T> q = transform.orientation;
transform.forward = { 2 * (q.x * q.z + q.w * q.y), 2 * (q.y * q.z - q.w * q.x), 1 - 2 * (q.x * q.x + q.y * q.y) };
transform.up = { 2 * (q.x * q.y - q.w * q.z), 1 - 2 * (q.x * q.x + q.z * q.z), 2 * (q.y * q.z + q.w * q.x)};
transform.right = { 1 - 2 * (q.y * q.y + q.z * q.z), 2 * (q.x * q.y + q.w * q.z), 2 * (q.x * q.z - q.w * q.y)};
return transform;
}
template<typename T>
pod::Transform<T> /*UF_API*/ uf::transform::reorient( const pod::Transform<T>& _transform ) {
pod::Transform<T> transform = _transform;
return uf::transform::reorient(transform);
}
template<typename T>
pod::Transform<T>& /*UF_API*/ uf::transform::rotate( pod::Transform<T>& transform, const pod::Vector3t<T>& axis, pod::Math::num_t delta ) {
pod::Quaternion<> quat = uf::quaternion::axisAngle( axis, delta );
transform.orientation = uf::vector::normalize(uf::quaternion::multiply(quat, transform.orientation));
return uf::transform::reorient(transform);
return transform;
}
template<typename T>
pod::Transform<T>& /*UF_API*/ uf::transform::rotate( pod::Transform<T>& transform, const pod::Quaternion<T>& quat ) {
transform.orientation = uf::vector::normalize(uf::quaternion::multiply(quat, transform.orientation));
return uf::transform::reorient(transform);
return transform;
}
template<typename T>
@ -98,12 +86,11 @@ pod::Transform<T> /*UF_API*/ uf::transform::flatten( const pod::Transform<T>& tr
combined.position = pointer->position + uf::quaternion::rotate(pointer->orientation, combined.position * pointer->scale);
combined.orientation = uf::quaternion::multiply( pointer->orientation, combined.orientation );
combined.scale = combined.scale * pointer->scale;
combined.model = pointer->model * combined.model;
if ( pointer == pointer->reference ) break;
pointer = pointer->reference;
}
return uf::transform::reorient(combined);
return combined;
}
template<typename T>
@ -112,8 +99,7 @@ pod::Matrix4t<T> /*UF_API*/ uf::transform::model( const pod::Transform<T>& trans
pod::Transform<T> flat = uf::transform::flatten(transform, depth);
return uf::matrix::translate( uf::matrix::identity(), flat.position ) *
uf::quaternion::matrix( flat.orientation ) *
uf::matrix::scale( uf::matrix::identity(), flat.scale ) *
flat.model;
uf::matrix::scale( uf::matrix::identity(), flat.scale );
}
pod::Matrix4t<T> matrix = uf::matrix::identity();
@ -122,8 +108,7 @@ pod::Matrix4t<T> /*UF_API*/ uf::transform::model( const pod::Transform<T>& trans
do {
pod::Matrix4t<T> modelMat = uf::matrix::translate( uf::matrix::identity(), pointer->position ) *
uf::quaternion::matrix( pointer->orientation ) *
uf::matrix::scale( uf::matrix::identity(), pointer->scale ) *
pointer->model;
uf::matrix::scale( uf::matrix::identity(), pointer->scale );
matrix = modelMat * matrix;
if ( pointer == pointer->reference ) break;
@ -136,29 +121,25 @@ 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<float>( matrix, pod::Vector4f{ 0, 0, 0, 1 } );
transform.orientation = uf::quaternion::fromMatrix( matrix );
transform.model = matrix;
return reorient( transform );
transform.position = uf::matrix::extractTranslation( matrix );
transform.orientation = uf::quaternion::fromMatrix( matrix );
transform.scale = uf::matrix::extractScale( matrix );
return transform;
}
template<typename T>
pod::Transform<T>& /*UF_API*/ uf::transform::reference( pod::Transform<T>& transform, const pod::Transform<T>& parent, bool reorient ) {
transform.reference = const_cast<pod::Transform<T>*>(&parent);
if ( !reorient ) {
return transform;
}
transform.position = parent.position - transform.position;
if ( reorient ) transform.position = parent.position - transform.position;
return transform;
}
template<typename T>
pod::Transform<T> /*UF_API*/ uf::transform::interpolate( const pod::Transform<T>& from, const pod::Transform<T>& to, float factor, bool reorient ) {
pod::Transform<T> /*UF_API*/ uf::transform::interpolate( const pod::Transform<T>& from, const pod::Transform<T>& to, float factor ) {
pod::Transform transform = to;
transform.position = uf::vector::lerp( from.position, to.position, factor );
transform.orientation = uf::quaternion::slerp( from.orientation, to.orientation, factor );
return reorient ? uf::transform::reorient( transform ) : transform;
return transform;
}
template<typename T>
@ -183,11 +164,11 @@ pod::Transform<T> uf::transform::inverse(const pod::Transform<T>& t) {
negPos.z * sInv.z
});
return uf::transform::reorient(inv);
return inv;
}
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;
}
@ -221,7 +202,6 @@ ext::json::Value /*UF_API*/ uf::transform::encode( const pod::Transform<T>& t, b
json["position"] = uf::vector::encode(transform.position, settings);
json["orientation"] = uf::vector::encode(transform.orientation, settings);
json["scale"] = uf::vector::encode(transform.scale, settings);
json["model"] = uf::matrix::encode(transform.model, settings);
return json;
}
@ -237,8 +217,6 @@ pod::Transform<T>& /*UF_API*/ uf::transform::decode( const ext::json::Value& _js
T angle = json["rotation"]["angle"].as<T>();
transform.orientation = uf::quaternion::axisAngle( axis, angle );
}
transform.model = uf::matrix::decode<T, 4, 4>(json["model"], transform.model);
return transform;
}

View File

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

View File

@ -12,8 +12,8 @@ constexpr void for_each_index(F&& f) {
#include "simd.h"
#endif
// #define FOR_EACH( N, F ) for ( auto i = 0; i < N; ++i ) F;
#define FOR_EACH( N, F ) for_each_index<N>([&](auto i) F )
#define FOR_EACH_( N, F ) for ( auto i = 0; i < N; ++i ) F;
template<typename T, typename Op>
T elementwise( const T& left, const T& right, Op&& op ) {
@ -309,7 +309,7 @@ T uf::vector::divide( typename T::type_t scalar, const T& vector ) {
}
template<typename T>
typename T::type_t uf::vector::sum( const T& vector ) {
auto res = 0;
typename T::type_t res = 0;
FOR_EACH(T::size, {
res += vector[i];
});
@ -317,7 +317,7 @@ typename T::type_t uf::vector::sum( const T& vector ) {
}
template<typename T>
typename T::type_t uf::vector::product( const T& vector ) {
auto res = 1;
typename T::type_t res = 1;
FOR_EACH(T::size, {
res *= vector[i];
});

View File

@ -1,15 +1,15 @@
#include <uf/utils/memory/alignment.h>
namespace {
FORCE_INLINE __m128i bias_unsigned(__m128i v) {
/**/namespace {
FORCE_INLINE __m128i _impl_bias_unsigned(__m128i v) {
const __m128i signbit = _mm_set1_epi32(0x80000000);
return _mm_xor_si128(v, signbit);
}
FORCE_INLINE int32_t boolMask(bool b) {
FORCE_INLINE int32_t _impl_bool_mask(bool b) {
return b ? -1 : 0; // 0xFFFFFFFF for true, 0x00000000 for false
}
}
/**/}
#define MV_INSTR_SET_DEFAULT __attribute__((target("default")))
#define MV_INSTR_SET_2 __attribute__((target("sse2")))
@ -202,13 +202,18 @@ FORCE_INLINE uf::simd::vector<float> uf::simd::sqrt( uf::simd::vector<float> v )
return _mm_sqrt_ps( v );
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<float> dot_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
return uf::simd::mul( x, y );
uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
__m128 mul = _mm_mul_ps(x, y);
__m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 3, 0, 1));
__m128 sums = _mm_add_ps(mul, shuf);
shuf = _mm_movehl_ps(shuf, sums);
return _mm_add_ss(sums, shuf);
}
MV_INSTR_SET_3
uf::simd::vector<float> dot_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
__m128 mulRes = _mm_mul_ps(x, y);
__m128 shufReg = _mm_movehdup_ps(mulRes);
__m128 sumsReg = _mm_add_ps(mulRes, shufReg);
@ -216,47 +221,93 @@ namespace {
return _mm_add_ss(sumsReg, shufReg);
}
MV_INSTR_SET_5
uf::simd::vector<float> dot_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
return _mm_dp_ps(x, y, 0xF1);
uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
return _mm_dp_ps(x, y, 0xFF);
}
}
#else
FORCE_INLINE uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
#if SSE_INSTR_SET >= 5
return _mm_dp_ps(x, y, 0xFF);
#elif SSE_INSTR_SET >= 3
__m128 mulRes = _mm_mul_ps(x, y);
__m128 shufReg = _mm_movehdup_ps(mulRes);
__m128 sumsReg = _mm_add_ps(mulRes, shufReg);
shufReg = _mm_movehl_ps(shufReg, sumsReg);
return _mm_add_ss(sumsReg, shufReg);
#else
__m128 mul = _mm_mul_ps(x, y);
__m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 3, 0, 1));
__m128 sums = _mm_add_ps(mul, shuf);
shuf = _mm_movehl_ps(shuf, sums);
return _mm_add_ss(sums, shuf);
#endif
}
#endif
/**/}
FORCE_INLINE float uf::simd::dot( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
return _mm_cvtss_f32( ::dot_impl( x, y ) );
return _mm_cvtss_f32( _impl_dot( x, y ) );
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> load_impl( const int32_t* f ) {
uf::simd::vector<int32_t> _impl_load( const int32_t* f ) {
return uf::simd::vector<int32_t>( f[0], f[1], f[2], f[3] );
}
MV_INSTR_SET_3
uf::simd::vector<int32_t> load_impl( const int32_t* f ) {
uf::simd::vector<int32_t> _impl_load( const int32_t* f ) {
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
}
}
#else
FORCE_INLINE uf::simd::vector<int32_t> _impl_load( const int32_t* f ) {
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
#if SSE_INSTR_SET >= 3
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
#else
return uf::simd::vector<int32_t>( f[0], f[1], f[2], f[3] );
#endif
}
#endif
/**/}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::load( const int32_t* f ) {
return ::load_impl( f );
return _impl_load( f );
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
void store_impl( uf::simd::vector<int32_t> v, int32_t* f ) {
void _impl_store( uf::simd::vector<int32_t> v, int32_t* f ) {
union { __m128i x; int32_t y[4]; } kludge;
kludge.x = v;
f[0] = kludge.y[0];
f[1] = kludge.y[1];
f[2] = kludge.y[2];
f[3] = kludge.y[3];
f[3] = kludge.y[3];//
}
MV_INSTR_SET_3
void store_impl( uf::simd::vector<int32_t> v, int32_t* f ) {
void _impl_store( uf::simd::vector<int32_t> v, int32_t* f ) {
/*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v);
else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v);
}
}
#else
FORCE_INLINE void _impl_store( uf::simd::vector<int32_t> v, int32_t* f ) {
#if SSE_INSTR_SET >= 3
/*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v);
else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v);
#else
union { __m128i x; int32_t y[4]; } kludge;
kludge.x = v;
f[0] = kludge.y[0];
f[1] = kludge.y[1];
f[2] = kludge.y[2];
f[3] = kludge.y[3];//
#endif
}
#endif
/**/}
FORCE_INLINE void uf::simd::store( uf::simd::vector<int32_t> v, int32_t* f ) {
return ::store_impl( v, f );
return _impl_store( v, f );
}
@ -273,20 +324,32 @@ FORCE_INLINE uf::simd::vector<int32_t> uf::simd::sub( uf::simd::vector<int32_t>
return _mm_sub_epi32(x, y);
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> mul_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]);
}
MV_INSTR_SET_4
uf::simd::vector<int32_t> mul_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {//
return _mm_mullo_epi32(x, y);
}
}
#else
FORCE_INLINE uf::simd::vector<int32_t> _impl_mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {//
#if SSE_INSTR_SET >= 4
return _mm_mullo_epi32(x, y);
#else
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]);
#endif
}
#endif
/**/}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::mul_impl( x, y );
return _impl_mul( x, y );
}
@ -302,34 +365,56 @@ FORCE_INLINE uf::simd::vector<int32_t> uf::simd::hadd( uf::simd::vector<int32_t>
return uf::simd::set( X[0] + Y[0], X[1] + Y[1], X[2] + Y[2], X[3] + Y[3] );
}
*/
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> min_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3]));
}
MV_INSTR_SET_4
uf::simd::vector<int32_t> min_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {//
return _mm_min_epi32(x, y);
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> max_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3]));
}
MV_INSTR_SET_4
uf::simd::vector<int32_t> max_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return _mm_max_epi32(x, y);
}
}
#else
FORCE_INLINE uf::simd::vector<int32_t> _impl_min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {//
#if SSE_INSTR_SET >= 4
return _mm_min_epi32(x, y);
#else
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3]));
#endif
}
FORCE_INLINE uf::simd::vector<int32_t> _impl_max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
#if SSE_INSTR_SET >= 4
return _mm_max_epi32(x, y);
#else
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3]));
#endif
}
#endif
/**/}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::min_impl(x, y);
return _impl_min(x, y);
}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::max_impl(x, y);
return _impl_max(x, y);
}
@ -340,60 +425,100 @@ FORCE_INLINE bool uf::simd::any( uf::simd::vector<int32_t> mask) {
return _mm_movemask_epi8( mask ) != 0x0; // any bit set
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> less_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_i(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]);
}
MV_INSTR_SET_4
uf::simd::vector<int32_t> less_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return _mm_cmplt_epi32( x, y );
uf::simd::vector<int32_t> _impl_less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return _mm_cmplt_epi32( x, y );//
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> lessEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_i(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]);
}
MV_INSTR_SET_4
uf::simd::vector<int32_t> lessEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
__m128i gt = _mm_cmpgt_epi32(x, y);
return _mm_xor_si128(gt, _mm_set1_epi32(-1));
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> greater_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_i(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]);
}
MV_INSTR_SET_4
uf::simd::vector<int32_t> greater_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return _mm_cmpgt_epi32(x, y);
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<int32_t> greaterEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_i(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]);
}
MV_INSTR_SET_4
uf::simd::vector<int32_t> greaterEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
uf::simd::vector<int32_t> _impl_greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
__m128i gt = _mm_cmplt_epi32(x, y);
return _mm_xor_si128(gt, _mm_set1_epi32(-1));
}
}
#else
FORCE_INLINE uf::simd::vector<int32_t> _impl_less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
#if SSE_INSTR_SET >= 4
return _mm_cmplt_epi32( x, y );//
#else
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_i(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]);
#endif
}
FORCE_INLINE uf::simd::vector<int32_t> _impl_lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
#if SSE_INSTR_SET >= 4
__m128i gt = _mm_cmpgt_epi32(x, y);
return _mm_xor_si128(gt, _mm_set1_epi32(-1));
#else
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_i(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]);
#endif
}
FORCE_INLINE uf::simd::vector<int32_t> _impl_greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
#if SSE_INSTR_SET >= 4
return _mm_cmpgt_epi32(x, y);
#else
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_i(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]);
#endif
}
FORCE_INLINE uf::simd::vector<int32_t> _impl_greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
#if SSE_INSTR_SET >= 4
__m128i gt = _mm_cmplt_epi32(x, y);
return _mm_xor_si128(gt, _mm_set1_epi32(-1));
#else
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_i(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]);
#endif
}
#endif
/**/}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::less_impl( x, y );
return _impl_less( x, y );
}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::lessEquals_impl( x, y );
return _impl_lessEquals( x, y );
}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::greater_impl( x, y );
return _impl_greater( x, y );
}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
return ::greaterEquals_impl( x, y );
return _impl_greaterEquals( x, y );
}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::equals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
@ -412,19 +537,20 @@ FORCE_INLINE int32_t uf::simd::dot( uf::simd::vector<int32_t> x, uf::simd::vecto
return X[0] * Y[0] + X[1] * Y[1] + X[2] * Y[2] + X[3] * Y[3];
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> load_impl( const uint32_t* f ) {
uf::simd::vector<uint32_t> _impl_load( const uint32_t* f ) {
return uf::simd::vector<uint32_t>( f[0], f[1], f[2], f[3] );
}
MV_INSTR_SET_3
uf::simd::vector<uint32_t> load_impl( const uint32_t* f ) {
uf::simd::vector<uint32_t> _impl_load( const uint32_t* f ) {
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
}
MV_INSTR_SET_DEFAULT
void store_impl( uf::simd::vector<uint32_t> v, uint32_t* f ) {
void _impl_store( uf::simd::vector<uint32_t> v, uint32_t* f ) {
union { __m128i x; uint32_t y[4]; } kludge;
kludge.x = v;
f[0] = kludge.y[0];
@ -433,16 +559,40 @@ namespace {
f[3] = kludge.y[3];
}
MV_INSTR_SET_3
void store_impl( uf::simd::vector<uint32_t> v, uint32_t* f ) {
void _impl_store( uf::simd::vector<uint32_t> v, uint32_t* f ) {
/*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v);
else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v);
}
}
#else
FORCE_INLINE uf::simd::vector<uint32_t> _impl_load( const uint32_t* f ) {
#if SSE_INSTR_SET >= 3
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
#else
return uf::simd::vector<uint32_t>( f[0], f[1], f[2], f[3] );
#endif
}
FORCE_INLINE void _impl_store( uf::simd::vector<uint32_t> v, uint32_t* f ) {
#if SSE_INSTR_SET >= 3
/*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v);
else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v);
#else
union { __m128i x; uint32_t y[4]; } kludge;
kludge.x = v;
f[0] = kludge.y[0];
f[1] = kludge.y[1];
f[2] = kludge.y[2];
f[3] = kludge.y[3];
#endif
}
#endif
/**/}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::load( const uint32_t* f ) {
return ::load_impl( f );
return _impl_load( f );
}
FORCE_INLINE void uf::simd::store( uf::simd::vector<uint32_t> v, uint32_t* f ) {
return ::store_impl( v, f );
return _impl_store( v, f );
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::set( uint32_t f ) {
@ -458,20 +608,32 @@ FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::sub( uf::simd::vector<uint32_t
return _mm_sub_epi32(x, y);
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> mul_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]);
}
MV_INSTR_SET_4
uf::simd::vector<uint32_t> mul_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {//
return _mm_mullo_epi32(x, y);
}
}
#else
FORCE_INLINE uf::simd::vector<uint32_t> _impl_mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {//
#if SSE_INSTR_SET >= 4
return _mm_mullo_epi32(x, y);
#else
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]);
#endif
}
#endif
/**/}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::mul_impl( x, y );
return _impl_mul( x, y );
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::div( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
@ -487,34 +649,56 @@ FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::hadd( uf::simd::vector<uint32_
}
*/
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> min_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3]));
}
MV_INSTR_SET_4
uf::simd::vector<uint32_t> min_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {//
return _mm_min_epu32(x, y); // unsigned min
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> max_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3]));
}
MV_INSTR_SET_4
uf::simd::vector<uint32_t> max_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return _mm_max_epu32(x, y); // unsigned max
}
}
#else
FORCE_INLINE uf::simd::vector<uint32_t> _impl_min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {//
#if SSE_INSTR_SET >= 4
return _mm_min_epu32(x, y); // unsigned min
#else
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3]));
#endif
}
FORCE_INLINE uf::simd::vector<uint32_t> _impl_max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
#if SSE_INSTR_SET >= 4
return _mm_max_epu32(x, y); // unsigned max
#else
auto X = uf::simd::cast(x);
auto Y = uf::simd::cast(y);
return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3]));
#endif
}
#endif
/**/}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::min_impl(x, y);
return _impl_min(x, y);
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::max_impl(x, y);
return _impl_max(x, y);
}
FORCE_INLINE bool uf::simd::all( uf::simd::vector<uint32_t> mask) {
@ -524,66 +708,112 @@ FORCE_INLINE bool uf::simd::any( uf::simd::vector<uint32_t> mask) {
return _mm_movemask_epi8( mask ) != 0x0; // any bit set
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> less_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_ui(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]);
}
MV_INSTR_SET_4
uf::simd::vector<uint32_t> less_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return _mm_cmplt_epi32( ::bias_unsigned(x), ::bias_unsigned(y) );
uf::simd::vector<uint32_t> _impl_less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return _mm_cmplt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );//
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> lessEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
uf::simd::vector<uint32_t> _impl_lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
auto X = uf::simd::cast(x), Y = uf::simd::cast(y);
return uf::simd::set_ui(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]);
}
MV_INSTR_SET_2
uf::simd::vector<uint32_t> lessEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
uf::simd::vector<uint32_t> _impl_lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
// a <= b <=> !(a > b)
__m128i bx = ::bias_unsigned(x);
__m128i by = ::bias_unsigned(y);
__m128i bx = _impl_bias_unsigned(x);
__m128i by = _impl_bias_unsigned(y);
__m128i gt = _mm_cmpgt_epi32(bx, by); // signed compare
return _mm_xor_si128(gt, _mm_set1_epi32(-1)); // invert mask
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> greater_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
uf::simd::vector<uint32_t> _impl_greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_ui(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]);
}
MV_INSTR_SET_4
uf::simd::vector<uint32_t> greater_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return _mm_cmpgt_epi32( ::bias_unsigned(x), ::bias_unsigned(y) );
uf::simd::vector<uint32_t> _impl_greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return _mm_cmpgt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );
}
MV_INSTR_SET_DEFAULT
uf::simd::vector<uint32_t> greaterEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
uf::simd::vector<uint32_t> _impl_greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
auto X = uf::simd::cast(x), Y = uf::simd::cast(y);
return uf::simd::set_ui(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]);
}
MV_INSTR_SET_2
uf::simd::vector<uint32_t> greaterEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
uf::simd::vector<uint32_t> _impl_greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
// a >= b <=> !(a < b)
__m128i bx = ::bias_unsigned(x);
__m128i by = ::bias_unsigned(y);
__m128i bx = _impl_bias_unsigned(x);
__m128i by = _impl_bias_unsigned(y);
__m128i lt = _mm_cmplt_epi32(bx, by); // signed compare
return _mm_xor_si128(lt, _mm_set1_epi32(-1)); // invert mask
}
}
#else
FORCE_INLINE uf::simd::vector<uint32_t> _impl_less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
#if SSE_INSTR_SET >= 4
return _mm_cmplt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );//
#else
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_ui(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]);
#endif
}
FORCE_INLINE uf::simd::vector<uint32_t> _impl_lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
#if SSE_INSTR_SET >= 2
// a <= b <=> !(a > b)
__m128i bx = _impl_bias_unsigned(x);
__m128i by = _impl_bias_unsigned(y);
__m128i gt = _mm_cmpgt_epi32(bx, by); // signed compare
return _mm_xor_si128(gt, _mm_set1_epi32(-1)); // invert mask
#else
auto X = uf::simd::cast(x), Y = uf::simd::cast(y);
return uf::simd::set_ui(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]);
#endif
}
FORCE_INLINE uf::simd::vector<uint32_t> _impl_greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
#if SSE_INSTR_SET >= 4
return _mm_cmpgt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );
#else
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
return uf::simd::set_ui(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]);
#endif
}
FORCE_INLINE uf::simd::vector<uint32_t> _impl_greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
#if SSE_INSTR_SET >= 2
// a >= b <=> !(a < b)
__m128i bx = _impl_bias_unsigned(x);
__m128i by = _impl_bias_unsigned(y);
__m128i lt = _mm_cmplt_epi32(bx, by); // signed compare
return _mm_xor_si128(lt, _mm_set1_epi32(-1)); // invert mask
#else
auto X = uf::simd::cast(x), Y = uf::simd::cast(y);
return uf::simd::set_ui(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]);
#endif
}
#endif
/**/}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::less_impl( x, y );
return _impl_less( x, y );
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::lessEquals_impl( x, y );
return _impl_lessEquals( x, y );
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::greater_impl( x, y );
return _impl_greater( x, y );
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
return ::greaterEquals_impl( x, y );
return _impl_greaterEquals( x, y );
}
@ -604,28 +834,29 @@ FORCE_INLINE uint32_t uf::simd::dot( uf::simd::vector<uint32_t> x, uf::simd::vec
}
FORCE_INLINE uf::simd::vector<float> uf::simd::set_f( bool x, bool y, bool z, bool w ) {
return _mm_castsi128_ps(_mm_setr_epi32(::boolMask(x), ::boolMask(y), ::boolMask(z), ::boolMask(w)));
return _mm_castsi128_ps(_mm_setr_epi32(_impl_bool_mask(x), _impl_bool_mask(y), _impl_bool_mask(z), _impl_bool_mask(w)));
}
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::set_i( bool x, bool y, bool z, bool w ) {
return _mm_setr_epi32(::boolMask(x), ::boolMask(y), ::boolMask(z), ::boolMask(w));
return _mm_setr_epi32(_impl_bool_mask(x), _impl_bool_mask(y), _impl_bool_mask(z), _impl_bool_mask(w));
}
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::set_ui( bool x, bool y, bool z, bool w ) {
return _mm_setr_epi32(::boolMask(x), ::boolMask(y), ::boolMask(z), ::boolMask(w));
return _mm_setr_epi32(_impl_bool_mask(x), _impl_bool_mask(y), _impl_bool_mask(z), _impl_bool_mask(w));
}
namespace {
/**/namespace {
#if SIMD_MV
MV_INSTR_SET_DEFAULT
uf::simd::vector<float> cross_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
uf::simd::vector<float> _impl_cross( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
__m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1));
__m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1));
tmp0 = _mm_mul_ps(tmp0,x);
tmp1 = _mm_mul_ps(tmp1,y);
__m128 tmp2 = _mm_sub_ps(tmp0,tmp1);
__m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));
__m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));//
return res;
}
MV_INSTR_SET_7
uf::simd::vector<float> cross_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
uf::simd::vector<float> _impl_cross( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
__m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1));
__m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1));
tmp1 = _mm_mul_ps(tmp1,y);
@ -633,17 +864,37 @@ namespace {
__m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));
return res;
}
}
#else
uf::simd::vector<float> _impl_cross( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
#if SSE_INSTR_SET >= 7
__m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1));
__m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1));
tmp1 = _mm_mul_ps(tmp1,y);
__m128 tmp2 = _mm_fmsub_ps( tmp0,x, tmp1 );
__m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));
return res;
#else
__m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1));
__m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1));
tmp0 = _mm_mul_ps(tmp0,x);
tmp1 = _mm_mul_ps(tmp1,y);
__m128 tmp2 = _mm_sub_ps(tmp0,tmp1);
__m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));//
return res;
#endif
}
#endif
/**/}
FORCE_INLINE uf::simd::vector<float> uf::simd::cross( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
return ::cross_impl( x, y );
return _impl_cross( x, y );
}
FORCE_INLINE uf::simd::vector<float> uf::simd::normalize( uf::simd::vector<float> v ) {
__m128 len = _mm_sqrt_ss( ::dot_impl( v,v ) );
__m128 len = _mm_sqrt_ss( _impl_dot( v,v ) );
len = _mm_shuffle_ps(len, len, 0x00);
return _mm_div_ps(v, len);
}
FORCE_INLINE uf::simd::vector<float> uf::simd::normalize_fast( uf::simd::vector<float> v ) {
__m128 invLen = _mm_rsqrt_ps(::dot_impl(v, v));
__m128 invLen = _mm_rsqrt_ps(_impl_dot(v, v));
return _mm_mul_ps(v, invLen);
}

View File

@ -311,8 +311,9 @@ namespace uf {
const buffer_t& getBuffer( const uf::Mesh::Input&, size_t = 0 ) const;
const buffer_t& getBuffer( const uf::Mesh::Input&, const uf::Mesh::Attribute& ) const;
void eraseAttribute( uf::Mesh::Input&, const uf::Mesh::Attribute& );
void eraseAttribute( uf::Mesh::Input&, size_t );
void clearAttribute( uf::Mesh::Input&, const uf::Mesh::Attribute& );
void clearAttribute( uf::Mesh::Input&, size_t );
void clear();
uf::Mesh::Input remapInput( const uf::Mesh::Input&, size_t = 0, size_t = 0 ) const;
uf::Mesh::Input remapVertexInput( size_t i = 0, size_t = 0 ) const;

View File

@ -0,0 +1,42 @@
#pragma once
#include <uf/config.h>
#include <uf/utils/memory/vector.h>
#include <uf/utils/image/atlas.h>
#include <uf/utils/mesh/mesh.h>
namespace pod {
struct GlyphBox {
struct {
float x, y, w, h, z;
} box;
pod::Vector4f color;
uint64_t code;
};
struct TextToken {
uf::stl::string text;
pod::Vector4f color;
};
struct GlyphSettings {
uf::stl::string alignment = "";
uf::stl::string font = "Coolvetica.ttf";
float size = 96;
bool sdf = true;
float spread = 8;
pod::Vector2ui padding = { 8, 8 };
};
}
namespace uf {
namespace glyph {
size_t UF_API hashSettings( uint64_t c, const pod::GlyphSettings& metadata );
size_t UF_API hashSettings( const uf::stl::string& c, const pod::GlyphSettings& metadata );
uf::stl::vector<pod::TextToken> UF_API parseTextTokens( const uf::stl::string& text, const pod::Vector4f& color = {1,1,1,1} );
uf::stl::vector<pod::GlyphBox> UF_API calculateLayout( const uf::stl::vector<pod::TextToken>& tokens, const pod::GlyphSettings& metadata );
bool UF_API generateAtlas( const uf::stl::vector<pod::GlyphBox>& layout, const pod::GlyphSettings& metadata, uf::Atlas& atlas );
void UF_API generateMesh( const uf::stl::vector<pod::GlyphBox>& layout, const pod::GlyphSettings& metadata, const uf::Atlas& atlas, uf::Mesh& mesh );
}
}

View File

@ -205,6 +205,7 @@ void UF_API uf::load( ext::json::Value& json ) {
uf::physics::settings.timestep = configEnginePhysicsJson["timestep"].as(uf::physics::settings.timestep);
uf::physics::settings.fixedStep = configEnginePhysicsJson["fixed step"].as(uf::physics::settings.fixedStep);
uf::physics::settings.substeps = configEnginePhysicsJson["substeps"].as(uf::physics::settings.substeps);
uf::physics::settings.flattenTransforms = configEnginePhysicsJson["flatten transforms"].as(uf::physics::settings.flattenTransforms);
auto& configEnginePhysicsSolverJson = configEnginePhysicsJson["solvers"];
if ( ext::json::isObject( configEnginePhysicsSolverJson ) ) {
@ -235,6 +236,7 @@ void UF_API uf::load( ext::json::Value& json ) {
uf::physics::settings.debugDraw.contacts = configEnginePhysicsDebugDrawJson["contacts"].as( uf::physics::settings.debugDraw.contacts );
uf::physics::settings.debugDraw.constraints = configEnginePhysicsDebugDrawJson["constraints"].as( uf::physics::settings.debugDraw.constraints );
uf::physics::settings.debugDraw.rays = configEnginePhysicsDebugDrawJson["rays"].as( uf::physics::settings.debugDraw.rays );
uf::physics::settings.debugDraw.depthTest = configEnginePhysicsDebugDrawJson["depthTest"].as( uf::physics::settings.debugDraw.depthTest );
} else if ( configEnginePhysicsDebugDrawJson.is<bool>() && configEnginePhysicsDebugDrawJson.as<bool>() ) {
uf::physics::settings.debugDraw.mask = pod::Collider::CATEGORY_ALL;

View File

@ -512,7 +512,7 @@ void ext::GuiBehavior::tick( uf::Object& self ) {
for ( auto i = 0; i < uf::renderer::settings::maxViews; ++i ) {
if ( metadata.mode == 1 ) {
uniforms.matrices[i].model = transform.model;
uniforms.matrices[i].model = uf::transform::model( transform );
} else if ( metadata.mode == 2 ) {
auto& scene = uf::scene::getCurrentScene();
auto& controller = scene.getController();
@ -523,14 +523,12 @@ void ext::GuiBehavior::tick( uf::Object& self ) {
camera.getProjection(i) *
uf::matrix::translate( uf::matrix::identity(), flatten.position ) *
uf::matrix::scale( uf::matrix::identity(), flatten.scale ) *
uf::quaternion::matrix( flatten.orientation ) *
flatten.model;
uf::quaternion::matrix( flatten.orientation );
} else {
uniforms.matrices[i].model =
uf::matrix::translate( uf::matrix::identity(), flatten.position ) *
uf::matrix::scale( uf::matrix::identity(), flatten.scale ) *
uf::quaternion::matrix( flatten.orientation ) *
flatten.model;
uf::quaternion::matrix( flatten.orientation );
}
}

View File

@ -2,6 +2,8 @@
#include <uf/utils/hook/hook.h>
#include <uf/utils/image/atlas.h>
#include <uf/utils/mesh/mesh.h>
#include <uf/utils/time/time.h>
#include <uf/utils/serialize/serializer.h>
#include <uf/utils/userdata/userdata.h>
@ -12,6 +14,7 @@
#include <uf/utils/string/ext.h>
#include <uf/utils/math/physics.h>
#include <uf/utils/text/glyph.h>
#include <uf/utils/text/glyph_.h>
#include <uf/engine/asset/asset.h>
#include <uf/engine/scene/scene.h>
#include <uf/utils/memory/unordered_map.h>
@ -20,359 +23,16 @@
#include <uf/utils/http/http.h>
#include <uf/utils/audio/audio.h>
#include <uf/utils/window/payloads.h>
#include <uf/utils/string/hash.h>
#include "../payload.h"
#include "../behavior.h"
#include "../manager/behavior.h"
#if UF_USE_OPENGL
#define EXT_COLOR_FLOATS 0
#else
#define EXT_COLOR_FLOATS 1
#endif
namespace {
struct Mesh {
pod::Vector3f position;
pod::Vector2f uv;
#if EXT_COLOR_FLOATS
pod::Vector4f color;
#else
pod::Vector4b color;
#endif
static uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
};
}
UF_VERTEX_DESCRIPTOR(Mesh,
UF_VERTEX_DESCRIPTION(Mesh, R32G32B32_SFLOAT, position)
UF_VERTEX_DESCRIPTION(Mesh, R32G32_SFLOAT, uv)
#if EXT_COLOR_FLOATS
UF_VERTEX_DESCRIPTION(Mesh, R32G32B32A32_SFLOAT, color)
#else
UF_VERTEX_DESCRIPTION(Mesh, R8G8B8A8_UNORM, color)
#endif
)
#include <regex>
#include <uf/utils/string/hash.h>
namespace {
const uint64_t COLOR_CTRL = 0x7F;
struct GlyphBox {
struct {
float x, y, w, h;
} box;
pod::Vector3f color;
uint64_t code;
};
struct {
#if UF_USE_FREETYPE
ext::freetype::Glyph glyph;
uf::stl::unordered_map<uf::stl::string, uf::stl::unordered_map<size_t, uf::Glyph>> cache;
#else
char glyph;
uf::stl::unordered_map<uf::stl::string, uf::stl::unordered_map<size_t, char>> cache;
#endif
} glyphs;
struct {
uf::Serializer settings;
#if UF_ENV_DREAMCAST
pod::Vector2ui size = { 640, 480 };
#else
pod::Vector2ui size = { 1920, 1080 };
#endif
} defaults;
}
UF_BEHAVIOR_REGISTER_CPP(ext::GuiGlyphBehavior)
UF_BEHAVIOR_TRAITS_CPP(ext::GuiGlyphBehavior, ticks = true, renders = false, thread = "")
#define this (&self)
namespace {
size_t hashGlyphSettings( uint64_t c, const ext::GuiGlyphBehavior::Metadata& metadata ) {
size_t seed{};
uf::hash( seed, c, metadata.padding[0], metadata.padding[1], metadata.spread, metadata.size, metadata.font, metadata.sdf );
return seed;
}
size_t hashGlyphSettings( const uf::stl::string& c, const ext::GuiGlyphBehavior::Metadata& metadata ) {
size_t seed{};
uf::hash( seed, c, metadata.padding[0], metadata.padding[1], metadata.spread, metadata.size, metadata.font, metadata.sdf );
return seed;
}
// default to left
pod::Vector2f parseAnchor( const uf::stl::string& anchor, const pod::Vector2f& def = {0.0f, 0.0f} ) {
if ( anchor == "top-center" || anchor == "top" ) return {0.5f, 0.0f};
if ( anchor == "top-left" ) return {0.0f, 0.0f};
if ( anchor == "top-right" ) return {1.0f, 0.0f};
if ( anchor == "center" ) return {0.5f, 0.5f};
if ( anchor == "center-left" || anchor == "left" ) return {0.0f, 0.5f};
if ( anchor == "center-right" || anchor == "right" ) return {1.0f, 0.5f};
if ( anchor == "bottom-center" || anchor == "bottom" ) return {0.5f, 1.0f};
if ( anchor == "bottom-left" ) return {0.0f, 1.0f};
if ( anchor == "bottom-right" ) return {1.0f, 1.0f};
return def;
}
struct TextToken {
uf::stl::string text;
pod::Vector3f color;
};
float hexToFloat( const uf::stl::string& str ) {
int value;
uf::stl::stringstream stream;
stream << str;
stream >> std::hex >> value;
return value / 255.0f;
}
// parses a text for special tokens, associating strings with color
// should maybe be utf8, but in theory it shouldn't matter since tags are ASCII
uf::stl::vector<TextToken> parseTextTokens( const uf::stl::string& text, pod::Vector3f color ) {
uf::stl::vector<TextToken> tokens;
auto tagLength = 10; // hard-coded cringe
bool colorChanged = false;
size_t currentPos = 0;
size_t textLength = text.length();
TextToken currentToken;
currentToken.color = color;
while ( currentPos < textLength ) {
size_t tagStart = text.find("${#", currentPos);
if ( tagStart == uf::stl::string::npos ) {
currentToken.text += text.substr(currentPos);
if ( !currentToken.text.empty() || colorChanged ) {
tokens.emplace_back(currentToken);
}
break;
}
if ( tagStart > currentPos ) {
currentToken.text += text.substr( currentPos, tagStart - currentPos ); // append everything up to the tag
tokens.emplace_back( currentToken ); // commit token
currentToken.text = ""; // reset text
}
// validate tag
if ( tagStart + tagLength <= textLength && text[tagStart + tagLength - 1] == '}' ) {
uf::stl::string rHex = text.substr(tagStart + 3, 2);
uf::stl::string gHex = text.substr(tagStart + 5, 2);
uf::stl::string bHex = text.substr(tagStart + 7, 2);
// change color
currentToken.color = { hexToFloat(rHex), hexToFloat(gHex), hexToFloat(bHex) };
colorChanged = true;
// advance past the tag
currentPos = tagStart + tagLength;
} else {
currentToken.text += "${#"; // treat it as normal text
currentPos = tagStart + 3;
}
}
return tokens;
}
// compute the boxes for a given string and settings
uf::stl::vector<GlyphBox> calculateGlyphLayout( uf::Object& self, const uf::stl::vector<TextToken>& tokens, const ext::GuiGlyphBehavior::Metadata& metadata, const ext::GuiBehavior::Metadata& metadataGui, const uf::stl::string& font ) {
uf::stl::vector<GlyphBox> layout;
auto& glyphsCache = ::glyphs.cache[font];
if ( glyphsCache.empty() ) {
ext::freetype::initialize( ::glyphs.glyph, font );
}
pod::Vector2f anchor = ::parseAnchor( metadataGui.alignment );
pod::Vector2f cursor = { 0.0f, 0.0f };
float maxTextWidth = 0.0f;
float maxTextHeight = 0.0f;
float tallestGlyphY = 0.0f;
float averageTabWidth = 0.0f;
float totalWidth = 0.0f;
int charCount = 0;
// generate glyph and line height
for ( const auto& token : tokens ) {
std::u8string str(token.text.begin(), token.text.end());
for ( uint64_t c : str ) {
if ( c == '\n' || c == '\t' ) continue; // special characters
auto key = hashGlyphSettings(c, metadata);
auto& glyph = glyphsCache[key];
// generate glyph
if ( !glyph.generated() ) {
glyph.setPadding({ metadata.padding[0], metadata.padding[1] });
glyph.setSpread(metadata.spread);
glyph.useSdf(metadata.sdf);
glyph.generate(::glyphs.glyph, c, metadata.size);
}
tallestGlyphY = std::max(tallestGlyphY, (float) glyph.getSize().y);
totalWidth += glyph.getSize().x; // should probably be reset on new-line to find the widest line
charCount++;
}
}
if ( charCount > 0 ) averageTabWidth = (totalWidth / charCount) * 4.0f;
cursor.y = tallestGlyphY;
// calculate positions
for ( const auto& token : tokens ) {
std::u8string str( token.text.begin(), token.text.end() );
for ( uint64_t c : str ) {
// advance cursor on special characters
if ( c == '\n' ) {
cursor.y += tallestGlyphY;
cursor.x = 0;
continue;
} else if ( c == '\t' ) {
cursor.x = ((int)(cursor.x / averageTabWidth) + 1) * averageTabWidth;
continue;
} else if ( c == ' ' ) {
cursor.x += averageTabWidth / 4.0f;
continue;
}
// retrieve glyph
auto key = hashGlyphSettings(c, metadata);
auto& glyph = glyphsCache[key];
auto& g = layout.emplace_back(GlyphBox{
.box = {
.x = cursor.x + glyph.getBearing().x,
.y = cursor.y - glyph.getBearing().y,
.w = glyph.getSize().x,
.h = glyph.getSize().y,
},
.color = token.color,
.code = c,
});
// advance cursor
cursor.x += glyph.getAdvance().x;
// advance bounding box
maxTextWidth = std::max(maxTextWidth, g.box.x + g.box.w);
maxTextHeight = std::max(maxTextHeight, g.box.y + g.box.h);
}
}
// calculate offset based on anchor
float offsetX = maxTextWidth * anchor.x;
float offsetY = maxTextHeight * anchor.y;
// adjust all glyphs for our offset
for ( auto& g : layout ) {
g.box.x -= offsetX;
g.box.y -= offsetY;
// normalize
g.box.x /= ::defaults.size.x;
g.box.w /= ::defaults.size.x;
g.box.y /= ::defaults.size.y;
g.box.h /= ::defaults.size.y;
}
return layout;
}
// generate the mesh and texture atlas
void generateAtlasAndMesh( const uf::stl::vector<GlyphBox>& layout, const ext::GuiGlyphBehavior::Metadata& metadata, const uf::stl::string& font, uf::Atlas& atlas, uf::Mesh& mesh ) {
auto& glyphs_cache = ::glyphs.cache[font];
uf::stl::unordered_map<size_t, uf::stl::string> glyph_atlas_map;
#if UF_USE_FREETYPE
// generate atlas
{
atlas.clear();
// generate atlas
for ( const auto& g : layout ) {
auto key = hashGlyphSettings(g.code, metadata);
auto& glyph = glyphs_cache[key];
// already in atlas map
if ( glyph_atlas_map.find(key) != glyph_atlas_map.end() ) continue;
const uint8_t* buffer = glyph.getBuffer();
uf::Image::container_t pixels;
size_t len = glyph.getSize().x * glyph.getSize().y;
if ( metadata.sdf ) {
glyph_atlas_map[key] = atlas.addImage(glyph.getBuffer(), glyph.getSize(), 8, 1, true);
} else {
pixels.reserve(len * 4);
for ( size_t i = 0; i < len; ++i ) {
pixels.emplace_back(buffer[i]); // R
pixels.emplace_back(buffer[i]); // G
pixels.emplace_back(buffer[i]); // B
pixels.emplace_back(buffer[i]); // A
}
glyph_atlas_map[key] = atlas.addImage(&pixels[0], glyph.getSize(), 8, 4, true);
}
}
atlas.generate();
atlas.clear(false);
}
#endif
// generate mesh
{
mesh.destroy();
mesh.bind<::Mesh, uint16_t>();
uf::stl::vector<::Mesh> vertices;
uf::stl::vector<uint16_t> indices;
vertices.reserve(layout.size() * 4);
indices.reserve(layout.size() * 6);
for ( const auto& g : layout ) {
auto key = hashGlyphSettings(g.code, metadata);
auto hash = glyph_atlas_map[key];
#if EXT_COLOR_FLOATS
auto& color = g.color;
#else
pod::Vector4b color = {
(uint8_t)(g.color[0] * 255),
(uint8_t)(g.color[1] * 255),
(uint8_t)(g.color[2] * 255),
(uint8_t)(g.color[3] * 255)
};
#endif
// insert indices
uint16_t idx = (uint16_t) vertices.size();
indices.insert( indices.end(), { idx, idx + 1, idx + 2, idx, idx + 2, idx + 3 });
// insert vertices
vertices.emplace_back(::Mesh{pod::Vector3f{ g.box.x, g.box.y + g.box.h, 0 }, atlas.mapUv(pod::Vector2f{ 0.0f, 0.0f }, hash), color});
vertices.emplace_back(::Mesh{pod::Vector3f{ g.box.x, g.box.y , 0 }, atlas.mapUv(pod::Vector2f{ 0.0f, 1.0f }, hash), color});
vertices.emplace_back(::Mesh{pod::Vector3f{ g.box.x + g.box.w, g.box.y , 0 }, atlas.mapUv(pod::Vector2f{ 1.0f, 1.0f }, hash), color});
vertices.emplace_back(::Mesh{pod::Vector3f{ g.box.x + g.box.w, g.box.y + g.box.h, 0 }, atlas.mapUv(pod::Vector2f{ 1.0f, 0.0f }, hash), color});
}
mesh.insertVertices(vertices);
mesh.insertIndices(indices);
}
}
}
void ext::GuiGlyphBehavior::initialize( uf::Object& self ) {
auto& metadata = this->getComponent<ext::GuiGlyphBehavior::Metadata>();
auto& metadataGui = this->getComponent<ext::GuiBehavior::Metadata>();
@ -380,7 +40,7 @@ void ext::GuiGlyphBehavior::initialize( uf::Object& self ) {
this->addHook( "gui:UpdateText.%UID%", [&](ext::json::Value& payload){
auto string = payload["string"].as(metadata.string);
auto font = uf::io::root+"/fonts/" + payload["font"].as(metadata.font);
auto font = payload["font"].as(metadata.font);
bool forced = payload["force"].as(false);
// override
@ -393,14 +53,21 @@ void ext::GuiGlyphBehavior::initialize( uf::Object& self ) {
auto& mesh = this->getComponent<uf::Mesh>();
auto& atlas = this->getComponent<uf::Atlas>();
auto& images = atlas.getImages();
auto& glyphs_cache = ::glyphs.cache;
uf::stl::unordered_map<size_t, uf::stl::string> glyph_atlas_map;
auto tokens = ::parseTextTokens(string, metadataGui.color);
auto layout = ::calculateGlyphLayout(self, tokens, metadata, metadataGui, font);
::generateAtlasAndMesh( layout, metadata, font, atlas, mesh );
auto settings = pod::GlyphSettings{
.alignment = metadataGui.alignment,
.font = font,
.size = metadata.size,
.sdf = metadata.sdf,
.spread = metadata.spread,
.padding = metadata.padding,
};
auto tokens = uf::glyph::parseTextTokens( string, metadataGui.color );
auto layout = uf::glyph::calculateLayout( tokens, settings );
uf::glyph::generateAtlas( layout, settings, atlas );
uf::glyph::generateMesh( layout, settings, atlas, mesh );
// set proper shaders
if ( metadata.sdf ) {
@ -487,7 +154,14 @@ void ext::GuiGlyphBehavior::Metadata::serialize( uf::Object& self, uf::Serialize
serializer["range"] = uf::vector::encode( /*this->*/shader.range);
}
void ext::GuiGlyphBehavior::Metadata::deserialize( uf::Object& self, uf::Serializer& serializer ){
size_t oldHash = ::hashGlyphSettings( string, *this );
size_t oldHash = uf::glyph::hashSettings( string, pod::GlyphSettings{
.alignment = "",
.font = /*this->*/font,
.size = /*this->*/size,
.sdf = /*this->*/sdf,
.spread = /*this->*/spread,
.padding = /*this->*/padding,
});
/*this->*/string = serializer["string"].as(/*this->*/string);
/*this->*/font = serializer["font"].as(/*this->*/font);
@ -501,7 +175,14 @@ void ext::GuiGlyphBehavior::Metadata::deserialize( uf::Object& self, uf::Seriali
/*this->*/shader.stroke = uf::vector::decode(serializer["stroke"], /*this->*/shader.stroke);
/*this->*/shader.range = uf::vector::decode(serializer["range"], /*this->*/shader.range);
size_t newHash = ::hashGlyphSettings( string, *this );
size_t newHash = uf::glyph::hashSettings( string, pod::GlyphSettings{
.alignment = "",
.font = /*this->*/font,
.size = /*this->*/size,
.sdf = /*this->*/sdf,
.spread = /*this->*/spread,
.padding = /*this->*/padding,
});
// fire text update
if ( oldHash != newHash ) {

View File

@ -126,10 +126,14 @@ void ext::PlayerBehavior::initialize( uf::Object& self ) {
UF_BEHAVIOR_METADATA_BIND_SERIALIZER_HOOKS(metadata, metadataJson);
}
void ext::PlayerBehavior::tick( uf::Object& self ) {
auto& transform = this->getComponent<pod::Transform<>>();
auto& camera = this->getComponent<uf::Camera>();
auto& cameraTransform = camera.getTransform();
auto& transform = this->getComponent<pod::Transform<>>();
auto cameraAxes = uf::transform::axes( cameraTransform );
auto axes = uf::transform::axes( transform );
auto& scene = uf::scene::getCurrentScene();
auto& metadata = this->getComponent<ext::PlayerBehavior::Metadata>();
@ -296,7 +300,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
uf::Object* pointer = NULL;
float length = metadata.use.length;
// pod::Vector3f center = transform.position + cameraTransform.position;
// pod::Vector3f direction = uf::vector::normalize( transform.forward + pod::Vector3f{ 0, cameraTransform.forward.y, 0 } ) * length;
// pod::Vector3f direction = uf::vector::normalize( axes.forward + pod::Vector3f{ 0, cameraAxes.forward.y, 0 } ) * length;
auto flattened = uf::transform::flatten( cameraTransform );
pod::Vector3f center = flattened.position;
pod::Vector3f direction = flattened.forward * length;
@ -385,10 +389,10 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
metadata.system.menu = stats.menu;
}
pod::Transform<> translator = transform;
pod::Axes translator = uf::transform::axes( transform );
#if UF_USE_OPENVR
// use the orientation of our controller to determine our target
if ( ext::openvr::context ) {
/*if ( ext::openvr::context ) {
bool useController = true;
translator.orientation = uf::quaternion::multiply( transform.orientation, useController ? ext::openvr::controllerQuaternion( vr::Controller_Hand::Hand_Right ) : ext::openvr::hmdQuaternion() );
translator = uf::transform::reorient( translator );
@ -401,17 +405,17 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
translator.forward = uf::vector::normalize( translator.forward );
translator.right = uf::vector::normalize( translator.right );
} else
} else*/
#endif
// un-flatted if noclipped
if ( metadata.camera.fixed ) {
translator = cameraTransform;
translator = uf::transform::axes( cameraTransform );
translator.forward.y = 0;
translator.forward = uf::vector::normalize( translator.forward );
}
else if ( stats.noclipped || physicsBody.gravity == pod::Vector3f{0,0,0} ){
translator.forward.y += cameraTransform.forward.y;
translator.forward.y += cameraAxes.forward.y;
translator.forward = uf::vector::normalize( translator.forward );
}
@ -452,12 +456,12 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
physicsBody.velocity += target * std::clamp( speed.move * factor - uf::vector::dot( physicsBody.velocity, target ), 0.0f, speed.move * 10 * ONE_OVER_SIXTY /*uf::physics::time::delta*/ );
}
auto dot = uf::vector::dot( transform.forward, target );
auto dot = uf::vector::dot( axes.forward, target );
if ( !metadata.movement.strafe && dot < 1.0f ) {
// auto cross = uf::vector::normalize( uf::vector::cross( transform.forward, target ) );
// auto axis = cross == pod::Vector3f{0, 0, 0} ? transform.up : cross;
auto axis = transform.up;
float angle = uf::vector::signedAngle( transform.forward, target, axis ) * ONE_OVER_SIXTY /*uf::physics::time::delta*/ * 4; // speed.rotate;
// auto cross = uf::vector::normalize( uf::vector::cross( axes.forward, target ) );
// auto axis = cross == pod::Vector3f{0, 0, 0} ? axes.up : cross;
auto axis = axes.up;
float angle = uf::vector::signedAngle( axes.forward, target, axis ) * ONE_OVER_SIXTY /*uf::physics::time::delta*/ * 4; // speed.rotate;
if ( physicsBody.object ) uf::physics::applyRotation( physicsBody, axis, angle ); else
uf::transform::rotate( transform, axis, angle );
@ -510,39 +514,39 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
if ( metadata.camera.invert.x ) lookDelta.x *= -1;
metadata.camera.limit.current.x += lookDelta.x;
if ( metadata.camera.limit.current.x != metadata.camera.limit.current.x || ( metadata.camera.limit.current.x < metadata.camera.limit.max.x && metadata.camera.limit.current.x > metadata.camera.limit.min.x ) ) {
if ( physicsBody.object ) uf::physics::applyRotation( physicsBody, transform.up, lookDelta.x ); else
uf::transform::rotate( transform, transform.up, lookDelta.x );
if ( physicsBody.object ) uf::physics::applyRotation( physicsBody, axes.up, lookDelta.x ); else
uf::transform::rotate( transform, axes.up, lookDelta.x );
} else metadata.camera.limit.current.x -= lookDelta.x;
}
if ( lookDelta.y != 0 ) {
if ( metadata.camera.invert.y ) lookDelta.y *= -1;
metadata.camera.limit.current.y += lookDelta.y;
if ( metadata.camera.limit.current.y != metadata.camera.limit.current.y || ( metadata.camera.limit.current.y < metadata.camera.limit.max.y && metadata.camera.limit.current.y > metadata.camera.limit.min.y ) ) {
// if ( physicsBody.object && !physicsBody.shared ) uf::physics::applyRotation( physicsBody, cameraTransform.right, lookDelta.y ); else
uf::transform::rotate( cameraTransform, cameraTransform.right, lookDelta.y );
// if ( physicsBody.object && !physicsBody.shared ) uf::physics::applyRotation( physicsBody, cameraAxes.right, lookDelta.y ); else
uf::transform::rotate( cameraTransform, cameraAxes.right, lookDelta.y );
} else metadata.camera.limit.current.y -= lookDelta.y;
}
} else if ( metadata.system.control ) {
if ( keys.lookRight ^ keys.lookLeft ) {
if ( physicsBody.object ) uf::physics::applyRotation( physicsBody, transform.up, speed.rotate * (keys.lookRight ? 1 : -1) ); else
uf::transform::rotate( transform, transform.up, speed.rotate * (keys.lookRight ? 1 : -1) );
if ( physicsBody.object ) uf::physics::applyRotation( physicsBody, axes.up, speed.rotate * (keys.lookRight ? 1 : -1) ); else
uf::transform::rotate( transform, axes.up, speed.rotate * (keys.lookRight ? 1 : -1) );
}
if ( keys.lookUp ^ keys.lookDown ) {
float direction = keys.lookUp ? 1 : -1;
if ( metadata.camera.invert.y ) direction *= -1;
uf::transform::rotate( cameraTransform, cameraTransform.right, speed.rotate * direction );
uf::transform::rotate( cameraTransform, cameraAxes.right, speed.rotate * direction );
}
}
} else {
if ( keys.lookRight ^ keys.lookLeft ) {
// auto rotation = uf::quaternion::axisAngle( cameraTransform.up, uf::physics::time::delta * (keys.lookRight ? 1 : -1) );
// auto rotation = uf::quaternion::axisAngle( cameraAxes.up, uf::physics::time::delta * (keys.lookRight ? 1 : -1) );
// cameraTransform.position = uf::quaternion::rotate( rotation, cameraTransform.position - transform.position );
}
if ( keys.lookUp ^ keys.lookDown ) {
// if ( physicsBody.object && !physicsBody.shared ) uf::physics::applyRotation( physicsBody, cameraTransform.right, lookDelta.y ); else
// if ( physicsBody.object && !physicsBody.shared ) uf::physics::applyRotation( physicsBody, cameraAxes.right, lookDelta.y ); else
float direction = keys.lookUp ? 1 : -1;
if ( metadata.camera.invert.y ) direction *= -1;
uf::transform::rotate( cameraTransform, cameraTransform.right, speed.rotate * direction );
uf::transform::rotate( cameraTransform, cameraAxes.right, speed.rotate * direction );
}
}
{
@ -556,7 +560,7 @@ void ext::PlayerBehavior::tick( uf::Object& self ) {
cameraTransform.reference = NULL;
cameraTransform.position = transform.position + metadata.camera.offset;
// cameraTransform.orientation = uf::vector::decode( metadataJson["camera"]["orientation"], uf::quaternion::identity() );
cameraTransform = uf::transform::reorient( cameraTransform );
cameraAxes = uf::transform::axes( cameraTransform );
} else {
if ( metadata.camera.offset != pod::Vector3f{0,0,0} ) {
//auto flattened = uf::transform::flatten( cameraTransform );

View File

@ -432,9 +432,7 @@ void ext::ExtSceneBehavior::tick( uf::Object& self ) {
transform = uf::transform::reorient( transform );
*/
}
transform = uf::transform::flatten( transform );
transform.forward *= -1;
transform = uf::transform::flatten( transform );
ext::al::listener( transform );
}
#endif

View File

@ -82,8 +82,7 @@ namespace {
return
uf::matrix::translate( uf::matrix::identity(), transform.position ) *
uf::quaternion::matrix(transform.orientation) *
uf::matrix::scale( uf::matrix::identity(), transform.scale ) *
transform.model;
uf::matrix::scale( uf::matrix::identity(), transform.scale );
}
pod::Matrix4f worldMatrix( const pod::Graph& graph, int32_t index ) {
pod::Matrix4f matrix = ::localMatrix( graph, index );
@ -128,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];
@ -153,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;
@ -205,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];
@ -308,8 +312,7 @@ uf::stl::vector<pod::OBB> uf::graph::obbFromSkin( const pod::Graph& graph, const
auto& skin = storage.skins[skinName];
auto& mesh = storage.meshes[meshName];
// store as min/max AABB
uf::stl::vector<pod::OBB> bounds(skin.joints.size(), {
uf::stl::vector<pod::AABB> aabbs(skin.joints.size(), {
pod::Vector3f{ FLT_MAX, FLT_MAX, FLT_MAX },
pod::Vector3f{-FLT_MAX,-FLT_MAX,-FLT_MAX }
});
@ -325,24 +328,36 @@ uf::stl::vector<pod::OBB> uf::graph::obbFromSkin( const pod::Graph& graph, const
auto joints = uf::mesh::fetchVertexAttribute<pod::Vector4us>( view, jointsView, i );
auto weights = uf::mesh::fetchVertexAttribute<pod::Vector4f>( view, weightView, i );
int bestW = -1;
float maxWeight = 0.0f;
for ( auto w = 0; w < 4; ++w ) {
if ( weights[w] <= wThresold ) continue;
uint16_t jointID = joints[w];
if ( weights[w] > maxWeight ) {
maxWeight = weights[w];
bestW = w;
}
}
if ( bestW != -1 ) {
float weight = weights[bestW];
uint16_t jointID = joints[bestW];
// transform from mesh => bone space
pod::Vector3f localPos = uf::matrix::multiply( skin.inverseBindMatrices[jointID], pos );
bounds[jointID].center = uf::vector::min( bounds[jointID].center, localPos );
bounds[jointID].extent = uf::vector::max( bounds[jointID].extent, localPos );
aabbs[jointID].min = uf::vector::min( aabbs[jointID].min, localPos );
aabbs[jointID].max = uf::vector::max( aabbs[jointID].max, localPos );
}
}
}
// convert from min-max to center-extent
for ( auto& box : bounds ) {
auto extent = (box.extent - box.center) * 0.5f;
auto center = (box.extent + box.center) * 0.5f;
box = pod::OBB{ center, extent };
uf::stl::vector<pod::OBB> bounds(skin.joints.size());
for ( auto i = 0; i < skin.joints.size(); ++i ) {
auto& aabb = aabbs[i];
bounds[i] = pod::OBB{
.center = (aabb.max + aabb.min) * 0.5f,
.extent = (aabb.max - aabb.min) * 0.5f,
};
}
return bounds;
}
@ -364,66 +379,59 @@ void uf::graph::rigRagdoll( pod::Graph& graph, pod::Node& node ) {
for ( auto i = 0; i < skin.joints.size(); ++i ) {
auto nodeID = skin.joints[i];
auto& node = graph.nodes[nodeID];
if ( node.parent < 0 ) continue;
if ( node.parent < 0 ) continue; // root node, skip
auto& entity = *node.entity;
auto transform = uf::transform::flatten( entity.getComponent<pod::Transform<>>() );
// copy to modify in-place
auto bone = bones[nodeID];
auto obb = bounds[i];
// invalid bounds
bool useBone = obb.extent.x > 0;
auto& bone = bones[nodeID];
auto& obb = bounds[i];
/*
auto boneCenter = uf::transform::apply( armatureTransform, (bone.end + bone.start) * 0.5f );
auto offset = uf::transform::applyInverse( transform, boneCenter );
auto orientation = uf::quaternion::identity();
float length = uf::vector::norm( offset ) * 2.0f;
*/
// skip leaf bones that aren't assigned joints in the mesh
if ( obb.extent.x < 0 && node.children.empty() ) continue;
auto targetShape = pod::ShapeType::OBB;
// skip leaf bones if they aren't used in the mesh
if ( !useBone && node.children.empty() ) continue;
// useBone = false; // disable for now
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
// transform into node space
auto start = uf::transform::applyInverse( transform, bone.start );
auto end = uf::transform::applyInverse( transform, bone.end );
float length = uf::vector::distance( bone.start, bone.end );
auto offset = ( start + end ) * 0.5f;
auto dir = uf::vector::normalize( end - start );
auto offset = pod::Vector3f{};
auto orientation = uf::quaternion::identity();
float mass = 100.0f;
float mass = 0.0f;
if ( false && useBone ) {
shapeType = pod::ShapeType::OBB;
// volume = obb.extent.x * obb.extent.y * obb.extent.z * 8.0f;
} else {
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] ) {
targetShape = pod::ShapeType::SPHERE;
// 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 ( targetShape ) {
switch ( shapeType ) {
case pod::ShapeType::CAPSULE: {
auto up = pod::Vector3f{0, 1, 0};
auto dir = uf::vector::normalize( end - start );
float dot = uf::vector::dot( up, dir );
if ( dot < -0.999f ) {
body.offsetOrientation = uf::quaternion::axisAngle( pod::Vector3f{1, 0, 0}, M_PI );
} else if ( dot < 0.999f ) {
auto cross = uf::vector::normalize( uf::vector::cross( up, dir ) );
body.offsetOrientation = uf::quaternion::axisAngle( cross, std::acos( dot ) );
}
float radius = length * 0.15f;
float halfHeight = std::max( 0.01f, length - (radius * 2.0f) ) * 0.5f;
uf::physics::initialize( body, pod::Capsule{ radius, halfHeight } );
float height = length - (thickness * 2); // subtract end-caps
uf::physics::initialize( body, pod::Capsule{ thickness, dir * height * 0.5f } );
} break;
case pod::ShapeType::AABB:
case pod::ShapeType::OBB: {
uf::physics::initialize( body, pod::OBB{ pod::Vector3f{}, obb.extent * armatureTransform.scale } );
uf::physics::initialize( body, obb );
} break;
// should probably add a NONE shape
default:

View File

@ -440,9 +440,7 @@ void uf::graph::load( pod::Graph& graph, const uf::stl::string& filename, const
}
uf::stl::string key = graph.metadata["key"].as<uf::stl::string>("");
if ( key != "" ) {
key += ":";
}
if ( key != "" ) key += ":";
tasks.queue([&]{
// load images

View File

@ -148,8 +148,11 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
offset = center;
center = {};
}
#if OBB_EXTENT_CENTER
uf::physics::initialize( body, pod::OBB{ .extent = extent, .center = center } );
#else
uf::physics::initialize( body, pod::OBB{ .center = center, .extent = extent } );
#endif
} else if ( type == "aabb" ) {
pod::Vector3f min = uf::vector::decode( metadataJsonPhysics["min"], pod::Vector3f{-0.5f, -0.5f, -0.5f} );
pod::Vector3f max = uf::vector::decode( metadataJsonPhysics["max"], pod::Vector3f{0.5f, 0.5f, 0.5f} );
@ -175,9 +178,12 @@ void uf::ObjectBehavior::initialize( uf::Object& self ) {
uf::physics::initialize( body, pod::Sphere{ radius } );
} else if ( type == "capsule" ) {
float radius = metadataJsonPhysics["radius"].as<float>();
float halfHeight = metadataJsonPhysics["height"].as<float>() * 0.5f;
auto up = uf::vector::decode( metadataJsonPhysics["up"], pod::Vector3f{0,1,0} );
if ( metadataJsonPhysics["height"].is<float>() ) {
up *= metadataJsonPhysics["height"].as<float>() * 0.5f;
}
uf::physics::initialize( body, pod::Capsule{ radius, halfHeight } );
uf::physics::initialize( body, pod::Capsule{ radius, up } );
} else if ( type == "mesh" ) {
// ...
} else {

View File

@ -59,8 +59,8 @@ void uf::GraphBehavior::initialize( uf::Object& self ) {
uf::graph::initialize( graph );
if ( graph.metadata["renderer"]["skinned"].as<bool>() ) {
if ( metadata["graph"]["animation"].is<uf::stl::string>() ) {
uf::graph::animate( graph, metadata["graph"]["animation"].as<uf::stl::string>(), metadata["graph"]["speed"].as<float>( 1.0f ) );
if ( ext::json::isObject( metadata["graph"]["animations"] ) ) {
uf::graph::animate( graph, metadata["graph"]["animations"]["animation"].as<uf::stl::string>(), metadata["graph"]["animations"]["speed"].as<float>( 1.0f ) );
}
}
@ -73,28 +73,32 @@ void uf::GraphBehavior::tick( uf::Object& self ) {
auto& graph = this->getComponent<pod::Graph>();
if ( !graph.metadata["debug"]["draw"]["armature"].as<bool>(false) ) return;
auto& transform = this->getComponent<pod::Transform<>>();
auto& scene = uf::scene::getCurrentScene();
auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent<pod::Graph::Storage>();
for ( auto& node : graph.nodes ) {
if ( node.skin < 0 || node.mesh < 0 ) continue;
auto bones = uf::graph::collectBones( graph, node );
for ( auto& bone : bones ) {
auto bounds = uf::graph::obbFromSkin( graph, node );
auto& skinName = graph.skins[node.skin];
auto& skin = storage.skins[skinName];
for ( auto bone : bones ) {
bone.start = uf::transform::apply( transform, bone.start );
bone.end = uf::transform::apply( transform, bone.end );
uf::debug::drawLine( bone.start, bone.end, pod::Vector4f{ 0, 1, 0, 1 } );
}
for ( auto i = 0; i < skin.joints.size(); ++i ) {
auto nodeID = skin.joints[i];
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 );
}
}
static bool hid = false;
if ( hid ) return;
hid = true;
this->process([&](uf::Entity* entity){
if ( !entity->hasComponent<uf::Graphic>() ) return;
auto& graphic = entity->getComponent<uf::Graphic>();
if ( !graphic.initialized ) return;
auto& descriptorSet = graphic.getDescriptorSet();
descriptorSet.metadata.process = false;
uf::renderer::states::rebuild = true;
});
}
void uf::GraphBehavior::render( uf::Object& self ) {}
void uf::GraphBehavior::Metadata::serialize( uf::Object& self, uf::Serializer& serializer ) {}

View File

@ -99,11 +99,13 @@ namespace {
} else {
transform.scale = { 1, 1, 1 };
}
/*
if ( node.matrix.size() == 16 ) {
for ( size_t i = 0; i < node.matrix.size(); ++i ) transform.model[i] = node.matrix[i];
} else {
transform.model = uf::matrix::identity();
}
*/
if ( 0 <= parentIndex && parentIndex < graph.nodes.size() && nodeIndex != parentIndex ) {
transform.reference = &graph.nodes[parentIndex].transform;
}

View File

@ -139,12 +139,18 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::OBB,
[]( pod::OBB& self, const pod::OBB& copy ) {
return self = copy;
},
[]( pod::OBB& self, const pod::Vector3f& center, const pod::Vector3f& extent ) {
return self = pod::OBB{ center, extent };
#if OBB_EXTENT_CENTER
[]( pod::OBB& self, const pod::Vector3f& extent, const pod::Vector3f& center ) {
return self = pod::OBB{ .extent = extent, .center = center };
}
#else
[]( pod::OBB& self, const pod::Vector3f& center, const pod::Vector3f& extent ) {
return self = pod::OBB{ .center = center, .extent = extent };
}
#endif
),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::OBB::center),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::OBB::extent)
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::OBB::extent),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::OBB::center)
)
UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Sphere,
sol::call_constructor, sol::initializers(
@ -168,12 +174,13 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Capsule,
[]( pod::Capsule& self, const pod::Capsule& copy ) {
return self = copy;
},
[]( pod::Capsule& self, float radius, float height ) {
return self = pod::Capsule{ radius, height * 0.5f };
[]( pod::Capsule& self, float radius, pod::Vector3f up ) {
//if ( up == pod::Vector3f{} ) up = pod::Vector3f{0,1,0};
return self = pod::Capsule{ .radius = radius, .up = up };
}
),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Capsule::radius),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Capsule::halfHeight)
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Capsule::up)
)
UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Plane,
sol::call_constructor, sol::initializers(
@ -184,7 +191,7 @@ UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Plane,
return self = copy;
},
[]( pod::Plane& self, const pod::Vector3f& normal, float offset ) {
return self = pod::Plane{ normal, offset };
return self = pod::Plane{ .normal = normal, .offset = offset };
}
),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Plane::normal),

View File

@ -28,8 +28,8 @@ namespace binds {
pod::Transform<> flatten( const pod::Transform<>& t ) {
return uf::transform::flatten( t );
}
pod::Transform<> reorient( const pod::Transform<>& t ) {
return uf::transform::reorient( t );
pod::Axes axes( const pod::Transform<>& t, sol::optional<pod::Vector3f> at ) {
return uf::transform::axes( t, at.value_or(pod::Vector3f{}) );
}
pod::Transform<> getReference( pod::Transform<>& t ) {
return t.reference ? *t.reference : t;
@ -54,8 +54,8 @@ namespace binds {
pod::Transform<>& reference( pod::Transform<>& transform, const pod::Transform<>& parent, sol::optional<bool> reorient ) {
return uf::transform::reference( transform, parent, reorient.value_or(true) );
}
pod::Transform<> interpolate( const pod::Transform<>& from, const pod::Transform<>& to, float factor, sol::optional<bool> reorient ) {
return uf::transform::interpolate( from, to, factor, reorient.value_or(true) );
pod::Transform<> interpolate( const pod::Transform<>& from, const pod::Transform<>& to, float factor ) {
return uf::transform::interpolate( from, to, factor );
}
pod::Transform<> inverse(const pod::Transform<>& t) {
return uf::transform::inverse( t );
@ -72,19 +72,29 @@ namespace binds {
}
#include <uf/ext/lua/component.h>
UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Axes,
sol::call_constructor, sol::initializers(
[]( pod::Axes& self ) {
return self = {};
},
[]( pod::Axes& self, const pod::Axes& copy ) {
return self = copy;
}
),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Axes::right),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Axes::up),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Axes::forward)
)
UF_LUA_REGISTER_USERTYPE_AND_COMPONENT(pod::Transform<>,
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Transform<>::position),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Transform<>::scale),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Transform<>::up),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Transform<>::right),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Transform<>::forward),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Transform<>::orientation),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Transform<>::model),
UF_LUA_REGISTER_USERTYPE_MEMBER(pod::Transform<>::scale),
UF_LUA_REGISTER_USERTYPE_DEFINE(move, UF_LUA_C_FUN(::binds::move) ),
UF_LUA_REGISTER_USERTYPE_DEFINE(rotate, UF_LUA_C_FUN(::binds::rotate)),
UF_LUA_REGISTER_USERTYPE_DEFINE(flatten, UF_LUA_C_FUN(::binds::flatten)),
UF_LUA_REGISTER_USERTYPE_DEFINE(reorient, UF_LUA_C_FUN(::binds::reorient)),
UF_LUA_REGISTER_USERTYPE_DEFINE(axes, UF_LUA_C_FUN(::binds::axes)),
UF_LUA_REGISTER_USERTYPE_DEFINE(getReference, UF_LUA_C_FUN(::binds::getReference)),
UF_LUA_REGISTER_USERTYPE_DEFINE(setReference, UF_LUA_C_FUN(::binds::setReference)),
UF_LUA_REGISTER_USERTYPE_DEFINE(unreference, UF_LUA_C_FUN(::binds::unreference)),

View File

@ -210,8 +210,9 @@ void ext::al::close( uf::audio::Metadata& metadata ) {
void ext::al::listener( const pod::Transform<>& transform ) {
if ( uf::audio::muted ) return;
float o[6] = { transform.forward.x, transform.forward.y, transform.forward.z, transform.up.x, transform.up.y, transform.up.z };
auto axes = uf::transform::axes( transform );
axes.forward *= -1;
float o[6] = { axes.forward.x, axes.forward.y, axes.forward.z, axes.up.x, axes.up.y, axes.up.z };
AL_CHECK_RESULT(alListener3f( AL_POSITION, transform.position.x, transform.position.y, transform.position.z ));
AL_CHECK_RESULT(alListener3f( AL_VELOCITY, 0, 0, 0 ));
AL_CHECK_RESULT(alListenerfv( AL_ORIENTATION, &o[0] ));

View File

@ -312,7 +312,10 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
});
auto& shader = blitter.material.getShader("fragment");
if ( !settings::pipelines::fsr || !ext::fsr::frameUpscale ) {
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
if ( !settings::pipelines::fsr || !ext::fsr::frameUpscale )
#endif
{
shader.aliasAttachment("output", this);
}
}

View File

@ -7,7 +7,7 @@
#include <uf/ext/ffx/fsr.h>
VkResult ext::vulkan::Swapchain::acquireNextImage( uint32_t* imageIndex, VkSemaphore presentCompleteSemaphore, VkFence acquireFence ) {
#if UF_USE_FFX_SDK
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
if ( ext::fsr::frameInterpolation ) {
return ext::fsr::acquireNextImage( imageIndex, presentCompleteSemaphore, acquireFence );
}
@ -17,7 +17,7 @@ VkResult ext::vulkan::Swapchain::acquireNextImage( uint32_t* imageIndex, VkSemap
}
VkResult ext::vulkan::Swapchain::queuePresent( VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore ) {
#if UF_USE_FFX_SDK
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
if ( ext::fsr::frameInterpolation ) {
return ext::fsr::queuePresent( queue, imageIndex, waitSemaphore );
}
@ -178,7 +178,7 @@ void ext::vulkan::Swapchain::initialize( Device& device ) {
swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
#if UF_USE_FFX_SDK
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
VK_CHECK_RESULT(ext::fsr::createSwapchain( device.logicalDevice, &swapchainCI, nullptr, &swapChain ));
#else
VK_CHECK_RESULT(vkCreateSwapchainKHR( device.logicalDevice, &swapchainCI, nullptr, &swapChain ));
@ -188,14 +188,14 @@ void ext::vulkan::Swapchain::initialize( Device& device ) {
// If an existing swap chain is re-created, destroy the old swap chain
// This also cleans up all the presentable images
if ( oldSwapchain != VK_NULL_HANDLE ) {
#if UF_USE_FFX_SDK
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
ext::fsr::destroySwapchain( device.logicalDevice, oldSwapchain, nullptr);
#else
vkDestroySwapchainKHR( device.logicalDevice, oldSwapchain, nullptr);
#endif
// VK_UNREGISTER_HANDLE( oldSwapchain );
}
#if UF_USE_FFX_SDK
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
VK_CHECK_RESULT(ext::fsr::getSwapchainImages( device.logicalDevice, swapChain, &buffers, NULL));
#else
VK_CHECK_RESULT(vkGetSwapchainImagesKHR( device.logicalDevice, swapChain, &buffers, NULL));
@ -204,7 +204,7 @@ void ext::vulkan::Swapchain::initialize( Device& device ) {
// Bind images
{
images.resize( buffers );
#if UF_USE_FFX_SDK
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
VK_CHECK_RESULT(ext::fsr::getSwapchainImages( device, swapChain, &buffers, images.data()));
#else
VK_CHECK_RESULT(vkGetSwapchainImagesKHR( device, swapChain, &buffers, images.data()));
@ -222,7 +222,7 @@ void ext::vulkan::Swapchain::destroy() {
}
swapchain.images.clear();
if ( swapChain != VK_NULL_HANDLE ) {
#if UF_USE_FFX_SDK
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
ext::fsr::destroySwapchain( *device, swapChain, nullptr );
#else
vkDestroySwapchainKHR( *device, swapChain, nullptr);

View File

@ -504,7 +504,9 @@ void ext::vulkan::tick() {
ext::vulkan::states::rebuild = true;
}
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
ext::fsr::tick();
#endif
auto renderModes = ::fetchRenderModes();
for ( auto& renderMode : renderModes ) {

View File

@ -2,6 +2,8 @@
#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 {
@ -18,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,
@ -38,7 +54,8 @@ 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 } );
}
void uf::debug::drawAabb( pod::AABB aabb, pod::Transform<> transform ) {
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},
{aabb.max.x, aabb.max.y, aabb.min.z}, {aabb.min.x, aabb.max.y, aabb.min.z},
@ -56,7 +73,7 @@ void uf::debug::drawAabb( pod::AABB aabb, pod::Transform<> transform ) {
uf::debug::drawLine( corners[0], corners[4] ); uf::debug::drawLine( corners[1], corners[5] );
uf::debug::drawLine( corners[2], corners[6] ); uf::debug::drawLine( corners[3], corners[7] );
}
void uf::debug::drawObb( pod::OBB obb, pod::Transform<> transform ) {
void uf::debug::drawShape( const pod::OBB& obb, const pod::Transform<>& transform ) {
auto aabb = pod::AABB{
.min = obb.center - obb.extent,
.max = obb.center + obb.extent,
@ -83,7 +100,7 @@ void uf::debug::drawObb( pod::OBB obb, pod::Transform<> transform ) {
uf::debug::drawLine( corners[2], corners[6] ); uf::debug::drawLine( corners[3], corners[7] );
}
void uf::debug::drawSphere( pod::Sphere sphere, pod::Transform<> transform ) {
void uf::debug::drawShape( const pod::Sphere& sphere, const pod::Transform<>& transform ) {
const int segments = 16;
const float angleIncrement = (2.0f * M_PI) / segments;
for ( auto i = 0; i < segments; ++i ) {
@ -109,12 +126,11 @@ void uf::debug::drawSphere( pod::Sphere sphere, pod::Transform<> transform ) {
uf::debug::drawLine( transform.position + yz1, transform.position + yz2 );
}
}
// for some reason compiling these two and not even using them causes physics to break
/*
void uf::debug::drawCapsule( pod::Capsule capsule, pod::Transform<> transform ) {
const pod::Vector3f up = uf::quaternion::rotate( transform.orientation, pod::Vector3f{0,1,0} );
auto p1 = transform.position + up * capsule.halfHeight;
auto p2 = transform.position - up * capsule.halfHeight;
void uf::debug::drawShape( const pod::Capsule& capsule, const pod::Transform<>& transform ) {
const pod::Vector3f up = uf::quaternion::rotate( transform.orientation, capsule.up );
auto p1 = transform.position + up;
auto p2 = transform.position - up;
const int segments = 16;
const float angleIncrement = (2.0f * M_PI) / segments;
@ -160,7 +176,7 @@ void uf::debug::drawCapsule( pod::Capsule capsule, pod::Transform<> transform )
uf::debug::drawLine( p1 - rz, p2 - rz );
}
// to-do: properly implement this
void uf::debug::drawPlane( pod::Plane plane, pod::Transform<> transform ) {
void uf::debug::drawShape( const pod::Plane& plane, const pod::Transform<>& transform ) {
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});
@ -178,8 +194,7 @@ void uf::debug::drawPlane( pod::Plane plane, pod::Transform<> transform ) {
uf::debug::drawLine( p0, p2 );
uf::debug::drawLine( p1, p3 );
}
*/
void uf::debug::drawTriangle( pod::Triangle tri, pod::Transform<> transform ) {
void uf::debug::drawShape( const pod::Triangle& tri, const pod::Transform<>& transform ) {
pod::Vector3f v0 = uf::transform::apply(transform, tri.points[0]);
pod::Vector3f v1 = uf::transform::apply(transform, tri.points[1]);
pod::Vector3f v2 = uf::transform::apply(transform, tri.points[2]);
@ -189,37 +204,42 @@ void uf::debug::drawTriangle( pod::Triangle tri, pod::Transform<> transform ) {
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;
// to-do: bin by descriptor instead of one global set
graphic.descriptor.depth.test = false;
graphic.descriptor.depth.test = uf::physics::settings.debugDraw.depthTest;
graphic.descriptor.depth.write = false;
graphic.descriptor.renderTarget = 1; // "forward";
graphic.descriptor.topology = uf::renderer::enums::PrimitiveTopology::LINE_LIST;
@ -243,12 +263,123 @@ void uf::debug::draw( float dt ) {
}
graphic.initialize();
graphic.initializeMesh( mesh );
UF_MSG_DEBUG("Initialized graphic");
graphic.initializeMesh( impl::lineMesh );
} else {
bool rebuild = graphic.updateMesh( mesh );
bool rebuild = graphic.updateMesh( impl::lineMesh );
if ( rebuild ) uf::renderer::states::rebuild = true; // to-do: rebuild the defer mode only
}
}
impl::lines.clear();
void uf::debug::drawText( const uf::stl::string& string, const pod::Vector3f& position, const pod::Vector4f& color ) {
impl::texts.emplace_back( impl::Text{ string, position, color } );
}
void uf::debug::drawTexts( float dt ) {
if ( impl::texts.empty() ) return;
// double buffer
STATIC_THREAD_LOCAL(uf::stl::vector<impl::Text>, texts);
std::swap( impl::texts, texts );
auto& scene = uf::scene::getCurrentScene();
auto& controller = scene.getController();
auto& camera = scene.getCamera( controller );
auto& transform = camera.getTransform();
uf::stl::vector<pod::GlyphBox> textLayout;
// pre-init with ASCII characters
if ( !impl::textAtlas.generated() ) {
uf::stl::string ascii = "";
for ( char c = 32; c < 127; ++c ) ascii += c;
auto tokens = uf::glyph::parseTextTokens( ascii, {0,0,0,0} );
auto layout = uf::glyph::calculateLayout( tokens, impl::textSettings );
for ( auto& g : layout ) {
g.box.x = 0;
g.box.y = 0;
g.box.w = 0;
g.box.h = 0;
textLayout.emplace_back( g );
}
}
for ( auto& text : texts ) {
auto tokens = uf::glyph::parseTextTokens( text.string, text.color );
auto layout = uf::glyph::calculateLayout( tokens, impl::textSettings );
auto world = pod::Vector4f{ text.position.x, text.position.y, text.position.z, 1.0f };
auto view = camera.getProjection() * camera.getView();
auto clip = uf::matrix::multiply( view, world );
if ( clip.w <= 0.0f ) continue;
auto ndc = pod::Vector3f{ clip.x, clip.y, clip.z } / clip.w;
float scale = 2.0f / clip.w;
if ( ndc.z < 0.0f || ndc.z > 1.0f ) continue;
for ( auto& g : layout ) {
g.box.x = g.box.x * scale + ndc.x;
g.box.y = g.box.y * scale + ndc.y;
g.box.z = ndc.z;
g.box.w *= scale;
g.box.h *= scale;
if ( (g.box.x + g.box.w < -1.0f) || (g.box.x > 1.0f) || (g.box.y + g.box.h < -1.0f) || (g.box.y > 1.0f) ) continue;
textLayout.emplace_back( g );
}
}
bool dirty = uf::glyph::generateAtlas( textLayout, impl::textSettings, impl::textAtlas );
uf::glyph::generateMesh( textLayout, impl::textSettings, impl::textAtlas, impl::textMesh );
impl::textMesh.generateIndirect();
auto& graphics = scene.getComponent<uf::renderer::Graphics>();
auto& graphic = graphics["immediate:texts"];
if ( !graphic.initialized ) {
graphic.device = &uf::renderer::device;
graphic.material.device = &uf::renderer::device;
graphic.descriptor.depth.test = uf::physics::settings.debugDraw.depthTest;
graphic.descriptor.depth.write = false;
graphic.descriptor.renderTarget = 1; // "forward";
graphic.descriptor.blend.enabled = true;
graphic.descriptor.cullMode = uf::renderer::enums::CullMode::NONE;
uf::stl::string vertexShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/base/textured/vert.spv");
uf::stl::string fragmentShaderFilename = uf::io::resolveURI(uf::io::root+"/shaders/base/textured/frag.spv");
graphic.material.metadata.autoInitializeUniformBuffers = false;
graphic.material.attachShader(vertexShaderFilename, uf::renderer::enums::Shader::VERTEX);
graphic.material.attachShader(fragmentShaderFilename, uf::renderer::enums::Shader::FRAGMENT);
graphic.material.metadata.autoInitializeUniformBuffers = true;
auto& storage = uf::graph::globalStorage ? uf::graph::storage : scene.getComponent<pod::Graph::Storage>();
// vertex shader
{
auto& shader = graphic.material.getShader("vertex");
shader.aliasBuffer( storage.buffers.camera );
}
// fragment shader
auto& texture = graphic.material.textures.emplace_back();
texture.loadFromImage( impl::textAtlas.getAtlas() );
graphic.initialize();
graphic.initializeMesh( impl::textMesh );
} else {
if ( dirty ) {
graphic.material.textures.clear();
auto& texture = graphic.material.textures.emplace_back();
texture.loadFromImage( impl::textAtlas.getAtlas() );
}
bool rebuild = graphic.updateMesh( impl::textMesh );
if ( rebuild ) uf::renderer::states::rebuild = true; // to-do: rebuild the defer mode only
}
}
void uf::debug::draw( float dt ) {
uf::debug::drawLines( dt );
uf::debug::drawTexts( dt );
}

View File

@ -1,38 +1,23 @@
#include <uf/utils/image/atlas.h>
#include <binpack2d/binpack2d.hpp>
#include <iostream>
#include <bit>
uf::Atlas::hash_t uf::Atlas::addImage( const uf::Image& _image, bool regenerate ) {
uf::Atlas::hash_t uf::Atlas::addImage( const uf::Image& image, const uf::Atlas::hash_t& hash ) {
size_t index = this->m_tiles.size();
auto hash = _image.getHash();
if ( this->m_tiles.count( hash ) > 0 ) return hash;
auto& tile = this->m_tiles[hash];
tile.image = _image;
tile.image = image;
tile.identifier.hash = hash;
tile.identifier.index = index;
if ( regenerate ) this->generate();
return hash;
}
uf::Atlas::hash_t uf::Atlas::addImage( uf::Image&& _image, bool regenerate ) {
size_t index = this->m_tiles.size();
auto hash = _image.getHash();
if ( this->m_tiles.count( hash ) > 0 ) return hash;
auto& tile = this->m_tiles[hash];
tile.image = _image;
tile.identifier.hash = hash;
tile.identifier.index = index;
if ( regenerate ) this->generate();
return hash;
}
uf::Atlas::hash_t uf::Atlas::addImage( const uint8_t* pointer, const pod::Vector2ui& size, std::size_t bpp, std::size_t channels, bool flip, bool regenerate ) {
uf::Image image;
image.loadFromBuffer( pointer, size, bpp, channels, flip );
return this->addImage( std::move( image ), regenerate );
uf::Atlas::hash_t uf::Atlas::addImage( const uf::Image& image ) {
return this->addImage( image, image.getHash() );
}
void uf::Atlas::generate( const uf::Atlas::images_t& images, float padding ) {
for ( auto& image : images ) this->addImage( image, true );
for ( auto& image : images ) this->addImage( image );
generate( padding );
}
void uf::Atlas::generate( float padding ) {
@ -59,27 +44,8 @@ void uf::Atlas::generate( float padding ) {
}
size_t tries = 16;
do {
{
// size_t area = largest.x * largest.y * this->m_tiles.size();
size_t side = std::sqrt( area ) * padding;
size = { side, side };
{
size.x--;
size.x |= size.x >> 1;
size.x |= size.x >> 2;
size.x |= size.x >> 4;
size.x |= size.x >> 8;
size.x |= size.x >> 16;
size.x++;
size.y--;
size.y |= size.y >> 1;
size.y |= size.y >> 2;
size.y |= size.y >> 4;
size.y |= size.y >> 8;
size.y |= size.y >> 16;
size.y++;
}
}
size_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 };
}

View File

@ -64,10 +64,12 @@ void impl::updateActivity( pod::PhysicsBody& body, float dt ) {
// returns an absolute transform while also allowing offsetting the collision body
// to-do: find a succinct way to explain this madness
pod::Transform<> impl::getTransform( const pod::PhysicsBody& body ) {
pod::Transform<> t;
t.position = body.offsetPosition;
t.orientation = body.offsetOrientation;
t.reference = body.transform;
pod::Transform<> t = {
.position = body.offsetPosition,
.orientation = body.offsetOrientation,
.scale = {1, 1, 1},
.reference = body.transform,
};
return uf::transform::flatten( t );
}
// get position of a body, uses bounds center or transform's position
@ -78,6 +80,7 @@ pod::Vector3f impl::getPosition( const pod::PhysicsBody& body, bool useTransform
// applies a transform
pod::Vector3f impl::apply( const pod::Transform<>& t, const pod::Vector3f& p ) {
return uf::transform::apply( t, p );
// return uf::quaternion::rotate( t.orientation, p * t.scale ) + t.position; // explicitly needed to copy or GCC breaks
}
// applies an inverse transform
pod::Vector3f impl::applyInverse( const pod::Transform<>& t, const pod::Vector3f& p ) {
@ -528,7 +531,7 @@ pod::TriangleWithNormal impl::fetchTriangle( const uf::Mesh& mesh, size_t triID,
if ( body.collider.type == pod::ShapeType::MESH || body.collider.type == pod::ShapeType::CONVEX_HULL ) {
FOR_EACH(3, {
tri.points[i] = uf::transform::apply( transform, tri.points[i] );
tri.points[i] = impl::apply( transform, tri.points[i] );
});
tri.normal = uf::quaternion::rotate( transform.orientation, tri.normal );
}
@ -612,10 +615,17 @@ pod::Vector3f impl::obbMax( const pod::OBB& obb ) {
}
// converts a min-max AABB to center-extents OBB
pod::OBB impl::aabbToObb( const pod::AABB& aabb ) {
#if OBB_EXTENT_CENTER
return pod::OBB{
.extent = impl::aabbExtent( aabb ),
.center = impl::aabbCenter( aabb ),
};
#else
return pod::OBB{
.center = impl::aabbCenter( aabb ),
.extent = impl::aabbExtent( aabb ),
};
#endif
}
// converts a center-extents OBB to min-max AABB
pod::AABB impl::obbToAabb( const pod::OBB& obb ) {
@ -655,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::quaternion::rotate(transform.orientation, box.center) + transform.position;
pod::Vector3f center = impl::apply( transform, box.center );
pod::Vector3f extent = impl::extentFromAxes( box, axes );
return { center - extent, center + extent };
@ -664,11 +674,11 @@ pod::AABB impl::transformAabbToWorld( const pod::AABB& aabb, const pod::Transfor
std::pair<pod::Vector3f, pod::Vector3f> impl::getCapsuleSegment( const pod::PhysicsBody& body ) {
const auto transform = impl::getTransform( body );
const auto& capsule = body.collider.capsule;
const pod::Vector3f up = uf::quaternion::rotate( transform.orientation, pod::Vector3f{0,1,0} );
const pod::Vector3f up = uf::quaternion::rotate( transform.orientation, capsule.up );
// segment defines the cylinder axis only (ignore spherical ends)
auto p1 = transform.position + up * capsule.halfHeight;
auto p2 = transform.position - up * capsule.halfHeight;
auto p1 = transform.position + up;
auto p2 = transform.position - up;
return { p1, p2 };
}
// computes the AABB for a given body
@ -706,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 },
@ -728,7 +752,7 @@ pod::AABB impl::transformAabbToLocal( const pod::AABB& box, const pod::Transform
};
FOR_EACH(8, {
auto local = uf::transform::apply( inv, corners[i] );
auto local = impl::applyInverse( transform, corners[i] );
out.min = uf::vector::min( out.min, local );
out.max = uf::vector::max( out.max, local );
});

View File

@ -14,11 +14,8 @@ void impl::solveBallSocketConstraint( pod::Constraint& constraint, float dt ) {
auto tA = impl::getTransform( a );
auto tB = impl::getTransform( b );
auto anchorA = joint.localAnchorA * tA.scale;
auto anchorB = joint.localAnchorB * tB.scale;
auto rA = uf::quaternion::rotate( tA.orientation, anchorA );
auto rB = uf::quaternion::rotate( tB.orientation, anchorB );
auto rA = uf::quaternion::rotate( tA.orientation, joint.localAnchorA * tA.scale );
auto rB = uf::quaternion::rotate( tB.orientation, joint.localAnchorB * tB.scale );
pod::Matrix3f K = {};
pod::Vector3f axes[3] = { {1,0,0}, {0,1,0}, {0,0,1} };
@ -56,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;
@ -70,8 +67,8 @@ void impl::drawBallSocket( const pod::Constraint& constraint ) {
auto tA = impl::getTransform( *constraint.a );
auto tB = impl::getTransform( *constraint.b );
auto pA = uf::transform::apply( tA, joint.localAnchorA );
auto pB = uf::transform::apply( tB, joint.localAnchorB );
auto pA = impl::apply( tA, joint.localAnchorA );
auto pB = impl::apply( tB, joint.localAnchorB );
uf::debug::drawLine( tA.position, pA );
uf::debug::drawLine( tB.position, pB );

View File

@ -150,8 +150,8 @@ pod::Constraint& uf::physics::constrainConeTwist( pod::Constraint& constraint, c
auto& joint = constraint.coneTwist;
constraint.type = pod::ConstraintType::CONE_TWIST;
joint.localAnchorA = uf::transform::applyInverse( tA, p );
joint.localAnchorB = uf::transform::applyInverse( tB, p );
joint.localAnchorA = impl::applyInverse( tA, p );
joint.localAnchorB = impl::applyInverse( tB, p );
joint.accumulatedImpulse = {};
joint.accumulatedAngularImpulse = {};
@ -176,8 +176,8 @@ void impl::drawConeTwist( const pod::Constraint& constraint ) {
auto tA = impl::getTransform( *constraint.a );
auto tB = impl::getTransform( *constraint.b );
auto pA = uf::transform::apply( tA, joint.localAnchorA );
auto pB = uf::transform::apply( tB, joint.localAnchorB );
auto pA = impl::apply( tA, joint.localAnchorA );
auto pB = impl::apply( tB, joint.localAnchorB );
auto taA = uf::quaternion::rotate( tA.orientation, joint.localTwistAxisA );
auto taB = uf::quaternion::rotate( tB.orientation, joint.localTwistAxisB );

View File

@ -104,8 +104,8 @@ void impl::computeLocalManifold( pod::Manifold& manifold ) {
auto tB = impl::getTransform( b );
for ( auto& c : manifold.points ) {
c.localA = uf::transform::applyInverse( tA, c.point - c.normal * (c.penetration * 0.5f) );
c.localB = uf::transform::applyInverse( tB, c.point + c.normal * (c.penetration * 0.5f) );
c.localA = impl::applyInverse( tA, c.point - c.normal * (c.penetration * 0.5f) );
c.localB = impl::applyInverse( tB, c.point + c.normal * (c.penetration * 0.5f) );
}
}
@ -243,8 +243,8 @@ void impl::retrieveManifold( pod::Manifold& current, const pod::Manifold& previo
float distSqThresh = distanceThreshold * distanceThreshold;
for ( const auto& oldContact : previous.points ) {
// reproject point according to current transform
auto worldA = uf::transform::apply( tA, oldContact.localA );
auto worldB = uf::transform::apply( tB, oldContact.localB );
auto worldA = impl::apply( tA, oldContact.localA );
auto worldB = impl::apply( tB, oldContact.localB );
auto delta = worldB - worldA;
auto normal = current.points.empty() ? oldContact.normal : current.points[0].normal;

View File

@ -11,11 +11,8 @@ void impl::solveDistanceConstraint( pod::Constraint& constraint, float dt ) {
auto tA = impl::getTransform( a );
auto tB = impl::getTransform( b );
auto anchorA = joint.localAnchorA * tA.scale;
auto anchorB = joint.localAnchorB * tB.scale;
auto rA = uf::quaternion::rotate( tA.orientation, anchorA );
auto rB = uf::quaternion::rotate( tB.orientation, anchorB );
auto rA = uf::quaternion::rotate( tA.orientation, joint.localAnchorA * tA.scale );
auto rB = uf::quaternion::rotate( tB.orientation, joint.localAnchorB * tB.scale );
auto pA = tA.position + rA;
auto pB = tB.position + rB;
@ -48,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;
@ -65,8 +62,8 @@ void impl::drawDistance( const pod::Constraint& constraint ) {
auto tA = impl::getTransform( *constraint.a );
auto tB = impl::getTransform( *constraint.b );
auto pA = uf::transform::apply( tA, joint.localAnchorA );
auto pB = uf::transform::apply( tB, joint.localAnchorB );
auto pA = impl::apply( tA, joint.localAnchorA );
auto pB = impl::apply( tB, joint.localAnchorB );
uf::debug::drawLine( tA.position, pA );
uf::debug::drawLine( tB.position, pB );

View File

@ -63,8 +63,8 @@ pod::Constraint& uf::physics::constrainHinge( pod::Constraint& constraint, const
auto& joint = constraint.hinge;
constraint.type = pod::ConstraintType::HINGE;
joint.localAnchorA = uf::transform::applyInverse( tA, p );
joint.localAnchorB = uf::transform::applyInverse( tB, p );
joint.localAnchorA = impl::applyInverse( tA, p );
joint.localAnchorB = impl::applyInverse( tB, p );
joint.accumulatedImpulse = {};
joint.localAxisA = uf::quaternion::rotate( uf::quaternion::inverse( tA.orientation ), axis );
joint.localAxisB = uf::quaternion::rotate( uf::quaternion::inverse( tB.orientation ), axis );
@ -80,8 +80,8 @@ void impl::drawHinge( const pod::Constraint& constraint ) {
auto tA = impl::getTransform( *constraint.a );
auto tB = impl::getTransform( *constraint.b );
auto pA = uf::transform::apply( tA, joint.localAnchorA );
auto pB = uf::transform::apply( tB, joint.localAnchorB );
auto pA = impl::apply( tA, joint.localAnchorA );
auto pB = impl::apply( tB, joint.localAnchorB );
auto aA = uf::quaternion::rotate( tA.orientation, joint.localAxisA );
auto aB = uf::quaternion::rotate( tB.orientation, joint.localAxisB );

View File

@ -12,11 +12,8 @@ void impl::solveSliderConstraint( pod::Constraint& constraint, float dt ) {
auto tA = impl::getTransform( a );
auto tB = impl::getTransform( b );
auto anchorA = joint.localAnchorA * tA.scale;
auto anchorB = joint.localAnchorB * tB.scale;
auto rA = uf::quaternion::rotate( tA.orientation, anchorA );
auto rB = uf::quaternion::rotate( tB.orientation, anchorB );
auto rA = uf::quaternion::rotate( tA.orientation, joint.localAnchorA * tA.scale );
auto rB = uf::quaternion::rotate( tB.orientation, joint.localAnchorB * tB.scale );
auto pA = tA.position + rA;
auto pB = tB.position + rB;
@ -118,8 +115,8 @@ pod::Constraint& uf::physics::constrainSlider( pod::Constraint& constraint, cons
auto& joint = constraint.slider;
constraint.type = pod::ConstraintType::SLIDER;
joint.localAnchorA = uf::transform::applyInverse( tA, p );
joint.localAnchorB = uf::transform::applyInverse( tB, p );
joint.localAnchorA = impl::applyInverse( tA, p );
joint.localAnchorB = impl::applyInverse( tB, p );
joint.localAxisA = uf::quaternion::rotate( invqA, axis );
joint.localAxisB = uf::quaternion::rotate( invqB, axis );

View File

@ -11,11 +11,8 @@ void impl::solveSpringConstraint( pod::Constraint& constraint, float dt ) {
auto tA = impl::getTransform( a );
auto tB = impl::getTransform( b );
auto anchorA = joint.localAnchorA * tA.scale;
auto anchorB = joint.localAnchorB * tB.scale;
auto rA = uf::quaternion::rotate( tA.orientation, anchorA );
auto rB = uf::quaternion::rotate( tB.orientation, anchorB );
auto rA = uf::quaternion::rotate( tA.orientation, joint.localAnchorA * tA.scale );
auto rB = uf::quaternion::rotate( tB.orientation, joint.localAnchorB * tB.scale );
auto worldAnchorA = tA.position + rA;
auto worldAnchorB = tB.position + rB;
@ -52,8 +49,8 @@ pod::Constraint& uf::physics::constrainSpring( pod::Constraint& constraint, cons
auto& joint = constraint.spring;
constraint.type = pod::ConstraintType::SPRING;
joint.localAnchorA = uf::transform::applyInverse( tA, pA );
joint.localAnchorB = uf::transform::applyInverse( tB, pB );
joint.localAnchorA = impl::applyInverse( tA, pA );
joint.localAnchorB = impl::applyInverse( tB, pB );
joint.restLength = uf::vector::distance( pB, pA );
joint.stiffness = stiffness;

View File

@ -58,8 +58,8 @@ pod::Constraint& uf::physics::constrainWeld( pod::Constraint& constraint, const
auto& joint = constraint.weld;
constraint.type = pod::ConstraintType::WELD;
joint.localAnchorA = uf::transform::applyInverse( tA, p );
joint.localAnchorB = uf::transform::applyInverse( tB, p );
joint.localAnchorA = impl::applyInverse( tA, p );
joint.localAnchorB = impl::applyInverse( tB, p );
joint.localAxisA = uf::quaternion::rotate( invqA, axis );
joint.localAxisB = uf::quaternion::rotate( invqB, axis );

View File

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

View File

@ -295,7 +295,7 @@ void uf::physics::step( pod::World& world, float dt ) {
parentTransform = *transformsMap[originalTransform->reference];
}
transform.position = uf::transform::applyInverse( parentTransform, flattenedTransform.position );
transform.position = impl::applyInverse( parentTransform, flattenedTransform.position );
transform.orientation = uf::quaternion::multiply(
uf::quaternion::inverse( parentTransform.orientation ),
flattenedTransform.orientation
@ -377,7 +377,7 @@ void uf::physics::updateInertia( pod::PhysicsBody& body ) {
} break;
case pod::ShapeType::CAPSULE: {
float r = body.collider.capsule.radius;
float h = body.collider.capsule.halfHeight * 2.0f; // full cyl height
float h = uf::vector::norm( body.collider.capsule.up ) * 2.0f; // full cyl height
float Ixx = 0.25f * mass * r * r + (1.0f/12.0f) * mass * h * h;
float Iyy = 0.5f * mass * r * r;

View File

@ -81,7 +81,21 @@ 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 );
uf::debug::drawAabb( aabb, transform );
#if 1
uf::debug::drawShape( aabb, transform );
#else
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] );
uf::debug::drawLine( corners[4], corners[5] ); uf::debug::drawLine( corners[5], corners[6] );
uf::debug::drawLine( corners[6], corners[7] ); uf::debug::drawLine( corners[7], corners[4] );
uf::debug::drawLine( corners[0], corners[4] ); uf::debug::drawLine( corners[1], corners[5] );
uf::debug::drawLine( corners[2], corners[6] ); uf::debug::drawLine( corners[3], corners[7] );
#endif
}
pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::AABB& aabb ) {

View File

@ -109,8 +109,8 @@ bool impl::capsuleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, po
void impl::drawCapsule( const pod::PhysicsBody& body ) {
const auto& capsule = body.collider.capsule;
auto transform = impl::getTransform(body);
#if 0
uf::debug::drawCapsule( capsule, transform );
#if 1
uf::debug::drawShape( capsule, transform );
#else
auto [p1, p2] = impl::getCapsuleSegment(body);
const int segments = 16;

View File

@ -47,7 +47,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di
outCount = 3;
bool hasTransform = ( body.transform != nullptr );
FOR_EACH(3, {
outPoly[i] = hasTransform ? uf::transform::apply( transform, body.collider.triangle.points[i] ) : body.collider.triangle.points[i];
outPoly[i] = hasTransform ? impl::apply( transform, body.collider.triangle.points[i] ) : body.collider.triangle.points[i];
});
} break;
case pod::ShapeType::AABB: {
@ -81,7 +81,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di
if (n.z < 0) std::swap(outPoly[1], outPoly[3]);
}
FOR_EACH(4, {
outPoly[i] = uf::transform::apply(transform, outPoly[i]);
outPoly[i] = impl::apply(transform, outPoly[i]);
});
} break;
case pod::ShapeType::OBB: {
@ -115,7 +115,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di
if (n.z < 0) std::swap(outPoly[1], outPoly[3]);
}
FOR_EACH(4, {
outPoly[i] = uf::transform::apply(transform, outPoly[i]);
outPoly[i] = impl::apply(transform, outPoly[i]);
});
} break;
case pod::ShapeType::SPHERE: {
@ -123,9 +123,9 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di
outPoly[0] = transform.position + uf::vector::normalize( dir ) * body.collider.sphere.radius;
} break;
case pod::ShapeType::CAPSULE: {
auto up = uf::quaternion::rotate( transform.orientation, pod::Vector3f{0,1,0} );
auto p1 = transform.position + up * body.collider.capsule.halfHeight;
auto p2 = transform.position - up * body.collider.capsule.halfHeight;
auto up = uf::quaternion::rotate( transform.orientation, body.collider.capsule.up );
auto p1 = transform.position + up;
auto p2 = transform.position - up;
if ( std::fabs( uf::vector::dot( dir, up ) ) < 0.01f ) {
outCount = 2;
@ -165,7 +165,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di
}
outCount = 3;
FOR_EACH(3, {
outPoly[i] = uf::transform::apply(transform, bestTri.points[i]);
outPoly[i] = impl::apply(transform, bestTri.points[i]);
});
} break;
// unsupported, fallback to single contact point

View File

@ -19,7 +19,7 @@ pod::Vector3f impl::support( const pod::PhysicsBody& body, const pod::Vector3f&
( localDir.y >= 0.0f ) ? box.max.y : box.min.y,
( localDir.z >= 0.0f ) ? box.max.z : box.min.z
};
return uf::transform::apply( transform, localPt );
return impl::apply( transform, localPt );
} break;
case pod::ShapeType::SPHERE: {
return transform.position + uf::vector::normalize( dir ) * body.collider.sphere.radius;
@ -37,9 +37,9 @@ pod::Vector3f impl::support( const pod::PhysicsBody& body, const pod::Vector3f&
return basePoint + tangent * 100000.0f;
} break;
case pod::ShapeType::CAPSULE: {
auto up = uf::quaternion::rotate( transform.orientation, pod::Vector3f{0,1,0} );
auto p1 = transform.position + up * body.collider.capsule.halfHeight;
auto p2 = transform.position - up * body.collider.capsule.halfHeight;
auto up = uf::quaternion::rotate( transform.orientation, body.collider.capsule.up );
auto p1 = transform.position + up;
auto p2 = transform.position - up;
auto end = ( uf::vector::dot( dir, p1 ) > uf::vector::dot( dir, p2 ) ) ? p1 : p2; // get closest end
return end + uf::vector::normalize( dir ) * body.collider.capsule.radius;
}
@ -77,7 +77,7 @@ pod::Vector3f impl::support( const pod::PhysicsBody& body, const pod::Vector3f&
}
}
return uf::transform::apply( transform, furthestVertex );
return impl::apply( transform, furthestVertex );
} break;
default: {

View File

@ -92,6 +92,16 @@ void impl::drawHull( const pod::PhysicsBody& body ) {
for ( size_t i = 0; i < totalTriangles; ++i ) {
auto tri = uf::mesh::fetchTriangle( *meshData, i );
uf::debug::drawTriangle( tri, transform );
#if 1
uf::debug::drawShape( tri, transform );
#else
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 );
uf::debug::drawLine( v2, v0 );
#endif
}
}

View File

@ -200,7 +200,17 @@ void impl::drawMesh( const pod::PhysicsBody& body ) {
for ( size_t i = 0; i < totalTriangles; ++i ) {
auto tri = uf::mesh::fetchTriangle( *meshData, i );
uf::debug::drawTriangle( tri, transform );
#if 1
uf::debug::drawShape( tri, transform );
#else
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 );
uf::debug::drawLine( v2, v0 );
#endif
}
}

View File

@ -129,8 +129,8 @@ bool impl::obbObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Ma
auto boxA = a.collider.obb;
auto boxB = b.collider.obb;
boxA.center = uf::quaternion::rotate(tA.orientation, boxA.center) + tA.position;
boxB.center = uf::quaternion::rotate(tB.orientation, boxB.center) + tB.position;
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::quaternion::rotate(tA.orientation, boxA.center) + tA.position;
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::quaternion::rotate(tA.orientation, box.center) + tA.position;
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::quaternion::rotate(tA.orientation, box.center) + tA.position;
box.center = impl::apply( tA, box.center );
pod::Vector3f axesA[3];
impl::boxAxes( axesA, tA );
@ -242,15 +242,20 @@ 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::quaternion::rotate(tA.orientation, box.center) + tA.position;
box.center = impl::apply( tA, box.center );
pod::Vector3f axesA[3];
impl::boxAxes( axesA, tA );
auto [p1, p2] = impl::getCapsuleSegment( b );
pod::Vector3f cB = (p1 + p2) * 0.5f;
pod::Vector3f capAxis = uf::vector::normalize(p2 - p1);
float halfHeight = b.collider.capsule.halfHeight;
pod::Vector3f segmentHalf = (p2 - p1) * 0.5f;
float halfHeight = uf::vector::norm(segmentHalf);
pod::Vector3f capAxis = (halfHeight > EPS2) ? (segmentHalf / halfHeight) : pod::Vector3f{0, 1, 0};
float radius = b.collider.capsule.radius;
float minOverlap = FLT_MAX;
@ -264,7 +269,8 @@ bool impl::obbCapsule( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod
float pA = uf::vector::dot(box.center, n);
float rA = impl::projectExtents( box, n, axesA );
float pB = uf::vector::dot(cB, n);
float rB = halfHeight * std::fabs(uf::vector::dot(capAxis, n)) + radius;
float rB = std::fabs(uf::vector::dot(segmentHalf, n)) + radius;
float dist = std::fabs(pB - pA);
float overlap = (rA + rB) - dist;
@ -301,7 +307,20 @@ 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);
uf::debug::drawObb( obb, transform );
#if 1
uf::debug::drawShape( obb, transform );
#else
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] );
uf::debug::drawLine( corners[4], corners[5] ); uf::debug::drawLine( corners[5], corners[6] );
uf::debug::drawLine( corners[6], corners[7] ); uf::debug::drawLine( corners[7], corners[4] );
uf::debug::drawLine( corners[0], corners[4] ); uf::debug::drawLine( corners[1], corners[5] );
uf::debug::drawLine( corners[2], corners[6] ); uf::debug::drawLine( corners[3], corners[7] );
#endif
}
pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::OBB& obb ) {

View File

@ -70,8 +70,8 @@ void impl::drawPlane( const pod::PhysicsBody& body ) {
const auto& plane = body.collider.plane;
auto transform = impl::getTransform(body);
#if 0
uf::debug::drawPlane( plane, transform );
#if 1
uf::debug::drawShape( plane, transform );
#else
pod::Vector3f right = uf::quaternion::rotate(transform.orientation, pod::Vector3f{1, 0, 0});
pod::Vector3f forward = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0, 0, 1});

View File

@ -82,7 +82,7 @@ bool impl::rayObb( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQu
auto tA = impl::getTransform( body );
pod::Ray localRay;
localRay.origin = uf::transform::applyInverse( tA, ray.origin );
localRay.origin = impl::applyInverse( tA, ray.origin );
localRay.direction = uf::quaternion::rotate( uf::quaternion::inverse(tA.orientation), ray.direction );
auto box = impl::obbToAabb( body.collider.obb );
@ -235,7 +235,7 @@ bool impl::rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue
const auto transform = impl::getTransform( body );
pod::Ray ray;
ray.origin = uf::transform::applyInverse( transform, r.origin );
ray.origin = impl::applyInverse( transform, r.origin );
ray.direction = uf::quaternion::rotate( uf::quaternion::inverse( transform.orientation ), r.direction );
thread_local uf::stl::vector<pod::BVH::index_t> candidates;
@ -253,7 +253,7 @@ bool impl::rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue
auto n = impl::triangleNormal( tri );
// push back to world
auto p = uf::transform::apply( transform, l);
auto p = impl::apply( transform, l);
n = uf::quaternion::rotate( transform.orientation, n );
rayHit.hit = true;
@ -273,7 +273,7 @@ bool impl::rayHull( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue
const auto transform = impl::getTransform( body );
pod::Ray ray;
ray.origin = uf::transform::applyInverse( transform, r.origin );
ray.origin = impl::applyInverse( transform, r.origin );
ray.direction = uf::quaternion::rotate( uf::quaternion::inverse( transform.orientation ), r.direction );
thread_local uf::stl::vector<pod::BVH::index_t> candidates;
@ -292,7 +292,7 @@ bool impl::rayHull( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue
rayHit.hit = true;
rayHit.body = &body;
rayHit.contact.point = uf::transform::apply( transform, ray.origin + ray.direction * t );
rayHit.contact.point = impl::apply( transform, ray.origin + ray.direction * t );
rayHit.contact.normal = uf::quaternion::rotate( transform.orientation, normal );
rayHit.contact.penetration = t;
}
@ -326,6 +326,7 @@ pod::RayQuery uf::physics::rayCast( const pod::Ray& ray, const pod::World& world
impl::queryBVH( dynamicBvh, ray, candidates );
if ( uf::physics::settings.useSplitBvhs ) impl::queryBVH( staticBvh, ray, candidates );
for ( auto i : candidates ) {
auto* b = bodies[i];

View File

@ -71,7 +71,34 @@ 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);
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;
for ( auto i = 0; i < segments; ++i ) {
float theta1 = i * angleIncrement;
float theta2 = (i + 1) * angleIncrement;
float c1 = std::cos(theta1) * sphere.radius;
float s1 = std::sin(theta1) * sphere.radius;
float c2 = std::cos(theta2) * sphere.radius;
float s2 = std::sin(theta2) * sphere.radius;
pod::Vector3f xy1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, s1, 0.0f});
pod::Vector3f xy2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, s2, 0.0f});
pod::Vector3f xz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c1, 0.0f, s1});
pod::Vector3f xz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{c2, 0.0f, s2});
pod::Vector3f yz1 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c1, s1});
pod::Vector3f yz2 = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0.0f, c2, s2});
uf::debug::drawLine( transform.position + xy1, transform.position + xy2 );
uf::debug::drawLine( transform.position + xz1, transform.position + xz2 );
uf::debug::drawLine( transform.position + yz1, transform.position + yz2 );
}
#endif
}
pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::Sphere& sphere ) {

View File

@ -190,7 +190,7 @@ bool impl::triangleObb( const pod::TriangleWithNormal& tri, const pod::PhysicsBo
auto transform = impl::getTransform( body );
auto box = body.collider.obb;
box.center = uf::quaternion::rotate(transform.orientation, box.center) + transform.position;
box.center = impl::apply( transform, box.center );
pod::Vector3f axes[3];
impl::boxAxes( axes, transform );
@ -358,7 +358,17 @@ 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);
uf::debug::drawTriangle( tri, transform );
#if 1
uf::debug::drawShape( tri, transform );
#else
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 );
uf::debug::drawLine( v2, v0 );
#endif
}
pod::PhysicsBody& uf::physics::initialize( pod::PhysicsBody& body, const pod::TriangleWithNormal& tri ) {

View File

@ -21,8 +21,8 @@ void impl::solvePositions( uf::stl::vector<pod::Manifold>& manifolds, float dt,
auto ctxA = impl::solverBodyContext( a );
auto ctxB = impl::solverBodyContext( b );
auto rA = uf::quaternion::rotate( tA.orientation, c.localA );
auto rB = uf::quaternion::rotate( tB.orientation, c.localB );
auto rA = uf::quaternion::rotate( tA.orientation, c.localA * tA.scale );
auto rB = uf::quaternion::rotate( tB.orientation, c.localB * tB.scale );
auto pA = tA.position + rA;
auto pB = tB.position + rB;

View File

@ -172,8 +172,8 @@ TEST(SpherePlane_NoCollision, {
TEST(CapsuleCapsule_Collision, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, 1.0f}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f, 1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0,1,0}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f, pod::Vector3f{0,1,0}}, 1.0f);
bodyA.transform->position = {0,0,0};
bodyB.transform->position = {0.8f,0,0}; // slight overlap
@ -384,7 +384,7 @@ TEST(PhysicsStep_StaticFriction_Slips, {
TEST(CapsulePlane_Slope_StaticHold, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f,1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0, 1, 0}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Plane{{0,1,1},0.0f}, 0.0f);
// Place capsule on slope
@ -402,7 +402,7 @@ TEST(CapsulePlane_Slope_StaticHold, {
TEST(CapsulePlane_Slope_Slip, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f,1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0, 1, 0}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Plane{{0,1,1},0.0f}, 0.0f);
bodyA.transform->position = {0,2,0};
@ -417,7 +417,7 @@ TEST(CapsulePlane_Slope_Slip, {
TEST(CapsulePlane_RestingContact, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, 1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0,1,0}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Plane{{0,1,0}, 0.0f}, 0.0f);
bodyA.transform->position = {0, 1.5f, 0}; // halfHeight=1, radius=0.5, so "foot" at y=0
@ -434,7 +434,7 @@ TEST(CapsuleAabb_RestingContact, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, 1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0,1,0}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::AABB{{-5, -1, -5},{5, 0, 5}}, 0.0f);
bodyA.transform->position = {0, 1.5f, 0};
@ -450,7 +450,7 @@ TEST(CapsulePlane_Settling, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, 1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0,1,0}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Plane{{0,1,0},0.0f}, 0.0f);
bodyA.transform->position = {0, 2.0f, 0}; // slightly above
@ -466,7 +466,7 @@ TEST(CapsulePlane_SlopeStaticFriction, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, 1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0,1,0}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Plane{{0,1,1},0.0f}, 0.0f); // 45° slope
bodyA.transform->position = {0, 3.0f, 0};
@ -484,7 +484,7 @@ TEST(CapsuleAabb_StepEdge, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, 1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0,1,0}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::AABB{{0,-1,-5},{5,0,5}}, 0.0f);
bodyA.transform->position = {0.25f, 1.5f, 0}; // Capsule foot half on, half off
@ -500,7 +500,7 @@ TEST(Diagnostic_CapsuleGrounding, {
uf::Object objA, objFloor;
// Capsule: radius 0.5, half-height 1.0 (total height 2.0 + end caps)
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, 1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0,1,0}}, 1.0f);
// Test toggle: try both AABB floors and Plane floors.
bool usePlane = true;
@ -519,7 +519,7 @@ TEST(CapsulePlane_ContactNormal, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, 1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0,1,0}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Plane{{0,1,0},0.0f}, 0.0f);
bodyA.transform->position = {0,1.5f,0};
@ -557,7 +557,7 @@ TEST(AabbPlane_RestingNoSink, {
TEST(CapsuleSphere_Collision, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f,1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0, 1, 0}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Sphere{0.5f}, 1.0f);
bodyA.transform->position = {0,0,0};
@ -575,7 +575,7 @@ TEST(CapsuleSphere_Collision, {
TEST(CapsuleSphere_NoCollision, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f,1.0f}, 1.0f);
auto& bodyA = uf::physics::create(world, objA, pod::Capsule{0.5f, pod::Vector3f{0, 1, 0}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Sphere{0.5f}, 1.0f);
bodyA.transform->position = {0,0,0};
@ -664,7 +664,7 @@ TEST(AabbCapsule_Collision, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::AABB{{-1,-1,-1},{1,1,1}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f,1.0f}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f, pod::Vector3f{0, 1, 0}}, 1.0f);
bodyA.transform->position = {0,0,0};
bodyB.transform->position = {0,0.5f,0}; // partially overlapping
@ -682,7 +682,7 @@ TEST(AabbCapsule_NoCollision, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::AABB{{-1,-1,-1},{1,1,1}}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f,1.0f}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f, pod::Vector3f{0, 1, 0}}, 1.0f);
bodyA.transform->position = {0,0,0};
bodyB.transform->position = {0,5,0};
@ -700,7 +700,7 @@ TEST(SphereCapsule_Collision, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Sphere{0.5f}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f,1.0f}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f, pod::Vector3f{0, 1, 0}}, 1.0f);
bodyA.transform->position = {0,0.0f,0};
bodyB.transform->position = {0,0.25f,0};
@ -718,7 +718,7 @@ TEST(SphereCapsule_NoCollision, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Sphere{0.5f}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f,1.0f}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f, pod::Vector3f{0, 1, 0}}, 1.0f);
bodyA.transform->position = {0,5,0};
bodyB.transform->position = {0,0,0};
@ -750,7 +750,7 @@ TEST(PlaneCapsule_Collision, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Plane{{0,1,0},0.0f}, 0.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f,1.0f}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f, pod::Vector3f{0, 1, 0}}, 1.0f);
bodyB.transform->position = {0,0.25f,0}; // foot intersecting
@ -767,7 +767,7 @@ TEST(PlaneCapsule_NoCollision, {
pod::World world;
uf::Object objA, objB;
auto& bodyA = uf::physics::create(world, objA, pod::Plane{{0,1,0},0.0f}, 0.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f,1.0f}, 1.0f);
auto& bodyB = uf::physics::create(world, objB, pod::Capsule{0.5f, pod::Vector3f{0, 1, 0}}, 1.0f);
bodyB.transform->position = {0,5,0}; // far above
@ -1161,7 +1161,7 @@ TEST(TriangleCapsule_Collision_Overlap, {
// Capsule aligned along Z axis, radius 0.2
pod::Capsule capsule;
capsule.radius = 0.2f;
capsule.halfHeight = 1.0f; // segment lengt * 0.5fh
capsule.up = pod::Vector3f{0,1,0} * 1.0f; // segment lengt * 0.5fh
// placed so capsule overlaps the tri plane
auto& bodyA = uf::physics::create(world, objA, tri, 0.0f);
auto& bodyB = uf::physics::create(world, objB, capsule, 0.0f);
@ -1191,7 +1191,7 @@ TEST(TriangleCapsule_Collision_NoOverlap, {
pod::Capsule capsule;
capsule.radius = 0.2f;
capsule.halfHeight = 1.0f * 0.5f;
capsule.up = pod::Vector3f{0,1,0} * 1.0f * 0.5f;
// place it well above the tri plane
auto& bodyA = uf::physics::create(world, objA, tri, 0.0f);
auto& bodyB = uf::physics::create(world, objB, capsule, 0.0f);
@ -1216,7 +1216,7 @@ TEST(TriangleCapsule_Collision_Tangent, {
pod::Capsule capsule;
capsule.radius = 0.5f;
capsule.halfHeight = 1.0f * 0.5f;
capsule.up = pod::Vector3f{0,1,0} * 1.0f * 0.5f;
auto& bodyA = uf::physics::create(world, objA, tri, 0.0f);
auto& bodyB = uf::physics::create(world, objB, capsule, 0.0f);
// place the capsule so its sphere-bottom just kisses the triangle
@ -1248,7 +1248,7 @@ TEST(TriangleCapsule_Collision_EdgeAlignment, {
pod::Capsule capsule;
capsule.radius = 0.1f;
capsule.halfHeight = 2.0f; // segment tall and skinn * 0.5fy
capsule.up = pod::Vector3f{0,1,0} * 2.0f; // segment tall and skinn * 0.5fy
auto& bodyA = uf::physics::create(world, objA, tri, 0.0f);
auto& bodyB = uf::physics::create(world, objB, capsule, 0.0f);
// lay the capsule along edge (x-axis direction near tris base)

View File

@ -228,15 +228,26 @@ uf::Mesh uf::Mesh::expand( bool interleaved ) {
return res;
}
void uf::Mesh::eraseAttribute( uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute ) {
for ( size_t i = 0; i < input.attributes.size(); ++i ) if ( input.attributes[i].descriptor == attribute.descriptor ) return eraseAttribute( input, i );
void uf::Mesh::clearAttribute( uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute ) {
for ( size_t i = 0; i < input.attributes.size(); ++i ) if ( input.attributes[i].descriptor == attribute.descriptor ) return clearAttribute( input, i );
}
void uf::Mesh::eraseAttribute( uf::Mesh::Input& input, size_t i ) {
void uf::Mesh::clearAttribute( uf::Mesh::Input& input, size_t i ) {
UF_ASSERT( !isInterleaved( input ) ); // can't be assed to de-interleave, erase, and then interleave again
auto attribute = input.attributes[i];
buffers[attribute.buffer].clear();
}
void uf::Mesh::clear() {
for ( size_t i = 0; i < vertex.attributes.size(); ++i ) clearAttribute( vertex, i );
for ( size_t i = 0; i < index.attributes.size(); ++i ) clearAttribute( index, i );
for ( size_t i = 0; i < instance.attributes.size(); ++i ) clearAttribute( instance, i );
for ( size_t i = 0; i < indirect.attributes.size(); ++i ) clearAttribute( indirect, i );
vertex.count = 0;
index.count = 0;
instance.count = 0;
indirect.count = 0;
}
void uf::Mesh::generateIndirect() {
if ( index.count == 0 ) generateIndices();

View File

@ -0,0 +1,329 @@
#include <uf/utils/text/glyph.h>
#include <uf/utils/text/glyph_.h>
#if UF_USE_OPENGL
#define EXT_COLOR_FLOATS 0
#else
#define EXT_COLOR_FLOATS 1
#endif
namespace {
struct {
#if UF_ENV_DREAMCAST
pod::Vector2ui size = { 640, 480 };
#else
pod::Vector2ui size = { 1920, 1080 };
#endif
} defaults;
}
namespace {
struct GlyphVertex {
pod::Vector3f position;
pod::Vector2f uv;
#if EXT_COLOR_FLOATS
pod::Vector4f color;
#else
pod::Vector4b color;
#endif
static uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
};
}
UF_VERTEX_DESCRIPTOR(GlyphVertex,
UF_VERTEX_DESCRIPTION(GlyphVertex, R32G32B32_SFLOAT, position)
UF_VERTEX_DESCRIPTION(GlyphVertex, R32G32_SFLOAT, uv)
#if EXT_COLOR_FLOATS
UF_VERTEX_DESCRIPTION(GlyphVertex, R32G32B32A32_SFLOAT, color)
#else
UF_VERTEX_DESCRIPTION(GlyphVertex, R8G8B8A8_UNORM, color)
#endif
)
namespace {
struct {
#if UF_USE_FREETYPE
ext::freetype::Glyph glyph;
uf::stl::unordered_map<uf::stl::string, uf::stl::unordered_map<size_t, uf::Glyph>> cache;
#else
char glyph;
uf::stl::unordered_map<uf::stl::string, uf::stl::unordered_map<size_t, char>> cache;
#endif
} glyphs;
float hexToFloat( const uf::stl::string& str ) {
int value;
uf::stl::stringstream stream;
stream << str;
stream >> std::hex >> value;
return value / 255.0f;
}
// default to left
pod::Vector2f parseAnchor( const uf::stl::string& anchor, const pod::Vector2f& def = {0.0f, 0.0f} ) {
if ( anchor == "top-center" || anchor == "top" ) return {0.5f, 0.0f};
if ( anchor == "top-left" ) return {0.0f, 0.0f};
if ( anchor == "top-right" ) return {1.0f, 0.0f};
if ( anchor == "center" ) return {0.5f, 0.5f};
if ( anchor == "center-left" || anchor == "left" ) return {0.0f, 0.5f};
if ( anchor == "center-right" || anchor == "right" ) return {1.0f, 0.5f};
if ( anchor == "bottom-center" || anchor == "bottom" ) return {0.5f, 1.0f};
if ( anchor == "bottom-left" ) return {0.0f, 1.0f};
if ( anchor == "bottom-right" ) return {1.0f, 1.0f};
return def;
}
}
size_t uf::glyph::hashSettings( uint64_t c, const pod::GlyphSettings& metadata ) {
size_t seed{};
uf::hash( seed, c, metadata.padding[0], metadata.padding[1], metadata.spread, metadata.size, metadata.font, metadata.sdf );
return seed;
}
size_t uf::glyph::hashSettings( const uf::stl::string& c, const pod::GlyphSettings& metadata ) {
size_t seed{};
uf::hash( seed, c, metadata.padding[0], metadata.padding[1], metadata.spread, metadata.size, metadata.font, metadata.sdf );
return seed;
}
// parses a text for special tokens, associating strings with color
// should maybe be utf8, but in theory it shouldn't matter since tags are ASCII
uf::stl::vector<pod::TextToken> uf::glyph::parseTextTokens( const uf::stl::string& text, const pod::Vector4f& color ) {
uf::stl::vector<pod::TextToken> tokens;
auto tagLength = 12; // hard-coded cringe
bool colorChanged = false;
size_t currentPos = 0;
size_t textLength = text.length();
pod::TextToken currentToken;
currentToken.color = color;
while ( currentPos < textLength ) {
size_t tagStart = text.find("${#", currentPos);
if ( tagStart == uf::stl::string::npos ) {
currentToken.text += text.substr(currentPos);
if ( !currentToken.text.empty() || colorChanged ) {
tokens.emplace_back(currentToken);
}
break;
}
if ( tagStart > currentPos ) {
currentToken.text += text.substr( currentPos, tagStart - currentPos ); // append everything up to the tag
tokens.emplace_back( currentToken ); // commit token
currentToken.text = ""; // reset text
}
// validate tag
if ( tagStart + tagLength <= textLength && text[tagStart + tagLength - 1] == '}' ) {
uf::stl::string rHex = text.substr(tagStart + 3, 2);
uf::stl::string gHex = text.substr(tagStart + 5, 2);
uf::stl::string bHex = text.substr(tagStart + 7, 2);
uf::stl::string aHex = text.substr(tagStart + 9, 2);
// change color
currentToken.color = { hexToFloat(rHex), hexToFloat(gHex), hexToFloat(bHex), hexToFloat(aHex) };
colorChanged = true;
// advance past the tag
currentPos = tagStart + tagLength;
} else {
currentToken.text += "${#"; // treat it as normal text
currentPos = tagStart + 3;
}
}
return tokens;
}
// compute the boxes for a given string and settings
uf::stl::vector<pod::GlyphBox> uf::glyph::calculateLayout( const uf::stl::vector<pod::TextToken>& tokens, const pod::GlyphSettings& metadata ) {
uf::stl::vector<pod::GlyphBox> layout;
auto& glyphsCache = ::glyphs.cache[metadata.font];
if ( glyphsCache.empty() ) {
ext::freetype::initialize( ::glyphs.glyph, uf::io::root + "/fonts/" + metadata.font );
}
pod::Vector2f anchor = ::parseAnchor( metadata.alignment );
pod::Vector2f cursor = { 0.0f, 0.0f };
float maxTextWidth = 0.0f;
float maxTextHeight = 0.0f;
float tallestGlyphY = 0.0f;
float averageTabWidth = 0.0f;
float totalWidth = 0.0f;
int charCount = 0;
// generate glyph and line height
for ( const auto& token : tokens ) {
std::u8string str(token.text.begin(), token.text.end());
for ( uint64_t c : str ) {
if ( c == '\n' || c == '\t' ) continue; // special characters
auto key = uf::glyph::hashSettings(c, metadata);
auto& glyph = glyphsCache[key];
// generate glyph
if ( !glyph.generated() ) {
glyph.setPadding({ metadata.padding[0], metadata.padding[1] });
glyph.setSpread(metadata.spread);
glyph.useSdf(metadata.sdf);
glyph.generate(::glyphs.glyph, c, metadata.size);
}
tallestGlyphY = std::max(tallestGlyphY, (float) glyph.getSize().y);
totalWidth += glyph.getSize().x; // should probably be reset on new-line to find the widest line
charCount++;
}
}
if ( charCount > 0 ) averageTabWidth = (totalWidth / charCount) * 4.0f;
cursor.y = tallestGlyphY;
// calculate positions
for ( const auto& token : tokens ) {
std::u8string str( token.text.begin(), token.text.end() );
for ( uint64_t c : str ) {
// advance cursor on special characters
if ( c == '\n' ) {
cursor.y += tallestGlyphY;
cursor.x = 0;
continue;
} else if ( c == '\t' ) {
cursor.x = ((int)(cursor.x / averageTabWidth) + 1) * averageTabWidth;
continue;
} else if ( c == ' ' ) {
cursor.x += averageTabWidth / 4.0f;
continue;
}
// retrieve glyph
auto key = uf::glyph::hashSettings(c, metadata);
auto& glyph = glyphsCache[key];
auto& g = layout.emplace_back(pod::GlyphBox{
.box = {
.x = cursor.x + glyph.getBearing().x,
.y = cursor.y - glyph.getBearing().y,
.w = glyph.getSize().x,
.h = glyph.getSize().y,
.z = 0,
},
.color = token.color,
.code = c,
});
// advance cursor
cursor.x += glyph.getAdvance().x;
// advance bounding box
maxTextWidth = std::max(maxTextWidth, g.box.x + g.box.w);
maxTextHeight = std::max(maxTextHeight, g.box.y + g.box.h);
}
}
// calculate offset based on anchor
float offsetX = maxTextWidth * anchor.x;
float offsetY = maxTextHeight * anchor.y;
// adjust all glyphs for our offset
for ( auto& g : layout ) {
g.box.x -= offsetX;
g.box.y -= offsetY;
// normalize
g.box.x /= ::defaults.size.x;
g.box.w /= ::defaults.size.x;
g.box.y /= ::defaults.size.y;
g.box.h /= ::defaults.size.y;
}
return layout;
}
// generate the mesh and texture atlas
bool uf::glyph::generateAtlas( const uf::stl::vector<pod::GlyphBox>& layout, const pod::GlyphSettings& metadata, uf::Atlas& atlas ) {
bool dirty = false;
auto& cache = ::glyphs.cache[metadata.font];
#if UF_USE_FREETYPE
// generate atlas
for ( const auto& g : layout ) {
auto key = uf::glyph::hashSettings( g.code, metadata );
auto hash = std::to_string( key );
auto& glyph = cache[key];
// already in atlas map
if ( atlas.has( hash ) ) continue;
dirty = true;
uf::Image image;
const uint8_t* buffer = glyph.getBuffer();
if ( metadata.sdf ) {
image.loadFromBuffer( glyph.getBuffer(), glyph.getSize(), 8, 1, true );
} else {
uf::Image::container_t pixels;
size_t len = glyph.getSize().x * glyph.getSize().y;
pixels.resize(len * 4);
for ( auto i = 0; i < len; ++i ) {
pixels[i * 4 + 0] = buffer[i]; // R
pixels[i * 4 + 1] = buffer[i]; // G
pixels[i * 4 + 2] = buffer[i]; // B
pixels[i * 4 + 3] = buffer[i]; // A
}
image.loadFromBuffer( &pixels[0], glyph.getSize(), 8, 4, true );
}
atlas.addImage( image, hash );
}
atlas.generate();
#endif
return dirty;
}
void uf::glyph::generateMesh( const uf::stl::vector<pod::GlyphBox>& layout, const pod::GlyphSettings& metadata, const uf::Atlas& atlas, uf::Mesh& mesh ) {
// generate mesh
mesh.clear();
mesh.bind<::GlyphVertex, uint16_t>();
uf::stl::vector<::GlyphVertex> vertices;
uf::stl::vector<uint16_t> indices;
vertices.reserve(layout.size() * 4);
indices.reserve(layout.size() * 6);
for ( const auto& g : layout ) {
auto hash = std::to_string( uf::glyph::hashSettings(g.code, metadata) );
// zero-width
if ( g.box.w == 0 || g.box.h == 0 || g.color.w == 0.0f ) continue;
#if EXT_COLOR_FLOATS
auto& color = g.color;
#else
pod::Vector4b color = {
(uint8_t)(g.color[0] * 255),
(uint8_t)(g.color[1] * 255),
(uint8_t)(g.color[2] * 255),
(uint8_t)(g.color[3] * 255)
};
#endif
// insert indices
uint16_t idx = (uint16_t) vertices.size();
indices.insert( indices.end(), { idx, idx + 1, idx + 2, idx, idx + 2, idx + 3 });
// insert vertices
vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x, g.box.y + g.box.h, g.box.z }, atlas.mapUv(pod::Vector2f{ 0.0f, 0.0f }, hash), color});
vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x, g.box.y, g.box.z }, atlas.mapUv(pod::Vector2f{ 0.0f, 1.0f }, hash), color});
vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x + g.box.w, g.box.y, g.box.z }, atlas.mapUv(pod::Vector2f{ 1.0f, 1.0f }, hash), color});
vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x + g.box.w, g.box.y + g.box.h, g.box.z }, atlas.mapUv(pod::Vector2f{ 1.0f, 0.0f }, hash), color});
}
mesh.insertVertices(vertices);
mesh.insertIndices(indices);
}

View File

@ -20,7 +20,7 @@
UF_BEHAVIOR_REGISTER_CPP(ext::CraetureBehavior)
UF_BEHAVIOR_TRAITS_CPP(ext::CraetureBehavior, ticks = true, renders = false, thread = "")
UF_BEHAVIOR_TRAITS_CPP(ext::CraetureBehavior, ticks = false, renders = false, thread = "")
#define this (&self)
namespace {
void load( uf::Object& self, const uf::Image& image ) {

View File

@ -3,8 +3,8 @@ CDIR =
CC = clang
CXX = clang++
OPTIMIZATIONS = -O3 -fstrict-aliasing -DUF_NO_EXCEPTIONS # -flto # -march=native
WARNINGS = -Wall -Wno-deprecated-literal-operator -Wno-pointer-arith -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder-ctor -Wno-ignored-attributes -Wno-c++11-narrowing -Wno-unknown-pragmas -Wno-nullability-completeness -Wno-defaulted-function-deleted -Wno-mismatched-tags
SANITIZE = -fsanitize=address # -fuse-ld=lld -fno-omit-frame-pointer
WARNINGS = -Wall -Wno-missing-braces -Wno-deprecated-literal-operator -Wno-pointer-arith -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder-ctor -Wno-ignored-attributes -Wno-c++11-narrowing -Wno-unknown-pragmas -Wno-nullability-completeness -Wno-defaulted-function-deleted -Wno-mismatched-tags
SANITIZE = -fsanitize=address,undefined # -fuse-ld=lld -fno-omit-frame-pointer
FLAGS += -std=c++2b $(OPTIMIZATIONS) $(WARNINGS) $(SANITIZE) -fcolor-diagnostics -fansi-escape-codes
# MSYS2

View File

@ -1,5 +1,5 @@
ifneq (,$(findstring -DUF_DEV_ENV,$(FLAGS)))
REQ_DEPS += meshoptimizer toml xatlas curl ffx:sdk dc:texconv # vall_e cpptrace # openvr # ncurses draco discord bullet ultralight-ux
REQ_DEPS += meshoptimizer toml xatlas curl dc:texconv # ffx:sdk vall_e cpptrace # openvr # ncurses draco discord bullet ultralight-ux
FLAGS += -march=native -g # -flto # -g
endif