/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "MeshWriter.hpp" #include "Scene/Geometry.hpp" #include "Scene/Vertex.hpp" #include #include #include #include #include #include namespace { uint32_t GetIndexFromGeometry(OpenVulkano::Scene::Geometry* geometry, int index) { uint32_t result = 0; if (geometry->indexType == OpenVulkano::Scene::VertexIndexType::UINT16) { uint16_t *indices = static_cast(geometry->indices); result = indices[index]; } else if (geometry->indexType == OpenVulkano::Scene::VertexIndexType::UINT32) { uint32_t *indices = static_cast(geometry->indices); result = indices[index]; } else { throw std::runtime_error("Invalid geometry index type"); } return result; } } namespace OpenVulkano::Scene { void MeshWriter::WriteAsOBJ(Geometry* geometry, const std::string& filePath) { std::ofstream file(filePath); if (!file.is_open()) throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); // Vertices for (int i = 0; i < geometry->vertexCount; ++i) { const OpenVulkano::Vertex& v = geometry->vertices[i]; std::string line; line = fmt::format("v {} {} {}\n", v.position.x, v.position.y, v.position.z); file << line; } // Normals for (int i = 0; i < geometry->vertexCount; ++i) { const OpenVulkano::Vertex& v = geometry->vertices[i]; std::string line; line = fmt::format("vn {} {} {}\n", v.normal.x, v.normal.y, v.normal.z); file << line; } // TexCoords for (int i = 0; i < geometry->vertexCount; ++i) { const OpenVulkano::Vertex& v = geometry->vertices[i]; std::string line; line = fmt::format("vt {} {}\n", v.textureCoordinates.x, v.textureCoordinates.y); file << line; } // Indices for (int i = 0; i < geometry->indexCount; i += 3) { uint32_t i0 = GetIndexFromGeometry(geometry, i + 0) + 1; uint32_t i1 = GetIndexFromGeometry(geometry, i + 1) + 1; uint32_t i2 = GetIndexFromGeometry(geometry, i + 2) + 1; std::string line = fmt::format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2); file << line; } file.close(); } void MeshWriter::WriteAsUSD(Geometry* geometry, const std::string& filePath) { tinyusdz::Stage stage; tinyusdz::Xform xform; tinyusdz::GeomMesh mesh; mesh.name = "TheMesh"; std::vector pts(geometry->vertexCount); std::vector indices(geometry->indexCount); tinyusdz::Attribute uvAttr; std::vector uvs(geometry->vertexCount); for (uint32_t i = 0; i < geometry->vertexCount; ++i) { const Vertex& v = geometry->vertices[i]; pts[i].x = v.position.x; pts[i].y = v.position.y; pts[i].z = v.position.z; uvs[i] = { v.textureCoordinates.x, v.textureCoordinates.y }; } mesh.points.set_value(pts); uvAttr.set_value(uvs); std::vector counts(geometry->indexCount / 3, 3); // NOTE(vb): The value 3 is kind of arbitrary, but this array must be in the mesh! mesh.faceVertexCounts.set_value(counts); for (uint32_t i = 0; i < geometry->indexCount; ++i) { uint32_t index = GetIndexFromGeometry(geometry, i); indices[i] = index; } mesh.faceVertexIndices.set_value(indices); uvAttr.metas().interpolation = tinyusdz::Interpolation::FaceVarying; tinyusdz::Property uvProp(uvAttr); mesh.props.emplace("primvars:UVMap", uvProp); tinyusdz::Prim xformPrim(xform); tinyusdz::Prim meshPrim(mesh); std::string err; if (!xformPrim.add_child(std::move(meshPrim), true, &err)) { throw std::runtime_error("Failed to construct scene: " + err); } if (!stage.add_root_prim(std::move(xformPrim))) { throw std::runtime_error("Failed to add prim to stage root: " + stage.get_error()); } stage.metas().defaultPrim = tinyusdz::value::token(xformPrim.element_name()); stage.metas().comment = "Generated by OpenVulkanoCpp"; if (!stage.commit()) { throw std::runtime_error("Failed to commit stage: " + stage.get_error()); } std::ofstream file(filePath); if (!file.is_open()) throw std::runtime_error("Failed to open file '" + filePath + "' for writing!"); std::string scene = to_string(stage); file << scene << "\n"; file.close(); } }