#include "VertexData.h" #include "Graphics/Descriptor.h" #include "Graphics/Enums.h" #include "Graphics/Graphics.h" #include "Graphics/Initializer.h" #include "Graphics/Mesh.h" #include "Graphics/Shader.h" #include "Material/Material.h" #include "Material/MaterialInstance.h" #include #include #include #include #include using namespace Seele; constexpr static uint64 NUM_DEFAULT_ELEMENTS = 36; uint64 VertexData::meshletCount = 0; void VertexData::resetMeshData() { std::unique_lock l(materialDataLock); instanceData.clear(true); instanceMeshData.clear(true); rayTracingScene.clear(true); transparentData.clear(true); for (auto& mat : materialData) { for (auto& inst : mat.instances) { inst.instanceData.clear(true); inst.instanceMeshData.clear(true); inst.cullingOffsets.clear(true); inst.rayTracingData.clear(true); } if (mat.material != nullptr) { mat.material->getDescriptorLayout()->reset(); } } if (dirty) { updateBuffers(); dirty = false; } } void VertexData::updateMesh(uint32 meshletOffset, PMesh mesh, Component::Transform& transform) { std::unique_lock l(materialDataLock); PMaterialInstance referencedInstance = mesh->referencedMaterial->getHandle(); PMaterial mat = referencedInstance->getBaseMaterial(); Matrix4 transformMatrix = transform.toMatrix() * mesh->transform; InstanceData inst = InstanceData{ .transformMatrix = transformMatrix, .inverseTransformMatrix = glm::inverse(transformMatrix), }; referencedInstance->updateDescriptor(); if (materialData.size() <= mat->getId()) { materialData.resize(mat->getId() + 1); } MaterialData& matData = materialData[mat->getId()]; matData.material = mat; if (matData.instances.size() <= referencedInstance->getId()) { matData.instances.resize(referencedInstance->getId() + 1); } BatchedDrawCall& matInstanceData = matData.instances[referencedInstance->getId()]; matInstanceData.materialInstance = referencedInstance; const auto& data = registeredMeshes[mesh->id].meshData; uint32 numMeshlets = data.meshletRange.size; for (uint32 i = 0; i < (numMeshlets + Gfx::numMeshletsPerTask - 1) / Gfx::numMeshletsPerTask; ++i) { MeshData chunkMeshData = data; chunkMeshData.meshletRange = { .offset = data.meshletRange.offset + i * Gfx::numMeshletsPerTask, .size = std::min(numMeshlets - i * Gfx::numMeshletsPerTask, Gfx::numMeshletsPerTask), }; if (mat->hasTransparency()) { auto params = referencedInstance->getMaterialOffsets(); transparentData.add(TransparentDraw{ .matInst = referencedInstance, .vertexData = this, .offsets = { .instanceOffset = 0, .textureOffset = params.textureOffset, .samplerOffset = params.samplerOffset, .floatOffset = params.floatOffset, }, .worldPosition = Vector(inst.transformMatrix[3]), .instanceData = inst, .meshData = chunkMeshData, .cullingOffset = meshletOffset, .rayTracingScene = mesh->blas, }); } else { // opaque matInstanceData.rayTracingData.add(mesh->blas); matInstanceData.instanceData.add(inst); matInstanceData.instanceMeshData.add(chunkMeshData); matInstanceData.cullingOffsets.add(meshletOffset); } } } void VertexData::createDescriptors() { std::unique_lock l(materialDataLock); Array cullingOffsets; for (auto& mat : materialData) { for (auto& instance : mat.instances) { instance.offsets.instanceOffset = (uint32)instanceData.size(); MaterialOffsets offsets = instance.materialInstance->getMaterialOffsets(); instance.offsets.textureOffset = offsets.textureOffset; instance.offsets.samplerOffset = offsets.samplerOffset; instance.offsets.floatOffset = offsets.floatOffset; for (size_t i = 0; i < instance.instanceData.size(); ++i) { cullingOffsets.add(instance.cullingOffsets[i]); instanceData.add(instance.instanceData[i]); instanceMeshData.add(instance.instanceMeshData[i]); rayTracingScene.add(instance.rayTracingData[i]); } } } for (uint32 i = 0; i < transparentData.size(); ++i) { transparentData[i].offsets.instanceOffset = (uint32)instanceData.size(); cullingOffsets.add(transparentData[i].cullingOffset); instanceData.add(transparentData[i].instanceData); instanceMeshData.add(transparentData[i].meshData); rayTracingScene.add(transparentData[i].rayTracingScene); } cullingOffsetBuffer->rotateBuffer(cullingOffsets.size() * sizeof(uint32)); cullingOffsetBuffer->updateContents(0, cullingOffsets.size() * sizeof(uint32), cullingOffsets.data()); cullingOffsetBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_MEMORY_READ_BIT, Gfx::SE_PIPELINE_STAGE_TOP_OF_PIPE_BIT); instanceBuffer->rotateBuffer(instanceData.size() * sizeof(InstanceData)); instanceBuffer->updateContents(0, instanceData.size() * sizeof(InstanceData), instanceData.data()); instanceBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_MEMORY_READ_BIT, Gfx::SE_PIPELINE_STAGE_TOP_OF_PIPE_BIT); instanceMeshDataBuffer->rotateBuffer(sizeof(MeshData) * instanceMeshData.size()); instanceMeshDataBuffer->updateContents(0, sizeof(MeshData) * instanceMeshData.size(), instanceMeshData.data()); instanceMeshDataBuffer->pipelineBarrier(Gfx::SE_ACCESS_TRANSFER_WRITE_BIT, Gfx::SE_PIPELINE_STAGE_TRANSFER_BIT, Gfx::SE_ACCESS_MEMORY_READ_BIT, Gfx::SE_PIPELINE_STAGE_TOP_OF_PIPE_BIT); instanceDataLayout->reset(); descriptorSet = instanceDataLayout->allocateDescriptorSet(); descriptorSet->updateBuffer(POSITIONS_NAME, 0, positionBuffer); descriptorSet->updateBuffer(INDEXBUFFER_NAME, 0, indexBuffer); descriptorSet->updateBuffer(INSTANCES_NAME, 0, instanceBuffer); descriptorSet->updateBuffer(MESHDATA_NAME, 0, instanceMeshDataBuffer); descriptorSet->updateBuffer(MESHLET_NAME, 0, meshletBuffer); descriptorSet->updateBuffer(PRIMITIVEINDICES_NAME, 0, primitiveIndicesBuffer); descriptorSet->updateBuffer(VERTEXINDICES_NAME, 0, vertexIndicesBuffer); descriptorSet->updateBuffer(CULLINGOFFSETS_NAME, 0, cullingOffsetBuffer); Material::updateDescriptor(); } Array VertexData::groupMeshlets(std::span meshlets) { auto groupWithAllMeshets = [&]() { MeshletGroup group; for (uint32 i = 0; i < meshlets.size(); i++) { group.meshlets.add(i); } return Array{group}; }; if (meshlets.size() < 8) { return groupWithAllMeshets(); } struct MeshletEdge { explicit MeshletEdge(size_t a, size_t b) : first(std::min(a, b)), second(std::max(a, b)) {} bool operator==(const MeshletEdge& other) const = default; const size_t first; const size_t second; }; struct MeshletEdgeHasher { size_t operator()(const MeshletEdge& edge) const { return CRC::Calculate(&edge, sizeof(MeshletEdge), CRC::CRC_32()); } }; std::unordered_map, MeshletEdgeHasher> edges2Meshlets; std::unordered_map> meshlets2Edges; for (size_t meshletIndex = 0; meshletIndex < meshlets.size(); ++meshletIndex) { const auto& meshlet = meshlets[meshletIndex]; auto getVertexIndex = [&](size_t index) { return vertexIndices[meshlet.vertexIndices.offset + primitiveIndices[meshlet.primitiveIndices.offset + index]]; }; const size_t triangleCount = meshlet.primitiveIndices.size; for (size_t triangleIndex = 0; triangleIndex < triangleCount; ++triangleIndex) { for (size_t i = 0; i < 3; ++i) { MeshletEdge edge{getVertexIndex(i + triangleIndex * 3), getVertexIndex(((i + 1) % 3) + triangleIndex * 3)}; edges2Meshlets[edge].add(meshletIndex); meshlets2Edges[meshletIndex].emplace(edge); } } } std::erase_if(edges2Meshlets, [&](const auto& pair) { return pair.second.size() <= 1; }); if (edges2Meshlets.empty()) { return groupWithAllMeshets(); } idx_t vertexCount = meshlets.size(); idx_t ncon = 1; idx_t nparts = meshlets.size() / 4; assert(nparts > 1); idx_t options[METIS_NOPTIONS]; METIS_SetDefaultOptions(options); options[METIS_OPTION_OBJTYPE] = METIS_OBJTYPE_CUT; options[METIS_OPTION_CCORDER] = 1; Array partition; partition.resize(vertexCount); Array xadjacency; xadjacency.reserve(vertexCount + 1); Array edgeAdjacency; Array edgeWeights; for (size_t meshletIndex = 0; meshletIndex < meshlets.size(); ++meshletIndex) { size_t startIndexInEdgeAdjacency = edgeAdjacency.size(); for (const auto& edge : meshlets2Edges[meshletIndex]) { auto connectionsIter = edges2Meshlets.find(edge); if (connectionsIter == edges2Meshlets.end()) { continue; } const auto& connections = connectionsIter->second; for (const auto& connectedMeshlet : connections) { if (connectedMeshlet != meshletIndex) { auto existingEdgeIter = std::find(edgeAdjacency.begin() + startIndexInEdgeAdjacency, edgeAdjacency.end(), connectedMeshlet); if (existingEdgeIter == edgeAdjacency.end()) { edgeAdjacency.emplace(connectedMeshlet); edgeWeights.emplace(1); } else { ptrdiff_t d = std::distance(edgeAdjacency.begin(), existingEdgeIter); assert(d >= 0); assert(d < edgeWeights.size()); edgeWeights[d]++; } } } } xadjacency.add(startIndexInEdgeAdjacency); } xadjacency.add(edgeAdjacency.size()); assert(xadjacency.size() == meshlets.size() + 1); assert(edgeAdjacency.size() == edgeWeights.size()); idx_t edgeCut; int result = METIS_PartGraphKway(&vertexCount, &ncon, xadjacency.data(), edgeAdjacency.data(), nullptr, nullptr, edgeWeights.data(), &nparts, nullptr, nullptr, options, &edgeCut, partition.data()); if(result != METIS_OK) { abort(); } Array groups; groups.resize(nparts); for (size_t i = 0; i < meshlets.size(); ++i) { idx_t partitionNumber = partition[i]; groups[partitionNumber].meshlets.add(i); } return groups; } void VertexData::loadMesh(MeshId id, Array loadedPositions, Array loadedIndices) { std::unique_lock l(vertexDataLock); RegisteredMesh& mesh = registeredMeshes[id]; MeshData& data = mesh.meshData; // generate an LOD hierarchy for the given source mesh // load LOD 0 size_t previousMeshletsStart = meshlets.size(); // todo: loadMeshlets(id, loadedPositions, loadedIndices); std::memcpy(positions.data() + mesh.vertexOffset, loadedPositions.data(), loadedPositions.size() * sizeof(Vector)); /*const int maxLod = 25; for (int lod = 0; lod < maxLod; ++lod) { float tLod = lod / (float)maxLod; std::span previousLevelMeshlets = std::span{meshlets.data() + previousMeshletsStart, meshlets.size() - previousMeshletsStart}; if (previousLevelMeshlets.size() <= 1) { break; } auto groups = groupMeshlets(previousLevelMeshlets); const uint32 newMeshletStart = meshlets.size(); for (const auto& group : groups) { previousLevelMeshlets = std::span{meshlets.data() + previousMeshletsStart, meshlets.size() - previousMeshletsStart}; Array groupVertexIndices; for (const auto& meshletIndex : group.meshlets) { const auto& meshlet = meshlets[meshletIndex]; size_t start = groupVertexIndices.size(); groupVertexIndices.resize(start + meshlet.primitiveIndices.size * 3); for (size_t j = 0; j < meshlet.primitiveIndices.size * 3; ++j) { groupVertexIndices[j + start] = vertexIndices[meshlet.vertexIndices.offset + primitiveIndices[meshlet.primitiveIndices.offset + j]]; } } const float threshold = 0.5f; size_t targetIndexCount = groupVertexIndices.size() * threshold; float targetError = 0.9f * tLod + 0.01f * (1 - tLod); uint32 options = meshopt_SimplifyLockBorder; Array simplifiedIndexBuffer; simplifiedIndexBuffer.resize(groupVertexIndices.size()); float simplificationError = 0.f; size_t simplifiedIndexCount = meshopt_simplify( simplifiedIndexBuffer.data(), groupVertexIndices.data(), groupVertexIndices.size(), (float*)loadedPositions.data(), loadedPositions.size(), sizeof(Vector), targetIndexCount, targetError, options, &simplificationError); simplifiedIndexBuffer.resize(simplifiedIndexCount); if (simplifiedIndexCount > 0) { loadMeshlets(id, loadedPositions, simplifiedIndexBuffer); for (size_t i = newMeshletStart; i < meshlets.size(); ++i) { meshlets[i].lod = lod + 1; } previousMeshletsStart = newMeshletStart; } } }*/ } void VertexData::removeMesh(MeshId id) { RegisteredMesh& removing = registeredMeshes[id]; uint32 meshletOffset = meshlets.size(); uint32 numMeshlets = 0; uint32 vertexIndicesOffset = vertexIndices.size(); uint32 numVertexIndices = 0; uint32 primitiveIndicesOffset = primitiveIndices.size(); uint32 numPrimitiveIndices = 0; uint32 indicesOffset = removing.meshData.indicesRange.offset; uint32 numIndices = removing.meshData.indicesRange.size; const auto& data = removing.meshData; meshletOffset = std::min(meshletOffset, data.meshletRange.offset); numMeshlets += data.meshletRange.size; for (uint32 m = 0; m < data.meshletRange.size; ++m) { MeshletDescription& meshlet = meshlets[data.meshletRange.offset + m]; vertexIndicesOffset = std::min(vertexIndicesOffset, meshlet.vertexIndices.offset); numVertexIndices += meshlet.vertexIndices.size; primitiveIndicesOffset = std::min(primitiveIndicesOffset, meshlet.primitiveIndices.offset); numPrimitiveIndices += meshlet.primitiveIndices.size; } for (auto& mesh : registeredMeshes) { auto& data = mesh.meshData; if (data.meshletRange.offset > meshletOffset) { for (uint32 i = 0; i < data.meshletRange.size; ++i) { MeshletDescription& m = meshlets[data.meshletRange.offset + i]; if (m.primitiveIndices.offset > primitiveIndicesOffset) { m.primitiveIndices.offset -= numPrimitiveIndices; } if (m.vertexIndices.offset > vertexIndicesOffset) { m.vertexIndices.offset -= numVertexIndices; } } data.meshletRange.offset -= numMeshlets; data.indicesRange.offset -= numIndices; } } uint32 numMeshletsToMove = meshlets.size() - (meshletOffset + numMeshlets); uint32 numVertexIndicesToMove = vertexIndices.size() - (vertexIndicesOffset + numVertexIndices); uint32 numPrimitiveIndicesToMove = primitiveIndices.size() - (primitiveIndicesOffset + numPrimitiveIndices); std::move(meshlets.begin() + meshletOffset + numMeshlets, meshlets.begin() + meshletOffset + numMeshlets + numMeshletsToMove, meshlets.begin() + meshletOffset); std::move(vertexIndices.begin() + vertexIndicesOffset + numVertexIndices, vertexIndices.begin() + vertexIndicesOffset + numVertexIndices + numVertexIndicesToMove, vertexIndices.begin() + vertexIndicesOffset); std::move(primitiveIndices.begin() + primitiveIndicesOffset + numPrimitiveIndices, primitiveIndices.begin() + primitiveIndicesOffset + numPrimitiveIndices + numPrimitiveIndicesToMove, primitiveIndices.begin() + primitiveIndicesOffset); uint32 numIndicesToMove = indices.size() - (indicesOffset + numIndices); std::move(indices.begin() + indicesOffset + numIndices, indices.begin() + indicesOffset + numIndices + numIndicesToMove, indices.begin() + indicesOffset); } void VertexData::commitMeshes() { indexBuffer = graphics->createIndexBuffer(IndexBufferCreateInfo{ .sourceData = { .size = sizeof(uint32) * indices.size(), .data = (uint8*)indices.data(), }, .indexType = Gfx::SE_INDEX_TYPE_UINT32, .name = "IndexBuffer", }); meshletBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .sourceData = { .size = sizeof(MeshletDescription) * meshlets.size(), .data = (uint8*)meshlets.data(), }, .numElements = meshlets.size(), .name = "MeshletBuffer", }); vertexIndicesBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .sourceData = { .size = sizeof(uint32) * vertexIndices.size(), .data = (uint8*)vertexIndices.data(), }, .numElements = vertexIndices.size(), .name = "VertexIndicesBuffer", }); primitiveIndicesBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .sourceData = { .size = sizeof(uint8) * primitiveIndices.size(), .data = (uint8*)primitiveIndices.data(), }, .numElements = primitiveIndices.size(), .name = "PrimitiveIndicesBuffer", }); updateBuffers(); dirty = false; graphics->buildBottomLevelAccelerationStructures(std::move(dataToBuild)); } MeshId VertexData::allocateVertexData(uint64 numVertices) { std::unique_lock l(vertexDataLock); MeshId res{idCounter++}; registeredMeshes.add({ .vertexOffset = head, .vertexCount = numVertices, }); head += numVertices; if (head > verticesAllocated) { verticesAllocated = 2 * head; // double capacity std::cout << "Resizing buffers to " << verticesAllocated << std::endl; resizeBuffers(); } return res; } void VertexData::serializeMesh(MeshId id, ArchiveBuffer& buffer) { std::unique_lock l(vertexDataLock); Array ind(registeredMeshes[id].meshData.indicesRange.size); std::memcpy(ind.data(), indices.data() + registeredMeshes[id].meshData.indicesRange.offset, registeredMeshes[id].meshData.indicesRange.size * sizeof(uint32)); Array pos(registeredMeshes[id].vertexCount); std::memcpy(pos.data(), positions.data() + registeredMeshes[id].vertexOffset, registeredMeshes[id].vertexCount * sizeof(Vector)); Serialization::save(buffer, ind); Serialization::save(buffer, pos); } uint64 VertexData::deserializeMesh(MeshId id, ArchiveBuffer& buffer) { Array pos; Array ind; Serialization::load(buffer, ind); Serialization::load(buffer, pos); loadMesh(id, pos, ind); uint64 result = pos.size() * sizeof(Vector); result += ind.size() * sizeof(uint32); return result; } List vertexDataList; void VertexData::addVertexDataInstance(VertexData* vertexData) { vertexDataList.add(vertexData); } List VertexData::getList() { return vertexDataList; } VertexData* VertexData::findByTypeName(std::string name) { for (auto vd : vertexDataList) { if (vd->getTypeName() == name) { return vd; } } return nullptr; } void VertexData::init(Gfx::PGraphics _graphics) { graphics = _graphics; verticesAllocated = NUM_DEFAULT_ELEMENTS; instanceDataLayout = graphics->createDescriptorLayout("pScene"); // positions instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .name = POSITIONS_NAME, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, }); // indexBuffer instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .name = INDEXBUFFER_NAME, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, }); // instanceData instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .name = INSTANCES_NAME, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, }); // meshData instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .name = MESHDATA_NAME, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, }); // meshletData instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .name = MESHLET_NAME, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, }); // primitiveIndices instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .name = PRIMITIVEINDICES_NAME, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, }); // vertexIndices instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .name = VERTEXINDICES_NAME, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, }); // cullingOffset instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .name = CULLINGOFFSETS_NAME, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, }); // cullingInfos instanceDataLayout->addDescriptorBinding(Gfx::DescriptorBinding{ .name = CULLINGDATA_NAME, .descriptorType = Gfx::SE_DESCRIPTOR_TYPE_STORAGE_BUFFER, }); instanceDataLayout->create(); cullingOffsetBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .name = "MeshletOffset", }); instanceBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .name = "InstanceBuffer", }); instanceMeshDataBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .name = "MeshDataBuffer", }); resizeBuffers(); graphics->getShaderCompiler()->registerVertexData(this); } void VertexData::destroy() { cullingOffsetBuffer = nullptr; instanceBuffer = nullptr; instanceMeshDataBuffer = nullptr; instanceDataLayout = nullptr; meshletBuffer = nullptr; vertexIndicesBuffer = nullptr; primitiveIndicesBuffer = nullptr; indexBuffer = nullptr; registeredMeshes.clear(); materialData.clear(); } uint32 VertexData::addCullingMapping(MeshId id) { uint32 result = (uint32)meshletCount; const auto& md = getMeshData(id); meshletCount += md.meshletRange.size; return result; } void VertexData::resizeBuffers() { positions.resize(verticesAllocated); } void VertexData::updateBuffers() { positionBuffer = graphics->createShaderBuffer(ShaderBufferCreateInfo{ .sourceData = { .size = verticesAllocated * sizeof(Vector), .data = (uint8*)positions.data(), }, .name = "Positions", }); } void VertexData::loadMeshlets(MeshId id, const Array& loadedPositions, const Array& loadedIndices) { // Array optimizedIndices = indices; // tipsifyIndexBuffer(indices, positions.size(), 25, optimizedIndices); const float coneWeight = 0.0f; const uint32 meshletOffset = meshlets.size(); const uint32 vertexOffset = vertexIndices.size(); const uint32 primitiveOffset = primitiveIndices.size(); const uint32 maxMeshlets = meshopt_buildMeshletsBound(loadedIndices.size(), Gfx::numVerticesPerMeshlet, Gfx::numPrimitivesPerMeshlet); Array meshoptMeshlets; meshoptMeshlets.resize(maxMeshlets); Array meshletVertexIndices; Array meshletTriangles; meshletVertexIndices.resize(maxMeshlets * Gfx::numVerticesPerMeshlet); meshletTriangles.resize(maxMeshlets * Gfx::numPrimitivesPerMeshlet * 3); const uint32 meshletCount = meshopt_buildMeshlets(meshoptMeshlets.data(), meshletVertexIndices.data(), meshletTriangles.data(), loadedIndices.data(), loadedIndices.size(), (float*)loadedPositions.data(), loadedPositions.size(), sizeof(Vector), Gfx::numVerticesPerMeshlet, Gfx::numPrimitivesPerMeshlet, coneWeight); const meshopt_Meshlet& last = meshoptMeshlets[meshletCount - 1]; const uint32 vertexCount = last.vertex_offset + last.vertex_count; const uint32 indexCount = last.triangle_offset + last.triangle_count * 3; vertexIndices.resize(vertexOffset + vertexCount); primitiveIndices.resize(primitiveOffset + indexCount); meshlets.resize(meshletOffset + meshletCount); std::memcpy(vertexIndices.data() + vertexOffset, meshletVertexIndices.data(), vertexCount * sizeof(uint32)); std::memcpy(primitiveIndices.data() + primitiveOffset, meshletTriangles.data(), indexCount * sizeof(uint8)); for (size_t i = 0; i < meshletCount; ++i) { MeshletDescription& m = meshlets[meshletOffset + i]; m.vertexIndices = { .offset = vertexOffset + meshoptMeshlets[i].vertex_offset, .size = meshoptMeshlets[i].vertex_count, }; m.primitiveIndices = { .offset = primitiveOffset + meshoptMeshlets[i].triangle_offset, .size = meshoptMeshlets[i].triangle_count, }; m.indicesOffset = registeredMeshes[id].vertexOffset; // todo: use meshopt for bb generation m.bounding = AABB(); for (size_t j = 0; j < m.vertexIndices.size; ++j) { m.bounding.adjust(loadedPositions[vertexIndices[meshoptMeshlets[i].vertex_offset + j + vertexOffset]]); } } registeredMeshes[id].meshData = MeshData{ .bounding = AABB(), .meshletRange = { .offset = meshletOffset, .size = meshletCount, }, .indicesRange = { .offset = (uint32)indices.size(), .size = (uint32)loadedIndices.size(), }, }; // todo: in case of a index split for 16 bit, do something here indices.resize(indices.size() + loadedIndices.size()); std::memcpy(indices.data() + registeredMeshes[id].meshData.indicesRange.offset, loadedIndices.data(), loadedIndices.size() * sizeof(uint32)); } VertexData::VertexData() : idCounter(0), head(0), verticesAllocated(0), dirty(false) {}