From 9e823645a4b151340dd5594c05edbf55b83e09b4 Mon Sep 17 00:00:00 2001 From: ecker Date: Sun, 7 Jun 2026 23:53:41 -0500 Subject: [PATCH] several agonizing hours of dealing with more GCC v13 gremlins involving calculating tangents --- bin/data/config.json | 6 +- bin/data/entities/model.json | 2 +- .../mds_mcdonalds.json | 4 +- bin/data/scenes/sourceengine/cs_office.json | 4 +- .../scenes/sourceengine/sourceengine.json | 4 +- bin/data/shaders/common/functions.h | 8 +- engine/inc/uf/utils/mesh/grid.h | 14 +- engine/inc/uf/utils/mesh/mesh.h | 301 +++++++++++++++--- engine/src/engine/graph/graph.cpp | 10 +- engine/src/ext/gltf/gltf.cpp | 22 +- engine/src/ext/gltf/processPrimitives.inl | 116 +++---- engine/src/ext/valve/bsp.cpp | 29 +- engine/src/ext/xatlas/xatlas.cpp | 6 +- 13 files changed, 380 insertions(+), 146 deletions(-) diff --git a/bin/data/config.json b/bin/data/config.json index 2aa4715b..7de2e3b6 100644 --- a/bin/data/config.json +++ b/bin/data/config.json @@ -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 diff --git a/bin/data/entities/model.json b/bin/data/entities/model.json index 2e52b1aa..5ca5fff2 100644 --- a/bin/data/entities/model.json +++ b/bin/data/entities/model.json @@ -62,7 +62,7 @@ "renderer": { "front face": "auto", "cull mode": "back", - "filter": "nearest", + "filter": "linear", "atlas": false, "invert": true, diff --git a/bin/data/scenes/sourceengine-deprecated/mds_mcdonalds.json b/bin/data/scenes/sourceengine-deprecated/mds_mcdonalds.json index 2d85cb0d..5402b5d9 100644 --- a/bin/data/scenes/sourceengine-deprecated/mds_mcdonalds.json +++ b/bin/data/scenes/sourceengine-deprecated/mds_mcdonalds.json @@ -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 } diff --git a/bin/data/scenes/sourceengine/cs_office.json b/bin/data/scenes/sourceengine/cs_office.json index f181a19f..60581037 100644 --- a/bin/data/scenes/sourceengine/cs_office.json +++ b/bin/data/scenes/sourceengine/cs_office.json @@ -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": { diff --git a/bin/data/scenes/sourceengine/sourceengine.json b/bin/data/scenes/sourceengine/sourceengine.json index 065d566b..bdb37541 100644 --- a/bin/data/scenes/sourceengine/sourceengine.json +++ b/bin/data/scenes/sourceengine/sourceengine.json @@ -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" } \ No newline at end of file diff --git a/bin/data/shaders/common/functions.h b/bin/data/shaders/common/functions.h index 95fe7c8d..e4ed6600 100644 --- a/bin/data/shaders/common/functions.h +++ b/bin/data/shaders/common/functions.h @@ -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); } diff --git a/engine/inc/uf/utils/mesh/grid.h b/engine/inc/uf/utils/mesh/grid.h index dbcc3397..0628e7d1 100644 --- a/engine/inc/uf/utils/mesh/grid.h +++ b/engine/inc/uf/utils/mesh/grid.h @@ -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 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( 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( slice.vertices, slice.indices ); } slice.primitive.instance = meshlet.primitive.instance; diff --git a/engine/inc/uf/utils/mesh/mesh.h b/engine/inc/uf/utils/mesh/mesh.h index f9cfd24b..df200d61 100644 --- a/engine/inc/uf/utils/mesh/mesh.h +++ b/engine/inc/uf/utils/mesh/mesh.h @@ -7,6 +7,7 @@ #include #include +#include #include #if UF_USE_VULKAN @@ -603,54 +604,26 @@ namespace uf { }; namespace mesh { + template struct has_tangent : std::false_type {}; + template struct has_tangent().tangent)>> : std::true_type {}; + + template struct has_normal : std::false_type {}; + template struct has_normal().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 - 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() == attributeView.type() && T::size == attributeView.components() ) { - return uf::vector::copy( (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 size_t windingOrder( uf::stl::vector& vertices ); + template size_t windingOrder( uf::stl::vector& vertices, uf::stl::vector& indices ); + template void normals( uf::stl::vector& vertices ); + template void normals( uf::stl::vector& vertices, const uf::stl::vector& indices ); + // specifically refuses to work properly + template void tangents( uf::stl::vector& vertices ); + template void tangents( uf::stl::vector& vertices, const uf::stl::vector& indices ); + template T fetchVertexAttribute( const uf::Mesh::View& view, const uf::Mesh::AttributeView& attributeView, size_t index ); template T& getVertexAttribute( const uf::Mesh::View& view, const uf::Mesh::AttributeView& attributeView, size_t index ) { UF_ASSERT( uf::renderer::typeToEnum() == attributeView.type() && T::size == attributeView.components() ); @@ -711,6 +684,250 @@ namespace uf { } } +template +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() == attributeView.type() && T::size == attributeView.components() ) { + return uf::vector::copy( (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 +size_t uf::mesh::windingOrder( uf::stl::vector& vertices ) { + if constexpr ( !uf::mesh::has_normal::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 +size_t uf::mesh::windingOrder( uf::stl::vector& vertices, uf::stl::vector& indices ) { + if constexpr ( !uf::mesh::has_normal::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 +void uf::mesh::tangents( uf::stl::vector& vertices ) { + if constexpr ( !uf::mesh::has_tangent::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 +void uf::mesh::tangents( uf::stl::vector& vertices, const uf::stl::vector& indices ) { + if constexpr ( !uf::mesh::has_tangent::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 +void uf::mesh::normals( uf::stl::vector& vertices ) { + if constexpr ( !uf::mesh::has_normal::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 +void uf::mesh::normals( uf::stl::vector& vertices, const uf::stl::vector& indices ) { + if constexpr ( !uf::mesh::has_normal::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 uf::stl::vector uf::Mesh::compile( const uf::stl::vector& meshlets ) { uf::stl::vector primitives; uf::mesh::compile( *this, meshlets, primitives ); diff --git a/engine/src/engine/graph/graph.cpp b/engine/src/engine/graph/graph.cpp index d72a236e..e7bf36bc 100644 --- a/engine/src/engine/graph/graph.cpp +++ b/engine/src/engine/graph/graph.cpp @@ -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] ); diff --git a/engine/src/ext/gltf/gltf.cpp b/engine/src/ext/gltf/gltf.cpp index bae4b277..ea7dedbb 100644 --- a/engine/src/ext/gltf/gltf.cpp +++ b/engine/src/ext/gltf/gltf.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -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 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(""); - if ( key != "" ) { - key += ":"; - } + if ( key != "" ) key += ":"; auto& storage = uf::graph::getStorage( graph ); diff --git a/engine/src/ext/gltf/processPrimitives.inl b/engine/src/ext/gltf/processPrimitives.inl index fbbb12ab..213e43eb 100644 --- a/engine/src/ext/gltf/processPrimitives.inl +++ b/engine/src/ext/gltf/processPrimitives.inl @@ -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(true) || graph.metadata["renderer"]["invert"].as(true) ) { sanitizer.windingOrder.should = true; } +if ( graph.metadata["sanitizer"]["normals"].as(false) ) { + sanitizer.normals.should = true; +} if ( graph.metadata["sanitizer"]["tangents"].as(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(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(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(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(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 ); \ No newline at end of file + for ( auto& meshlet : partitioned ) { + uf::mesh::tangents( meshlet.vertices, meshlet.indices ); + } + mesh.compile( partitioned, primitives ); +} else { + mesh.compile( meshlets, primitives ); +} \ No newline at end of file diff --git a/engine/src/ext/valve/bsp.cpp b/engine/src/ext/valve/bsp.cpp index a8632875..64d35a51 100644 --- a/engine/src/ext/valve/bsp.cpp +++ b/engine/src/ext/valve/bsp.cpp @@ -5,6 +5,8 @@ #include #include +#include + 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 d = meshgrid.metadata["size"].as(); + 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 diff --git a/engine/src/ext/xatlas/xatlas.cpp b/engine/src/ext/xatlas/xatlas.cpp index e835d8d2..b0cc0944 100644 --- a/engine/src/ext/xatlas/xatlas.cpp +++ b/engine/src/ext/xatlas/xatlas.cpp @@ -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() );