From 73ce105328e32d15397a2a74f103b3b56819700a Mon Sep 17 00:00:00 2001 From: Vladyslav Baranovskyi Date: Fri, 13 Dec 2024 12:12:09 +0200 Subject: [PATCH] Basic FBX export (without attached textures) --- openVulkanoCpp/Scene/MeshWriter.cpp | 148 ++++++++++++++++++++++++++++ openVulkanoCpp/Scene/MeshWriter.hpp | 1 + 2 files changed, 149 insertions(+) diff --git a/openVulkanoCpp/Scene/MeshWriter.cpp b/openVulkanoCpp/Scene/MeshWriter.cpp index 7ee4ab5..16e4f85 100644 --- a/openVulkanoCpp/Scene/MeshWriter.cpp +++ b/openVulkanoCpp/Scene/MeshWriter.cpp @@ -5,6 +5,7 @@ */ #include "MeshWriter.hpp" +#include "Base/Utils.hpp" #include "IO/MemMappedFile.hpp" #include "Scene/Geometry.hpp" #include "Scene/Vertex.hpp" @@ -14,7 +15,13 @@ #include "IO/Archive/ZipWriter.hpp" #include +#include #include +#include +#include +#include +#include +#include namespace OpenVulkano::Scene { @@ -81,4 +88,145 @@ namespace OpenVulkano::Scene zipWriter.AddFile(texDesc, textureFile.Data()); } } + + void MeshWriter::WriteAsFBX(Geometry* geometry, const std::string& texturePath, const std::string& fbxPath) + { + aiScene scene; + scene.mRootNode = new aiNode(); + + aiMesh* mesh = new aiMesh(); + mesh->mNumVertices = geometry->vertexCount; + mesh->mVertices = new aiVector3D[geometry->vertexCount]; + mesh->mNormals = new aiVector3D[geometry->vertexCount]; + mesh->mTextureCoords[0] = new aiVector3D[geometry->vertexCount]; + mesh->mMaterialIndex = 0; + mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + for (uint32_t i = 0; i < geometry->vertexCount; ++i) + { + const Vertex& vertex = geometry->vertices[i]; + mesh->mVertices[i] = aiVector3D(vertex.position.x, vertex.position.y, vertex.position.z); + mesh->mNormals[i] = aiVector3D(vertex.normal.x, vertex.normal.y, vertex.normal.z); + mesh->mTextureCoords[0][i] = aiVector3D(vertex.textureCoordinates.x, vertex.textureCoordinates.y, 0.0f); + } + + mesh->mNumFaces = geometry->indexCount / 3; + mesh->mFaces = new aiFace[mesh->mNumFaces]; + + for (uint32_t i = 0; i < mesh->mNumFaces; ++i) + { + aiFace& face = mesh->mFaces[i]; + face.mNumIndices = 3; + face.mIndices = new unsigned int[3]; + + face.mIndices[0] = geometry->GetIndex(i * 3 + 0); + face.mIndices[1] = geometry->GetIndex(i * 3 + 1); + face.mIndices[2] = geometry->GetIndex(i * 3 + 2); + } + + scene.mMeshes = new aiMesh*[1]; + scene.mMeshes[0] = mesh; + scene.mNumMeshes = 1; + scene.mRootNode->mMeshes = new unsigned int[1]; + scene.mRootNode->mMeshes[0] = 0; + scene.mRootNode->mNumMeshes = 1; + + aiMaterial* material = new aiMaterial(); + scene.mMaterials = new aiMaterial*[1]; + scene.mMaterials[0] = material; + scene.mNumMaterials = 1; + + // aiColor3D diffuseColor(1.0f, 1.0f, 1.0f); + // material->AddProperty(&diffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE); + + Array textureData = Utils::ReadFile(texturePath); + + aiTexture* texture = new aiTexture(); + texture->mWidth = static_cast(textureData.Size()); + texture->mHeight = 0; // indicates that this is compressed texture and the pcData has size mWidth + texture->pcData = reinterpret_cast(new uint8_t[textureData.Size()]); + std::memcpy(texture->pcData, textureData.Data(), textureData.Size()); + + scene.mTextures = new aiTexture*[1]; + scene.mTextures[0] = texture; + scene.mNumTextures = 1; + + std::string ext = texturePath.substr(texturePath.find_last_of('.') + 1); + if (ext == "png") + { + std::memcpy(texture->achFormatHint, "png", 3); + } + else if (ext == "jpg" || ext == "jpeg") + { + std::memcpy(texture->achFormatHint, "jpg", 3); + } + texture->achFormatHint[3] = '\0'; + + aiString texName = aiString("*0"); + material->AddProperty(&texName, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); + + // Validation + for (uint32_t i = 0; i < geometry->indexCount; ++i) { + if (geometry->GetIndex(i) >= geometry->vertexCount) { + std::cerr << "Invalid index at position " << i << ": " + << geometry->GetIndex(i) << " (vertex count: " + << geometry->vertexCount << ")" << std::endl; + assert(false); + } + } + + if (geometry->indexCount % 3 != 0) { + std::cerr << "Invalid index count: " << geometry->indexCount + << " (not divisible by 3)" << std::endl; + assert(false); + } + + // + //aiString externalPath(texturePath); + //scene.mMaterials[0]->AddProperty(&externalPath, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0)); + + // Export + + Assimp::Exporter exporter; + aiReturn result = exporter.Export(&scene, "fbx", fbxPath); + +#define SAFE_DELETE_ARRAY(arr) \ + do \ + { \ + if (arr) \ + delete[] arr; \ + arr = nullptr; \ + } while (0) + + SAFE_DELETE_ARRAY(mesh->mVertices); + SAFE_DELETE_ARRAY(mesh->mNormals); + SAFE_DELETE_ARRAY(mesh->mTextureCoords[0]); + + for (uint32_t i = 0; i < mesh->mNumFaces; ++i) + { + SAFE_DELETE_ARRAY(mesh->mFaces[i].mIndices); + } + + SAFE_DELETE_ARRAY(mesh->mFaces); + + delete mesh; + mesh = nullptr; + + delete material; + material = nullptr; + + SAFE_DELETE_ARRAY(scene.mMeshes); + SAFE_DELETE_ARRAY(scene.mRootNode->mMeshes); + SAFE_DELETE_ARRAY(scene.mMaterials); + SAFE_DELETE_ARRAY(scene.mTextures); + SAFE_DELETE_ARRAY(texture->pcData); + delete scene.mRootNode; + scene.mRootNode = nullptr; +#undef SAFE_DELETE_ARRAY + + if (result != aiReturn_SUCCESS) + { + throw std::runtime_error("Unable to write a fbx file to " + fbxPath + ": " + exporter.GetErrorString()); + } + } } \ No newline at end of file diff --git a/openVulkanoCpp/Scene/MeshWriter.hpp b/openVulkanoCpp/Scene/MeshWriter.hpp index c484bee..ba24bce 100644 --- a/openVulkanoCpp/Scene/MeshWriter.hpp +++ b/openVulkanoCpp/Scene/MeshWriter.hpp @@ -18,5 +18,6 @@ namespace OpenVulkano::Scene static void WriteAsUSD(Geometry* geometry, const std::string& filePath); static void WriteObjAsZip(Geometry* geometry, const std::string& texturePath, const std::string& zipPath); static void WriteAsUSDZ(Geometry* geometry, const std::string& texturePath, const std::string& usdzPath); + static void WriteAsFBX(Geometry* geometry, const std::string& texturePath, const std::string& fbxPath); }; } \ No newline at end of file