attempt at generating a ragdoll from a skinned mesh (doesn't work right now), fixed normal mapping

This commit is contained in:
ecker 2026-05-24 22:23:41 -05:00
parent 3a2dfb0833
commit 0a92fed14a
33 changed files with 594 additions and 216 deletions

View File

@ -350,7 +350,8 @@
"max": 0.01 // 0.2
},
"debug draw": {
"dynamic": true
"static": false,
"dynamic": false
},
"fixed step": true,
"substeps": 4

View File

@ -16,7 +16,7 @@
"events": {
"click": {
"name": "game:Scene.Load",
"payload": { "scene": "SourceEngine" },
"payload": { "scene": "Sponza" },
"delay": 0.125
}
},

View File

@ -200,7 +200,8 @@ local function tickGravGun( transform, inputs )
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(), transform.up )
heldObjectTransform.orientation = cameraTransform:flatten().orientation
end
-- move held object

View File

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

View File

@ -0,0 +1,10 @@
{
"import": "/player.json",
"assets": [
// { "filename": "/gui/hud/hud.json", "delay": 0 }
],
"transform": {
// "orientation": [ 0, 1, 0, 0 ]
}
// "metadata": { "physics": { "gravity": [ 0, 0, 0 ] } }
}

View File

@ -0,0 +1,25 @@
{
"import": "/scene.json",
"assets": [
"./sponza.json"
],
"metadata": {
"light": {
"fog-": {
// "color": [ 0.1, 0.1, 0.1 ],
// "color": [ 0.2, 0.2, 0.2 ],
"color": [ 0.3, 0.3, 0.3 ],
"range": [ 64, 256 ],
"step scale": 4,
"absorbtion": 0.125,
"density": {
"threshold": 0.35,
"multiplier": 1.0,
"scale": 25.0,
"offset": [0.2, 0, 1],
"timescale": 32
}
}
}
}
}

View File

@ -0,0 +1,36 @@
{
"import": "/model.json",
"assets": [
// "./models/sponza.glb"
"./models/sponza/graph.json"
],
"metadata": {
"graph": {
"renderer": { "separate": false },
"exporter": {
"optimize": { "simplify": 0, "lods": true, "print": true }
},
"baking": { "enabled": true },
"tags": {
"worldspawn": {
"physics": { "type": "mesh", "static": true, "mass": 0 },
"grid": { "size": [8,1,8], "epsilon": 0.001, "cleanup": true, "print": true, "clip": true },
"unwrap mesh": true
},
"info_player_spawn": { "action": "attach", "filename": "./player.json", "transform": { "orientation": [ 0, 1, 0, 0 ] } },
"light_environment": {"light": {
// "color": [0.1, 0.1, 0.1],
"power": 1000,
"global": true,
"bias": {
"constant": 0,
"slope": 5,
"shader": 0.000025
},
"radius": [0.9999999, 0],
"resolution": 2048
} }
}
}
}
}

View File

@ -24,18 +24,18 @@ float mipLevels( ivec2 size ) {
vec4 rgbaUVec4toVec4( uvec4 rgba ) {
/*
return vec4(float((val & 0x000000FF)),
float((val & 0x0000FF00) >> 8U),
float((val & 0x00FF0000) >> 16U),
float((val & 0xFF000000) >> 24U));
float((val & 0x0000FF00) >> 8U),
float((val & 0x00FF0000) >> 16U),
float((val & 0xFF000000) >> 24U));
*/
return rgba / vec4(256.0);
}
uvec4 rgbaVec4toUvec4( vec4 rgba ) {
/*
return (uint(val.w) & 0x000000FF) << 24U |
(uint(val.z) & 0x000000FF) << 16U |
(uint(val.y) & 0x000000FF) << 8U |
(uint(val.x) & 0x000000FF);
(uint(val.z) & 0x000000FF) << 16U |
(uint(val.y) & 0x000000FF) << 8U |
(uint(val.x) & 0x000000FF);
*/
return uvec4(rgba * uvec4(256));
}
@ -67,7 +67,7 @@ uint tea(uint val0, uint val1) {
uint lcg(inout uint prev) {
uint LCG_A = 1664525u;
uint LCG_C = 1013904223u;
prev = (LCG_A * prev + LCG_C);
prev = (LCG_A * prev + LCG_C);
return prev & 0x00FFFFFF;
}
float rnd(inout uint prev) { return (float(lcg(prev)) / float(0x01000000)); }
@ -99,7 +99,7 @@ vec3 samplingHemisphere(inout uint seed, in vec3 x, in vec3 y, in vec3 z) {
float r2 = rnd(seed);
float sq = sqrt(1.0 - r2);
vec3 direction = vec3(cos(2 * PI * r1) * sq, sin(2 * PI * r1) * sq, sqrt(r2));
direction = direction.x * x + direction.y * y + direction.z * z;
direction = direction.x * x + direction.y * y + direction.z * z;
return direction;
}
vec3 samplingHemisphere(inout uint seed, in vec3 z) {
@ -111,7 +111,7 @@ vec3 samplingHemisphere(inout uint seed, in vec3 z) {
float r2 = rnd(seed);
float sq = sqrt(1.0 - r2);
vec3 direction = vec3(cos(2 * PI * r1) * sq, sin(2 * PI * r1) * sq, sqrt(r2));
direction = direction.x * x + direction.y * y + direction.z * z;
direction = direction.x * x + direction.y * y + direction.z * z;
return direction;
}
//
@ -376,7 +376,23 @@ void populateSurface( InstanceAddresses instanceAddresses, uvec3 indices ) {
if ( isValidAddress(instanceAddresses.tangent) ) {
VTangent buf = VTangent(nonuniformEXT(instanceAddresses.tangent));
#pragma unroll 3
for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].tangent[_] = buf.v[/*triangle.*/indices[_]*3+_];
for ( uint _ = 0; _ < 3; ++_ ) points[_].tangent = vec3( buf.v[indices[_]*3+0], buf.v[indices[_]*3+1], buf.v[indices[_]*3+2] );
// for ( uint _ = 0; _ < 3; ++_ ) /*triangle.*/points[_].tangent[_] = buf.v[/*triangle.*/indices[_]*3+_];
} else {
vec3 edge1 = points[1].position - points[0].position;
vec3 edge2 = points[2].position - points[0].position;
vec2 deltaUV1 = points[1].uv - points[0].uv;
vec2 deltaUV2 = points[2].uv - points[0].uv;
float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x);
vec3 tangent_tri = (edge1 * deltaUV2.y - edge2 * deltaUV1.y) * r;
#pragma unroll 3
for ( uint i = 0; i < 3; ++i ) {
vec3 n = points[i].normal;
// Gram-Schmidt orthogonalization
points[i].tangent = normalize(tangent_tri - n * dot(n, tangent_tri));
}
}
}
@ -413,14 +429,6 @@ void populateSurface( InstanceAddresses instanceAddresses, uvec3 indices ) {
triangle.point.st = /*triangle.*/points[0].st * surface.barycentric[0] + /*triangle.*/points[1].st * surface.barycentric[1] + /*triangle.*/points[2].st * surface.barycentric[2];
triangle.point.tangent = /*triangle.*/points[0].tangent * surface.barycentric[0] + /*triangle.*/points[1].tangent * surface.barycentric[1] + /*triangle.*/points[2].tangent * surface.barycentric[2];
// triangle.point.normal = triangle.geomNormal;
if ( triangle.point.tangent != vec3(0) ) {
surface.tangent.world = normalize(vec3( surface.object.model * vec4(triangle.point.tangent, 0.0) ));
vec3 bitangent = normalize(vec3( surface.object.model * vec4(cross( triangle.point.normal, triangle.point.tangent ), 0.0) ));
surface.tbn = mat3(surface.tangent.world, bitangent, triangle.point.normal);
}
// bind position (seems to muck with the skybox + fog)
#if 0 && BARYCENTRIC_CALCULATE
{
@ -433,6 +441,12 @@ void populateSurface( InstanceAddresses instanceAddresses, uvec3 indices ) {
surface.normal.world = normalize(vec3( surface.object.model * vec4(triangle.point.normal, 0.0 ) ));
// surface.normal.eye = vec3( VIEW_MATRIX * vec4(surface.normal.world, 0.0) );
}
// bind tangent
if ( triangle.point.tangent != vec3(0) ) {
surface.tangent.world = normalize(vec3( surface.object.model * vec4(triangle.point.tangent, 0.0) ));
vec3 bitangent = normalize(vec3( surface.object.model * vec4(cross( triangle.point.normal, triangle.point.tangent ), 0.0) ));
surface.tbn = mat3(surface.tangent.world, bitangent, surface.normal.world);
}
// bind UVs
{
surface.uv.xy = triangle.point.uv;

View File

@ -82,7 +82,7 @@
#endif
#if BARYCENTRIC
#ifndef BARYCENTRIC_CALCULATE
#define BARYCENTRIC_CALCULATE 0
#define BARYCENTRIC_CALCULATE 1
#endif
#ifndef BUFFER_REFERENCE
#define BUFFER_REFERENCE 1

View File

@ -149,6 +149,9 @@ namespace uf {
void UF_API updateAnimation( pod::Graph&, pod::Node& );
void UF_API override( pod::Graph& );
void UF_API animate( pod::Graph&, const uf::stl::string&, float = 1, bool = true );
uf::stl::vector<pod::OBB> obbFromSkin( const pod::Graph& graph, const pod::Node& node );
void rigRagdoll( pod::Graph& graph, pod::Node& node );
void UF_API destroy( pod::Graph& );

View File

@ -4,11 +4,11 @@ namespace uf {
struct Base {
pod::Vector3f position{};
pod::Vector2f uv{};
pod::ColorRgba color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector4b color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2f st{};
pod::Vector3f normal{};
pod::Vector3f tangent{};
pod::Vector<uint16_t, 2> id{};
pod::Vector2us id{};
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Base interpolate( const Base& p1, const Base& p2, float t );
@ -16,12 +16,12 @@ namespace uf {
struct Skinned {
pod::Vector3f position{};
pod::Vector2f uv{};
pod::ColorRgba color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector4b color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2f st{};
pod::Vector3f normal{};
pod::Vector3f tangent{};
pod::Vector<uint16_t, 2> id{};
pod::Vector<uint16_t, 4> joints{};
pod::Vector2us id{};
pod::Vector4us joints{};
pod::Vector4f weights{};
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
@ -31,11 +31,11 @@ namespace uf {
struct Base_16f {
pod::Vector3f16 position{};
pod::Vector2f16 uv{};
pod::ColorRgba color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector4b color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2f16 st{};
pod::Vector3f16 normal{};
pod::Vector3f16 tangent{};
pod::Vector<uint16_t, 2> id{};
pod::Vector2us id{};
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Base_16f interpolate( const Base_16f& p1, const Base_16f& p2, float t );
@ -43,12 +43,12 @@ namespace uf {
struct Skinned_16f {
pod::Vector3f16 position{};
pod::Vector2f16 uv{};
pod::ColorRgba color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector4b color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2f16 st{};
pod::Vector3f16 normal{};
pod::Vector3f16 tangent{};
pod::Vector<uint16_t, 2> id{};
pod::Vector<uint16_t, 4> joints{};
pod::Vector2us id{};
pod::Vector4us joints{};
pod::Vector3f16 weights{};
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
@ -56,27 +56,27 @@ namespace uf {
};
#endif
struct Base_u16q {
pod::Vector<uint16_t, 3> position{};
pod::Vector<uint16_t, 2> uv{};
pod::ColorRgba color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector<uint16_t, 2> st{};
pod::Vector<uint16_t, 3> normal{};
pod::Vector<uint16_t, 3> tangent{};
pod::Vector<uint16_t, 2> id{};
pod::Vector3us position{};
pod::Vector2us uv{};
pod::Vector4b color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2us st{};
pod::Vector3us normal{};
pod::Vector3us tangent{};
pod::Vector2us id{};
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Base_u16q interpolate( const Base_u16q& p1, const Base_u16q& p2, float t );
};
struct Skinned_u16q {
pod::Vector<uint16_t, 3> position{};
pod::Vector<uint16_t, 2> uv{};
pod::ColorRgba color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector<uint16_t, 2> st{};
pod::Vector<uint16_t, 3> normal{};
pod::Vector<uint16_t, 3> tangent{};
pod::Vector<uint16_t, 2> id{};
pod::Vector<uint16_t, 4> joints{};
pod::Vector<uint16_t, 3> weights{};
pod::Vector3us position{};
pod::Vector2us uv{};
pod::Vector4b color{ (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0, (uint8_t) ~0 };
pod::Vector2us st{};
pod::Vector3us normal{};
pod::Vector3us tangent{};
pod::Vector2us id{};
pod::Vector4us joints{};
pod::Vector3us weights{};
static UF_API uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
static UF_API Skinned_u16q interpolate( const Skinned_u16q& p1, const Skinned_u16q& p2, float t );

View File

@ -10,7 +10,7 @@ namespace uf {
class UF_API Image {
public:
typedef pod::Vector2ui vec2_t;
typedef pod::Vector<uint8_t, 4> pixel_t;
typedef pod::Vector4b pixel_t;
typedef uf::stl::vector<pixel_t::type_t> container_t;
protected:
uf::stl::string m_filename;

View File

@ -96,6 +96,11 @@ namespace uf {
template<typename T=pod::Matrix4> /*FORCE_INLINE*/ T /*UF_API*/ translate( const T& matrix, const pod::Vector3t<typename T::type_t>& vector );
template<typename T=pod::Matrix4> /*FORCE_INLINE*/ T /*UF_API*/ rotate( const T& matrix, const pod::Vector3t<typename T::type_t>& vector );
template<typename T=pod::Matrix4> /*FORCE_INLINE*/ T /*UF_API*/ scale( const T& matrix, const pod::Vector3t<typename T::type_t>& vector );
template<typename T=pod::Matrix4> /*FORCE_INLINE*/ pod::Vector3t<typename T::type_t> extractTranslation( const T& matrix );
template<typename T=pod::Matrix4> /*FORCE_INLINE*/ pod::Vector3t<typename T::type_t> extractScale( const T& matrix );
template<typename T=pod::Matrix4> /*FORCE_INLINE*/ pod::Vector4t<typename T::type_t> extractRotation( const T& matrix );
template<typename T=pod::Matrix4> /*FORCE_INLINE*/ pod::Vector3t<typename T::type_t> /*UF_API*/ eulerAngles( const T& matrix );
template<typename T=NUM> /*FORCE_INLINE*/ pod::Matrix4t<T> /*UF_API*/ orthographic( T, T, T, T, T, T );

View File

@ -166,7 +166,7 @@ template<typename T, size_t M, size_t N> pod::Matrix<T,M,N> /*UF_API*/ uf::matri
m(i, j) = a[i] * b[j];
}
}
return m;
return m;
}
template<typename T, size_t R, size_t C> pod::Vector<T, R> /*UF_API*/ uf::matrix::diagonal(const pod::Matrix<T, R, C>& mat ) {
@ -287,7 +287,7 @@ pod::Vector2t<T> uf::matrix::multiply(const pod::Matrix2t<T>& mat, const pod::Ve
return pod::Vector2t<T>{
v[0] * mat(0,0) + v[1] * mat(0,1),
v[0] * mat(1,0) + v[1] * mat(1,1)
};
};
}
template<typename T>
@ -333,7 +333,7 @@ template<typename T> T uf::matrix::translate( const T& matrix, const pod::Vector
return res;
}
template<typename T> T uf::matrix::rotate( const T& matrix, const pod::Vector3t<typename T::type_t>& vector ) {
T res = matrix;
T res = matrix;
if (vector.x != 0) {
T Rx = uf::matrix::identity<T>();
@ -353,7 +353,7 @@ template<typename T> T uf::matrix::rotate( const T& matrix, const pod::Vector3t<
Rz(1,0) = sin(vector.z); Rz(1,1) = cos(vector.z);
res = uf::matrix::multiply(res, Rz);
}
return res;
return res;
}
template<typename T> T uf::matrix::scale( const T& matrix, const pod::Vector3t<typename T::type_t>& vector ) {
T res = matrix;
@ -362,6 +362,85 @@ template<typename T> T uf::matrix::scale( const T& matrix, const pod::Vector3t<t
res(2,2) = vector.z;
return res;
}
// extract translation from matrix
template<typename T>
pod::Vector3t<typename T::type_t> uf::matrix::extractTranslation( const T& matrix ) {
return { matrix(0,3), matrix(1,3), matrix(2,3) };
}
// extracts the scale by calculating the length of the 3 basis column vectors
template<typename T>
pod::Vector3t<typename T::type_t> uf::matrix::extractScale( const T& matrix ) {
using type_t = typename T::type_t;
type_t sx = std::sqrt( matrix(0,0) * matrix(0,0) + matrix(1,0) * matrix(1,0) + matrix(2,0) * matrix(2,0) );
type_t sy = std::sqrt( matrix(0,1) * matrix(0,1) + matrix(1,1) * matrix(1,1) + matrix(2,1) * matrix(2,1) );
type_t sz = std::sqrt( matrix(0,2) * matrix(0,2) + matrix(1,2) * matrix(1,2) + matrix(2,2) * matrix(2,2) );
// to-do: write uf::matrix::determinant()
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; }
return { sx, sy, sz };
}
// extracts the rotation by normalizing out the scale
template<typename T>
pod::Vector4t<typename T::type_t> uf::matrix::extractRotation( const T& matrix ) {
using type_t = typename T::type_t;
pod::Vector4t<typename T::type_t> q;
pod::Vector3t<type_t> s = uf::matrix::extractScale( matrix );
type_t invX = (s.x != 0) ? (1.0 / s.x) : 0;
type_t invY = (s.y != 0) ? (1.0 / s.y) : 0;
type_t invZ = (s.z != 0) ? (1.0 / s.z) : 0;
type_t m00 = matrix(0,0) * invX; type_t m01 = matrix(0,1) * invY; type_t m02 = matrix(0,2) * invZ;
type_t m10 = matrix(1,0) * invX; type_t m11 = matrix(1,1) * invY; type_t m12 = matrix(1,2) * invZ;
type_t m20 = matrix(2,0) * invX; type_t m21 = matrix(2,1) * invY; type_t m22 = matrix(2,2) * invZ;
type_t trace = m00 + m11 + m22;
if ( trace > 0.0 ) {
type_t root = std::sqrt(trace + 1.0);
q.w = 0.5 * root;
root = 0.5 / root;
q.x = (m21 - m12) * root;
q.y = (m02 - m20) * root;
q.z = (m10 - m01) * root;
} else {
int i = 0;
if ( m11 > m00 ) i = 1;
if ( m22 > (i == 0 ? m00 : m11) ) i = 2;
if (i == 0) {
type_t root = std::sqrt(m00 - m11 - m22 + 1.0);
q.x = 0.5 * root;
root = 0.5 / root;
q.w = (m21 - m12) * root;
q.y = (m01 + m10) * root;
q.z = (m02 + m20) * root;
} else if (i == 1) {
type_t root = std::sqrt(m11 - m00 - m22 + 1.0);
q.y = 0.5 * root;
root = 0.5 / root;
q.w = (m02 - m20) * root;
q.x = (m01 + m10) * root;
q.z = (m12 + m21) * root;
} else {
type_t root = std::sqrt(m22 - m00 - m11 + 1.0);
q.z = 0.5 * root;
root = 0.5 / root;
q.w = (m10 - m01) * root;
q.x = (m02 + m20) * root;
q.y = (m12 + m21) * root;
}
}
return uf::vector::normalize( q );
}
template<typename T> pod::Matrix<typename T::type_t, T::columns, T::columns> uf::matrix::multiply_( T& left, const T& right ) {
return left = uf::matrix::multiply((const T&) left, right);
@ -382,14 +461,14 @@ template<typename T> T& uf::matrix::inverse_( T& matrix ) {
template<typename T>
pod::Matrix4t<T> /*UF_API*/ uf::matrix::orthographic( T l, T r, T b, T t, T f, T n ) {
pod::Matrix4t<T> m = uf::matrix::identity();
m(0,0) = static_cast<T>(2) / (r - l);
m(1,1) = static_cast<T>(2) / (t - b);
m(2,2) = static_cast<T>(-2) / (f - n);
m(0,0) = static_cast<T>(2) / (r - l);
m(1,1) = static_cast<T>(2) / (t - b);
m(2,2) = static_cast<T>(-2) / (f - n);
// Translation terms go in the last column (col = 3)
m(0,3) = - (r + l) / (r - l);
m(1,3) = - (t + b) / (t - b);
m(2,3) = - (f + n) / (f - n);
// Translation terms go in the last column (col = 3)
m(0,3) = - (r + l) / (r - l);
m(1,3) = - (t + b) / (t - b);
m(2,3) = - (f + n) / (f - n);
return m;
}
template<typename T>

View File

@ -45,13 +45,6 @@ namespace impl {
pod::Vector3f triangleCenter( const pod::Triangle& tri );
pod::Vector3f triangleNormal( const pod::Triangle& tri );
pod::Vector3f triangleNormal( const pod::TriangleWithNormal& tri );
size_t getIndex( const void* pointer, size_t stride, size_t index );
size_t getIndex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, size_t index );
pod::Vector3f getVertex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& positions, size_t index );
pod::Triangle fetchTriangle( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, const uf::Mesh::AttributeView& positions, size_t triID );
/*FORCE_INLINE*/ pod::Triangle fetchTriangle( const uf::Mesh::View& view, size_t triID );
pod::TriangleWithNormal fetchTriangle( const uf::Mesh& mesh, size_t triID );
pod::TriangleWithNormal fetchTriangle( const uf::Mesh& mesh, size_t triID, const pod::PhysicsBody& body );
/*FORCE_INLINE*/ bool aabbOverlap( const pod::AABB& a, const pod::AABB& b );

View File

@ -28,6 +28,8 @@ namespace pod {
template<typename T = float> using Vector1t = Vector<T,1>;
typedef Vector1t<NUM> Vector1;
typedef Vector1t<int16_t> Vector1s;
typedef Vector1t<uint16_t> Vector1us;
typedef Vector1t<int32_t> Vector1i;
typedef Vector1t<uint32_t> Vector1ui;
@ -37,6 +39,8 @@ namespace pod {
template<typename T = float> using Vector2t = Vector<T,2>;
typedef Vector2t<NUM> Vector2;
typedef Vector2t<int16_t> Vector2s;
typedef Vector2t<uint16_t> Vector2us;
typedef Vector2t<int32_t> Vector2i;
typedef Vector2t<uint32_t> Vector2ui;
@ -46,9 +50,11 @@ namespace pod {
template<typename T = float> using Vector3t = Vector<T,3>;
typedef Vector3t<NUM> Vector3;
typedef Vector3t<uint8_t> Vector3b;
typedef Vector3t<int16_t> Vector3s;
typedef Vector3t<uint16_t> Vector3us;
typedef Vector3t<int32_t> Vector3i;
typedef Vector3t<uint32_t> Vector3ui;
typedef Vector3t<uint8_t> ColorRGB;
typedef Vector3t<long> Vector3l;
typedef Vector3t<float> Vector3f;
@ -56,9 +62,11 @@ namespace pod {
template<typename T = float> using Vector4t = Vector<T,4>;
typedef Vector4t<NUM> Vector4;
typedef Vector4t<uint8_t> Vector4b;
typedef Vector4t<int16_t> Vector4s;
typedef Vector4t<uint16_t> Vector4us;
typedef Vector4t<int32_t> Vector4i;
typedef Vector4t<uint32_t> Vector4ui;
typedef Vector4t<uint8_t> ColorRgba;
typedef Vector4t<long> Vector4l;
typedef Vector4t<float> Vector4f;
@ -86,6 +94,7 @@ namespace uf {
template<typename T> /*FORCE_INLINE*/ pod::Vector3t<T> /*UF_API*/ create( T x, T y, T z ); // creates a 3D vector
template<typename T> /*FORCE_INLINE*/ pod::Vector4t<T> /*UF_API*/ create( T x, T y, T z, T w ); // creates a 4D vector
template<typename T, size_t N> /*FORCE_INLINE*/ pod::Vector<T, N> /*UF_API*/ copy( const pod::Vector<T, N>& = {}); // creates a copy of a vector (for whatever reason)
template<typename T, size_t N> /*FORCE_INLINE*/ pod::Vector<T, N> /*UF_API*/ copy( const T* ); // creates a copy of a vector (for whatever reason)
template<typename T, size_t N, typename U> /*FORCE_INLINE*/ pod::Vector<T, N> /*UF_API*/ cast( const U& from ); // casts one vector of one type to another (of the same size)
// Equality checking
template<typename T> /*FORCE_INLINE*/ bool /*UF_API*/ equals( const T& left, const T& right ); // equality check between two vectors (==)

View File

@ -44,6 +44,10 @@ template<typename T, size_t N>
pod::Vector<T, N> uf::vector::copy( const pod::Vector<T, N>& v ) {
return v;
}
template<typename T, size_t N>
pod::Vector<T, N> uf::vector::copy( const T* p ) {
return *((pod::Vector<T, N>*) p);
}
template<typename T, size_t N, typename U>
pod::Vector<T, N> uf::vector::cast( const U& from ) {
pod::Vector<T, N> to;

View File

@ -3,6 +3,7 @@
#include <uf/utils/math/vector.h>
#include <uf/utils/math/matrix.h>
#include <uf/utils/math/quant.h>
#include <uf/utils/math/shapes.h>
#include <functional>
#include <uf/utils/memory/unordered_map.h>
@ -36,10 +37,10 @@ namespace ext {
// essential for vertex input
size_t offset = 0;
size_t size = 0;
ext::RENDERER::enums::Format::type_t format = ext::RENDERER::enums::Format::UNDEFINED;
uf::renderer::enums::Format::type_t format = uf::renderer::enums::Format::UNDEFINED;
// not as essential
uf::stl::string name = "";
ext::RENDERER::enums::Type::type_t type = 0;
uf::renderer::enums::Type::type_t type = 0;
size_t components = 0;
bool operator==( const AttributeDescriptor& right ) const { return name == right.name;
@ -153,7 +154,7 @@ namespace uf {
static bool defaultInterleaved;
typedef uf::stl::vector<uint8_t> buffer_t;
struct Attribute {
ext::RENDERER::AttributeDescriptor descriptor;
uf::renderer::AttributeDescriptor descriptor;
int32_t buffer = -1;
size_t offset = 0;
@ -187,6 +188,7 @@ namespace uf {
bool valid() const { return attribute.pointer != NULL; }
size_t stride() const { return attribute.stride; }
size_t components() const { return attribute.descriptor.components; }
uf::renderer::enums::Type::type_t type() const { return attribute.descriptor.type; }
};
struct View {
@ -202,6 +204,41 @@ namespace uf {
UF_EXCEPTION("invalid view: {}", name);
//return null;
}
// to-do: resolve dependency order hell
// these probably won't be directly called anyways?
#if 0
size_t fetchIndex( size_t index ) {
return uf::mesh::fetchIndex( index );
}
size_t fetchIndex( const uf::Mesh::AttributeView& indices, size_t index ) {
return uf::mesh::fetchIndex( indices, index );
}
size_t fetchIndex( const uf::stl::string& indices, size_t index ) {
return uf::mesh::fetchIndex( indices, index );
}
pod::Vector3f fetchVertex( size_t index ) {
return uf::mesh::fetchVertex( index );
}
pod::Vector3f fetchVertex( const uf::Mesh::AttributeView& positions, size_t index ) {
return uf::mesh::fetchVertex( positions, index );
}
pod::Vector3f fetchVertex( const uf::stl::string& positions, size_t index ) {
return uf::mesh::fetchVertex( positions, index );
}
pod::TriangleWithNormal fetchTriangle( size_t triID ) {
return uf::mesh::fetchTriangle( *this, triID );
}
pod::TriangleWithNormal fetchTriangle( const uf::Mesh::AttributeView& indices, const uf::Mesh::AttributeView& positions, size_t triID ) {
return uf::mesh::fetchTriangle( *this, indices, positions, triID );
}
pod::TriangleWithNormal fetchTriangle( const uf::stl::string& indices, const uf::stl::string& positions, size_t triID ) {
auto& view = *this;
return uf::mesh::fetchTriangle( view, view[indices], view[positions], triID );
}
#endif
};
typedef uf::stl::vector<uf::Mesh::View> views_t;
@ -218,9 +255,9 @@ namespace uf {
void _updateViews();
uf::Mesh::Attribute _remapAttribute( const uf::Mesh::Input& input, const uf::Mesh::Attribute& attribute, size_t i = 0 ) const;
bool _hasV( const uf::Mesh::Input& input, const uf::stl::vector<ext::RENDERER::AttributeDescriptor>& descriptors ) const;
bool _hasV( const uf::Mesh::Input& input, const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors ) const;
bool _hasV( const uf::Mesh::Input& input, const uf::Mesh::Input& src ) const;
void _bindV( uf::Mesh::Input& input, const uf::stl::vector<ext::RENDERER::AttributeDescriptor>& descriptors );
void _bindV( uf::Mesh::Input& input, const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors );
void _resizeVs( uf::Mesh::Input& input, size_t count );
void _reserveVs( uf::Mesh::Input& input, size_t count );
void _insertV( uf::Mesh::Input& input, const void* data );
@ -232,14 +269,14 @@ namespace uf {
template<typename T> inline void _insertV( uf::Mesh::Input& input, const T& vertex ) { return _insertV( input, (const void*) &vertex ); }
template<typename T> inline void _insertVs( uf::Mesh::Input& input, const uf::stl::vector<T>& vs ) { return _insertVs( input, (const void*) vs.data(), vs.size() ); }
void _bindI( uf::Mesh::Input& input, size_t size, ext::RENDERER::enums::Type::type_t type, size_t count = 1 );
void _bindI( uf::Mesh::Input& input, size_t size, uf::renderer::enums::Type::type_t type, size_t count = 1 );
void _reserveIs( uf::Mesh::Input& input, size_t count, size_t i = 0 );
void _resizeIs( uf::Mesh::Input& input, size_t count, size_t i = 0 );
void _insertI( uf::Mesh::Input& input, const void* data, size_t i );
void _insertIs( uf::Mesh::Input& input, const void* data, size_t size, size_t i );
void _insertIs( uf::Mesh::Input& input, const uf::Mesh& mesh, const uf::Mesh::Input& srcInput );
template<typename U> inline void _bindI( uf::Mesh::Input& input, size_t indices = 1 ) { return _bindI( input, sizeof(U), ext::RENDERER::typeToEnum<U>(), indices ); }
template<typename U> inline void _bindI( uf::Mesh::Input& input, size_t indices = 1 ) { return _bindI( input, sizeof(U), uf::renderer::typeToEnum<U>(), indices ); }
template<typename U> inline void _insertI( uf::Mesh::Input& input, U index, size_t i = 0 ) { return _insertI( input, (const void*) &index, i ); }
template<typename U> inline void _insertIs( uf::Mesh::Input& input, const uf::stl::vector<U>& is, size_t i = 0 ) { return _insertIs( input, (const void*) is.data(), is.size(), i ); }
public:
@ -292,9 +329,9 @@ namespace uf {
uf::Mesh::View makeView( size_t commandIndex, const uf::stl::vector<uf::stl::string>& wanted = {}, size_t index = 0 ) const;
uf::stl::vector<uf::Mesh::View> makeViews( const uf::stl::vector<uf::stl::string>& wanted = {}, size_t index = 0 ) const;
inline bool hasVertex( const uf::stl::vector<ext::RENDERER::AttributeDescriptor>& descriptors ) const { return _hasV( vertex, descriptors ); }
inline bool hasVertex( const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors ) const { return _hasV( vertex, descriptors ); }
inline bool hasVertex( const uf::Mesh& mesh ) const { return _hasV( vertex, mesh.vertex ); }
inline void bindVertex( const uf::stl::vector<ext::RENDERER::AttributeDescriptor>& descriptors ) { return _bindV( vertex, descriptors ); }
inline void bindVertex( const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors ) { return _bindV( vertex, descriptors ); }
inline void resizeVertices( size_t count ) { return _resizeVs( vertex, count ); }
inline void reserveVertices( size_t count ) { return _reserveVs( vertex, count ); }
inline void insertVertex( const void* data ) { return _insertV( vertex, data ); }
@ -308,7 +345,7 @@ namespace uf {
template<typename T> inline void insertVertex( const T& v ) { return _insertV( vertex, (const void*) &v ); }
template<typename T> inline void insertVertices( const uf::stl::vector<T>& vertices ) { return _insertVs( vertex, (const void*) vertices.data(), vertices.size() ); }
inline void bindIndex( size_t size, ext::RENDERER::enums::Type::type_t type, size_t count = 1 ) { return _bindI( index, size, type, count ); }
inline void bindIndex( size_t size, uf::renderer::enums::Type::type_t type, size_t count = 1 ) { return _bindI( index, size, type, count ); }
inline void reserveIndices( size_t count, size_t i = 0 ) { return _reserveIs( index, count, i ); }
inline void resizeIndices( size_t count, size_t i = 0 ) { return _resizeIs( index, count, i ); }
inline void insertIndex( const void* data, size_t i = 0 ) { return _insertI( index, data, i ); }
@ -317,13 +354,13 @@ namespace uf {
inline void updateIndexDescriptor() { return _updateDescriptor( index ); }
inline uf::Mesh::Attribute remapIndexAttribute( const uf::Mesh::Attribute& attribute, size_t i = 0 ) const { return _remapAttribute( index, attribute, i ); }
template<typename U> inline void bindIndex( size_t count = 1 ) { return _bindI( index, sizeof(U), ext::RENDERER::typeToEnum<U>(), count ); }
template<typename U> inline void bindIndex( size_t count = 1 ) { return _bindI( index, sizeof(U), uf::renderer::typeToEnum<U>(), count ); }
template<typename U> inline void insertIndex( U I, size_t i = 0 ) { return _insertI( index, (const void*) &I, i ); }
template<typename U> inline void insertIndices( const uf::stl::vector<U>& indices, size_t i = 0 ) { return _insertIs( index, (const void*) indices.data(), indices.size(), i ); }
inline bool hasInstance( const uf::stl::vector<ext::RENDERER::AttributeDescriptor>& descriptors ) const { return _hasV( instance, descriptors ); }
inline bool hasInstance( const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors ) const { return _hasV( instance, descriptors ); }
inline bool hasInstance( const uf::Mesh& mesh ) const { return _hasV( instance, mesh.instance ); }
inline void bindInstance( const uf::stl::vector<ext::RENDERER::AttributeDescriptor>& descriptors ) { return _bindV( instance, descriptors ); }
inline void bindInstance( const uf::stl::vector<uf::renderer::AttributeDescriptor>& descriptors ) { return _bindV( instance, descriptors ); }
inline void resizeInstances( size_t count ) { return _resizeVs( instance, count ); }
inline void reserveInstances( size_t count ) { return _reserveVs( instance, count ); }
inline void insertInstance( const void* data ) { return _insertV( instance, data ); }
@ -336,7 +373,7 @@ namespace uf {
template<typename T> inline void insertInstance( const T& v ) { return _insertV( instance, (const void*) &v ); }
template<typename T> inline void insertInstances( const uf::stl::vector<T>& instances ) { return _insertVs( instance, (const void*) instances.data(), instances.size() ); }
inline void bindIndirect( size_t size, ext::RENDERER::enums::Type::type_t type, size_t count = 1 ) { return _bindI( indirect, size, type, count ); }
inline void bindIndirect( size_t size, uf::renderer::enums::Type::type_t type, size_t count = 1 ) { return _bindI( indirect, size, type, count ); }
inline void reserveIndirects( size_t count, size_t i = 0 ) { return _reserveIs( indirect, count, i ); }
inline void resizeIndirects( size_t count, size_t i = 0 ) { return _resizeIs( indirect, count, i ); }
inline void insertIndirect( const void* data, size_t i = 0 ) { return _insertI( indirect, data, i ); }
@ -344,11 +381,11 @@ namespace uf {
inline void insertIndirects( const uf::Mesh& mesh ) { return _insertIs( indirect, mesh, mesh.indirect ); }
inline void updateIndirectDescriptor() { return _updateDescriptor( indirect ); }
template<typename U> inline void bindIndirect( size_t i = 1 ) { return _bindI( indirect, sizeof(U), ext::RENDERER::typeToEnum<U>(), i ); }
template<typename U> inline void bindIndirect( size_t i = 1 ) { return _bindI( indirect, sizeof(U), uf::renderer::typeToEnum<U>(), i ); }
template<typename U> inline void insertIndirect( U v, size_t i = 0 ) { return _insertI( indirect, (const void*) &v, i ); }
template<typename U> inline void insertIndirects( const uf::stl::vector<U>& indirects, size_t i = 0 ) { return _insertIs( indirect, (const void*) indirects.data(), indirects.size(), i ); }
template<typename T, typename U = ext::RENDERER::index_t>
template<typename T, typename U = uf::renderer::index_t>
void bind( bool interleave = uf::Mesh::defaultInterleaved, size_t indices = 1 ) {
bindVertex<T>();
bindIndex<U>( indices );
@ -448,16 +485,16 @@ namespace ext {
size_t bufferOffset = 0;
} inputs;
ext::RENDERER::enums::PrimitiveTopology::type_t topology = ext::RENDERER::enums::PrimitiveTopology::TRIANGLE_LIST;
ext::RENDERER::enums::PolygonMode::type_t fill = ext::RENDERER::enums::PolygonMode::FILL;
ext::RENDERER::enums::CullMode::type_t cullMode = ext::RENDERER::enums::CullMode::BACK;
ext::RENDERER::enums::Face::type_t frontFace = ext::RENDERER::enums::Face::CW;
uf::renderer::enums::PrimitiveTopology::type_t topology = uf::renderer::enums::PrimitiveTopology::TRIANGLE_LIST;
uf::renderer::enums::PolygonMode::type_t fill = uf::renderer::enums::PolygonMode::FILL;
uf::renderer::enums::CullMode::type_t cullMode = uf::renderer::enums::CullMode::BACK;
uf::renderer::enums::Face::type_t frontFace = uf::renderer::enums::Face::CW;
float lineWidth = 1.0f;
struct {
bool test = true;
bool write = true;
ext::RENDERER::enums::Compare::type_t operation = ext::RENDERER::enums::Compare::GREATER_OR_EQUAL;
uf::renderer::enums::Compare::type_t operation = uf::renderer::enums::Compare::GREATER_OR_EQUAL;
struct {
bool enable = false;
float constant = 0;
@ -492,8 +529,8 @@ namespace ext {
namespace std {
template <>
struct hash<ext::RENDERER::GraphicDescriptor> {
size_t operator()(const ext::RENDERER::GraphicDescriptor& descriptor) const { return descriptor.hash(); }
struct hash<uf::renderer::GraphicDescriptor> {
size_t operator()(const uf::renderer::GraphicDescriptor& descriptor) const { return descriptor.hash(); }
};
}
@ -503,7 +540,7 @@ namespace std {
.size = sizeof(decltype(TYPE::ATTRIBUTE)),\
.format = uf::renderer::enums::Format::FORMAT,\
.name = #ATTRIBUTE,\
.type = ext::RENDERER::typeToEnum<decltype(TYPE::ATTRIBUTE)::type_t>(),\
.type = uf::renderer::typeToEnum<decltype(TYPE::ATTRIBUTE)::type_t>(),\
.components = decltype(TYPE::ATTRIBUTE)::size,\
},
@ -580,7 +617,7 @@ namespace pod {
}
namespace uf {
template<typename T = pod::Vertex_3F, typename U = ext::RENDERER::index_t>
template<typename T = pod::Vertex_3F, typename U = uf::renderer::index_t>
struct UF_API Mesh_T {
typedef T vertex_t;
typedef U index_t;
@ -590,7 +627,7 @@ namespace uf {
uf::stl::vector<pod::Primitive> primitives;
};
template<typename T = pod::Vertex_3F, typename U = ext::RENDERER::index_t>
template<typename T = pod::Vertex_3F, typename U = uf::renderer::index_t>
struct UF_API Meshlet_T {
typedef T vertex_t;
typedef U index_t;
@ -599,4 +636,78 @@ namespace uf {
uf::stl::vector<index_t> indices;
pod::Primitive primitive;
};
namespace mesh {
size_t UF_API fetchIndex( const void* pointer, size_t stride, size_t index );
pod::Vector3f UF_API fetchVertex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& positions, size_t index );
pod::Triangle UF_API fetchTriangle( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, const uf::Mesh::AttributeView& positions, size_t triID );
pod::TriangleWithNormal UF_API fetchTriangle( const uf::Mesh& mesh, size_t triID );
static inline size_t fetchIndex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, size_t index ) {
return uf::mesh::fetchIndex( indices.data(view.index.first), indices.stride(), index );
}
// for clean code, these would be preferable
// but they incur additional lookups every triangle fetch, and I doubt the optimizer will optimize that away, so explicitly passing attribute views is preferable
static inline size_t fetchIndex( const uf::Mesh::View& view, size_t index ) {
return uf::mesh::fetchIndex( view, view["indices"], index );
}
static inline size_t fetchIndex( const uf::Mesh::View& view, const uf::stl::string& indices, size_t index ) {
return uf::mesh::fetchIndex( view, view[indices], index );
}
static inline pod::Vector3f fetchVertex( const uf::Mesh::View& view, size_t index ) {
return uf::mesh::fetchVertex( view, view["positions"], index );
}
static inline pod::Vector3f fetchVertex( const uf::Mesh::View& view, const uf::stl::string& positions, size_t index ) {
return uf::mesh::fetchVertex( view, view[positions], index );
}
static inline pod::Triangle fetchTriangle( const uf::Mesh::View& view, const uf::stl::string& indices, const uf::stl::string& positions, size_t triID ) {
return uf::mesh::fetchTriangle( view, view[indices], view[positions], triID );
}
static inline pod::Triangle fetchTriangle( const uf::Mesh::View& view, size_t triID ) {
return uf::mesh::fetchTriangle( view, view["index"], view["position"], triID );
}
template<typename T>
T fetchVertexAttribute( const uf::Mesh::View& view, const uf::Mesh::AttributeView& attributeView, size_t index ) {
#define CAST_VERTEX(type) {\
const type* vertices = (type*) attributeView.data(view.vertex.first + index);\
for ( auto i = 0; i < T::size; ++i ) res[i] = vertices[i];\
return res;\
}
#define DEQUANTIZE_VERTEX(type) {\
const type* vertices = (type*) attributeView.data(view.vertex.first + index);\
for ( auto i = 0; i < T::size; ++i ) res[i] = uf::quant::dequantize(vertices[i]);\
return res;\
}
// direct copy
if ( uf::renderer::typeToEnum<typename T::type_t>() == attributeView.type() && T::size == attributeView.components() ) {
return uf::vector::copy<typename T::type_t, T::size>( (typename T::type_t*) attributeView.data( view.vertex.first + index ) );
}
// implicit copy
T res;
switch ( attributeView.type() ) {
// dequantize
case uf::renderer::enums::Type::USHORT:
case uf::renderer::enums::Type::SHORT: {
DEQUANTIZE_VERTEX(uint16_t);
} break;
case uf::renderer::enums::Type::FLOAT: {
CAST_VERTEX(float);
} break;
#if UF_USE_FLOAT16
case uf::renderer::enums::Type::HALF: {
CAST_VERTEX(std::float16_t);
} break;
#endif
#if UF_USE_BFLOAT16
case uf::renderer::enums::Type::BFLOAT: {
CAST_VERTEX(std::bfloat16_t);
} break;
#endif
default: UF_EXCEPTION("unsupported attribute type: {}", attributeView.attribute.descriptor.type); break;
}
}
}
}

View File

@ -35,7 +35,7 @@ namespace {
#if EXT_COLOR_FLOATS
pod::Vector4f color;
#else
pod::ColorRgba color;
pod::Vector4b color;
#endif
static uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;

View File

@ -39,7 +39,7 @@ namespace {
#if EXT_COLOR_FLOATS
pod::Vector4f color;
#else
pod::ColorRgba color;
pod::Vector4b color;
#endif
static uf::stl::vector<uf::renderer::AttributeDescriptor> descriptor;
@ -349,7 +349,7 @@ namespace {
#if EXT_COLOR_FLOATS
auto& color = g.color;
#else
pod::ColorRgba color = {
pod::Vector4b color = {
(uint8_t)(g.color[0] * 255),
(uint8_t)(g.color[1] * 255),
(uint8_t)(g.color[2] * 255),

View File

@ -256,4 +256,117 @@ void uf::graph::updateAnimation( pod::Graph& graph, pod::Node& node ) {
}
}
}
}
// separate function in the event something later might need it
uf::stl::vector<pod::OBB> uf::graph::obbFromSkin( const pod::Graph& graph, const pod::Node& node ) {
const float wThresold = 0.15f;
auto& storage = ::getGraphStorage( uf::scene::getCurrentScene() );
auto& meshName = graph.meshes[node.mesh];
auto& skinName = graph.skins[node.skin];
auto& skin = storage.skins[skinName];
auto& mesh = storage.meshes[meshName];
// store as min/max AABB
uf::stl::vector<pod::OBB> bounds(skin.joints.size(), {
pod::Vector3f{ FLT_MAX, FLT_MAX, FLT_MAX },
pod::Vector3f{-FLT_MAX,-FLT_MAX,-FLT_MAX }
});
// iterate through mesh to fetch attributes
for ( const auto& view : mesh.buffer_views ) {
auto posView = view["position"];
auto jointsView = view["joints"];
auto weightView = view["weights"];
for ( auto i = 0; i < view.vertex.count; ++i ) {
auto pos = uf::mesh::fetchVertex( view, posView, i );
auto joints = uf::mesh::fetchVertexAttribute<pod::Vector4us>( view, jointsView, i );
auto weights = uf::mesh::fetchVertexAttribute<pod::Vector4f>( view, weightView, i );
for ( auto w = 0; w < 4; ++w ) {
if ( weights[w] <= wThresold ) continue;
uint16_t boneID = joints[w];
pod::Vector3f localPos = uf::matrix::multiply( skin.inverseBindMatrices[boneID], pos );
bounds[boneID].center = uf::vector::min( bounds[boneID].center, localPos );
bounds[boneID].extent = uf::vector::max( bounds[boneID].extent, 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 * 0.5f };
}
return bounds;
}
void uf::graph::rigRagdoll( pod::Graph& graph, pod::Node& node ) {
auto& storage = ::getGraphStorage(uf::scene::getCurrentScene());
auto& name = graph.skins[node.skin];
auto& skin = storage.skins[name];
auto bounds = uf::graph::obbFromSkin( graph, node );
uf::stl::unordered_map<int32_t, pod::PhysicsBody*> bodies;
// create physics bodies
const float density = 1.0f;
for ( auto i = 0; i < skin.joints.size(); ++i ) {
auto& obb = bounds[i];
if ( obb.extent.x < 0 ) continue; // invalid bounds
auto jointID = skin.joints[i]; // gLTF standard guarantees a jointID is the nodeID
auto& node = graph.nodes[jointID];
auto& entity = *node.entity;
auto offset = obb.center;
/*
auto matrix = ::worldMatrix( graph, jointID );
auto offset = uf::matrix::multiply( matrix, obb.center );
*/
float volume = 8.0f * obb.extent.x * obb.extent.y * obb.extent.z;
float mass = 10.0f; // volume * density;
auto& body = uf::physics::create( entity, mass, offset );
uf::physics::initialize( body, pod::OBB{ pod::Vector3f{}, obb.extent } );
bodies[jointID] = &body;
}
// create constraints
for ( auto i = 0; i < skin.joints.size(); ++i ) {
int32_t jointID = skin.joints[i];
auto& node = graph.nodes[jointID];
// no body: cannot constrain
if ( bodies.count( jointID ) == 0 ) continue;
// no parent: cannot constrain
if ( bodies.count( node.parent ) == 0 ) continue;
auto* bodyA = bodies[node.parent];
auto* bodyB = bodies[jointID];
auto matrixA = ::worldMatrix( graph, node.parent );
auto matrixB = ::worldMatrix( graph, jointID );
auto pivotA = uf::matrix::extractTranslation( matrixA );
auto pivotB = uf::matrix::extractTranslation( matrixB );
auto pivot = pivotB; // pivot is where the bone starts
auto axis = uf::vector::normalize( pivotB - pivotA );
// fallback
if ( uf::vector::distanceSquared( pivotB, pivotA ) < EPS2 ) {
axis = uf::quaternion::rotate( uf::matrix::extractRotation(matrixB), pod::Vector3f{0, 1, 0} );
}
// auto& constraint = uf::physics::constrain( *bodyA, *bodyB );
// uf::physics::constrainConeTwist( constraint, pivot, axis );
}
}

View File

@ -1107,6 +1107,17 @@ void uf::graph::process( pod::Graph& graph ) {
}
*/
UF_DEBUG_TIMER_MULTITRACE("Rigging ragdolls");
for ( auto& node : graph.nodes ) {
if ( node.skin < 0 || node.mesh < 0 ) continue;
ext::json::Value tag = ext::json::find( node.name, graphMetadataJson["tags"] );
if ( ext::json::isNull( tag ) ) tag["physics"] = graphMetadataJson["physics"];
if ( tag["physics"]["ragdoll"].as<bool>(false) ) {
uf::graph::rigRagdoll( graph, node );
}
}
UF_DEBUG_TIMER_MULTITRACE_END("Rigged ragdolls.");
UF_DEBUG_TIMER_MULTITRACE("Updating master graph");
#if UF_GRAPH_EXTENDED
uf::graph::reload( graph );
@ -1116,6 +1127,7 @@ void uf::graph::process( pod::Graph& graph ) {
UF_DEBUG_TIMER_MULTITRACE_END("Processed graph.");
}
void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent ) {
auto& scene = uf::scene::getCurrentScene();
auto& storage = ::getGraphStorage( scene );
@ -1333,7 +1345,7 @@ void uf::graph::process( pod::Graph& graph, int32_t index, uf::Object& parent )
float mass = phyziks["mass"].as(0.0f);
auto center = uf::vector::decode( phyziks["center"], pod::Vector3f{} );
auto& body = rig.initialized() ? rig.get() : uf::physics::create( entity, mass, center );
auto& body = uf::physics::create( entity, mass, center );
uf::physics::initialize( body, mesh, type != "mesh" );
body.material.staticFriction = phyziks["friction"].as(body.material.staticFriction);

View File

@ -115,6 +115,12 @@ namespace {
}
return nodeIndex;
}
pod::Vector3f computeTangent( const pod::Vector3f& normal ) {
pod::Vector3f up = ( std::fabs(normal.y) < 0.999f ) ? pod::Vector3f{0,1,0} : pod::Vector3f{1,0,0}; // pick a vector not parallel to normal
pod::Vector3f tangent = uf::vector::normalize( uf::vector::cross( up, normal ) );
return tangent;
}
}
void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const uf::Serializer& metadata ) {

View File

@ -12,13 +12,9 @@ struct {
if ( graph.metadata["sanitizer"]["winding order"].as<bool>(true) || graph.metadata["renderer"]["invert"].as<bool>(true) ) {
sanitizer.windingOrder.should = true;
}
#if UF_GRAPH_PROCESS_PRIMITIVES_FULL
if ( graph.metadata["sanitizer"]["tangents"].as<bool>(true) ) {
if ( graph.metadata["sanitizer"]["tangents"].as<bool>(false) ) {
sanitizer.tangents.should = true;
}
#else
sanitizer.tangents.should = false;
#endif
uf::stl::vector<uf::Meshlet_T<UF_GRAPH_MESH_FORMAT>> meshlets;
@ -44,8 +40,8 @@ for ( auto& p : m.primitives ) {
{"TEXCOORD_0", {}},
{"COLOR_0", {}},
{"NORMAL", {}},
#if UF_GRAPH_PROCESS_PRIMITIVES_FULL
{"TANGENT", {}},
#if UF_GRAPH_PROCESS_PRIMITIVES_FULL
{"JOINTS_0", {}},
{"WEIGHTS_0", {}},
#endif
@ -127,8 +123,8 @@ for ( auto& p : m.primitives ) {
ITERATE_ATTRIBUTE("TEXCOORD_0", uv, 1);
ITERATE_ATTRIBUTE("COLOR_0", color, 255.0f);
ITERATE_ATTRIBUTE("NORMAL", normal, 1);
#if UF_GRAPH_PROCESS_PRIMITIVES_FULL
ITERATE_ATTRIBUTE("TANGENT", tangent, 1);
#if UF_GRAPH_PROCESS_PRIMITIVES_FULL
ITERATE_ATTRIBUTE("JOINTS_0", joints, 1);
ITERATE_ATTRIBUTE("WEIGHTS_0", weights, 1);
#endif
@ -243,7 +239,6 @@ for ( auto& p : m.primitives ) {
}
}
}
#if UF_GRAPH_PROCESS_PRIMITIVES_FULL
/* calculate tangents */ if ( sanitizer.tangents.should ) {
if ( !meshlet.indices.empty() ) {
for ( size_t i = 0; i < meshlet.indices.size() / 3; ++i ) {
@ -276,12 +271,12 @@ for ( auto& p : m.primitives ) {
auto tangent_tri = (deltaTriPosition[0] * deltaTriUV[1].y - deltaTriPosition[1] * deltaTriUV[0].y) * r;
auto bitangent_tri = (deltaTriPosition[1] * deltaTriUV[0].x - deltaTriPosition[0] * deltaTriUV[1].x) * r;
for ( auto i = 0; i < 3; ++i ) {
auto& normal = meshlet.vertices[indices[0]].normal;
auto& tangent = meshlet.vertices[indices[0]].tangent;
for ( auto j = 0; j < 3; ++j ) {
auto& normal = meshlet.vertices[indices[j]].normal;
auto& tangent = meshlet.vertices[indices[j]].tangent;
tangent = uf::vector::normalize(tangent_tri - normal * uf::vector::dot(normal, tangent_tri));
if (uf::vector::dot(uf::vector::cross(normal, tangent), bitangent_tri) < 0.0f)
if ( uf::vector::dot(uf::vector::cross(normal, tangent), bitangent_tri) < 0.0f )
tangent = tangent * -1.0f;
}
}
@ -316,18 +311,17 @@ for ( auto& p : m.primitives ) {
auto tangent_tri = (deltaTriPosition[0] * deltaTriUV[1].y - deltaTriPosition[1] * deltaTriUV[0].y) * r;
auto bitangent_tri = (deltaTriPosition[1] * deltaTriUV[0].x - deltaTriPosition[0] * deltaTriUV[1].x) * r;
for ( auto i = 0; i < 3; ++i ) {
auto& normal = meshlet.vertices[indices[0]].normal;
auto& tangent = meshlet.vertices[indices[0]].tangent;
for ( auto j = 0; j < 3; ++j ) {
auto& normal = meshlet.vertices[indices[j]].normal;
auto& tangent = meshlet.vertices[indices[j]].tangent;
tangent = uf::vector::normalize(tangent_tri - normal * uf::vector::dot(normal, tangent_tri));
if (uf::vector::dot(uf::vector::cross(normal, tangent), bitangent_tri) < 0.0f)
tangent = tangent * -1.0f;
}
}
}
}
#endif
}
if ( sanitizer.windingOrder.should && !graph.metadata["renderer"]["invert"].as<bool>(true) ) {

View File

@ -21,7 +21,7 @@
#if BARYCENTRIC
// 0 keeps a buffer for barycentric coordinates, 1 will reconstruct in the deferred pass
#ifndef BARYCENTRIC_CALCULATE
#define BARYCENTRIC_CALCULATE 0
#define BARYCENTRIC_CALCULATE 1
#endif
#endif

View File

@ -228,7 +228,7 @@ void impl::buildMeshBVH( pod::BVH& bvh, const uf::Mesh& mesh, pod::BVH::index_t
auto tris = view.index.count / 3;
for ( auto triIndexID = 0; triIndexID < tris; ++triIndexID ) {
auto tri = impl::fetchTriangle( view, indices, positions, triIndexID );
auto tri = uf::mesh::fetchTriangle( view, indices, positions, triIndexID );
auto aabb = impl::computeTriangleAABB( tri );
auto triID = triIndexID + (view.index.first / 3);
@ -446,7 +446,7 @@ void impl::refitBVH( pod::BVH& bvh, const uf::Mesh& mesh ) {
auto tris = view.index.count / 3;
for ( auto triIndexID = 0; triIndexID < tris; ++triIndexID ) {
auto tri = impl::fetchTriangle( view, indices, positions, triIndexID );
auto tri = uf::mesh::fetchTriangle( view, indices, positions, triIndexID );
auto aabb = impl::computeTriangleAABB( tri );
bounds.emplace_back(aabb);
}

View File

@ -500,98 +500,9 @@ pod::Vector3f impl::triangleNormal( const pod::TriangleWithNormal& tri ) {
if ( uf::vector::magnitude( tri.normal ) < 0.001f ) return impl::triangleNormal( (const pod::Triangle&) tri );
return tri.normal;
}
// mesh accessing
size_t impl::getIndex( const void* pointer, size_t stride, size_t index ) {
#define CAST_INDEX(T) case sizeof(T): return ((T*) pointer)[index];
switch ( stride ) {
CAST_INDEX(uint8_t);
CAST_INDEX(uint16_t);
CAST_INDEX(uint32_t);
default: {
UF_EXCEPTION("invalid stride type: {}", stride);
} break;
}
}
size_t impl::getIndex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, size_t index ) {
return impl::getIndex( indices.data(view.index.first), indices.stride(), index );
}
pod::Vector3f impl::getVertex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& positions, size_t index ) {
const auto stride = positions.stride();
#define CAST_VERTEX(T) {\
const T* vertices = (T*) positions.data(view.vertex.first + index);\
return { vertices[0], vertices[1], vertices[2], };\
}
#define DEQUANTIZE_VERTEX(T) {\
const T* vertices = (T*) positions.data(view.vertex.first + index);\
return { uf::quant::dequantize(vertices[0]), uf::quant::dequantize(vertices[1]), uf::quant::dequantize(vertices[2]), };\
}
switch ( positions.attribute.descriptor.type ) {
// dequantize
case uf::renderer::enums::Type::USHORT:
case uf::renderer::enums::Type::SHORT: {
DEQUANTIZE_VERTEX(uint16_t);
} break;
case uf::renderer::enums::Type::FLOAT: {
CAST_VERTEX(float);
} break;
#if UF_USE_FLOAT16
case uf::renderer::enums::Type::HALF: {
CAST_VERTEX(std::float16_t);
} break;
#endif
#if UF_USE_BFLOAT16
case uf::renderer::enums::Type::BFLOAT: {
CAST_VERTEX(std::bfloat16_t);
} break;
#endif
default: UF_EXCEPTION("unsupported vertex type: {}", positions.attribute.descriptor.type); break;
}
// return impl::getVertex( positions.data(view.vertex.first), positions.stride(), index );
}
pod::Triangle impl::fetchTriangle( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, const uf::Mesh::AttributeView& positions, size_t triID ) {
auto index = triID * 3;
pod::Triangle tri;
FOR_EACH(3, {
tri.points[i] = impl::getVertex( view, positions, impl::getIndex( view, indices, index + i ) );
});
return tri;
}
// for clean code, this would be preferable
// but this incurs two lookups every triangle fetch, and I doubt the optimizer will optimize that away, so explicitly passing attribute views is preferable
pod::Triangle impl::fetchTriangle( const uf::Mesh::View& view, size_t triID ) {
return impl::fetchTriangle( view, view["index"], view["position"], triID );
}
pod::TriangleWithNormal impl::fetchTriangle( const uf::Mesh& mesh, size_t triID ) {
const auto& views = mesh.buffer_views;
UF_ASSERT(!views.empty());
// find which view contains this triangle index.
size_t triBase = 0;
const uf::Mesh::View* view = nullptr;
for ( auto& v : views ) {
auto trisInView = v.index.count / 3;
if (triID < triBase + trisInView) {
view = &v;
triID -= triBase; // local triangle index inside this view
break;
}
triBase += trisInView;
}
UF_ASSERT( view );
pod::TriangleWithNormal tri = { impl::fetchTriangle( *view, triID ) };
tri.normal = uf::vector::normalize(uf::vector::cross(tri.points[1] - tri.points[0], tri.points[2] - tri.points[0]));
return tri;
}
// if body is a mesh, apply its transform to the triangles, else reorient the normal with respect to the body
pod::TriangleWithNormal impl::fetchTriangle( const uf::Mesh& mesh, size_t triID, const pod::PhysicsBody& body ) {
auto tri = impl::fetchTriangle( mesh, triID );
auto tri = uf::mesh::fetchTriangle( mesh, triID );
auto transform = impl::getTransform( body );
@ -646,7 +557,7 @@ pod::AABB impl::computeTriangleAABB( const pod::Triangle& tri ) {
// returns the AABB of a hull
pod::AABB impl::computeConvexHullAABB( const uf::Mesh::View& view, const uf::Mesh::AttributeView& positions, pod::AABB bounds ) {
for ( size_t i = 0; i < view.vertex.count; ++i ) {
pod::Vector3f v = impl::getVertex( view, positions, i );
pod::Vector3f v = uf::mesh::fetchVertex( view, positions, i );
bounds.min = uf::vector::min( bounds.min, v );
bounds.max = uf::vector::max( bounds.max, v );
}

View File

@ -154,7 +154,7 @@ void impl::getSupportFace( const pod::PhysicsBody& body, const pod::Vector3f& di
auto& indices = view["index"];
auto& positions = view["position"];
for ( size_t i = 0; i < view.index.count / 3; ++i ) {
pod::Triangle tri = impl::fetchTriangle( view, indices, positions, i );
pod::Triangle tri = uf::mesh::fetchTriangle( view, indices, positions, i );
pod::Vector3f normal = impl::triangleNormal( tri );
float d = uf::vector::dot( normal, localDir );
if ( d > bestDot ) {

View File

@ -68,7 +68,7 @@ pod::Vector3f impl::support( const pod::PhysicsBody& body, const pod::Vector3f&
const auto& view = mesh.buffer_views[viewIdx];
auto& positions = view["position"];
for ( size_t i = 0; i < view.vertex.count; ++i ) {
pod::Vector3f v = impl::getVertex( view, positions, i );
pod::Vector3f v = uf::mesh::fetchVertex( view, positions, i );
float dist = uf::vector::dot( v, localDir );
if ( dist > maxDist ) {
maxDist = dist;

View File

@ -142,7 +142,7 @@ bool impl::obbObb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Ma
bool impl::obbAabb( const pod::PhysicsBody& a, const pod::PhysicsBody& b, pod::Manifold& manifold ) {
ASSERT_COLLIDER_TYPES( OBB, OBB );
ASSERT_COLLIDER_TYPES( OBB, AABB );
auto tA = impl::getTransform( a );
auto tB = impl::getTransform( b );

View File

@ -243,7 +243,7 @@ bool impl::rayMesh( const pod::Ray& r, const pod::PhysicsBody& body, pod::RayQue
impl::queryBVH( bvh, ray, candidates );
for ( auto triID : candidates ) {
auto tri = impl::fetchTriangle( meshData, triID );
auto tri = uf::mesh::fetchTriangle( meshData, triID );
float t, u, v;
if ( !impl::rayTriangleIntersect( ray, tri, t, u, v ) ) continue;

View File

@ -756,4 +756,54 @@ void uf::Mesh::_insertIs( uf::Mesh::Input& input, const void* data, size_t size,
buffers[attribute.buffer].insert( buffers[attribute.buffer].end(), p + attribute.descriptor.offset, p + attribute.descriptor.offset + attribute.descriptor.size );
}
#endif
}
////
size_t uf::mesh::fetchIndex( const void* pointer, size_t stride, size_t index ) {
#define CAST_INDEX(T) case sizeof(T): return ((T*) pointer)[index];
switch ( stride ) {
CAST_INDEX(uint8_t);
CAST_INDEX(uint16_t);
CAST_INDEX(uint32_t);
default: {
UF_EXCEPTION("invalid stride type: {}", stride);
} break;
}
}
pod::Vector3f uf::mesh::fetchVertex( const uf::Mesh::View& view, const uf::Mesh::AttributeView& positions, size_t index ) {
return uf::mesh::fetchVertexAttribute<pod::Vector3f>( view, positions, index );
}
pod::Triangle uf::mesh::fetchTriangle( const uf::Mesh::View& view, const uf::Mesh::AttributeView& indices, const uf::Mesh::AttributeView& positions, size_t triID ) {
auto index = triID * 3;
pod::Triangle tri;
FOR_EACH(3, {
tri.points[i] = uf::mesh::fetchVertex( view, positions, uf::mesh::fetchIndex( view, indices, index + i ) );
});
return tri;
}
pod::TriangleWithNormal uf::mesh::fetchTriangle( const uf::Mesh& mesh, size_t triID ) {
const auto& views = mesh.buffer_views;
UF_ASSERT(!views.empty());
// find which view contains this triangle index.
size_t triBase = 0;
const uf::Mesh::View* view = nullptr;
for ( auto& v : views ) {
auto trisInView = v.index.count / 3;
if (triID < triBase + trisInView) {
view = &v;
triID -= triBase; // local triangle index inside this view
break;
}
triBase += trisInView;
}
UF_ASSERT( view );
pod::TriangleWithNormal tri = { uf::mesh::fetchTriangle( *view, triID ) };
tri.normal = uf::vector::normalize(uf::vector::cross(tri.points[1] - tri.points[0], tri.points[2] - tri.points[0]));
return tri;
}

View File

@ -56,7 +56,7 @@ namespace {
pod::Vector4f tangent;
};
struct TexCoord {
pod::ColorRgba color;
pod::Vector4b color;
pod::Vector2f16 uv;
pod::Vector2f st;
pod::Vector2f16 wx;