Skip to content

Commit

Permalink
Added support for sparse vertex accessors in GLTF exporter.
Browse files Browse the repository at this point in the history
Added a default gltf material surface.
Added intermediate folder to gitignore
  • Loading branch information
fLindahl committed Nov 13, 2024
1 parent 10b13fc commit c3cbe11
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 34 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ export_old
imgui.ini
/syswork/export
/tests/export
/intermediate/*
2 changes: 1 addition & 1 deletion code/render/materials/gltf.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"variables": [
{
"name": "baseColorTexture",
"defaultValue": "tex:system/placeholder.dds"
"defaultValue": "tex:system/white.dds"
},
{
"name": "normalTexture",
Expand Down
8 changes: 8 additions & 0 deletions syswork/assets/system/gltf_default.sur
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<!--
Default Surface material for GLTF models that don't define any specific material
-->
<?xml version="1.0" encoding="UTF-8" ?>
<Nebula>
<Surface template="DefaultGLTF">
</Surface>
</Nebula>
108 changes: 83 additions & 25 deletions toolkit/toolkitutil/model/import/gltf/node/meshprimitive.cc
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ AttributeToComponentIndex(Gltf::Primitive::Attribute attribute, bool normalized)
*/
template <typename TYPE, int n>
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);
Expand All @@ -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<float, 1>(buffer, index); break;
case CoreGraphics::VertexComponent::Format::Float2: data = ReadVertexData<float, 2>(buffer, index); break;
case CoreGraphics::VertexComponent::Format::Float3: data = ReadVertexData<float, 3>(buffer, index); break;
case CoreGraphics::VertexComponent::Format::Float4: data = ReadVertexData<float, 4>(buffer, index); break;
case CoreGraphics::VertexComponent::Format::UShort4: data = ReadVertexData<ushort, 4>(buffer, index); break;
case CoreGraphics::VertexComponent::Format::UByte4: data = ReadVertexData<ubyte, 4>(buffer, index); break;
default:
n_error("ERROR: Invalid vertex component type!");
break;
//clang-format on
}
return data;
}

//------------------------------------------------------------------------------
/**
*/
Expand All @@ -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!");
Expand Down Expand Up @@ -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
Expand All @@ -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<float, 1>(vb, j); break;
case CoreGraphics::VertexComponent::Format::Float2: data = ReadVertexData<float, 2>(vb, j); break;
case CoreGraphics::VertexComponent::Format::Float3: data = ReadVertexData<float, 3>(vb, j); break;
case CoreGraphics::VertexComponent::Format::Float4: data = ReadVertexData<float, 4>(vb, j); break;
case CoreGraphics::VertexComponent::Format::UShort4: data = ReadVertexData<ushort, 4>(vb, j); break;
case CoreGraphics::VertexComponent::Format::UByte4: data = ReadVertexData<ubyte, 4>(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)
{
Expand Down Expand Up @@ -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
Expand All @@ -245,9 +303,9 @@ MeshPrimitiveFunc(SizeT totalJobs, SizeT groupSize, IndexT groupIndex, SizeT inv

switch (indexBufferAccessor.componentType)
{
case Gltf::Accessor::ComponentType::UnsignedByte: SetupIndexBuffer<uchar>(*meshBuilder, indexBuffer, bufferOffset, indexBufferAccessor, 0); break;
case Gltf::Accessor::ComponentType::UnsignedShort: SetupIndexBuffer<ushort>(*meshBuilder, indexBuffer, bufferOffset, indexBufferAccessor, 0); break;
case Gltf::Accessor::ComponentType::UnsignedInt: SetupIndexBuffer<uint>(*meshBuilder, indexBuffer, bufferOffset, indexBufferAccessor, 0); break;
case Gltf::Accessor::ComponentType::UnsignedByte: SetupIndexBuffer<uchar>(*meshBuilder, indexBuffer, bufferOffset, indexBufferAccessor, 0); break;
default:
n_error("ERROR: Invalid vertex index type!");
break;
Expand Down
25 changes: 17 additions & 8 deletions toolkit/toolkitutil/model/scenewriter.cc
Original file line number Diff line number Diff line change
Expand Up @@ -149,20 +149,21 @@ 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;
}
else if (!state.material.IsValid())
{
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);
}
Expand Down Expand Up @@ -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);
}
}

Expand Down

0 comments on commit c3cbe11

Please sign in to comment.