Compare commits
2 Commits
e434f33092
...
d8baa484da
| Author | SHA1 | Date | |
|---|---|---|---|
| d8baa484da | |||
| 78171039a1 |
@ -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,
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
"axis": [ 0, 1, 0 ],
|
||||
"angle": 0
|
||||
},
|
||||
"scale": [ 10.0, 10.0, 10.0 ]
|
||||
"scale": [ 1.0, 1.0, 1.0 ]
|
||||
},
|
||||
"system": {
|
||||
"hot reload": {
|
||||
@ -29,7 +29,7 @@
|
||||
"animations": true
|
||||
},
|
||||
"draw": {
|
||||
"armature": true
|
||||
"armature": false
|
||||
}
|
||||
},
|
||||
"exporter": {
|
||||
|
||||
@ -11,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": {
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 )
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
--[[
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
// { "filename": "./models/mds_mcdonalds.glb" }
|
||||
{ "filename": "./models/mds_mcdonalds/graph.json" }
|
||||
// ,{ "filename": "/ragdoll.json", "delay": 1 }
|
||||
,{ "filename": "/craeture.json", "delay": 2.0 }
|
||||
// ,{ "filename": "/craeture.json", "delay": 2.0 }
|
||||
// ,{ "filename": "/cesiumMan.json", "delay": 2.0 }
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
17
bin/data/shaders/base/textured/frag.glsl
Normal file
17
bin/data/shaders/base/textured/frag.glsl
Normal file
@ -0,0 +1,17 @@
|
||||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
|
||||
layout (binding = 1) uniform sampler2D samplerTexture;
|
||||
|
||||
layout (location = 0) in vec2 inUv;
|
||||
layout (location = 1) in vec4 inColor;
|
||||
|
||||
layout (location = 0) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
vec4 color = texture( samplerTexture, inUv ) * inColor;
|
||||
if ( color.a == 0.0f ) discard;
|
||||
|
||||
outColor.rgb = color.rgb;
|
||||
outColor.a = color.a;
|
||||
}
|
||||
25
bin/data/shaders/base/textured/vert.glsl
Normal file
25
bin/data/shaders/base/textured/vert.glsl
Normal file
@ -0,0 +1,25 @@
|
||||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
|
||||
#include "../../common/macros.h"
|
||||
#include "../../common/structs.h"
|
||||
|
||||
layout (constant_id = 0) const uint PASSES = 1;
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec2 inUv;
|
||||
layout (location = 2) in vec4 inColor;
|
||||
|
||||
layout( push_constant ) uniform PushBlock {
|
||||
uint pass;
|
||||
uint draw;
|
||||
} PushConstant;
|
||||
|
||||
layout (location = 0) out vec2 outUv;
|
||||
layout (location = 1) out vec4 outColor;
|
||||
|
||||
void main() {
|
||||
outUv = inUv;
|
||||
outColor = inColor;
|
||||
gl_Position = vec4(inPos.xyz, 1.0);
|
||||
}
|
||||
@ -153,6 +153,8 @@
|
||||
#define UF_DEBUG 1
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if UF_DEBUG
|
||||
#include <uf/utils/io/fmt.h>
|
||||
#endif
|
||||
@ -4,6 +4,14 @@
|
||||
#define __x86_64__ 1
|
||||
#endif
|
||||
|
||||
#if __clang__
|
||||
// clang produces multiple symbols or something because the anonymous namespace trick doesn't work
|
||||
// also crashes when producing debug symbols for multi-versioned functions
|
||||
#define SIMD_MV 0
|
||||
#else
|
||||
#define SIMD_MV 0 // off for now because there MIGHT be a runtime cost
|
||||
#endif
|
||||
|
||||
// Find sse instruction set from compiler macros if SSE_INSTR_SET not defined
|
||||
// Note: Not all compilers define these macros automatically
|
||||
#ifndef SSE_INSTR_SET
|
||||
|
||||
@ -8,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 );
|
||||
}
|
||||
}
|
||||
@ -23,18 +23,18 @@ namespace uf {
|
||||
uf::Image m_atlas;
|
||||
atlas_t m_tiles;
|
||||
public:
|
||||
hash_t addImage( const uf::Image&, bool = false );
|
||||
hash_t addImage( uf::Image&&, bool = false );
|
||||
hash_t addImage( const uint8_t*, const pod::Vector2ui&, std::size_t, std::size_t, bool = false, bool = false );
|
||||
hash_t addImage( const uf::Image&, const hash_t& hash );
|
||||
hash_t addImage( const uf::Image& );
|
||||
|
||||
void generate(float padding = 1);
|
||||
void generate( const uf::stl::vector<uf::Image>&, float padding = 1);
|
||||
void clear(bool = true);
|
||||
bool generated() const;
|
||||
bool has( const hash_t& ) const;
|
||||
|
||||
pod::Vector2f mapUv( const pod::Vector2f&, const hash_t& );
|
||||
pod::Vector2f mapUv( const pod::Vector2f&, size_t );
|
||||
pod::Vector3f mapUv( const pod::Vector3f& );
|
||||
pod::Vector2f mapUv( const pod::Vector2f&, const hash_t& ) const;
|
||||
pod::Vector2f mapUv( const pod::Vector2f&, size_t ) const;
|
||||
pod::Vector3f mapUv( const pod::Vector3f& ) const;
|
||||
|
||||
uf::Image& getAtlas();
|
||||
const uf::Image& getAtlas() const;
|
||||
|
||||
@ -381,7 +381,10 @@ pod::Vector3t<typename T::type_t> uf::matrix::extractScale( const T& matrix ) {
|
||||
type_t det = matrix(0,0) * ( matrix(1,1) * matrix(2,2) - matrix(2,1) * matrix(1,2))
|
||||
- matrix(0,1) * ( matrix(1,0) * matrix(2,2) - matrix(1,2) * matrix(2,0))
|
||||
+ matrix(0,2) * ( matrix(1,0) * matrix(2,1) - matrix(1,1) * matrix(2,0));
|
||||
if ( det < 0 ) { sx = -sx; sy = -sy; sz = -sz; }
|
||||
|
||||
if ( det < 0 ) {
|
||||
sx = -sx;
|
||||
}
|
||||
return { sx, sy, sz };
|
||||
}
|
||||
|
||||
|
||||
@ -22,12 +22,13 @@ namespace uf {
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
/***/ namespace {
|
||||
#if SIMD_MV
|
||||
__attribute__((target("default")))
|
||||
uf::simd::matrix_value<float> matMult_impl(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
|
||||
uf::simd::matrix_value<float> _impl_matMult(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
|
||||
uf::simd::matrix_value<float> R;
|
||||
uf::simd::matrix_value<float> Bt = uf::simd::matTranspose(B);
|
||||
FOR_EACH(4, {
|
||||
FOR_EACH_(4, {
|
||||
__m128 bcol = B.m[i];
|
||||
|
||||
__m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx
|
||||
@ -48,13 +49,12 @@ namespace {
|
||||
return R;
|
||||
|
||||
}
|
||||
#if 1
|
||||
__attribute__((target("sse4.1")))
|
||||
uf::simd::matrix_value<float> matMult_impl(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
|
||||
uf::simd::matrix_value<float> _impl_matMult(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
|
||||
uf::simd::matrix_value<float> R;
|
||||
uf::simd::matrix_value<float> Bt = uf::simd::matTranspose(B);
|
||||
|
||||
FOR_EACH(4, {
|
||||
FOR_EACH_(4, {
|
||||
__m128 bcol = B.m[i];
|
||||
|
||||
__m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx
|
||||
@ -74,14 +74,12 @@ namespace {
|
||||
|
||||
return R;
|
||||
}
|
||||
#endif
|
||||
#if 1
|
||||
__attribute__((target("avx2,fma")))
|
||||
uf::simd::matrix_value<float> matMult_impl(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
|
||||
uf::simd::matrix_value<float> _impl_matMult(const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
|
||||
uf::simd::matrix_value<float> R;
|
||||
uf::simd::matrix_value<float> Bt = uf::simd::matTranspose(B);
|
||||
|
||||
FOR_EACH(4, {
|
||||
FOR_EACH_(4, {
|
||||
__m128 bcol = B.m[i];
|
||||
|
||||
__m256 vx = _mm256_broadcastss_ps(bcol); // xxxx
|
||||
@ -109,10 +107,8 @@ namespace {
|
||||
|
||||
return R;
|
||||
}
|
||||
#endif
|
||||
#if 1
|
||||
__attribute__((target("avx512f")))
|
||||
uf::simd::matrix_value<float> matMult_impl( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
|
||||
uf::simd::matrix_value<float> _impl_matMult( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
|
||||
uf::simd::matrix_value<float> R;
|
||||
uf::simd::matrix_value<float> Bt = uf::simd::matTranspose(B);
|
||||
|
||||
@ -148,10 +144,9 @@ namespace {
|
||||
|
||||
return R;
|
||||
}
|
||||
#endif
|
||||
|
||||
__attribute__((target("default")))
|
||||
uf::simd::vector<float> matMult_impl( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
|
||||
uf::simd::vector<float> _impl_matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
|
||||
__m128 vx = _mm_shuffle_ps(v, v, 0x00);
|
||||
__m128 vy = _mm_shuffle_ps(v, v, 0x55);
|
||||
__m128 vz = _mm_shuffle_ps(v, v, 0xAA);
|
||||
@ -164,9 +159,8 @@ namespace {
|
||||
|
||||
return _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3));
|
||||
}
|
||||
#if 1
|
||||
__attribute__((target("fma")))
|
||||
uf::simd::vector<float> matMult_impl( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
|
||||
uf::simd::vector<float> _impl_matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
|
||||
__m128 vx = _mm_shuffle_ps(v, v, 0x00);
|
||||
__m128 vy = _mm_shuffle_ps(v, v, 0x55);
|
||||
__m128 vz = _mm_shuffle_ps(v, v, 0xAA);
|
||||
@ -177,8 +171,134 @@ namespace {
|
||||
_mm_fmadd_ps(M.m[2], vz,
|
||||
_mm_mul_ps(M.m[3], vw))));
|
||||
}
|
||||
#else
|
||||
uf::simd::matrix_value<float> _impl_matMult( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B) {
|
||||
uf::simd::matrix_value<float> R;
|
||||
uf::simd::matrix_value<float> Bt = uf::simd::matTranspose(B);
|
||||
|
||||
#if 0 && SSE_INSTR_SET >= 9 // AVX512F
|
||||
FOR_EACH(4, {
|
||||
__m128 bcol = B.m[i];
|
||||
|
||||
__m512 vx = _mm512_set1_ps(((const float*)&bcol)[0]); // xxxx
|
||||
__m512 vy = _mm512_set1_ps(((const float*)&bcol)[1]); // yyyy
|
||||
__m512 vz = _mm512_set1_ps(((const float*)&bcol)[2]); // zzzz
|
||||
__m512 vw = _mm512_set1_ps(((const float*)&bcol)[3]); // wwww
|
||||
|
||||
__m512 a0 = _mm512_castps128_ps512(A.m[0]);
|
||||
__m512 a1 = _mm512_castps128_ps512(A.m[1]);
|
||||
__m512 a2 = _mm512_castps128_ps512(A.m[2]);
|
||||
__m512 a3 = _mm512_castps128_ps512(A.m[3]);
|
||||
|
||||
__m512 r = _mm512_fmadd_ps(a0, vx,
|
||||
_mm512_fmadd_ps(a1, vy,
|
||||
_mm512_fmadd_ps(a2, vz,
|
||||
_mm512_mul_ps(a3, vw))));
|
||||
|
||||
__m128 r128 = _mm_add_ps(
|
||||
_mm_add_ps(
|
||||
_mm512_castps512_ps128(r), // low 128
|
||||
_mm512_extractf32x4_ps(r, 1)), // next 128
|
||||
_mm_add_ps(
|
||||
_mm512_extractf32x4_ps(r, 2), // next 128
|
||||
_mm512_extractf32x4_ps(r, 3)) // high 128
|
||||
);
|
||||
|
||||
R.m[i] = r128;
|
||||
});
|
||||
#elif 0 && SSE_INSTR_SET >= 8 && __FMA__ // AVX2,FMA
|
||||
FOR_EACH_(4, {
|
||||
__m128 bcol = B.m[i];
|
||||
|
||||
__m256 vx = _mm256_broadcastss_ps(bcol); // xxxx
|
||||
__m256 vy = _mm256_broadcastss_ps(_mm_shuffle_ps(bcol, bcol, 0x55)); // yyyy
|
||||
__m256 vz = _mm256_broadcastss_ps(_mm_shuffle_ps(bcol, bcol, 0xAA)); // zzzz
|
||||
__m256 vw = _mm256_broadcastss_ps(_mm_shuffle_ps(bcol, bcol, 0xFF)); // wwww
|
||||
|
||||
__m256 a0 = _mm256_castps128_ps256(A.m[0]);
|
||||
__m256 a1 = _mm256_castps128_ps256(A.m[1]);
|
||||
__m256 a2 = _mm256_castps128_ps256(A.m[2]);
|
||||
__m256 a3 = _mm256_castps128_ps256(A.m[3]);
|
||||
|
||||
__m256 r = _mm256_fmadd_ps(a0, vx,
|
||||
_mm256_fmadd_ps(a1, vy,
|
||||
_mm256_fmadd_ps(a2, vz,
|
||||
_mm256_mul_ps(a3, vw))));
|
||||
|
||||
__m128 r128 = _mm_add_ps(
|
||||
_mm256_castps256_ps128(r),
|
||||
_mm256_extractf128_ps(r, 1)
|
||||
);
|
||||
|
||||
R.m[i] = r128;
|
||||
});
|
||||
#elif 0 && SSE_INSTR_SET >= 5 // SSE4.1
|
||||
FOR_EACH_(4, {
|
||||
__m128 bcol = B.m[i];
|
||||
|
||||
__m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx
|
||||
__m128 vy = _mm_shuffle_ps(bcol, bcol, 0x55); // yyyy
|
||||
__m128 vz = _mm_shuffle_ps(bcol, bcol, 0xAA); // zzzz
|
||||
__m128 vw = _mm_shuffle_ps(bcol, bcol, 0xFF); // wwww
|
||||
|
||||
R.m[i] = _mm_add_ps(
|
||||
_mm_add_ps(
|
||||
_mm_mul_ps(A.m[0], vx),
|
||||
_mm_mul_ps(A.m[1], vy)),
|
||||
_mm_add_ps(
|
||||
_mm_mul_ps(A.m[2], vz),
|
||||
_mm_mul_ps(A.m[3], vw))
|
||||
);
|
||||
});
|
||||
#else
|
||||
FOR_EACH_(4, {
|
||||
__m128 bcol = B.m[i];
|
||||
|
||||
__m128 vx = _mm_shuffle_ps(bcol, bcol, 0x00); // xxxx
|
||||
__m128 vy = _mm_shuffle_ps(bcol, bcol, 0x55); // yyyy
|
||||
__m128 vz = _mm_shuffle_ps(bcol, bcol, 0xAA); // zzzz
|
||||
__m128 vw = _mm_shuffle_ps(bcol, bcol, 0xFF); // wwww
|
||||
|
||||
R.m[i] = _mm_add_ps(
|
||||
_mm_add_ps(
|
||||
_mm_mul_ps(A.m[0], vx),
|
||||
_mm_mul_ps(A.m[1], vy)),
|
||||
_mm_add_ps(
|
||||
_mm_mul_ps(A.m[2], vz),
|
||||
_mm_mul_ps(A.m[3], vw))
|
||||
);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<float> _impl_matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> v ) {
|
||||
#if __FMA__
|
||||
__m128 vx = _mm_shuffle_ps(v, v, 0x00);
|
||||
__m128 vy = _mm_shuffle_ps(v, v, 0x55);
|
||||
__m128 vz = _mm_shuffle_ps(v, v, 0xAA);
|
||||
__m128 vw = _mm_shuffle_ps(v, v, 0xFF);
|
||||
|
||||
return _mm_fmadd_ps(M.m[0], vx,
|
||||
_mm_fmadd_ps(M.m[1], vy,
|
||||
_mm_fmadd_ps(M.m[2], vz,
|
||||
_mm_mul_ps(M.m[3], vw))));
|
||||
#else
|
||||
__m128 vx = _mm_shuffle_ps(v, v, 0x00);
|
||||
__m128 vy = _mm_shuffle_ps(v, v, 0x55);
|
||||
__m128 vz = _mm_shuffle_ps(v, v, 0xAA);
|
||||
__m128 vw = _mm_shuffle_ps(v, v, 0xFF);
|
||||
|
||||
__m128 r0 = _mm_mul_ps(M.m[0], vx);
|
||||
__m128 r1 = _mm_mul_ps(M.m[1], vy);
|
||||
__m128 r2 = _mm_mul_ps(M.m[2], vz);
|
||||
__m128 r3 = _mm_mul_ps(M.m[3], vw);
|
||||
|
||||
return _mm_add_ps(_mm_add_ps(r0, r1), _mm_add_ps(r2, r3));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
|
||||
template<typename T>
|
||||
FORCE_INLINE uf::simd::matrix_value<T>::matrix_value() {}
|
||||
@ -204,10 +324,10 @@ FORCE_INLINE uf::simd::matrix_value<T>::operator pod::Matrix<T,4>() const {
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::matrix_value<float> uf::simd::matMult( const uf::simd::matrix_value<float>& A, const uf::simd::matrix_value<float>& B ) {
|
||||
return ::matMult_impl( A, B );
|
||||
return _impl_matMult( A, B );
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<float> uf::simd::matMult( const uf::simd::matrix_value<float>& M, uf::simd::vector<float> vec ) {
|
||||
return ::matMult_impl( M, vec );
|
||||
return _impl_matMult( M, vec );
|
||||
}
|
||||
FORCE_INLINE uf::simd::matrix_value<float> uf::simd::matTranspose( const uf::simd::matrix_value<float>& M ) {
|
||||
uf::simd::matrix_value<float> R = M;
|
||||
|
||||
@ -67,6 +67,9 @@ namespace impl {
|
||||
/*FORCE_INLINE*/ void boxAxes( pod::Vector3f axes[3], const pod::Transform<>& transform );
|
||||
/*FORCE_INLINE*/ pod::Vector3f extentFromAxes( const pod::OBB& box, const pod::Vector3f axes[3] );
|
||||
/*FORCE_INLINE*/ float projectExtents( const pod::OBB& box, const pod::Vector3f& normal, const pod::Vector3f axes[3] );
|
||||
|
||||
void getCorners( const pod::AABB& aabb, pod::Vector3f corners[8] );
|
||||
void getCorners( const pod::AABB& aabb, const pod::Transform<>& transform, pod::Vector3f corners[8] );
|
||||
pod::AABB transformAabbToWorld( const pod::AABB& localBox, const pod::Transform<>& transform );
|
||||
std::pair<pod::Vector3f, pod::Vector3f> getCapsuleSegment( const pod::PhysicsBody& body );
|
||||
pod::AABB computeAABB( const pod::PhysicsBody& body );
|
||||
|
||||
@ -491,6 +491,7 @@ namespace pod {
|
||||
bool contacts = false;
|
||||
bool constraints = false;
|
||||
bool rays = false;
|
||||
bool depthTest = true;
|
||||
} debugDraw;
|
||||
};
|
||||
|
||||
|
||||
@ -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 );
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -166,7 +166,7 @@ namespace uf {
|
||||
template<typename T> /*FORCE_INLINE*/ typename T::type_t /*UF_API*/ magnitude( const T& vector ); // gets the magnitude of the vector
|
||||
template<typename T> /*FORCE_INLINE*/ typename T::type_t /*UF_API*/ norm( const T& vector ); // compute the norm (length) of the vector
|
||||
template<typename T> /*FORCE_INLINE*/ T /*UF_API*/ normalize( const T& vector ); // normalizes a vector
|
||||
template<typename T> /*FORCE_INLINE*/ T /*UF_API*/ clampMagnitude( const T& vector ); // clamps the magnitude of a vector
|
||||
template<typename T> /*FORCE_INLINE*/ T /*UF_API*/ clampMagnitude( const T& vector, float maxMag ); // clamps the magnitude of a vector
|
||||
template<typename T> /*FORCE_INLINE*/ void /*UF_API*/ orthonormalize( T& x, T& y ); // orthonormalizes a vector against another vector
|
||||
template<typename T> /*FORCE_INLINE*/ T /*UF_API*/ orthonormalize( const T& x, const T& y ); // orthonormalizes a vector against another vector
|
||||
|
||||
|
||||
@ -12,8 +12,8 @@ constexpr void for_each_index(F&& f) {
|
||||
#include "simd.h"
|
||||
#endif
|
||||
|
||||
// #define FOR_EACH( N, F ) for ( auto i = 0; i < N; ++i ) F;
|
||||
#define FOR_EACH( N, F ) for_each_index<N>([&](auto i) F )
|
||||
#define FOR_EACH_( N, F ) for ( auto i = 0; i < N; ++i ) F;
|
||||
|
||||
template<typename T, typename Op>
|
||||
T elementwise( const T& left, const T& right, Op&& op ) {
|
||||
@ -309,7 +309,7 @@ T uf::vector::divide( typename T::type_t scalar, const T& vector ) {
|
||||
}
|
||||
template<typename T>
|
||||
typename T::type_t uf::vector::sum( const T& vector ) {
|
||||
auto res = 0;
|
||||
typename T::type_t res = 0;
|
||||
FOR_EACH(T::size, {
|
||||
res += vector[i];
|
||||
});
|
||||
@ -317,7 +317,7 @@ typename T::type_t uf::vector::sum( const T& vector ) {
|
||||
}
|
||||
template<typename T>
|
||||
typename T::type_t uf::vector::product( const T& vector ) {
|
||||
auto res = 1;
|
||||
typename T::type_t res = 1;
|
||||
FOR_EACH(T::size, {
|
||||
res *= vector[i];
|
||||
});
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
#include <uf/utils/memory/alignment.h>
|
||||
|
||||
namespace {
|
||||
FORCE_INLINE __m128i bias_unsigned(__m128i v) {
|
||||
/**/namespace {
|
||||
FORCE_INLINE __m128i _impl_bias_unsigned(__m128i v) {
|
||||
const __m128i signbit = _mm_set1_epi32(0x80000000);
|
||||
return _mm_xor_si128(v, signbit);
|
||||
}
|
||||
|
||||
FORCE_INLINE int32_t boolMask(bool b) {
|
||||
FORCE_INLINE int32_t _impl_bool_mask(bool b) {
|
||||
return b ? -1 : 0; // 0xFFFFFFFF for true, 0x00000000 for false
|
||||
}
|
||||
}
|
||||
/**/}
|
||||
|
||||
#define MV_INSTR_SET_DEFAULT __attribute__((target("default")))
|
||||
#define MV_INSTR_SET_2 __attribute__((target("sse2")))
|
||||
@ -202,13 +202,18 @@ FORCE_INLINE uf::simd::vector<float> uf::simd::sqrt( uf::simd::vector<float> v )
|
||||
return _mm_sqrt_ps( v );
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**/namespace {
|
||||
#if SIMD_MV
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<float> dot_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
return uf::simd::mul( x, y );
|
||||
uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
__m128 mul = _mm_mul_ps(x, y);
|
||||
__m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128 sums = _mm_add_ps(mul, shuf);
|
||||
shuf = _mm_movehl_ps(shuf, sums);
|
||||
return _mm_add_ss(sums, shuf);
|
||||
}
|
||||
MV_INSTR_SET_3
|
||||
uf::simd::vector<float> dot_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
__m128 mulRes = _mm_mul_ps(x, y);
|
||||
__m128 shufReg = _mm_movehdup_ps(mulRes);
|
||||
__m128 sumsReg = _mm_add_ps(mulRes, shufReg);
|
||||
@ -216,47 +221,93 @@ namespace {
|
||||
return _mm_add_ss(sumsReg, shufReg);
|
||||
}
|
||||
MV_INSTR_SET_5
|
||||
uf::simd::vector<float> dot_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
return _mm_dp_ps(x, y, 0xF1);
|
||||
uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
return _mm_dp_ps(x, y, 0xFF);
|
||||
}
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE uf::simd::vector<float> _impl_dot( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
#if SSE_INSTR_SET >= 5
|
||||
return _mm_dp_ps(x, y, 0xFF);
|
||||
#elif SSE_INSTR_SET >= 3
|
||||
__m128 mulRes = _mm_mul_ps(x, y);
|
||||
__m128 shufReg = _mm_movehdup_ps(mulRes);
|
||||
__m128 sumsReg = _mm_add_ps(mulRes, shufReg);
|
||||
shufReg = _mm_movehl_ps(shufReg, sumsReg);
|
||||
return _mm_add_ss(sumsReg, shufReg);
|
||||
#else
|
||||
__m128 mul = _mm_mul_ps(x, y);
|
||||
__m128 shuf = _mm_shuffle_ps(mul, mul, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128 sums = _mm_add_ps(mul, shuf);
|
||||
shuf = _mm_movehl_ps(shuf, sums);
|
||||
return _mm_add_ss(sums, shuf);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
FORCE_INLINE float uf::simd::dot( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
return _mm_cvtss_f32( ::dot_impl( x, y ) );
|
||||
return _mm_cvtss_f32( _impl_dot( x, y ) );
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**/namespace {
|
||||
#if SIMD_MV
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<int32_t> load_impl( const int32_t* f ) {
|
||||
uf::simd::vector<int32_t> _impl_load( const int32_t* f ) {
|
||||
return uf::simd::vector<int32_t>( f[0], f[1], f[2], f[3] );
|
||||
}
|
||||
MV_INSTR_SET_3
|
||||
uf::simd::vector<int32_t> load_impl( const int32_t* f ) {
|
||||
uf::simd::vector<int32_t> _impl_load( const int32_t* f ) {
|
||||
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
|
||||
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));
|
||||
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
|
||||
}
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE uf::simd::vector<int32_t> _impl_load( const int32_t* f ) {
|
||||
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
|
||||
#if SSE_INSTR_SET >= 3
|
||||
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
|
||||
#else
|
||||
return uf::simd::vector<int32_t>( f[0], f[1], f[2], f[3] );
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::load( const int32_t* f ) {
|
||||
return ::load_impl( f );
|
||||
return _impl_load( f );
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**/namespace {
|
||||
#if SIMD_MV
|
||||
MV_INSTR_SET_DEFAULT
|
||||
void store_impl( uf::simd::vector<int32_t> v, int32_t* f ) {
|
||||
void _impl_store( uf::simd::vector<int32_t> v, int32_t* f ) {
|
||||
union { __m128i x; int32_t y[4]; } kludge;
|
||||
kludge.x = v;
|
||||
f[0] = kludge.y[0];
|
||||
f[1] = kludge.y[1];
|
||||
f[2] = kludge.y[2];
|
||||
f[3] = kludge.y[3];
|
||||
f[3] = kludge.y[3];//
|
||||
}
|
||||
MV_INSTR_SET_3
|
||||
void store_impl( uf::simd::vector<int32_t> v, int32_t* f ) {
|
||||
void _impl_store( uf::simd::vector<int32_t> v, int32_t* f ) {
|
||||
/*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v);
|
||||
else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v);
|
||||
}
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE void _impl_store( uf::simd::vector<int32_t> v, int32_t* f ) {
|
||||
#if SSE_INSTR_SET >= 3
|
||||
/*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v);
|
||||
else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v);
|
||||
#else
|
||||
union { __m128i x; int32_t y[4]; } kludge;
|
||||
kludge.x = v;
|
||||
f[0] = kludge.y[0];
|
||||
f[1] = kludge.y[1];
|
||||
f[2] = kludge.y[2];
|
||||
f[3] = kludge.y[3];//
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
FORCE_INLINE void uf::simd::store( uf::simd::vector<int32_t> v, int32_t* f ) {
|
||||
return ::store_impl( v, f );
|
||||
return _impl_store( v, f );
|
||||
}
|
||||
|
||||
|
||||
@ -273,20 +324,32 @@ FORCE_INLINE uf::simd::vector<int32_t> uf::simd::sub( uf::simd::vector<int32_t>
|
||||
return _mm_sub_epi32(x, y);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**/namespace {
|
||||
#if SIMD_MV
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<int32_t> mul_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]);
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<int32_t> mul_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {//
|
||||
return _mm_mullo_epi32(x, y);
|
||||
}
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE uf::simd::vector<int32_t> _impl_mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {//
|
||||
#if SSE_INSTR_SET >= 4
|
||||
return _mm_mullo_epi32(x, y);
|
||||
#else
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::mul( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
return ::mul_impl( x, y );
|
||||
return _impl_mul( x, y );
|
||||
}
|
||||
|
||||
|
||||
@ -302,34 +365,56 @@ FORCE_INLINE uf::simd::vector<int32_t> uf::simd::hadd( uf::simd::vector<int32_t>
|
||||
return uf::simd::set( X[0] + Y[0], X[1] + Y[1], X[2] + Y[2], X[3] + Y[3] );
|
||||
}
|
||||
*/
|
||||
namespace {
|
||||
/**/namespace {
|
||||
#if SIMD_MV
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<int32_t> min_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3]));
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<int32_t> min_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {//
|
||||
return _mm_min_epi32(x, y);
|
||||
}
|
||||
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<int32_t> max_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3]));
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<int32_t> max_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
return _mm_max_epi32(x, y);
|
||||
}
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE uf::simd::vector<int32_t> _impl_min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {//
|
||||
#if SSE_INSTR_SET >= 4
|
||||
return _mm_min_epi32(x, y);
|
||||
#else
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3]));
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<int32_t> _impl_max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
#if SSE_INSTR_SET >= 4
|
||||
return _mm_max_epi32(x, y);
|
||||
#else
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3]));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::min( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
return ::min_impl(x, y);
|
||||
return _impl_min(x, y);
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::max( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
return ::max_impl(x, y);
|
||||
return _impl_max(x, y);
|
||||
}
|
||||
|
||||
|
||||
@ -340,60 +425,100 @@ FORCE_INLINE bool uf::simd::any( uf::simd::vector<int32_t> mask) {
|
||||
return _mm_movemask_epi8( mask ) != 0x0; // any bit set
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**/namespace {
|
||||
#if SIMD_MV
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<int32_t> less_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_i(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]);
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<int32_t> less_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
return _mm_cmplt_epi32( x, y );
|
||||
uf::simd::vector<int32_t> _impl_less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
return _mm_cmplt_epi32( x, y );//
|
||||
}
|
||||
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<int32_t> lessEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_i(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]);
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<int32_t> lessEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
__m128i gt = _mm_cmpgt_epi32(x, y);
|
||||
return _mm_xor_si128(gt, _mm_set1_epi32(-1));
|
||||
}
|
||||
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<int32_t> greater_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_i(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]);
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<int32_t> greater_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
return _mm_cmpgt_epi32(x, y);
|
||||
}
|
||||
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<int32_t> greaterEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_i(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]);
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<int32_t> greaterEquals_impl( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
uf::simd::vector<int32_t> _impl_greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
__m128i gt = _mm_cmplt_epi32(x, y);
|
||||
return _mm_xor_si128(gt, _mm_set1_epi32(-1));
|
||||
}
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE uf::simd::vector<int32_t> _impl_less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
#if SSE_INSTR_SET >= 4
|
||||
return _mm_cmplt_epi32( x, y );//
|
||||
#else
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_i(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<int32_t> _impl_lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
#if SSE_INSTR_SET >= 4
|
||||
__m128i gt = _mm_cmpgt_epi32(x, y);
|
||||
return _mm_xor_si128(gt, _mm_set1_epi32(-1));
|
||||
#else
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_i(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<int32_t> _impl_greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
#if SSE_INSTR_SET >= 4
|
||||
return _mm_cmpgt_epi32(x, y);
|
||||
#else
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_i(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<int32_t> _impl_greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
#if SSE_INSTR_SET >= 4
|
||||
__m128i gt = _mm_cmplt_epi32(x, y);
|
||||
return _mm_xor_si128(gt, _mm_set1_epi32(-1));
|
||||
#else
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_i(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::less( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
return ::less_impl( x, y );
|
||||
return _impl_less( x, y );
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::lessEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
return ::lessEquals_impl( x, y );
|
||||
return _impl_lessEquals( x, y );
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::greater( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
return ::greater_impl( x, y );
|
||||
return _impl_greater( x, y );
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::greaterEquals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
return ::greaterEquals_impl( x, y );
|
||||
return _impl_greaterEquals( x, y );
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::equals( uf::simd::vector<int32_t> x, uf::simd::vector<int32_t> y ) {
|
||||
@ -412,19 +537,20 @@ FORCE_INLINE int32_t uf::simd::dot( uf::simd::vector<int32_t> x, uf::simd::vecto
|
||||
return X[0] * Y[0] + X[1] * Y[1] + X[2] * Y[2] + X[3] * Y[3];
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**/namespace {
|
||||
#if SIMD_MV
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<uint32_t> load_impl( const uint32_t* f ) {
|
||||
uf::simd::vector<uint32_t> _impl_load( const uint32_t* f ) {
|
||||
return uf::simd::vector<uint32_t>( f[0], f[1], f[2], f[3] );
|
||||
}
|
||||
MV_INSTR_SET_3
|
||||
uf::simd::vector<uint32_t> load_impl( const uint32_t* f ) {
|
||||
uf::simd::vector<uint32_t> _impl_load( const uint32_t* f ) {
|
||||
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
|
||||
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));
|
||||
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
|
||||
}
|
||||
|
||||
MV_INSTR_SET_DEFAULT
|
||||
void store_impl( uf::simd::vector<uint32_t> v, uint32_t* f ) {
|
||||
void _impl_store( uf::simd::vector<uint32_t> v, uint32_t* f ) {
|
||||
union { __m128i x; uint32_t y[4]; } kludge;
|
||||
kludge.x = v;
|
||||
f[0] = kludge.y[0];
|
||||
@ -433,16 +559,40 @@ namespace {
|
||||
f[3] = kludge.y[3];
|
||||
}
|
||||
MV_INSTR_SET_3
|
||||
void store_impl( uf::simd::vector<uint32_t> v, uint32_t* f ) {
|
||||
void _impl_store( uf::simd::vector<uint32_t> v, uint32_t* f ) {
|
||||
/*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v);
|
||||
else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v);
|
||||
}
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> _impl_load( const uint32_t* f ) {
|
||||
#if SSE_INSTR_SET >= 3
|
||||
// if ( uf::aligned(f, 16) ) return _mm_load_si128(reinterpret_cast<const __m128i*>(f));
|
||||
return _mm_loadu_si128(reinterpret_cast<const __m128i*>(f));//
|
||||
#else
|
||||
return uf::simd::vector<uint32_t>( f[0], f[1], f[2], f[3] );
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE void _impl_store( uf::simd::vector<uint32_t> v, uint32_t* f ) {
|
||||
#if SSE_INSTR_SET >= 3
|
||||
/*if ( uf::aligned(f, 16) ) _mm_store_si128(reinterpret_cast<__m128i*>(f), v);
|
||||
else*/ _mm_storeu_si128(reinterpret_cast<__m128i*>(f), v);
|
||||
#else
|
||||
union { __m128i x; uint32_t y[4]; } kludge;
|
||||
kludge.x = v;
|
||||
f[0] = kludge.y[0];
|
||||
f[1] = kludge.y[1];
|
||||
f[2] = kludge.y[2];
|
||||
f[3] = kludge.y[3];
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::load( const uint32_t* f ) {
|
||||
return ::load_impl( f );
|
||||
return _impl_load( f );
|
||||
}
|
||||
FORCE_INLINE void uf::simd::store( uf::simd::vector<uint32_t> v, uint32_t* f ) {
|
||||
return ::store_impl( v, f );
|
||||
return _impl_store( v, f );
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::set( uint32_t f ) {
|
||||
@ -458,20 +608,32 @@ FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::sub( uf::simd::vector<uint32_t
|
||||
return _mm_sub_epi32(x, y);
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**/namespace {
|
||||
#if SIMD_MV
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<uint32_t> mul_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
uf::simd::vector<uint32_t> _impl_mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]);
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<uint32_t> mul_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
uf::simd::vector<uint32_t> _impl_mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {//
|
||||
return _mm_mullo_epi32(x, y);
|
||||
}
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> _impl_mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {//
|
||||
#if SSE_INSTR_SET >= 4
|
||||
return _mm_mullo_epi32(x, y);
|
||||
#else
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(X[0]*Y[0], X[1]*Y[1], X[2]*Y[2], X[3]*Y[3]);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::mul( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return ::mul_impl( x, y );
|
||||
return _impl_mul( x, y );
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::div( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
@ -487,34 +649,56 @@ FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::hadd( uf::simd::vector<uint32_
|
||||
}
|
||||
*/
|
||||
|
||||
namespace {
|
||||
/**/namespace {
|
||||
#if SIMD_MV
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<uint32_t> min_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
uf::simd::vector<uint32_t> _impl_min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3]));
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<uint32_t> min_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
uf::simd::vector<uint32_t> _impl_min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {//
|
||||
return _mm_min_epu32(x, y); // unsigned min
|
||||
}
|
||||
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<uint32_t> max_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
uf::simd::vector<uint32_t> _impl_max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3]));
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<uint32_t> max_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
uf::simd::vector<uint32_t> _impl_max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return _mm_max_epu32(x, y); // unsigned max
|
||||
}
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> _impl_min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {//
|
||||
#if SSE_INSTR_SET >= 4
|
||||
return _mm_min_epu32(x, y); // unsigned min
|
||||
#else
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(std::min(X[0],Y[0]), std::min(X[1],Y[1]), std::min(X[2],Y[2]), std::min(X[3],Y[3]));
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> _impl_max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
#if SSE_INSTR_SET >= 4
|
||||
return _mm_max_epu32(x, y); // unsigned max
|
||||
#else
|
||||
auto X = uf::simd::cast(x);
|
||||
auto Y = uf::simd::cast(y);
|
||||
return uf::simd::set(std::max(X[0],Y[0]), std::max(X[1],Y[1]), std::max(X[2],Y[2]), std::max(X[3],Y[3]));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::min( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return ::min_impl(x, y);
|
||||
return _impl_min(x, y);
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::max( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return ::max_impl(x, y);
|
||||
return _impl_max(x, y);
|
||||
}
|
||||
|
||||
FORCE_INLINE bool uf::simd::all( uf::simd::vector<uint32_t> mask) {
|
||||
@ -524,66 +708,112 @@ FORCE_INLINE bool uf::simd::any( uf::simd::vector<uint32_t> mask) {
|
||||
return _mm_movemask_epi8( mask ) != 0x0; // any bit set
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**/namespace {
|
||||
#if SIMD_MV
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<uint32_t> less_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
uf::simd::vector<uint32_t> _impl_less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_ui(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]);
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<uint32_t> less_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return _mm_cmplt_epi32( ::bias_unsigned(x), ::bias_unsigned(y) );
|
||||
uf::simd::vector<uint32_t> _impl_less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return _mm_cmplt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );//
|
||||
}
|
||||
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<uint32_t> lessEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
|
||||
uf::simd::vector<uint32_t> _impl_lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
|
||||
auto X = uf::simd::cast(x), Y = uf::simd::cast(y);
|
||||
return uf::simd::set_ui(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]);
|
||||
}
|
||||
MV_INSTR_SET_2
|
||||
uf::simd::vector<uint32_t> lessEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
|
||||
uf::simd::vector<uint32_t> _impl_lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
|
||||
// a <= b <=> !(a > b)
|
||||
__m128i bx = ::bias_unsigned(x);
|
||||
__m128i by = ::bias_unsigned(y);
|
||||
__m128i bx = _impl_bias_unsigned(x);
|
||||
__m128i by = _impl_bias_unsigned(y);
|
||||
__m128i gt = _mm_cmpgt_epi32(bx, by); // signed compare
|
||||
return _mm_xor_si128(gt, _mm_set1_epi32(-1)); // invert mask
|
||||
}
|
||||
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<uint32_t> greater_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
uf::simd::vector<uint32_t> _impl_greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_ui(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]);
|
||||
}
|
||||
MV_INSTR_SET_4
|
||||
uf::simd::vector<uint32_t> greater_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return _mm_cmpgt_epi32( ::bias_unsigned(x), ::bias_unsigned(y) );
|
||||
uf::simd::vector<uint32_t> _impl_greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return _mm_cmpgt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );
|
||||
}
|
||||
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<uint32_t> greaterEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
|
||||
uf::simd::vector<uint32_t> _impl_greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
|
||||
auto X = uf::simd::cast(x), Y = uf::simd::cast(y);
|
||||
return uf::simd::set_ui(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]);
|
||||
}
|
||||
MV_INSTR_SET_2
|
||||
uf::simd::vector<uint32_t> greaterEquals_impl( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
|
||||
uf::simd::vector<uint32_t> _impl_greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
|
||||
// a >= b <=> !(a < b)
|
||||
__m128i bx = ::bias_unsigned(x);
|
||||
__m128i by = ::bias_unsigned(y);
|
||||
__m128i bx = _impl_bias_unsigned(x);
|
||||
__m128i by = _impl_bias_unsigned(y);
|
||||
__m128i lt = _mm_cmplt_epi32(bx, by); // signed compare
|
||||
return _mm_xor_si128(lt, _mm_set1_epi32(-1)); // invert mask
|
||||
}
|
||||
}
|
||||
#else
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> _impl_less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
#if SSE_INSTR_SET >= 4
|
||||
return _mm_cmplt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );//
|
||||
#else
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_ui(X[0] < Y[0], X[1] < Y[1], X[2] < Y[2], X[3] < Y[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> _impl_lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
|
||||
#if SSE_INSTR_SET >= 2
|
||||
// a <= b <=> !(a > b)
|
||||
__m128i bx = _impl_bias_unsigned(x);
|
||||
__m128i by = _impl_bias_unsigned(y);
|
||||
__m128i gt = _mm_cmpgt_epi32(bx, by); // signed compare
|
||||
return _mm_xor_si128(gt, _mm_set1_epi32(-1)); // invert mask
|
||||
#else
|
||||
auto X = uf::simd::cast(x), Y = uf::simd::cast(y);
|
||||
return uf::simd::set_ui(X[0] <= Y[0], X[1] <= Y[1], X[2] <= Y[2], X[3] <= Y[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> _impl_greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
#if SSE_INSTR_SET >= 4
|
||||
return _mm_cmpgt_epi32( _impl_bias_unsigned(x), _impl_bias_unsigned(y) );
|
||||
#else
|
||||
auto X = uf::simd::cast( x ), Y = uf::simd::cast( y );
|
||||
return uf::simd::set_ui(X[0] > Y[0], X[1] > Y[1], X[2] > Y[2], X[3] > Y[3]);
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> _impl_greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y) {
|
||||
#if SSE_INSTR_SET >= 2
|
||||
// a >= b <=> !(a < b)
|
||||
__m128i bx = _impl_bias_unsigned(x);
|
||||
__m128i by = _impl_bias_unsigned(y);
|
||||
__m128i lt = _mm_cmplt_epi32(bx, by); // signed compare
|
||||
return _mm_xor_si128(lt, _mm_set1_epi32(-1)); // invert mask
|
||||
#else
|
||||
auto X = uf::simd::cast(x), Y = uf::simd::cast(y);
|
||||
return uf::simd::set_ui(X[0] >= Y[0], X[1] >= Y[1], X[2] >= Y[2], X[3] >= Y[3]);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::less( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return ::less_impl( x, y );
|
||||
return _impl_less( x, y );
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::lessEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return ::lessEquals_impl( x, y );
|
||||
return _impl_lessEquals( x, y );
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::greater( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return ::greater_impl( x, y );
|
||||
return _impl_greater( x, y );
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::greaterEquals( uf::simd::vector<uint32_t> x, uf::simd::vector<uint32_t> y ) {
|
||||
return ::greaterEquals_impl( x, y );
|
||||
return _impl_greaterEquals( x, y );
|
||||
}
|
||||
|
||||
|
||||
@ -604,28 +834,29 @@ FORCE_INLINE uint32_t uf::simd::dot( uf::simd::vector<uint32_t> x, uf::simd::vec
|
||||
}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<float> uf::simd::set_f( bool x, bool y, bool z, bool w ) {
|
||||
return _mm_castsi128_ps(_mm_setr_epi32(::boolMask(x), ::boolMask(y), ::boolMask(z), ::boolMask(w)));
|
||||
return _mm_castsi128_ps(_mm_setr_epi32(_impl_bool_mask(x), _impl_bool_mask(y), _impl_bool_mask(z), _impl_bool_mask(w)));
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<int32_t> uf::simd::set_i( bool x, bool y, bool z, bool w ) {
|
||||
return _mm_setr_epi32(::boolMask(x), ::boolMask(y), ::boolMask(z), ::boolMask(w));
|
||||
return _mm_setr_epi32(_impl_bool_mask(x), _impl_bool_mask(y), _impl_bool_mask(z), _impl_bool_mask(w));
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<uint32_t> uf::simd::set_ui( bool x, bool y, bool z, bool w ) {
|
||||
return _mm_setr_epi32(::boolMask(x), ::boolMask(y), ::boolMask(z), ::boolMask(w));
|
||||
return _mm_setr_epi32(_impl_bool_mask(x), _impl_bool_mask(y), _impl_bool_mask(z), _impl_bool_mask(w));
|
||||
}
|
||||
|
||||
namespace {
|
||||
/**/namespace {
|
||||
#if SIMD_MV
|
||||
MV_INSTR_SET_DEFAULT
|
||||
uf::simd::vector<float> cross_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
uf::simd::vector<float> _impl_cross( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
__m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1));
|
||||
__m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1));
|
||||
tmp0 = _mm_mul_ps(tmp0,x);
|
||||
tmp1 = _mm_mul_ps(tmp1,y);
|
||||
__m128 tmp2 = _mm_sub_ps(tmp0,tmp1);
|
||||
__m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));
|
||||
__m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));//
|
||||
return res;
|
||||
}
|
||||
MV_INSTR_SET_7
|
||||
uf::simd::vector<float> cross_impl( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
uf::simd::vector<float> _impl_cross( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
__m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1));
|
||||
__m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1));
|
||||
tmp1 = _mm_mul_ps(tmp1,y);
|
||||
@ -633,17 +864,37 @@ namespace {
|
||||
__m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));
|
||||
return res;
|
||||
}
|
||||
}
|
||||
#else
|
||||
uf::simd::vector<float> _impl_cross( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
#if SSE_INSTR_SET >= 7
|
||||
__m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1));
|
||||
__m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1));
|
||||
tmp1 = _mm_mul_ps(tmp1,y);
|
||||
__m128 tmp2 = _mm_fmsub_ps( tmp0,x, tmp1 );
|
||||
__m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));
|
||||
return res;
|
||||
#else
|
||||
__m128 tmp0 = _mm_shuffle_ps(y,y,_MM_SHUFFLE(3,0,2,1));
|
||||
__m128 tmp1 = _mm_shuffle_ps(x,x,_MM_SHUFFLE(3,0,2,1));
|
||||
tmp0 = _mm_mul_ps(tmp0,x);
|
||||
tmp1 = _mm_mul_ps(tmp1,y);
|
||||
__m128 tmp2 = _mm_sub_ps(tmp0,tmp1);
|
||||
__m128 res = _mm_shuffle_ps(tmp2,tmp2,_MM_SHUFFLE(3,0,2,1));//
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
/**/}
|
||||
|
||||
FORCE_INLINE uf::simd::vector<float> uf::simd::cross( uf::simd::vector<float> x, uf::simd::vector<float> y ) {
|
||||
return ::cross_impl( x, y );
|
||||
return _impl_cross( x, y );
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<float> uf::simd::normalize( uf::simd::vector<float> v ) {
|
||||
__m128 len = _mm_sqrt_ss( ::dot_impl( v,v ) );
|
||||
__m128 len = _mm_sqrt_ss( _impl_dot( v,v ) );
|
||||
len = _mm_shuffle_ps(len, len, 0x00);
|
||||
return _mm_div_ps(v, len);
|
||||
}
|
||||
FORCE_INLINE uf::simd::vector<float> uf::simd::normalize_fast( uf::simd::vector<float> v ) {
|
||||
__m128 invLen = _mm_rsqrt_ps(::dot_impl(v, v));
|
||||
__m128 invLen = _mm_rsqrt_ps(_impl_dot(v, v));
|
||||
return _mm_mul_ps(v, invLen);
|
||||
}
|
||||
@ -311,8 +311,9 @@ namespace uf {
|
||||
const buffer_t& getBuffer( const uf::Mesh::Input&, size_t = 0 ) const;
|
||||
const buffer_t& getBuffer( const uf::Mesh::Input&, const uf::Mesh::Attribute& ) const;
|
||||
|
||||
void eraseAttribute( uf::Mesh::Input&, const uf::Mesh::Attribute& );
|
||||
void eraseAttribute( uf::Mesh::Input&, size_t );
|
||||
void clearAttribute( uf::Mesh::Input&, const uf::Mesh::Attribute& );
|
||||
void clearAttribute( uf::Mesh::Input&, size_t );
|
||||
void clear();
|
||||
|
||||
uf::Mesh::Input remapInput( const uf::Mesh::Input&, size_t = 0, size_t = 0 ) const;
|
||||
uf::Mesh::Input remapVertexInput( size_t i = 0, size_t = 0 ) const;
|
||||
|
||||
42
engine/inc/uf/utils/text/glyph_.h
Normal file
42
engine/inc/uf/utils/text/glyph_.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <uf/config.h>
|
||||
#include <uf/utils/memory/vector.h>
|
||||
#include <uf/utils/image/atlas.h>
|
||||
#include <uf/utils/mesh/mesh.h>
|
||||
|
||||
|
||||
namespace pod {
|
||||
struct GlyphBox {
|
||||
struct {
|
||||
float x, y, w, h, z;
|
||||
} box;
|
||||
|
||||
pod::Vector4f color;
|
||||
uint64_t code;
|
||||
};
|
||||
struct TextToken {
|
||||
uf::stl::string text;
|
||||
pod::Vector4f color;
|
||||
};
|
||||
struct GlyphSettings {
|
||||
uf::stl::string alignment = "";
|
||||
uf::stl::string font = "Coolvetica.ttf";
|
||||
float size = 96;
|
||||
bool sdf = true;
|
||||
float spread = 8;
|
||||
pod::Vector2ui padding = { 8, 8 };
|
||||
};
|
||||
}
|
||||
|
||||
namespace uf {
|
||||
namespace glyph {
|
||||
size_t UF_API hashSettings( uint64_t c, const pod::GlyphSettings& metadata );
|
||||
size_t UF_API hashSettings( const uf::stl::string& c, const pod::GlyphSettings& metadata );
|
||||
|
||||
uf::stl::vector<pod::TextToken> UF_API parseTextTokens( const uf::stl::string& text, const pod::Vector4f& color = {1,1,1,1} );
|
||||
uf::stl::vector<pod::GlyphBox> UF_API calculateLayout( const uf::stl::vector<pod::TextToken>& tokens, const pod::GlyphSettings& metadata );
|
||||
bool UF_API generateAtlas( const uf::stl::vector<pod::GlyphBox>& layout, const pod::GlyphSettings& metadata, uf::Atlas& atlas );
|
||||
void UF_API generateMesh( const uf::stl::vector<pod::GlyphBox>& layout, const pod::GlyphSettings& metadata, const uf::Atlas& atlas, uf::Mesh& mesh );
|
||||
}
|
||||
}
|
||||
@ -205,6 +205,7 @@ void UF_API uf::load( ext::json::Value& json ) {
|
||||
uf::physics::settings.timestep = configEnginePhysicsJson["timestep"].as(uf::physics::settings.timestep);
|
||||
uf::physics::settings.fixedStep = configEnginePhysicsJson["fixed step"].as(uf::physics::settings.fixedStep);
|
||||
uf::physics::settings.substeps = configEnginePhysicsJson["substeps"].as(uf::physics::settings.substeps);
|
||||
uf::physics::settings.flattenTransforms = configEnginePhysicsJson["flatten transforms"].as(uf::physics::settings.flattenTransforms);
|
||||
|
||||
auto& configEnginePhysicsSolverJson = configEnginePhysicsJson["solvers"];
|
||||
if ( ext::json::isObject( configEnginePhysicsSolverJson ) ) {
|
||||
@ -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;
|
||||
|
||||
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 ) {
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 ) {}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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)),
|
||||
|
||||
@ -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] ));
|
||||
|
||||
@ -312,7 +312,10 @@ void ext::vulkan::DeferredRenderMode::initialize( Device& device ) {
|
||||
});
|
||||
|
||||
auto& shader = blitter.material.getShader("fragment");
|
||||
if ( !settings::pipelines::fsr || !ext::fsr::frameUpscale ) {
|
||||
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
|
||||
if ( !settings::pipelines::fsr || !ext::fsr::frameUpscale )
|
||||
#endif
|
||||
{
|
||||
shader.aliasAttachment("output", this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
#include <uf/ext/ffx/fsr.h>
|
||||
|
||||
VkResult ext::vulkan::Swapchain::acquireNextImage( uint32_t* imageIndex, VkSemaphore presentCompleteSemaphore, VkFence acquireFence ) {
|
||||
#if UF_USE_FFX_SDK
|
||||
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
|
||||
if ( ext::fsr::frameInterpolation ) {
|
||||
return ext::fsr::acquireNextImage( imageIndex, presentCompleteSemaphore, acquireFence );
|
||||
}
|
||||
@ -17,7 +17,7 @@ VkResult ext::vulkan::Swapchain::acquireNextImage( uint32_t* imageIndex, VkSemap
|
||||
}
|
||||
|
||||
VkResult ext::vulkan::Swapchain::queuePresent( VkQueue queue, uint32_t imageIndex, VkSemaphore waitSemaphore ) {
|
||||
#if UF_USE_FFX_SDK
|
||||
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
|
||||
if ( ext::fsr::frameInterpolation ) {
|
||||
return ext::fsr::queuePresent( queue, imageIndex, waitSemaphore );
|
||||
}
|
||||
@ -178,7 +178,7 @@ void ext::vulkan::Swapchain::initialize( Device& device ) {
|
||||
swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
#if UF_USE_FFX_SDK
|
||||
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
|
||||
VK_CHECK_RESULT(ext::fsr::createSwapchain( device.logicalDevice, &swapchainCI, nullptr, &swapChain ));
|
||||
#else
|
||||
VK_CHECK_RESULT(vkCreateSwapchainKHR( device.logicalDevice, &swapchainCI, nullptr, &swapChain ));
|
||||
@ -188,14 +188,14 @@ void ext::vulkan::Swapchain::initialize( Device& device ) {
|
||||
// If an existing swap chain is re-created, destroy the old swap chain
|
||||
// This also cleans up all the presentable images
|
||||
if ( oldSwapchain != VK_NULL_HANDLE ) {
|
||||
#if UF_USE_FFX_SDK
|
||||
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
|
||||
ext::fsr::destroySwapchain( device.logicalDevice, oldSwapchain, nullptr);
|
||||
#else
|
||||
vkDestroySwapchainKHR( device.logicalDevice, oldSwapchain, nullptr);
|
||||
#endif
|
||||
// VK_UNREGISTER_HANDLE( oldSwapchain );
|
||||
}
|
||||
#if UF_USE_FFX_SDK
|
||||
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
|
||||
VK_CHECK_RESULT(ext::fsr::getSwapchainImages( device.logicalDevice, swapChain, &buffers, NULL));
|
||||
#else
|
||||
VK_CHECK_RESULT(vkGetSwapchainImagesKHR( device.logicalDevice, swapChain, &buffers, NULL));
|
||||
@ -204,7 +204,7 @@ void ext::vulkan::Swapchain::initialize( Device& device ) {
|
||||
// Bind images
|
||||
{
|
||||
images.resize( buffers );
|
||||
#if UF_USE_FFX_SDK
|
||||
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
|
||||
VK_CHECK_RESULT(ext::fsr::getSwapchainImages( device, swapChain, &buffers, images.data()));
|
||||
#else
|
||||
VK_CHECK_RESULT(vkGetSwapchainImagesKHR( device, swapChain, &buffers, images.data()));
|
||||
@ -222,7 +222,7 @@ void ext::vulkan::Swapchain::destroy() {
|
||||
}
|
||||
swapchain.images.clear();
|
||||
if ( swapChain != VK_NULL_HANDLE ) {
|
||||
#if UF_USE_FFX_SDK
|
||||
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
|
||||
ext::fsr::destroySwapchain( *device, swapChain, nullptr );
|
||||
#else
|
||||
vkDestroySwapchainKHR( *device, swapChain, nullptr);
|
||||
|
||||
@ -504,7 +504,9 @@ void ext::vulkan::tick() {
|
||||
ext::vulkan::states::rebuild = true;
|
||||
}
|
||||
|
||||
#if UF_USE_FFX_FSR || UF_USE_FFX_SDK
|
||||
ext::fsr::tick();
|
||||
#endif
|
||||
|
||||
auto renderModes = ::fetchRenderModes();
|
||||
for ( auto& renderMode : renderModes ) {
|
||||
|
||||
@ -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 );
|
||||
}
|
||||
@ -1,38 +1,23 @@
|
||||
#include <uf/utils/image/atlas.h>
|
||||
#include <binpack2d/binpack2d.hpp>
|
||||
#include <iostream>
|
||||
#include <bit>
|
||||
|
||||
uf::Atlas::hash_t uf::Atlas::addImage( const uf::Image& _image, bool regenerate ) {
|
||||
uf::Atlas::hash_t uf::Atlas::addImage( const uf::Image& image, const uf::Atlas::hash_t& hash ) {
|
||||
size_t index = this->m_tiles.size();
|
||||
auto hash = _image.getHash();
|
||||
if ( this->m_tiles.count( hash ) > 0 ) return hash;
|
||||
|
||||
auto& tile = this->m_tiles[hash];
|
||||
tile.image = _image;
|
||||
tile.image = image;
|
||||
tile.identifier.hash = hash;
|
||||
tile.identifier.index = index;
|
||||
if ( regenerate ) this->generate();
|
||||
return hash;
|
||||
}
|
||||
uf::Atlas::hash_t uf::Atlas::addImage( uf::Image&& _image, bool regenerate ) {
|
||||
size_t index = this->m_tiles.size();
|
||||
auto hash = _image.getHash();
|
||||
if ( this->m_tiles.count( hash ) > 0 ) return hash;
|
||||
|
||||
auto& tile = this->m_tiles[hash];
|
||||
tile.image = _image;
|
||||
tile.identifier.hash = hash;
|
||||
tile.identifier.index = index;
|
||||
if ( regenerate ) this->generate();
|
||||
return hash;
|
||||
}
|
||||
uf::Atlas::hash_t uf::Atlas::addImage( const uint8_t* pointer, const pod::Vector2ui& size, std::size_t bpp, std::size_t channels, bool flip, bool regenerate ) {
|
||||
uf::Image image;
|
||||
image.loadFromBuffer( pointer, size, bpp, channels, flip );
|
||||
return this->addImage( std::move( image ), regenerate );
|
||||
uf::Atlas::hash_t uf::Atlas::addImage( const uf::Image& image ) {
|
||||
return this->addImage( image, image.getHash() );
|
||||
}
|
||||
void uf::Atlas::generate( const uf::Atlas::images_t& images, float padding ) {
|
||||
for ( auto& image : images ) this->addImage( image, true );
|
||||
for ( auto& image : images ) this->addImage( image );
|
||||
generate( padding );
|
||||
}
|
||||
void uf::Atlas::generate( float padding ) {
|
||||
@ -59,27 +44,8 @@ void uf::Atlas::generate( float padding ) {
|
||||
}
|
||||
size_t tries = 16;
|
||||
do {
|
||||
{
|
||||
// size_t area = largest.x * largest.y * this->m_tiles.size();
|
||||
size_t side = std::sqrt( area ) * padding;
|
||||
size = { side, side };
|
||||
{
|
||||
size.x--;
|
||||
size.x |= size.x >> 1;
|
||||
size.x |= size.x >> 2;
|
||||
size.x |= size.x >> 4;
|
||||
size.x |= size.x >> 8;
|
||||
size.x |= size.x >> 16;
|
||||
size.x++;
|
||||
size.y--;
|
||||
size.y |= size.y >> 1;
|
||||
size.y |= size.y >> 2;
|
||||
size.y |= size.y >> 4;
|
||||
size.y |= size.y >> 8;
|
||||
size.y |= size.y >> 16;
|
||||
size.y++;
|
||||
}
|
||||
}
|
||||
size_t side = std::sqrt( area ) * padding;
|
||||
size = { std::bit_ceil(side), std::bit_ceil(side) }; // to-do: non-C++20 method
|
||||
queue.Sort();
|
||||
internalAtlas = BinPack2D::UniformCanvasArrayBuilder<uf::Atlas::Identifier>(size.x, size.y, 1).Build();
|
||||
bool success = internalAtlas.Place( queue, remainder );
|
||||
@ -116,6 +82,9 @@ void uf::Atlas::generate( float padding ) {
|
||||
}
|
||||
}
|
||||
}
|
||||
bool uf::Atlas::has( const uf::Atlas::hash_t& hash ) const {
|
||||
return this->m_tiles.count( hash ) > 0;
|
||||
}
|
||||
bool uf::Atlas::generated() const {
|
||||
return !this->m_atlas.getPixels().empty();
|
||||
}
|
||||
@ -127,17 +96,20 @@ void uf::Atlas::clear( bool full ) {
|
||||
this->m_tiles.clear();
|
||||
this->m_atlas.clear();
|
||||
}
|
||||
pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, const uf::Atlas::hash_t& hash ) {
|
||||
auto& size = this->m_atlas.getDimensions();
|
||||
for ( auto pair : this->m_tiles ) {
|
||||
auto& tile = pair.second;
|
||||
if ( tile.identifier.hash != hash ) continue;
|
||||
pod::Vector2ui coord = { uv.x * tile.size.x + tile.coord.x, uv.y * tile.size.y + tile.coord.y };
|
||||
pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, const uf::Atlas::hash_t& hash ) const {
|
||||
auto it = this->m_tiles.find(hash);
|
||||
if ( it != this->m_tiles.end() ) {
|
||||
auto& tile = it->second;
|
||||
auto& size = this->m_atlas.getDimensions();
|
||||
pod::Vector2ui coord = {
|
||||
uv.x * tile.size.x + tile.coord.x,
|
||||
uv.y * tile.size.y + tile.coord.y
|
||||
};
|
||||
return pod::Vector2f{ (float) coord.x / (float) size.x, (float) coord.y / (float) size.y };
|
||||
}
|
||||
return uv;
|
||||
}
|
||||
pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, size_t index ) {
|
||||
pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, size_t index ) const {
|
||||
auto& size = this->m_atlas.getDimensions();
|
||||
for ( auto pair : this->m_tiles ) {
|
||||
auto& tile = pair.second;
|
||||
@ -147,7 +119,7 @@ pod::Vector2f uf::Atlas::mapUv( const pod::Vector2f& uv, size_t index ) {
|
||||
}
|
||||
return uv;
|
||||
}
|
||||
pod::Vector3f uf::Atlas::mapUv( const pod::Vector3f& uv ) {
|
||||
pod::Vector3f uf::Atlas::mapUv( const pod::Vector3f& uv ) const {
|
||||
pod::Vector2f nuv = mapUv( { uv.x, uv.y }, uv.z );
|
||||
return { nuv.x, nuv.y, uv.z };
|
||||
}
|
||||
|
||||
@ -64,10 +64,12 @@ void impl::updateActivity( pod::PhysicsBody& body, float dt ) {
|
||||
// returns an absolute transform while also allowing offsetting the collision body
|
||||
// to-do: find a succinct way to explain this madness
|
||||
pod::Transform<> impl::getTransform( const pod::PhysicsBody& body ) {
|
||||
pod::Transform<> t;
|
||||
t.position = body.offsetPosition;
|
||||
t.orientation = body.offsetOrientation;
|
||||
t.reference = body.transform;
|
||||
pod::Transform<> t = {
|
||||
.position = body.offsetPosition,
|
||||
.orientation = body.offsetOrientation,
|
||||
.scale = {1, 1, 1},
|
||||
.reference = body.transform,
|
||||
};
|
||||
return uf::transform::flatten( t );
|
||||
}
|
||||
// get position of a body, uses bounds center or transform's position
|
||||
@ -78,6 +80,7 @@ pod::Vector3f impl::getPosition( const pod::PhysicsBody& body, bool useTransform
|
||||
// applies a transform
|
||||
pod::Vector3f impl::apply( const pod::Transform<>& t, const pod::Vector3f& p ) {
|
||||
return uf::transform::apply( t, p );
|
||||
// return uf::quaternion::rotate( t.orientation, p * t.scale ) + t.position; // explicitly needed to copy or GCC breaks
|
||||
}
|
||||
// applies an inverse transform
|
||||
pod::Vector3f impl::applyInverse( const pod::Transform<>& t, const pod::Vector3f& p ) {
|
||||
@ -528,7 +531,7 @@ pod::TriangleWithNormal impl::fetchTriangle( const uf::Mesh& mesh, size_t triID,
|
||||
|
||||
if ( body.collider.type == pod::ShapeType::MESH || body.collider.type == pod::ShapeType::CONVEX_HULL ) {
|
||||
FOR_EACH(3, {
|
||||
tri.points[i] = uf::transform::apply( transform, tri.points[i] );
|
||||
tri.points[i] = impl::apply( transform, tri.points[i] );
|
||||
});
|
||||
tri.normal = uf::quaternion::rotate( transform.orientation, tri.normal );
|
||||
}
|
||||
@ -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 );
|
||||
});
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -150,8 +150,8 @@ pod::Constraint& uf::physics::constrainConeTwist( pod::Constraint& constraint, c
|
||||
|
||||
auto& joint = constraint.coneTwist;
|
||||
constraint.type = pod::ConstraintType::CONE_TWIST;
|
||||
joint.localAnchorA = uf::transform::applyInverse( tA, p );
|
||||
joint.localAnchorB = uf::transform::applyInverse( tB, p );
|
||||
joint.localAnchorA = impl::applyInverse( tA, p );
|
||||
joint.localAnchorB = impl::applyInverse( tB, p );
|
||||
|
||||
joint.accumulatedImpulse = {};
|
||||
joint.accumulatedAngularImpulse = {};
|
||||
@ -176,8 +176,8 @@ void impl::drawConeTwist( const pod::Constraint& constraint ) {
|
||||
auto tA = impl::getTransform( *constraint.a );
|
||||
auto tB = impl::getTransform( *constraint.b );
|
||||
|
||||
auto pA = uf::transform::apply( tA, joint.localAnchorA );
|
||||
auto pB = uf::transform::apply( tB, joint.localAnchorB );
|
||||
auto pA = impl::apply( tA, joint.localAnchorA );
|
||||
auto pB = impl::apply( tB, joint.localAnchorB );
|
||||
|
||||
auto taA = uf::quaternion::rotate( tA.orientation, joint.localTwistAxisA );
|
||||
auto taB = uf::quaternion::rotate( tB.orientation, joint.localTwistAxisB );
|
||||
|
||||
@ -104,8 +104,8 @@ void impl::computeLocalManifold( pod::Manifold& manifold ) {
|
||||
auto tB = impl::getTransform( b );
|
||||
|
||||
for ( auto& c : manifold.points ) {
|
||||
c.localA = uf::transform::applyInverse( tA, c.point - c.normal * (c.penetration * 0.5f) );
|
||||
c.localB = uf::transform::applyInverse( tB, c.point + c.normal * (c.penetration * 0.5f) );
|
||||
c.localA = impl::applyInverse( tA, c.point - c.normal * (c.penetration * 0.5f) );
|
||||
c.localB = impl::applyInverse( tB, c.point + c.normal * (c.penetration * 0.5f) );
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,8 +243,8 @@ void impl::retrieveManifold( pod::Manifold& current, const pod::Manifold& previo
|
||||
float distSqThresh = distanceThreshold * distanceThreshold;
|
||||
for ( const auto& oldContact : previous.points ) {
|
||||
// reproject point according to current transform
|
||||
auto worldA = uf::transform::apply( tA, oldContact.localA );
|
||||
auto worldB = uf::transform::apply( tB, oldContact.localB );
|
||||
auto worldA = impl::apply( tA, oldContact.localA );
|
||||
auto worldB = impl::apply( tB, oldContact.localB );
|
||||
|
||||
auto delta = worldB - worldA;
|
||||
auto normal = current.points.empty() ? oldContact.normal : current.points[0].normal;
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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 );
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -58,8 +58,8 @@ pod::Constraint& uf::physics::constrainWeld( pod::Constraint& constraint, const
|
||||
|
||||
auto& joint = constraint.weld;
|
||||
constraint.type = pod::ConstraintType::WELD;
|
||||
joint.localAnchorA = uf::transform::applyInverse( tA, p );
|
||||
joint.localAnchorB = uf::transform::applyInverse( tB, p );
|
||||
joint.localAnchorA = impl::applyInverse( tA, p );
|
||||
joint.localAnchorB = impl::applyInverse( tB, p );
|
||||
|
||||
joint.localAxisA = uf::quaternion::rotate( invqA, axis );
|
||||
joint.localAxisB = uf::quaternion::rotate( invqB, axis );
|
||||
|
||||
@ -17,7 +17,8 @@ void impl::drawManifold( const pod::Manifold& manifold ) {
|
||||
}
|
||||
void impl::drawBody( const pod::PhysicsBody& body ) {
|
||||
if ( !(body.collider.category & uf::physics::settings.debugDraw.mask) ) return;
|
||||
switch( body.collider.type ) {
|
||||
// draw wireframe
|
||||
switch ( body.collider.type ) {
|
||||
case pod::ShapeType::AABB:
|
||||
impl::drawAabb( body );
|
||||
break;
|
||||
@ -43,6 +44,48 @@ void impl::drawBody( const pod::PhysicsBody& body ) {
|
||||
impl::drawHull( body );
|
||||
break;
|
||||
}
|
||||
// draw name
|
||||
auto& scene = uf::scene::getCurrentScene();
|
||||
auto& controller = scene.getController();
|
||||
auto& camera = scene.getCamera( controller );
|
||||
auto& bounds = body.bounds;
|
||||
|
||||
auto transform = impl::getTransform( body );
|
||||
auto cameraTransform = uf::transform::flatten( camera.getTransform() );
|
||||
auto cameraAxes = uf::transform::axes( cameraTransform );
|
||||
auto& projection = camera.getProjection();
|
||||
auto fov = std::atan(1.0f / fabs(projection(1,1)));
|
||||
auto viewThreshold = std::cos(fov * 1.5f);
|
||||
|
||||
#if 1
|
||||
// (an attempt to) continuously pick the closest point on the AABB
|
||||
auto position = impl::closestPointOnAABB( cameraTransform.position, bounds );
|
||||
auto dir = position - cameraTransform.position;
|
||||
auto magSq = uf::vector::magnitude( dir );
|
||||
if ( magSq > EPS2 ) dir /= std::sqrt( magSq );
|
||||
auto dot = uf::vector::dot( cameraAxes.forward, dir );
|
||||
position -= cameraAxes.forward * 0.1f;
|
||||
if ( dot > viewThreshold ) uf::debug::drawText( body.object->getName(), position );
|
||||
#else
|
||||
// picks the closest corner
|
||||
int closestCorner = -1;
|
||||
float closestDistanceSq = FLT_MAX;
|
||||
float closestDot = FLT_MAX; //
|
||||
pod::Vector3f corners[8];
|
||||
impl::getCorners( bounds, corners );
|
||||
for ( auto i = 0; i < 8; ++i ) {
|
||||
auto dir = corners[i] - cameraTransform.position;
|
||||
auto distanceSq = uf::vector::magnitude( dir );
|
||||
if ( distanceSq > EPS2 ) dir /= std::sqrt( distanceSq );
|
||||
auto dot = uf::vector::dot( cameraAxes.forward, dir );
|
||||
if ( dot <= viewThreshold ) continue;
|
||||
if ( distanceSq >= closestDistanceSq ) continue;
|
||||
closestDistanceSq = distanceSq;
|
||||
closestCorner = i;
|
||||
}
|
||||
|
||||
if ( 0 <= closestCorner ) uf::debug::drawText( body.object->getName(), corners[closestCorner] );
|
||||
#endif
|
||||
}
|
||||
void impl::drawConstraint( const pod::Constraint& constraint ) {
|
||||
if ( !uf::physics::settings.debugDraw.constraints ) return;
|
||||
|
||||
@ -295,7 +295,7 @@ void uf::physics::step( pod::World& world, float dt ) {
|
||||
parentTransform = *transformsMap[originalTransform->reference];
|
||||
}
|
||||
|
||||
transform.position = uf::transform::applyInverse( parentTransform, flattenedTransform.position );
|
||||
transform.position = impl::applyInverse( parentTransform, flattenedTransform.position );
|
||||
transform.orientation = uf::quaternion::multiply(
|
||||
uf::quaternion::inverse( parentTransform.orientation ),
|
||||
flattenedTransform.orientation
|
||||
@ -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;
|
||||
|
||||
@ -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 ) {
|
||||
|
||||
@ -109,8 +109,8 @@ bool impl::capsuleHull( const pod::PhysicsBody& a, const pod::PhysicsBody& b, po
|
||||
void impl::drawCapsule( const pod::PhysicsBody& body ) {
|
||||
const auto& capsule = body.collider.capsule;
|
||||
auto transform = impl::getTransform(body);
|
||||
#if 0
|
||||
uf::debug::drawCapsule( capsule, transform );
|
||||
#if 1
|
||||
uf::debug::drawShape( capsule, transform );
|
||||
#else
|
||||
auto [p1, p2] = impl::getCapsuleSegment(body);
|
||||
const int segments = 16;
|
||||
|
||||
@ -47,7 +47,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di
|
||||
outCount = 3;
|
||||
bool hasTransform = ( body.transform != nullptr );
|
||||
FOR_EACH(3, {
|
||||
outPoly[i] = hasTransform ? uf::transform::apply( transform, body.collider.triangle.points[i] ) : body.collider.triangle.points[i];
|
||||
outPoly[i] = hasTransform ? impl::apply( transform, body.collider.triangle.points[i] ) : body.collider.triangle.points[i];
|
||||
});
|
||||
} break;
|
||||
case pod::ShapeType::AABB: {
|
||||
@ -81,7 +81,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di
|
||||
if (n.z < 0) std::swap(outPoly[1], outPoly[3]);
|
||||
}
|
||||
FOR_EACH(4, {
|
||||
outPoly[i] = uf::transform::apply(transform, outPoly[i]);
|
||||
outPoly[i] = impl::apply(transform, outPoly[i]);
|
||||
});
|
||||
} break;
|
||||
case pod::ShapeType::OBB: {
|
||||
@ -115,7 +115,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di
|
||||
if (n.z < 0) std::swap(outPoly[1], outPoly[3]);
|
||||
}
|
||||
FOR_EACH(4, {
|
||||
outPoly[i] = uf::transform::apply(transform, outPoly[i]);
|
||||
outPoly[i] = impl::apply(transform, outPoly[i]);
|
||||
});
|
||||
} break;
|
||||
case pod::ShapeType::SPHERE: {
|
||||
@ -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
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 ) {
|
||||
|
||||
@ -70,8 +70,8 @@ void impl::drawPlane( const pod::PhysicsBody& body ) {
|
||||
const auto& plane = body.collider.plane;
|
||||
auto transform = impl::getTransform(body);
|
||||
|
||||
#if 0
|
||||
uf::debug::drawPlane( plane, transform );
|
||||
#if 1
|
||||
uf::debug::drawShape( plane, transform );
|
||||
#else
|
||||
pod::Vector3f right = uf::quaternion::rotate(transform.orientation, pod::Vector3f{1, 0, 0});
|
||||
pod::Vector3f forward = uf::quaternion::rotate(transform.orientation, pod::Vector3f{0, 0, 1});
|
||||
|
||||
@ -82,7 +82,7 @@ bool impl::rayObb( const pod::Ray& ray, const pod::PhysicsBody& body, pod::RayQu
|
||||
auto tA = impl::getTransform( body );
|
||||
|
||||
pod::Ray localRay;
|
||||
localRay.origin = uf::transform::applyInverse( tA, ray.origin );
|
||||
localRay.origin = impl::applyInverse( tA, ray.origin );
|
||||
localRay.direction = uf::quaternion::rotate( uf::quaternion::inverse(tA.orientation), ray.direction );
|
||||
|
||||
auto box = impl::obbToAabb( body.collider.obb );
|
||||
@ -235,7 +235,7 @@ bool impl::rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue
|
||||
const auto transform = impl::getTransform( body );
|
||||
|
||||
pod::Ray ray;
|
||||
ray.origin = uf::transform::applyInverse( transform, r.origin );
|
||||
ray.origin = impl::applyInverse( transform, r.origin );
|
||||
ray.direction = uf::quaternion::rotate( uf::quaternion::inverse( transform.orientation ), r.direction );
|
||||
|
||||
thread_local uf::stl::vector<pod::BVH::index_t> candidates;
|
||||
@ -253,7 +253,7 @@ bool impl::rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue
|
||||
auto n = impl::triangleNormal( tri );
|
||||
|
||||
// push back to world
|
||||
auto p = uf::transform::apply( transform, l);
|
||||
auto p = impl::apply( transform, l);
|
||||
n = uf::quaternion::rotate( transform.orientation, n );
|
||||
|
||||
rayHit.hit = true;
|
||||
@ -273,7 +273,7 @@ bool impl::rayHull( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue
|
||||
const auto transform = impl::getTransform( body );
|
||||
|
||||
pod::Ray ray;
|
||||
ray.origin = uf::transform::applyInverse( transform, r.origin );
|
||||
ray.origin = impl::applyInverse( transform, r.origin );
|
||||
ray.direction = uf::quaternion::rotate( uf::quaternion::inverse( transform.orientation ), r.direction );
|
||||
|
||||
thread_local uf::stl::vector<pod::BVH::index_t> candidates;
|
||||
@ -292,7 +292,7 @@ bool impl::rayHull( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue
|
||||
rayHit.hit = true;
|
||||
rayHit.body = &body;
|
||||
|
||||
rayHit.contact.point = uf::transform::apply( transform, ray.origin + ray.direction * t );
|
||||
rayHit.contact.point = impl::apply( transform, ray.origin + ray.direction * t );
|
||||
rayHit.contact.normal = uf::quaternion::rotate( transform.orientation, normal );
|
||||
rayHit.contact.penetration = t;
|
||||
}
|
||||
@ -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];
|
||||
|
||||
|
||||
@ -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 ) {
|
||||
|
||||
@ -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 ) {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 tri’s base)
|
||||
|
||||
@ -228,15 +228,26 @@ uf::Mesh uf::Mesh::expand( bool interleaved ) {
|
||||
return res;
|
||||
}
|
||||
|
||||
void uf::Mesh::eraseAttribute( uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute ) {
|
||||
for ( size_t i = 0; i < input.attributes.size(); ++i ) if ( input.attributes[i].descriptor == attribute.descriptor ) return eraseAttribute( input, i );
|
||||
void uf::Mesh::clearAttribute( uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute ) {
|
||||
for ( size_t i = 0; i < input.attributes.size(); ++i ) if ( input.attributes[i].descriptor == attribute.descriptor ) return clearAttribute( input, i );
|
||||
}
|
||||
void uf::Mesh::eraseAttribute( uf::Mesh::Input& input, size_t i ) {
|
||||
void uf::Mesh::clearAttribute( uf::Mesh::Input& input, size_t i ) {
|
||||
UF_ASSERT( !isInterleaved( input ) ); // can't be assed to de-interleave, erase, and then interleave again
|
||||
|
||||
auto attribute = input.attributes[i];
|
||||
buffers[attribute.buffer].clear();
|
||||
}
|
||||
void uf::Mesh::clear() {
|
||||
for ( size_t i = 0; i < vertex.attributes.size(); ++i ) clearAttribute( vertex, i );
|
||||
for ( size_t i = 0; i < index.attributes.size(); ++i ) clearAttribute( index, i );
|
||||
for ( size_t i = 0; i < instance.attributes.size(); ++i ) clearAttribute( instance, i );
|
||||
for ( size_t i = 0; i < indirect.attributes.size(); ++i ) clearAttribute( indirect, i );
|
||||
|
||||
vertex.count = 0;
|
||||
index.count = 0;
|
||||
instance.count = 0;
|
||||
indirect.count = 0;
|
||||
}
|
||||
|
||||
void uf::Mesh::generateIndirect() {
|
||||
if ( index.count == 0 ) generateIndices();
|
||||
|
||||
329
engine/src/utils/text/glyph_.cpp
Normal file
329
engine/src/utils/text/glyph_.cpp
Normal file
@ -0,0 +1,329 @@
|
||||
#include <uf/utils/text/glyph.h>
|
||||
#include <uf/utils/text/glyph_.h>
|
||||
|
||||
#if UF_USE_OPENGL
|
||||
#define EXT_COLOR_FLOATS 0
|
||||
#else
|
||||
#define EXT_COLOR_FLOATS 1
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
struct {
|
||||
#if UF_ENV_DREAMCAST
|
||||
pod::Vector2ui size = { 640, 480 };
|
||||
#else
|
||||
pod::Vector2ui size = { 1920, 1080 };
|
||||
#endif
|
||||
} defaults;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct GlyphVertex {
|
||||
pod::Vector3f position;
|
||||
pod::Vector2f uv;
|
||||
|
||||
#if EXT_COLOR_FLOATS
|
||||
pod::Vector4f color;
|
||||
#else
|
||||
pod::Vector4b color;
|
||||
#endif
|
||||
|
||||
static uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
|
||||
};
|
||||
}
|
||||
|
||||
UF_VERTEX_DESCRIPTOR(GlyphVertex,
|
||||
UF_VERTEX_DESCRIPTION(GlyphVertex, R32G32B32_SFLOAT, position)
|
||||
UF_VERTEX_DESCRIPTION(GlyphVertex, R32G32_SFLOAT, uv)
|
||||
#if EXT_COLOR_FLOATS
|
||||
UF_VERTEX_DESCRIPTION(GlyphVertex, R32G32B32A32_SFLOAT, color)
|
||||
#else
|
||||
UF_VERTEX_DESCRIPTION(GlyphVertex, R8G8B8A8_UNORM, color)
|
||||
#endif
|
||||
)
|
||||
|
||||
namespace {
|
||||
struct {
|
||||
#if UF_USE_FREETYPE
|
||||
ext::freetype::Glyph glyph;
|
||||
uf::stl::unordered_map<uf::stl::string, uf::stl::unordered_map<size_t, uf::Glyph>> cache;
|
||||
#else
|
||||
char glyph;
|
||||
uf::stl::unordered_map<uf::stl::string, uf::stl::unordered_map<size_t, char>> cache;
|
||||
#endif
|
||||
} glyphs;
|
||||
|
||||
float hexToFloat( const uf::stl::string& str ) {
|
||||
int value;
|
||||
uf::stl::stringstream stream;
|
||||
stream << str;
|
||||
stream >> std::hex >> value;
|
||||
return value / 255.0f;
|
||||
}
|
||||
|
||||
// default to left
|
||||
pod::Vector2f parseAnchor( const uf::stl::string& anchor, const pod::Vector2f& def = {0.0f, 0.0f} ) {
|
||||
if ( anchor == "top-center" || anchor == "top" ) return {0.5f, 0.0f};
|
||||
if ( anchor == "top-left" ) return {0.0f, 0.0f};
|
||||
if ( anchor == "top-right" ) return {1.0f, 0.0f};
|
||||
|
||||
if ( anchor == "center" ) return {0.5f, 0.5f};
|
||||
if ( anchor == "center-left" || anchor == "left" ) return {0.0f, 0.5f};
|
||||
if ( anchor == "center-right" || anchor == "right" ) return {1.0f, 0.5f};
|
||||
|
||||
if ( anchor == "bottom-center" || anchor == "bottom" ) return {0.5f, 1.0f};
|
||||
if ( anchor == "bottom-left" ) return {0.0f, 1.0f};
|
||||
if ( anchor == "bottom-right" ) return {1.0f, 1.0f};
|
||||
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
size_t uf::glyph::hashSettings( uint64_t c, const pod::GlyphSettings& metadata ) {
|
||||
size_t seed{};
|
||||
uf::hash( seed, c, metadata.padding[0], metadata.padding[1], metadata.spread, metadata.size, metadata.font, metadata.sdf );
|
||||
return seed;
|
||||
}
|
||||
size_t uf::glyph::hashSettings( const uf::stl::string& c, const pod::GlyphSettings& metadata ) {
|
||||
size_t seed{};
|
||||
uf::hash( seed, c, metadata.padding[0], metadata.padding[1], metadata.spread, metadata.size, metadata.font, metadata.sdf );
|
||||
return seed;
|
||||
}
|
||||
|
||||
|
||||
// parses a text for special tokens, associating strings with color
|
||||
// should maybe be utf8, but in theory it shouldn't matter since tags are ASCII
|
||||
uf::stl::vector<pod::TextToken> uf::glyph::parseTextTokens( const uf::stl::string& text, const pod::Vector4f& color ) {
|
||||
uf::stl::vector<pod::TextToken> tokens;
|
||||
|
||||
auto tagLength = 12; // hard-coded cringe
|
||||
bool colorChanged = false;
|
||||
size_t currentPos = 0;
|
||||
size_t textLength = text.length();
|
||||
|
||||
pod::TextToken currentToken;
|
||||
currentToken.color = color;
|
||||
|
||||
while ( currentPos < textLength ) {
|
||||
size_t tagStart = text.find("${#", currentPos);
|
||||
|
||||
if ( tagStart == uf::stl::string::npos ) {
|
||||
currentToken.text += text.substr(currentPos);
|
||||
if ( !currentToken.text.empty() || colorChanged ) {
|
||||
tokens.emplace_back(currentToken);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( tagStart > currentPos ) {
|
||||
currentToken.text += text.substr( currentPos, tagStart - currentPos ); // append everything up to the tag
|
||||
tokens.emplace_back( currentToken ); // commit token
|
||||
currentToken.text = ""; // reset text
|
||||
}
|
||||
|
||||
// validate tag
|
||||
if ( tagStart + tagLength <= textLength && text[tagStart + tagLength - 1] == '}' ) {
|
||||
uf::stl::string rHex = text.substr(tagStart + 3, 2);
|
||||
uf::stl::string gHex = text.substr(tagStart + 5, 2);
|
||||
uf::stl::string bHex = text.substr(tagStart + 7, 2);
|
||||
uf::stl::string aHex = text.substr(tagStart + 9, 2);
|
||||
|
||||
// change color
|
||||
currentToken.color = { hexToFloat(rHex), hexToFloat(gHex), hexToFloat(bHex), hexToFloat(aHex) };
|
||||
colorChanged = true;
|
||||
|
||||
// advance past the tag
|
||||
currentPos = tagStart + tagLength;
|
||||
} else {
|
||||
currentToken.text += "${#"; // treat it as normal text
|
||||
currentPos = tagStart + 3;
|
||||
}
|
||||
}
|
||||
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// compute the boxes for a given string and settings
|
||||
uf::stl::vector<pod::GlyphBox> uf::glyph::calculateLayout( const uf::stl::vector<pod::TextToken>& tokens, const pod::GlyphSettings& metadata ) {
|
||||
uf::stl::vector<pod::GlyphBox> layout;
|
||||
auto& glyphsCache = ::glyphs.cache[metadata.font];
|
||||
|
||||
if ( glyphsCache.empty() ) {
|
||||
ext::freetype::initialize( ::glyphs.glyph, uf::io::root + "/fonts/" + metadata.font );
|
||||
}
|
||||
|
||||
pod::Vector2f anchor = ::parseAnchor( metadata.alignment );
|
||||
pod::Vector2f cursor = { 0.0f, 0.0f };
|
||||
float maxTextWidth = 0.0f;
|
||||
float maxTextHeight = 0.0f;
|
||||
float tallestGlyphY = 0.0f;
|
||||
float averageTabWidth = 0.0f;
|
||||
float totalWidth = 0.0f;
|
||||
int charCount = 0;
|
||||
|
||||
// generate glyph and line height
|
||||
for ( const auto& token : tokens ) {
|
||||
std::u8string str(token.text.begin(), token.text.end());
|
||||
for ( uint64_t c : str ) {
|
||||
if ( c == '\n' || c == '\t' ) continue; // special characters
|
||||
|
||||
auto key = uf::glyph::hashSettings(c, metadata);
|
||||
auto& glyph = glyphsCache[key];
|
||||
|
||||
// generate glyph
|
||||
if ( !glyph.generated() ) {
|
||||
glyph.setPadding({ metadata.padding[0], metadata.padding[1] });
|
||||
glyph.setSpread(metadata.spread);
|
||||
glyph.useSdf(metadata.sdf);
|
||||
glyph.generate(::glyphs.glyph, c, metadata.size);
|
||||
}
|
||||
|
||||
tallestGlyphY = std::max(tallestGlyphY, (float) glyph.getSize().y);
|
||||
totalWidth += glyph.getSize().x; // should probably be reset on new-line to find the widest line
|
||||
charCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( charCount > 0 ) averageTabWidth = (totalWidth / charCount) * 4.0f;
|
||||
cursor.y = tallestGlyphY;
|
||||
|
||||
// calculate positions
|
||||
for ( const auto& token : tokens ) {
|
||||
std::u8string str( token.text.begin(), token.text.end() );
|
||||
for ( uint64_t c : str ) {
|
||||
// advance cursor on special characters
|
||||
if ( c == '\n' ) {
|
||||
cursor.y += tallestGlyphY;
|
||||
cursor.x = 0;
|
||||
continue;
|
||||
} else if ( c == '\t' ) {
|
||||
cursor.x = ((int)(cursor.x / averageTabWidth) + 1) * averageTabWidth;
|
||||
continue;
|
||||
} else if ( c == ' ' ) {
|
||||
cursor.x += averageTabWidth / 4.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
// retrieve glyph
|
||||
auto key = uf::glyph::hashSettings(c, metadata);
|
||||
auto& glyph = glyphsCache[key];
|
||||
auto& g = layout.emplace_back(pod::GlyphBox{
|
||||
.box = {
|
||||
.x = cursor.x + glyph.getBearing().x,
|
||||
.y = cursor.y - glyph.getBearing().y,
|
||||
.w = glyph.getSize().x,
|
||||
.h = glyph.getSize().y,
|
||||
.z = 0,
|
||||
},
|
||||
.color = token.color,
|
||||
.code = c,
|
||||
});
|
||||
|
||||
// advance cursor
|
||||
cursor.x += glyph.getAdvance().x;
|
||||
|
||||
// advance bounding box
|
||||
maxTextWidth = std::max(maxTextWidth, g.box.x + g.box.w);
|
||||
maxTextHeight = std::max(maxTextHeight, g.box.y + g.box.h);
|
||||
}
|
||||
}
|
||||
|
||||
// calculate offset based on anchor
|
||||
float offsetX = maxTextWidth * anchor.x;
|
||||
float offsetY = maxTextHeight * anchor.y;
|
||||
|
||||
// adjust all glyphs for our offset
|
||||
for ( auto& g : layout ) {
|
||||
g.box.x -= offsetX;
|
||||
g.box.y -= offsetY;
|
||||
|
||||
// normalize
|
||||
g.box.x /= ::defaults.size.x;
|
||||
g.box.w /= ::defaults.size.x;
|
||||
g.box.y /= ::defaults.size.y;
|
||||
g.box.h /= ::defaults.size.y;
|
||||
}
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
// generate the mesh and texture atlas
|
||||
bool uf::glyph::generateAtlas( const uf::stl::vector<pod::GlyphBox>& layout, const pod::GlyphSettings& metadata, uf::Atlas& atlas ) {
|
||||
bool dirty = false;
|
||||
auto& cache = ::glyphs.cache[metadata.font];
|
||||
|
||||
#if UF_USE_FREETYPE
|
||||
// generate atlas
|
||||
for ( const auto& g : layout ) {
|
||||
auto key = uf::glyph::hashSettings( g.code, metadata );
|
||||
auto hash = std::to_string( key );
|
||||
auto& glyph = cache[key];
|
||||
|
||||
// already in atlas map
|
||||
if ( atlas.has( hash ) ) continue;
|
||||
dirty = true;
|
||||
|
||||
uf::Image image;
|
||||
const uint8_t* buffer = glyph.getBuffer();
|
||||
|
||||
if ( metadata.sdf ) {
|
||||
image.loadFromBuffer( glyph.getBuffer(), glyph.getSize(), 8, 1, true );
|
||||
} else {
|
||||
uf::Image::container_t pixels;
|
||||
size_t len = glyph.getSize().x * glyph.getSize().y;
|
||||
pixels.resize(len * 4);
|
||||
for ( auto i = 0; i < len; ++i ) {
|
||||
pixels[i * 4 + 0] = buffer[i]; // R
|
||||
pixels[i * 4 + 1] = buffer[i]; // G
|
||||
pixels[i * 4 + 2] = buffer[i]; // B
|
||||
pixels[i * 4 + 3] = buffer[i]; // A
|
||||
}
|
||||
image.loadFromBuffer( &pixels[0], glyph.getSize(), 8, 4, true );
|
||||
}
|
||||
atlas.addImage( image, hash );
|
||||
}
|
||||
|
||||
atlas.generate();
|
||||
#endif
|
||||
return dirty;
|
||||
}
|
||||
void uf::glyph::generateMesh( const uf::stl::vector<pod::GlyphBox>& layout, const pod::GlyphSettings& metadata, const uf::Atlas& atlas, uf::Mesh& mesh ) {
|
||||
// generate mesh
|
||||
mesh.clear();
|
||||
mesh.bind<::GlyphVertex, uint16_t>();
|
||||
|
||||
uf::stl::vector<::GlyphVertex> vertices;
|
||||
uf::stl::vector<uint16_t> indices;
|
||||
vertices.reserve(layout.size() * 4);
|
||||
indices.reserve(layout.size() * 6);
|
||||
|
||||
for ( const auto& g : layout ) {
|
||||
auto hash = std::to_string( uf::glyph::hashSettings(g.code, metadata) );
|
||||
|
||||
// zero-width
|
||||
if ( g.box.w == 0 || g.box.h == 0 || g.color.w == 0.0f ) continue;
|
||||
|
||||
#if EXT_COLOR_FLOATS
|
||||
auto& color = g.color;
|
||||
#else
|
||||
pod::Vector4b color = {
|
||||
(uint8_t)(g.color[0] * 255),
|
||||
(uint8_t)(g.color[1] * 255),
|
||||
(uint8_t)(g.color[2] * 255),
|
||||
(uint8_t)(g.color[3] * 255)
|
||||
};
|
||||
#endif
|
||||
// insert indices
|
||||
uint16_t idx = (uint16_t) vertices.size();
|
||||
indices.insert( indices.end(), { idx, idx + 1, idx + 2, idx, idx + 2, idx + 3 });
|
||||
|
||||
// insert vertices
|
||||
vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x, g.box.y + g.box.h, g.box.z }, atlas.mapUv(pod::Vector2f{ 0.0f, 0.0f }, hash), color});
|
||||
vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x, g.box.y, g.box.z }, atlas.mapUv(pod::Vector2f{ 0.0f, 1.0f }, hash), color});
|
||||
vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x + g.box.w, g.box.y, g.box.z }, atlas.mapUv(pod::Vector2f{ 1.0f, 1.0f }, hash), color});
|
||||
vertices.emplace_back(::GlyphVertex{pod::Vector3f{ g.box.x + g.box.w, g.box.y + g.box.h, g.box.z }, atlas.mapUv(pod::Vector2f{ 1.0f, 0.0f }, hash), color});
|
||||
}
|
||||
|
||||
mesh.insertVertices(vertices);
|
||||
mesh.insertIndices(indices);
|
||||
}
|
||||
@ -20,7 +20,7 @@
|
||||
|
||||
|
||||
UF_BEHAVIOR_REGISTER_CPP(ext::CraetureBehavior)
|
||||
UF_BEHAVIOR_TRAITS_CPP(ext::CraetureBehavior, ticks = true, renders = false, thread = "")
|
||||
UF_BEHAVIOR_TRAITS_CPP(ext::CraetureBehavior, ticks = false, renders = false, thread = "")
|
||||
#define this (&self)
|
||||
namespace {
|
||||
void load( uf::Object& self, const uf::Image& image ) {
|
||||
|
||||
@ -3,8 +3,8 @@ CDIR =
|
||||
CC = clang
|
||||
CXX = clang++
|
||||
OPTIMIZATIONS = -O3 -fstrict-aliasing -DUF_NO_EXCEPTIONS # -flto # -march=native
|
||||
WARNINGS = -Wall -Wno-deprecated-literal-operator -Wno-pointer-arith -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder-ctor -Wno-ignored-attributes -Wno-c++11-narrowing -Wno-unknown-pragmas -Wno-nullability-completeness -Wno-defaulted-function-deleted -Wno-mismatched-tags
|
||||
SANITIZE = -fsanitize=address # -fuse-ld=lld -fno-omit-frame-pointer
|
||||
WARNINGS = -Wall -Wno-missing-braces -Wno-deprecated-literal-operator -Wno-pointer-arith -Wno-unused-function -Wno-unused-variable -Wno-switch -Wno-reorder-ctor -Wno-ignored-attributes -Wno-c++11-narrowing -Wno-unknown-pragmas -Wno-nullability-completeness -Wno-defaulted-function-deleted -Wno-mismatched-tags
|
||||
SANITIZE = -fsanitize=address,undefined # -fuse-ld=lld -fno-omit-frame-pointer
|
||||
FLAGS += -std=c++2b $(OPTIMIZATIONS) $(WARNINGS) $(SANITIZE) -fcolor-diagnostics -fansi-escape-codes
|
||||
|
||||
# MSYS2
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
ifneq (,$(findstring -DUF_DEV_ENV,$(FLAGS)))
|
||||
REQ_DEPS += meshoptimizer toml xatlas curl ffx:sdk dc:texconv # vall_e cpptrace # openvr # ncurses draco discord bullet ultralight-ux
|
||||
REQ_DEPS += meshoptimizer toml xatlas curl dc:texconv # ffx:sdk vall_e cpptrace # openvr # ncurses draco discord bullet ultralight-ux
|
||||
FLAGS += -march=native -g # -flto # -g
|
||||
endif
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user