several agonizing hours of dealing with more GCC v13 gremlins involving calculating tangents

This commit is contained in:
ecker 2026-06-07 23:53:41 -05:00
parent 6ae24419e7
commit 9e823645a4
13 changed files with 380 additions and 146 deletions

View File

@ -1,13 +1,13 @@
{
"engine": {
"scenes": {
"start": "StartMenu",
"start": "SourceEngine",
"matrix": { "reverseInfinite": true },
"lights": { "enabled": true,
"lightmaps": true,
"lightmaps": false,
"max": 32,
"shadows": {
"enabled": true,
"enabled": false,
"update": 4,
"max": 16,
"samples": 2

View File

@ -62,7 +62,7 @@
"renderer": {
"front face": "auto",
"cull mode": "back",
"filter": "nearest",
"filter": "linear",
"atlas": false,
"invert": true,

View File

@ -1,8 +1,8 @@
{
"import": "./base_sourceengine.json",
"assets": [
// { "filename": "./models/mds_mcdonalds.glb" }
{ "filename": "./models/mds_mcdonalds/graph.json" }
{ "filename": "./models/mds_mcdonalds.glb" }
// { "filename": "./models/mds_mcdonalds/graph.json" }
// ,{ "filename": "/ball.json", "delay": 1 }
// ,{ "filename": "/ragdoll.json", "delay": 1 }
// ,{ "filename": "/craeture.json", "delay": 2.0 }

View File

@ -1,8 +1,8 @@
{
"import": "./base_sourceengine.json",
"assets": [
{ "filename": "./maps/cs_office.bsp" }
// { "filename": "./maps/cs_office/graph.json" }
// { "filename": "./maps/cs_office.bsp" }
{ "filename": "./maps/cs_office/graph.json" }
],
"metadata": {
"graph": {

View File

@ -1,7 +1,7 @@
{
// "import": "./rp_downtown_v2.json"
// "import": "./ss2_medsci1.json"
// "import": "./mds_mcdonalds.json"
"import": "./cs_office.json"
"import": "./mds_mcdonalds.json"
// "import": "./cs_office.json"
// "import": "./gm_construct.json"
}

View File

@ -400,11 +400,7 @@ void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
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));
}
for ( uint _ = 0; _ < 3; ++_ ) points[_].tangent = tangent_tri;
}
}
@ -456,6 +452,8 @@ void populateSurface( InstanceAddresses addresses, uvec3 indices ) {
// bind tangent
if ( triangle.point.tangent != vec3(0) ) {
surface.tangent.world = normalize(vec3( surface.object.model * vec4(triangle.point.tangent, 0.0) ));
surface.tangent.world = normalize(surface.tangent.world - dot(surface.tangent.world, surface.normal.world) * surface.normal.world);
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);
}

View File

@ -99,13 +99,23 @@ namespace uf {
size_t primitiveID = partitioned.size();
auto& slice = partitioned.emplace_back();
slice.vertices.reserve( mlet.indices.size() );
slice.indices.reserve( mlet.indices.size() );
uf::stl::unordered_map<U, U> indexMap;
for ( U globalID : mlet.indices ) {
if ( indexMap.find(globalID) == indexMap.end() ) {
indexMap[globalID] = (U)(slice.vertices.size());
slice.vertices.emplace_back(meshlet.vertices[globalID]);
}
slice.indices.emplace_back( indexMap[globalID] );
}
if ( clip ) {
node.effectiveExtents.min = node.extents.min;
node.effectiveExtents.max = node.extents.max;
uf::shapes::clip<T,U>( slice.vertices, slice.indices, pod::AABB{ node.extents.min, node.extents.max } );
// blender outputs poopy tangents that don't get fixed here
// better to recalculate them before slicing anyways
// uf::mesh::tangents<T,U>( slice.vertices, slice.indices );
}
slice.primitive.instance = meshlet.primitive.instance;

View File

@ -7,6 +7,7 @@
#include <uf/utils/string/hash.h>
#include <functional>
#include <type_traits>
#include <uf/utils/memory/unordered_map.h>
#if UF_USE_VULKAN
@ -603,54 +604,26 @@ namespace uf {
};
namespace mesh {
template <typename T, typename = void> struct has_tangent : std::false_type {};
template <typename T> struct has_tangent<T, std::void_t<decltype(std::declval<T>().tangent)>> : std::true_type {};
template <typename T, typename = void> struct has_normal : std::false_type {};
template <typename T> struct has_normal<T, std::void_t<decltype(std::declval<T>().normal)>> : std::true_type {};
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 );
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;
}
}
template<typename T> size_t windingOrder( uf::stl::vector<T>& vertices );
template<typename T, typename U> size_t windingOrder( uf::stl::vector<T>& vertices, uf::stl::vector<U>& indices );
template<typename T> void normals( uf::stl::vector<T>& vertices );
template<typename T, typename U = uint32_t> void normals( uf::stl::vector<T>& vertices, const uf::stl::vector<U>& indices );
// specifically refuses to work properly
template<typename T> void tangents( uf::stl::vector<T>& vertices );
template<typename T, typename U = uint32_t> void tangents( uf::stl::vector<T>& vertices, const uf::stl::vector<U>& indices );
template<typename T> T fetchVertexAttribute( const uf::Mesh::View& view, const uf::Mesh::AttributeView& attributeView, size_t index );
template<typename T>
T& getVertexAttribute( const uf::Mesh::View& view, const uf::Mesh::AttributeView& attributeView, size_t index ) {
UF_ASSERT( uf::renderer::typeToEnum<typename T::type_t>() == attributeView.type() && T::size == attributeView.components() );
@ -711,6 +684,250 @@ namespace uf {
}
}
template<typename T>
T uf::mesh::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;
}
}
template<typename T>
size_t uf::mesh::windingOrder( uf::stl::vector<T>& vertices ) {
if constexpr ( !uf::mesh::has_normal<T>::value ) return 0;
size_t corrected = 0;
for ( size_t i = 0; i < vertices.size() / 3; ++i ) {
pod::Vector3f position[3] = {
vertices[i * 3 + 0].position,
vertices[i * 3 + 1].position,
vertices[i * 3 + 2].position,
};
pod::Vector3f normal = uf::vector::normalize( uf::vector::cross((position[0] - position[1]), (position[1] - position[2])) );
if ( uf::vector::dot( vertices[i * 3 + 0].normal, normal ) < 0.0f ) {
std::swap( vertices[i * 3 + 0], vertices[i * 3 + 2] );
++corrected;
}
}
return corrected;
}
template<typename T, typename U>
size_t uf::mesh::windingOrder( uf::stl::vector<T>& vertices, uf::stl::vector<U>& indices ) {
if constexpr ( !uf::mesh::has_normal<T>::value ) return 0;
if ( indices.empty() ) return uf::mesh::windingOrder( vertices );
size_t corrected = 0;
for ( size_t i = 0; i < indices.size() / 3; ++i ) {
size_t idx[3] = {
indices[i * 3 + 0],
indices[i * 3 + 1],
indices[i * 3 + 2],
};
pod::Vector3f position[3] = {
vertices[idx[0]].position,
vertices[idx[1]].position,
vertices[idx[2]].position,
};
pod::Vector3f normal = uf::vector::normalize( uf::vector::cross((position[0] - position[1]), (position[1] - position[2])) );
if ( uf::vector::dot( vertices[idx[0]].normal, normal ) < 0.0f ) {
indices[i * 3 + 0] = idx[2];
indices[i * 3 + 2] = idx[0];
++corrected;
}
}
return corrected;
}
template<typename T>
void uf::mesh::tangents( uf::stl::vector<T>& vertices ) {
if constexpr ( !uf::mesh::has_tangent<T>::value ) return;
for ( size_t i = 0; i < vertices.size(); i += 3 ) {
size_t idx[3] = { i + 0, i + 1, i + 2 };
auto p0 = vertices[idx[0]].position;
auto p1 = vertices[idx[1]].position;
auto p2 = vertices[idx[2]].position;
auto uv0 = vertices[idx[0]].uv;
auto uv1 = vertices[idx[1]].uv;
auto uv2 = vertices[idx[2]].uv;
auto p10 = p1 - p0;
auto p20 = p2 - p0;
auto uv10 = uv1 - uv0;
auto uv20 = uv2 - uv0;
auto det = (uv10.x * uv20.y - uv10.y * uv20.x);
float r = 1.0f / det;
auto t = (p10 * uv20.y - p20 * uv10.y) * r;
auto b = (p20 * uv10.x - p10 * uv20.x) * r;
for ( auto j = 0; j < 3; ++j ) {
auto& n = vertices[idx[j]].normal;
auto& tangent = vertices[idx[j]].tangent;
tangent = uf::vector::normalize(t - n * uf::vector::dot(n, t));
if ( uf::vector::dot( uf::vector::cross(n, tangent), b) < 0.0f ) tangent = -tangent;
}
/*
pod::Vector3f position[3] = {
vertices[idx[0]].position, vertices[idx[1]].position, vertices[idx[2]].position
};
pod::Vector2f uv[3] = {
vertices[idx[0]].uv, vertices[idx[1]].uv, vertices[idx[2]].uv
};
pod::Vector3f dPosition[2] = { position[1] - position[0], position[2] - position[0] };
pod::Vector2f dUV[2] = { uv[1] - uv[0], uv[2] - uv[0] };
float det = (dUV[0].x * dUV[1].y - dUV[0].y * dUV[1].x);
if ( det == 0.0f ) continue;
float r = 1.0f / det;
auto t = (dPosition[0] * dUV[1].y - dPosition[1] * dUV[0].y) * r;
auto b = (dPosition[1] * dUV[0].x - dPosition[0] * dUV[1].x) * r;
for ( auto j = 0; j < 3; ++j ) {
auto& normal = vertices[idx[j]].normal;
auto& tangent = vertices[idx[j]].tangent;
tangent = uf::vector::normalize(t - normal * uf::vector::dot(normal, t));
if ( uf::vector::dot(uf::vector::cross(normal, tangent), b) < 0.0f ) tangent = -tangent;
}
*/
}
}
template<typename T, typename U>
void uf::mesh::tangents( uf::stl::vector<T>& vertices, const uf::stl::vector<U>& indices ) {
if constexpr ( !uf::mesh::has_tangent<T>::value ) return;
if ( indices.empty() ) return tangents( vertices );
for ( size_t i = 0; i < indices.size(); i += 3 ) {
size_t idx[3] = { indices[i + 0], indices[i + 1], indices[i + 2] };
auto p0 = vertices[idx[0]].position;
auto p1 = vertices[idx[1]].position;
auto p2 = vertices[idx[2]].position;
auto uv0 = vertices[idx[0]].uv;
auto uv1 = vertices[idx[1]].uv;
auto uv2 = vertices[idx[2]].uv;
auto p10 = p1 - p0;
auto p20 = p2 - p0;
auto uv10 = uv1 - uv0;
auto uv20 = uv2 - uv0;
auto det = (uv10.x * uv20.y - uv10.y * uv20.x);
float r = 1.0f / det;
auto t = (p10 * uv20.y - p20 * uv10.y) * r;
auto b = (p20 * uv10.x - p10 * uv20.x) * r;
for ( auto j = 0; j < 3; ++j ) {
auto& n = vertices[idx[j]].normal;
auto& tangent = vertices[idx[j]].tangent;
tangent = uf::vector::normalize(t - n * uf::vector::dot(n, t));
if ( uf::vector::dot( uf::vector::cross(n, tangent), b) < 0.0f ) tangent = -tangent;
}
/*
pod::Vector3f position[3] = { vertices[idx[0]].position, vertices[idx[1]].position, vertices[idx[2]].position };
pod::Vector2f uv[3] = { vertices[idx[0]].uv, vertices[idx[1]].uv, vertices[idx[2]].uv };
pod::Vector3f dPosition[2] = { position[1] - position[0], position[2] - position[0] };
pod::Vector2f dUV[2] = { uv[1] - uv[0], uv[2] - uv[0] };
float det = (dUV[0].x * dUV[1].y - dUV[0].y * dUV[1].x);
if ( det == 0.0f ) continue;
float r = 1.0f / det;
auto t = (dPosition[0] * dUV[1].y - dPosition[1] * dUV[0].y) * r;
auto b = (dPosition[1] * dUV[0].x - dPosition[0] * dUV[1].x) * r;
for ( auto j = 0; j < 3; ++j ) {
auto& normal = vertices[idx[j]].normal;
auto& tangent = vertices[idx[j]].tangent;
tangent = uf::vector::normalize(t - normal * uf::vector::dot(normal, t));
if ( uf::vector::dot(uf::vector::cross(normal, tangent), b) < 0.0f ) tangent = -tangent;
}
*/
}
}
template<typename T>
void uf::mesh::normals( uf::stl::vector<T>& vertices ) {
if constexpr ( !uf::mesh::has_normal<T>::value ) return;
for ( size_t i = 0; i < vertices.size(); i += 3 ) {
auto& v0 = vertices[i + 0];
auto& v1 = vertices[i + 1];
auto& v2 = vertices[i + 2];
pod::Vector3f normal = uf::vector::normalize(uf::vector::cross(v1.position - v0.position, v2.position - v0.position));
v0.normal = normal;
v1.normal = normal;
v2.normal = normal;
}
}
template<typename T, typename U>
void uf::mesh::normals( uf::stl::vector<T>& vertices, const uf::stl::vector<U>& indices ) {
if constexpr ( !uf::mesh::has_normal<T>::value ) return;
if ( indices.empty() ) return normals( vertices );
for ( auto& v : vertices ) v.normal = {};
for ( size_t i = 0; i < indices.size(); i += 3 ) {
auto& v0 = vertices[indices[i + 0]];
auto& v1 = vertices[indices[i + 1]];
auto& v2 = vertices[indices[i + 2]];
pod::Vector3f normal = uf::vector::cross(v1.position - v0.position, v2.position - v0.position);
v0.normal += normal;
v1.normal += normal;
v2.normal += normal;
}
for ( auto& v : vertices ) v.normal = uf::vector::normalize( v.normal );
}
template<typename T> uf::stl::vector<pod::Primitive> uf::Mesh::compile( const uf::stl::vector<T>& meshlets ) {
uf::stl::vector<pod::Primitive> primitives;
uf::mesh::compile( *this, meshlets, primitives );

View File

@ -539,8 +539,8 @@ UF_VERTEX_INTERPOLATE(uf::graph::mesh::Base, {
uf::vector::lerp( p1.uv, p2.uv, t ),
t < 0.5 ? p1.color : p2.color,
uf::vector::lerp( p1.st, p2.st, t ),
uf::vector::normalize( uf::vector::lerp( p1.normal, p2.normal, t ) ),
uf::vector::normalize( uf::vector::lerp( p1.tangent, p2.tangent, t ) ),
uf::vector::lerp( p1.normal, p2.normal, t ),
uf::vector::lerp( p1.tangent, p2.tangent, t ),
};
})
@ -560,8 +560,8 @@ UF_VERTEX_INTERPOLATE(uf::graph::mesh::Skinned, {
uf::vector::lerp( p1.uv, p2.uv, t ),
t < 0.5 ? p1.color : p2.color,
uf::vector::lerp( p1.st, p2.st, t ),
uf::vector::normalize( uf::vector::lerp( p1.normal, p2.normal, t ) ),
uf::vector::normalize( uf::vector::lerp( p1.tangent, p2.tangent, t ) ),
uf::vector::lerp( p1.normal, p2.normal, t ),
uf::vector::lerp( p1.tangent, p2.tangent, t ),
t < 0.5 ? p1.joints : p2.joints,
uf::vector::lerp( p1.weights, p2.weights, t ),
};
@ -1627,6 +1627,7 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) {
}
}
#if UF_USE_VULKAN
if ( commands && !grouped.empty() ) {
auto objectKeyName = std::to_string(grouped.front().objectID);
if ( storage.entities.map.count(objectKeyName) > 0 ) {
@ -1638,6 +1639,7 @@ bool uf::graph::tick( pod::Graph::Storage& storage ) {
}
}
}
#endif
}
for ( auto& key : storage.textures.keys ) textures.emplace_back( storage.textures.map[key] );

View File

@ -16,6 +16,7 @@
#include <uf/utils/image/atlas.h>
#include <uf/utils/string/hash.h>
#include <uf/utils/mesh/grid.h>
#include <uf/utils/io/vfs.h>
#include <gltf/tiny_gltf.h>
#include <uf/ext/gltf/gltf.h>
@ -130,10 +131,18 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
tinygltf::TinyGLTF loader;
uf::stl::string warn, err;
bool ret = extension == "glb" ? loader.LoadBinaryFromFile(&model, &err, &warn, filename) : loader.LoadASCIIFromFile(&model, &err, &warn, filename);
//bool ret = extension == "glb" ? loader.LoadBinaryFromFile(&model, &err, &warn, filename) : loader.LoadASCIIFromFile(&model, &err, &warn, filename);
graph.name = filename;
graph.metadata = metadata;
bool ret = false;
if ( extension == "glb" ) {
// uf::stl::vector<uint8_t> buffer;
// if ( !uf::io::readAsBuffer( buffer, filename ) ) return;
// ret = loader.LoadBinaryFromMemory(&model, &err, &warn, buffer.data(), buffer.size());
ret = loader.LoadBinaryFromFile(&model, &err, &warn, uf::vfs::resolveBase(filename));
} else {
// crunge
ret = loader.LoadASCIIFromFile(&model, &err, &warn, uf::vfs::resolveBase(filename));
}
if ( !warn.empty() ) UF_MSG_WARNING("glTF warning: {}", warn);
if ( !err.empty() ) UF_MSG_ERROR("glTF error: {}", err);
@ -141,10 +150,11 @@ void ext::gltf::load( pod::Graph& graph, const uf::stl::string& filename, const
return;
}
graph.name = filename;
graph.metadata = metadata;
uf::stl::string key = graph.metadata["key"].as<uf::stl::string>("");
if ( key != "" ) {
key += ":";
}
if ( key != "" ) key += ":";
auto& storage = uf::graph::getStorage( graph );

View File

@ -4,6 +4,9 @@ struct {
size_t corrected{};
size_t total{};
} windingOrder;
struct {
bool should = true;
} normals;
struct {
bool should = true;
} tangents;
@ -12,6 +15,9 @@ struct {
if ( graph.metadata["sanitizer"]["winding order"].as<bool>(true) || graph.metadata["renderer"]["invert"].as<bool>(true) ) {
sanitizer.windingOrder.should = true;
}
if ( graph.metadata["sanitizer"]["normals"].as<bool>(false) ) {
sanitizer.normals.should = true;
}
if ( graph.metadata["sanitizer"]["tangents"].as<bool>(false) ) {
sanitizer.tangents.should = true;
}
@ -93,28 +99,27 @@ for ( auto& p : m.primitives ) {
}
for ( size_t i = 0; i < meshlet.vertices.size(); ++i ) {
#if 0
#define ITERATE_ATTRIBUTE( name, member )\
memcpy( &vertex.member[0], &attributes[name].buffer[i * attributes[name].components], attributes[name].stride );
#else
#define ITERATE_ATTRIBUTE( name, member, floatScale )\
if ( !attributes[name].int8s.empty() ) { \
for ( size_t j = 0; j < attributes[name].components; ++j )\
vertex.member[j] = attributes[name].int8s[i * attributes[name].components + j];\
} else if ( !attributes[name].int16s.empty() ) { \
for ( size_t j = 0; j < attributes[name].components; ++j )\
vertex.member[j] = attributes[name].int16s[i * attributes[name].components + j];\
} else if ( !attributes[name].int32s.empty() ) { \
for ( size_t j = 0; j < attributes[name].components; ++j )\
vertex.member[j] = attributes[name].int32s[i * attributes[name].components + j];\
} else if ( !attributes[name].floats.empty() ) { \
for ( size_t j = 0; j < attributes[name].components; ++j )\
vertex.member[j] = attributes[name].floats[i * attributes[name].components + j] * floatScale;\
} else {\
/*for some reason setting this breaks VXGI reflections*/\
/*vertex.member = {};*/\
}
#endif
#define ITERATE_ATTRIBUTE( name, member, floatScale )\
if ( !attributes[name].int8s.empty() ) { \
size_t limit = std::min<size_t>(attributes[name].components, vertex.member.size); \
for ( size_t j = 0; j < limit; ++j )\
vertex.member[j] = attributes[name].int8s[i * attributes[name].components + j];\
} else if ( !attributes[name].int16s.empty() ) { \
size_t limit = std::min<size_t>(attributes[name].components, vertex.member.size); \
for ( size_t j = 0; j < limit; ++j )\
vertex.member[j] = attributes[name].int16s[i * attributes[name].components + j];\
} else if ( !attributes[name].int32s.empty() ) { \
size_t limit = std::min<size_t>(attributes[name].components, vertex.member.size); \
for ( size_t j = 0; j < limit; ++j )\
vertex.member[j] = attributes[name].int32s[i * attributes[name].components + j];\
} else if ( !attributes[name].floats.empty() ) { \
size_t limit = std::min<size_t>(attributes[name].components, vertex.member.size); \
for ( size_t j = 0; j < limit; ++j )\
vertex.member[j] = attributes[name].floats[i * attributes[name].components + j] * floatScale;\
} else {\
/*for some reason setting this breaks VXGI reflections*/\
/*vertex.member = {};*/\
}
auto& vertex = meshlet.vertices[i];
ITERATE_ATTRIBUTE("POSITION", position, 1);
@ -182,55 +187,16 @@ for ( auto& p : m.primitives ) {
meshlet.primitive.drawCommand.vertices = meshlet.vertices.size();
/* detect winding order */ if ( sanitizer.windingOrder.should ) {
if ( !meshlet.indices.empty() ) {
for ( size_t i = 0; i < meshlet.indices.size() / 3; ++i ) {
size_t indices[3] = {
meshlet.indices[i * 3 + 0],
meshlet.indices[i * 3 + 1],
meshlet.indices[i * 3 + 2],
};
pod::Vector3f triPosition[3] = {
meshlet.vertices[indices[0]].position,
meshlet.vertices[indices[1]].position,
meshlet.vertices[indices[2]].position,
};
pod::Vector3f normal = meshlet.vertices[indices[0]].normal;
pod::Vector3f geomNormal = uf::vector::normalize( uf::vector::cross((triPosition[0] - triPosition[1]), (triPosition[1] - triPosition[2])));
// negative dot = mismatched winding order
if ( uf::vector::dot( normal, geomNormal ) < 0.0f ) {
meshlet.indices[i * 3 + 0] = indices[2];
meshlet.indices[i * 3 + 2] = indices[0];
++sanitizer.windingOrder.corrected;
}
++sanitizer.windingOrder.total;
}
} else {
for ( size_t i = 0; i < meshlet.vertices.size() / 3; ++i ) {
size_t indices[3] = {
i * 3 + 0,
i * 3 + 1,
i * 3 + 2,
};
pod::Vector3f triPosition[3] = {
meshlet.vertices[indices[0]].position,
meshlet.vertices[indices[1]].position,
meshlet.vertices[indices[2]].position,
};
pod::Vector3f normal = meshlet.vertices[indices[0]].normal;
pod::Vector3f geomNormal = uf::vector::normalize( uf::vector::cross((triPosition[0] - triPosition[1]), (triPosition[1] - triPosition[2])));
// negative dot = mismatched winding order
if ( uf::vector::dot( normal, geomNormal ) < 0.0f ) {
meshlet.indices[i * 3 + 0] = indices[2];
meshlet.indices[i * 3 + 2] = indices[0];
++sanitizer.windingOrder.corrected;
}
++sanitizer.windingOrder.total;
}
}
sanitizer.windingOrder.corrected += uf::mesh::windingOrder( meshlet.vertices, meshlet.indices );
sanitizer.windingOrder.total += meshlet.indices.empty() ? meshlet.vertices.size() : meshlet.indices.size();
}
/* calculate normals */ if ( sanitizer.normals.should ) {
uf::mesh::normals( meshlet.vertices, meshlet.indices );
}
/* calculate tangents */ if ( sanitizer.tangents.should ) {
#if 1
uf::mesh::tangents( meshlet.vertices, meshlet.indices );
#else
if ( !meshlet.indices.empty() ) {
for ( size_t i = 0; i < meshlet.indices.size() / 3; ++i ) {
size_t indices[3] = {
@ -312,6 +278,7 @@ for ( auto& p : m.primitives ) {
}
}
}
#endif
}
}
@ -328,7 +295,10 @@ if ( meshgrid.grid.divisions.x > 1 || meshgrid.grid.divisions.y > 1 || meshgrid.
if ( meshgrid.print ) UF_MSG_DEBUG( "Draw commands: {}: {} -> {} | Partitions: {} -> {}", m.name, meshlets.size(), partitioned.size(),
(meshgrid.grid.divisions.x * meshgrid.grid.divisions.y * meshgrid.grid.divisions.z), meshgrid.grid.nodes.size()
);
meshlets = std::move( partitioned );
}
mesh.compile( meshlets, primitives );
for ( auto& meshlet : partitioned ) {
uf::mesh::tangents( meshlet.vertices, meshlet.indices );
}
mesh.compile( partitioned, primitives );
} else {
mesh.compile( meshlets, primitives );
}

View File

@ -5,6 +5,8 @@
#include <uf/ext/valve/common.h>
#include <uf/ext/zlib/zlib.h>
#include <uf/utils/mesh/grid.h>
namespace impl {
struct RGBE {
uint8_t r, g, b;
@ -753,6 +755,7 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
if ( meshlets.empty() ) continue;
auto meshName = ::fmt::format("model_{}", m);
context.modelToMesh[m] = graph.meshes.size();
@ -762,7 +765,31 @@ void ext::valve::loadBsp( pod::Graph& graph, const uf::stl::string& filename, co
auto& mesh = storage.meshes[meshName];
auto& primitives = storage.primitives[meshName];
mesh.compile( meshlets, primitives );
// slice worldspawn
if ( false && m == 0 ) {
/*
if ( ext::json::isObject( meshgrid.metadata ) ) {
if ( meshgrid.metadata["size"].is<size_t>() ) {
size_t d = meshgrid.metadata["size"].as<size_t>();
meshgrid.grid.divisions = {d, d, d};
} else {
meshgrid.grid.divisions = uf::vector::decode( meshgrid.metadata["size"], meshgrid.grid.divisions );
}
meshgrid.eps = meshgrid.metadata["epsilon"].as(meshgrid.eps);
meshgrid.print = meshgrid.metadata["print"].as(meshgrid.print);
meshgrid.clip = meshgrid.metadata["clip"].as(meshgrid.clip);
meshgrid.cleanup = meshgrid.metadata["cleanup"].as(meshgrid.cleanup);
}
*/
uf::meshgrid::Grid grid;
grid.divisions = {8, 8, 8};
auto mlets = uf::stl::values( meshlets );
auto partitioned = uf::meshgrid::partition( grid, mlets, EPS, true, true );
mesh.compile( partitioned, primitives );
} else {
mesh.compile( meshlets, primitives );
}
}
// read entities

View File

@ -68,9 +68,9 @@ size_t ext::xatlas::unwrap( pod::Graph& graph ) {
entry.commandID = viewIdx;
auto& decl = entry.decl;
auto posView = view["position"];
auto uvView = view["uv"];
auto idxView = view["index"];
auto posView = view["position"_hash];
auto uvView = view["uv"_hash];
auto idxView = view["index"_hash];
UF_ASSERT( posView.valid() && uvView.valid() );