diff --git a/openVulkanoCpp/Scene/MeshWriter.cpp b/openVulkanoCpp/Scene/MeshWriter.cpp new file mode 100644 index 0000000..fccd8ca --- /dev/null +++ b/openVulkanoCpp/Scene/MeshWriter.cpp @@ -0,0 +1,154 @@ +/* + * 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 (int 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 (int 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(); + } +} \ No newline at end of file diff --git a/openVulkanoCpp/Scene/MeshWriter.hpp b/openVulkanoCpp/Scene/MeshWriter.hpp new file mode 100644 index 0000000..940cc97 --- /dev/null +++ b/openVulkanoCpp/Scene/MeshWriter.hpp @@ -0,0 +1,20 @@ +/* + * 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/. + */ + +#pragma once + +#include + +namespace OpenVulkano::Scene +{ + class Geometry; + class MeshWriter + { + public: + static void WriteAsOBJ(Geometry* geometry, const std::string& filePath); + static void WriteAsUSD(Geometry* geometry, const std::string& filePath); + }; +} \ No newline at end of file