From c3cbe1171db2d81f63765f772b18169e81b44a50 Mon Sep 17 00:00:00 2001 From: Fredrik Lindahl Date: Wed, 13 Nov 2024 22:36:50 +0100 Subject: [PATCH] Added support for sparse vertex accessors in GLTF exporter. Added a default gltf material surface. Added intermediate folder to gitignore --- .gitignore | 1 + code/render/materials/gltf.json | 2 +- syswork/assets/system/gltf_default.sur | 8 ++ .../model/import/gltf/node/meshprimitive.cc | 108 ++++++++++++++---- toolkit/toolkitutil/model/scenewriter.cc | 25 ++-- 5 files changed, 110 insertions(+), 34 deletions(-) create mode 100644 syswork/assets/system/gltf_default.sur diff --git a/.gitignore b/.gitignore index 5f6eca7ef..19c50de3b 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ export_old imgui.ini /syswork/export /tests/export +/intermediate/* \ No newline at end of file diff --git a/code/render/materials/gltf.json b/code/render/materials/gltf.json index f29290551..277c9a53a 100644 --- a/code/render/materials/gltf.json +++ b/code/render/materials/gltf.json @@ -61,7 +61,7 @@ "variables": [ { "name": "baseColorTexture", - "defaultValue": "tex:system/placeholder.dds" + "defaultValue": "tex:system/white.dds" }, { "name": "normalTexture", diff --git a/syswork/assets/system/gltf_default.sur b/syswork/assets/system/gltf_default.sur new file mode 100644 index 000000000..557e30fc7 --- /dev/null +++ b/syswork/assets/system/gltf_default.sur @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/toolkit/toolkitutil/model/import/gltf/node/meshprimitive.cc b/toolkit/toolkitutil/model/import/gltf/node/meshprimitive.cc index 9f2dc6161..3f051faeb 100644 --- a/toolkit/toolkitutil/model/import/gltf/node/meshprimitive.cc +++ b/toolkit/toolkitutil/model/import/gltf/node/meshprimitive.cc @@ -73,7 +73,7 @@ AttributeToComponentIndex(Gltf::Primitive::Attribute attribute, bool normalized) */ template Math::vec4 -ReadVertexData(void const* buffer, const uint i) +ReadVertexData(void const* const buffer, const uint i) { static_assert(n >= 1 && n <= 4, "You're doing it wrong!"); TYPE const* const v = (TYPE*)(buffer); @@ -100,6 +100,30 @@ ReadVertexData(void const* buffer, const uint i) return ret; } +//------------------------------------------------------------------------------ +/** +*/ +Math::vec4 +ReadVertexData(CoreGraphics::VertexComponent::Format format, void const* const buffer, uint32_t index) +{ + Math::vec4 data; + switch (format) + { + //clang-format off + case CoreGraphics::VertexComponent::Format::Float: data = ReadVertexData(buffer, index); break; + case CoreGraphics::VertexComponent::Format::Float2: data = ReadVertexData(buffer, index); break; + case CoreGraphics::VertexComponent::Format::Float3: data = ReadVertexData(buffer, index); break; + case CoreGraphics::VertexComponent::Format::Float4: data = ReadVertexData(buffer, index); break; + case CoreGraphics::VertexComponent::Format::UShort4: data = ReadVertexData(buffer, index); break; + case CoreGraphics::VertexComponent::Format::UByte4: data = ReadVertexData(buffer, index); break; + default: + n_error("ERROR: Invalid vertex component type!"); + break; + //clang-format on + } + return data; +} + //------------------------------------------------------------------------------ /** */ @@ -118,14 +142,22 @@ MeshPrimitiveFunc(SizeT totalJobs, SizeT groupSize, IndexT groupIndex, SizeT inv const Gltf::Primitive* primitive = context->primitives[index]; SceneNode* node = context->outSceneNodes[index]; - Gltf::Material& material = context->scene->materials[primitive->material]; - if (!material.name.IsEmpty()) + + if (primitive->material == -1) { - node->mesh.material = material.name; + node->mesh.material = "syssur:gltf_default.sur"; } else { - node->mesh.material.Format("unnamed_%d", primitive->material); + Gltf::Material& material = context->scene->materials[primitive->material]; + if (!material.name.IsEmpty()) + { + node->mesh.material = material.name; + } + else + { + node->mesh.material.Format("unnamed_%d", primitive->material); + } } n_assert2(primitive->nebulaMode == CoreGraphics::PrimitiveTopology::Code::TriangleList, "Only triangle lists are supported currently!"); @@ -164,9 +196,6 @@ MeshPrimitiveFunc(SizeT totalJobs, SizeT groupSize, IndexT groupIndex, SizeT inv Gltf::Buffer const& buffer = context->scene->buffers[vertexBufferView.buffer]; const uint bufferOffset = vertexBufferAccessor.byteOffset + vertexBufferView.byteOffset; - // TODO: sparse accessors - n_assert(vertexBufferAccessor.sparse.count == 0); - MeshBuilderVertex::Components component = AttributeToComponentIndex(attribute.Key(), vertexBufferAccessor.normalized); // If component is invalid, skip it @@ -178,22 +207,8 @@ MeshPrimitiveFunc(SizeT totalJobs, SizeT groupSize, IndexT groupIndex, SizeT inv for (uint j = 0; j < count; j++) { - Math::vec4 data; - switch (vertexBufferAccessor.format) - { - //clang-format off - case CoreGraphics::VertexComponent::Format::Float: data = ReadVertexData(vb, j); break; - case CoreGraphics::VertexComponent::Format::Float2: data = ReadVertexData(vb, j); break; - case CoreGraphics::VertexComponent::Format::Float3: data = ReadVertexData(vb, j); break; - case CoreGraphics::VertexComponent::Format::Float4: data = ReadVertexData(vb, j); break; - case CoreGraphics::VertexComponent::Format::UShort4: data = ReadVertexData(vb, j); break; - case CoreGraphics::VertexComponent::Format::UByte4: data = ReadVertexData(vb, j); break; - default: - n_error("ERROR: Invalid vertex component type!"); - break; - //clang-format on - } - + Math::vec4 data = ReadVertexData(vertexBufferAccessor.format, vb, j); + MeshBuilderVertex& vtx = meshBuilder->VertexAt(j); switch (component) { @@ -225,6 +240,49 @@ MeshPrimitiveFunc(SizeT totalJobs, SizeT groupSize, IndexT groupIndex, SizeT inv } } + if (vertexBufferAccessor.sparse.count > 0) + { + // substitute the vertex data from the sparse buffer + + Gltf::BufferView const& sparseIndexBufferView = + context->scene->bufferViews[vertexBufferAccessor.sparse.indices.bufferView]; + Util::Blob const& sparseIndices = context->scene->buffers[sparseIndexBufferView.buffer].data; + int const sparseIndexBufferOffset = vertexBufferAccessor.sparse.indices.byteOffset + sparseIndexBufferView.byteOffset; + + byte const* const ibuf = (byte*)sparseIndices.GetPtr() + sparseIndexBufferOffset; + const uint count = vertexBufferAccessor.sparse.count; + + Gltf::BufferView const& sparseVertexBufferView = + context->scene->bufferViews[vertexBufferAccessor.sparse.values.bufferView]; + Util::Blob const& sparseVertices = context->scene->buffers[sparseVertexBufferView.buffer].data; + int const sparseVertexBufferOffset = + vertexBufferAccessor.sparse.values.byteOffset + sparseVertexBufferView.byteOffset; + + void const* const vbuf = (void*)((byte*)sparseIndices.GetPtr() + sparseVertexBufferOffset); + + uint64_t indexByteStride; + uint32_t indexMask; + + switch (vertexBufferAccessor.sparse.indices.componentType) + { + case Gltf::Accessor::ComponentType::UnsignedByte: indexByteStride = 1; indexMask = 0x000000FF; break; + case Gltf::Accessor::ComponentType::UnsignedShort: indexByteStride = 2; indexMask = 0x0000FFFF; break; + case Gltf::Accessor::ComponentType::UnsignedInt: indexByteStride = 4; indexMask = 0xFFFFFFFF; break; + default: + n_error("ERROR: Invalid index type!"); + break; + } + + for (size_t j = 0; j < count; j++) + { + Math::vec4 data = ReadVertexData(vertexBufferAccessor.format, vbuf, j); + uint32_t idx = *((uint32_t*)(ibuf + (j * indexByteStride))); + idx &= indexMask; + MeshBuilderVertex& vtx = meshBuilder->VertexAt(idx); + vtx.SetPosition(data); + } + } + } // Set components on the mesh @@ -245,9 +303,9 @@ MeshPrimitiveFunc(SizeT totalJobs, SizeT groupSize, IndexT groupIndex, SizeT inv switch (indexBufferAccessor.componentType) { + case Gltf::Accessor::ComponentType::UnsignedByte: SetupIndexBuffer(*meshBuilder, indexBuffer, bufferOffset, indexBufferAccessor, 0); break; case Gltf::Accessor::ComponentType::UnsignedShort: SetupIndexBuffer(*meshBuilder, indexBuffer, bufferOffset, indexBufferAccessor, 0); break; case Gltf::Accessor::ComponentType::UnsignedInt: SetupIndexBuffer(*meshBuilder, indexBuffer, bufferOffset, indexBufferAccessor, 0); break; - case Gltf::Accessor::ComponentType::UnsignedByte: SetupIndexBuffer(*meshBuilder, indexBuffer, bufferOffset, indexBufferAccessor, 0); break; default: n_error("ERROR: Invalid vertex index type!"); break; diff --git a/toolkit/toolkitutil/model/scenewriter.cc b/toolkit/toolkitutil/model/scenewriter.cc index 898906ef2..68cb0ec42 100644 --- a/toolkit/toolkitutil/model/scenewriter.cc +++ b/toolkit/toolkitutil/model/scenewriter.cc @@ -149,8 +149,13 @@ SetupDefaultState( } // If the node has a material that is already a surface, use that instead of creating a default one - auto surfacePath = Util::String::Sprintf("sur:%s.sur", nodeMaterial.AsCharPtr()); - if (IO::IoServer::Instance()->FileExists(surfacePath)) + Util::String surfacePath = nodeMaterial; + IO::URI uri = surfacePath; + if (!surfacePath.EndsWithString(".sur")) + { + surfacePath = Util::String::Sprintf("sur:%s.sur", nodeMaterial.AsCharPtr()); + } + if (IO::IoServer::Instance()->FileExists(surfacePath) || surfacePath.BeginsWithString("syssur:")) { state.material = surfacePath; } @@ -158,11 +163,7 @@ SetupDefaultState( { state.material = skinned ? "syssur:placeholder_skinned.sur" : "syssur:placeholder.sur"; } - else if (!state.material.EndsWithString(".sur")) // Temporary to fix all bad material assignments - { - state.material = Util::String::Sprintf("%s.sur", state.material.AsCharPtr()); - } - + // set state for attributes attributes->SetState(nodePath, state); } @@ -268,7 +269,15 @@ SceneWriter::CreateModel( // add to constants constants->AddShapeNode(shapeNode); - SetupDefaultState(shapeNode.path, Util::String::Sprintf("%s/%s/%s", category.AsCharPtr(), file.AsCharPtr(), mesh->mesh.material.AsCharPtr()), attributes); + Util::String nodeMaterial = mesh->mesh.material; + + // If the material is a .sur material, we pass it as is. + if ((nodeMaterial.Length() <= 4) || (!nodeMaterial.IsEmpty() && !nodeMaterial.EndsWithString(".sur"))) + { + nodeMaterial = Util::String::Sprintf("%s/%s/%s", category.AsCharPtr(), file.AsCharPtr(), nodeMaterial.AsCharPtr()); + } + + SetupDefaultState(shapeNode.path, nodeMaterial, attributes); } }