Move classes
This commit is contained in:
188
openVulkanoCpp/Scene/Export/MeshWriter.cpp
Normal file
188
openVulkanoCpp/Scene/Export/MeshWriter.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* 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 "Base/Utils.hpp"
|
||||
#include "IO/MemMappedFile.hpp"
|
||||
#include "Scene/Geometry.hpp"
|
||||
#include "Scene/Vertex.hpp"
|
||||
#include "Scene/Export/UsdEncoder.hpp"
|
||||
#include "ObjEncoder.hpp"
|
||||
#include "IO/Archive/ArchiveWriter.hpp"
|
||||
#include "IO/Archive/ZipWriter.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <fmt/core.h>
|
||||
|
||||
#if __has_include("assimp/Exporter.hpp")
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/Exporter.hpp>
|
||||
#include <assimp/mesh.h>
|
||||
#include <assimp/material.h>
|
||||
#endif
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
{
|
||||
void MeshWriter::WriteAsOBJ(Geometry* geometry, const std::string& filePath)
|
||||
{
|
||||
std::ofstream file(filePath);
|
||||
|
||||
if (!file.is_open()) [[unlikely]]
|
||||
throw std::runtime_error("Failed to open file '" + filePath + "' for writing!");
|
||||
|
||||
WriteObjContents(geometry, "", file);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void MeshWriter::WriteAsUSD(Geometry* geometry, const std::string& filePath)
|
||||
{
|
||||
std::ofstream file(filePath);
|
||||
if (!file.is_open()) [[unlikely]]
|
||||
throw std::runtime_error("Failed to open file '" + filePath + "' for writing!");
|
||||
WriteUsdContents(file, geometry);
|
||||
file.close();
|
||||
}
|
||||
|
||||
void MeshWriter::WriteObjAsZip(Geometry* geometry, const std::string& texturePath, const std::string& zipPath)
|
||||
{
|
||||
OpenVulkano::ArchiveWriter zipWriter(zipPath.c_str());
|
||||
|
||||
{
|
||||
std::stringstream objContents;
|
||||
WriteObjContents(geometry, DEFAULT_OBJ_MATERIAL_NAME, objContents);
|
||||
std::string objContentsStr = objContents.str();
|
||||
FileDescription objDesc = FileDescription::MakeDescriptionForFile("model.obj", objContentsStr.size());
|
||||
zipWriter.AddFile(objDesc, objContentsStr.data());
|
||||
}
|
||||
{
|
||||
FileDescription mtlDesc = FileDescription::MakeDescriptionForFile("material.mtl", DEFAULT_OBJ_MATERIAL_CONTENTS.size());
|
||||
zipWriter.AddFile(mtlDesc, DEFAULT_OBJ_MATERIAL_CONTENTS.data());
|
||||
}
|
||||
|
||||
if (!texturePath.empty() && std::filesystem::exists(texturePath))
|
||||
{
|
||||
MemMappedFile textureFile(texturePath);
|
||||
FileDescription texDesc = FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size());
|
||||
zipWriter.AddFile(texDesc, textureFile.Data());
|
||||
}
|
||||
}
|
||||
|
||||
void MeshWriter::WriteAsUSDZ(Geometry* geometry, const std::string& texturePath, const std::string& usdzPath)
|
||||
{
|
||||
OpenVulkano::ZipWriter zipWriter(usdzPath, true);
|
||||
|
||||
{
|
||||
std::stringstream usdFile;
|
||||
WriteUsdContents(usdFile, geometry);
|
||||
std::string usdFileStr = usdFile.str();
|
||||
FileDescription usdDesc = FileDescription::MakeDescriptionForFile("geometry.usda", usdFileStr.size());
|
||||
zipWriter.AddFile(usdDesc, usdFileStr.data());
|
||||
}
|
||||
|
||||
if (!texturePath.empty() && std::filesystem::exists(texturePath))
|
||||
{
|
||||
MemMappedFile textureFile(texturePath);
|
||||
FileDescription texDesc = FileDescription::MakeDescriptionForFile("texture.png", textureFile.Size());
|
||||
zipWriter.AddFile(texDesc, textureFile.Data());
|
||||
}
|
||||
}
|
||||
|
||||
void MeshWriter::WriteAsFBX(Geometry* geometry, const std::string& texturePath, const std::string& fbxPath)
|
||||
{
|
||||
#if __has_include("assimp/Exporter.hpp")
|
||||
aiNode rootNode;
|
||||
|
||||
aiScene scene;
|
||||
scene.mRootNode = &rootNode;
|
||||
|
||||
aiMesh mesh;
|
||||
mesh.mNumVertices = geometry->vertexCount;
|
||||
mesh.mMaterialIndex = 0;
|
||||
mesh.mPrimitiveTypes = aiPrimitiveType_TRIANGLE;
|
||||
mesh.mNumUVComponents[0] = 2;
|
||||
|
||||
std::unique_ptr<aiVector3D[]> vertices = std::make_unique<aiVector3D[]>(geometry->vertexCount);
|
||||
mesh.mVertices = vertices.get();
|
||||
|
||||
std::unique_ptr<aiVector3D[]> normals = std::make_unique<aiVector3D[]>(geometry->vertexCount);
|
||||
mesh.mNormals = normals.get();
|
||||
|
||||
std::unique_ptr<aiVector3D[]> texCoords = std::make_unique<aiVector3D[]>(geometry->vertexCount);
|
||||
mesh.mTextureCoords[0] = texCoords.get();
|
||||
|
||||
float scaling = 100; // fbx units are centimeters...
|
||||
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) * scaling;
|
||||
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;
|
||||
std::unique_ptr<aiFace[]> faces = std::make_unique<aiFace[]>(mesh.mNumFaces);
|
||||
mesh.mFaces = faces.get();
|
||||
|
||||
std::unique_ptr<unsigned int[]> indices = std::make_unique<unsigned int[]>(geometry->indexCount);
|
||||
size_t lastUsedIndex = 0;
|
||||
|
||||
for (uint32_t i = 0; i < mesh.mNumFaces; ++i)
|
||||
{
|
||||
aiFace& face = mesh.mFaces[i];
|
||||
face.mNumIndices = 3;
|
||||
face.mIndices = &indices[lastUsedIndex];
|
||||
|
||||
face.mIndices[0] = geometry->GetIndex(i * 3 + 0);
|
||||
face.mIndices[1] = geometry->GetIndex(i * 3 + 1);
|
||||
face.mIndices[2] = geometry->GetIndex(i * 3 + 2);
|
||||
|
||||
lastUsedIndex += face.mNumIndices;
|
||||
}
|
||||
|
||||
aiMesh* meshes[1] = { &mesh };
|
||||
scene.mMeshes = meshes;
|
||||
scene.mNumMeshes = 1;
|
||||
|
||||
unsigned int meshIndices[1];
|
||||
scene.mRootNode->mMeshes = meshIndices;
|
||||
scene.mRootNode->mMeshes[0] = 0;
|
||||
scene.mRootNode->mNumMeshes = 1;
|
||||
|
||||
aiMaterial material;
|
||||
aiMaterial* materials[1] = { &material };
|
||||
scene.mMaterials = materials;
|
||||
scene.mNumMaterials = 1;
|
||||
|
||||
aiString externalPath(texturePath);
|
||||
scene.mMaterials[0]->AddProperty(&externalPath, AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0));
|
||||
|
||||
Assimp::Exporter exporter;
|
||||
aiReturn result = exporter.Export(&scene, "fbx", fbxPath);
|
||||
|
||||
mesh.mVertices = nullptr;
|
||||
mesh.mNormals = nullptr;
|
||||
mesh.mTextureCoords[0] = nullptr;
|
||||
for (uint32_t i = 0; i < mesh.mNumFaces; ++i)
|
||||
{
|
||||
aiFace& face = mesh.mFaces[i];
|
||||
face.mIndices = nullptr;
|
||||
}
|
||||
mesh.mFaces = nullptr;
|
||||
|
||||
rootNode.mMeshes = nullptr;
|
||||
scene.mRootNode = nullptr;
|
||||
scene.mMeshes = nullptr;
|
||||
scene.mMaterials = nullptr;
|
||||
|
||||
if (result != aiReturn_SUCCESS)
|
||||
{
|
||||
throw std::runtime_error("Unable to write a fbx file to " + fbxPath + ": " + exporter.GetErrorString());
|
||||
}
|
||||
#else
|
||||
throw std::runtime_error("Unable to convert the scene to FBX: Assimp is not available!");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
23
openVulkanoCpp/Scene/Export/MeshWriter.hpp
Normal file
23
openVulkanoCpp/Scene/Export/MeshWriter.hpp
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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 <string>
|
||||
|
||||
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);
|
||||
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);
|
||||
};
|
||||
}
|
||||
68
openVulkanoCpp/Scene/Export/ObjEncoder.hpp
Normal file
68
openVulkanoCpp/Scene/Export/ObjEncoder.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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 "Scene/Geometry.hpp"
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <fmt/core.h>
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
{
|
||||
static constexpr std::string_view DEFAULT_OBJ_MATERIAL_NAME = "Material0";
|
||||
static constexpr std::string_view DEFAULT_OBJ_MATERIAL_CONTENTS = R"(newmtl Material0
|
||||
Ka 1.000 1.000 1.000
|
||||
Kd 1.000 1.000 1.000
|
||||
Ks 0.000 0.000 0.000
|
||||
map_Ka texture.png
|
||||
map_Kd texture.png
|
||||
)";
|
||||
|
||||
void WriteObjContents(Geometry* geometry, const std::string_view& materialName, std::ostream& objContent)
|
||||
{
|
||||
objContent << "# OBJ file generated by OpenVulkanoCpp\n";
|
||||
|
||||
if (materialName.size() != 0)
|
||||
{
|
||||
std::string_view content = "mtllib material.mtl\n";
|
||||
objContent.write(content.data(), content.size());
|
||||
|
||||
content = "usemtl ";
|
||||
objContent.write(content.data(), content.size());
|
||||
|
||||
objContent.write(materialName.data(), materialName.size());
|
||||
objContent.write("\n", 1);
|
||||
}
|
||||
|
||||
for (int i = 0; i < geometry->vertexCount; ++i)
|
||||
{
|
||||
const auto& v = geometry->vertices[i];
|
||||
const std::string content = fmt::format("v {} {} {}\n", v.position.x, v.position.y, v.position.z);
|
||||
objContent.write(content.data(), content.size());
|
||||
}
|
||||
for (int i = 0; i < geometry->vertexCount; ++i)
|
||||
{
|
||||
const auto& v = geometry->vertices[i];
|
||||
const std::string content = fmt::format("vn {} {} {}\n", v.normal.x, v.normal.y, v.normal.z);
|
||||
objContent.write(content.data(), content.size());
|
||||
}
|
||||
for (int i = 0; i < geometry->vertexCount; ++i)
|
||||
{
|
||||
const auto& v = geometry->vertices[i];
|
||||
const std::string content = fmt::format("vt {} {}\n", v.textureCoordinates.x, v.textureCoordinates.y);
|
||||
objContent.write(content.data(), content.size());
|
||||
}
|
||||
for (int i = 0; i < geometry->indexCount; i += 3)
|
||||
{
|
||||
uint32_t i0 = geometry->GetIndex(i + 0) + 1;
|
||||
uint32_t i1 = geometry->GetIndex(i + 1) + 1;
|
||||
uint32_t i2 = geometry->GetIndex(i + 2) + 1;
|
||||
const std::string content = fmt::format("f {0}/{0}/{0} {1}/{1}/{1} {2}/{2}/{2}\n", i0, i1, i2);
|
||||
objContent.write(content.data(), content.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
155
openVulkanoCpp/Scene/Export/UsdEncoder.hpp
Normal file
155
openVulkanoCpp/Scene/Export/UsdEncoder.hpp
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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 "Scene/Geometry.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace OpenVulkano::Scene
|
||||
{
|
||||
void WriteUsdContents(std::ostream& output, OpenVulkano::Scene::Geometry* geometry)
|
||||
{
|
||||
output << R"(#usda 1.0
|
||||
(
|
||||
defaultPrim = "root"
|
||||
doc = "Exported from OpenVulkano"
|
||||
metersPerUnit = 1
|
||||
upAxis = "Y"
|
||||
)
|
||||
|
||||
def Xform "root" (
|
||||
customData = {
|
||||
dictionary Blender = {
|
||||
bool generated = 1
|
||||
}
|
||||
}
|
||||
)
|
||||
{
|
||||
def Xform "model"
|
||||
{
|
||||
custom string userProperties:blender:object_name = "model"
|
||||
float3 xformOp:rotateXYZ = (0, -0, 0)
|
||||
float3 xformOp:scale = (1, 1, 1)
|
||||
double3 xformOp:translate = (0, 0, 0)
|
||||
uniform token[] xformOpOrder = ["xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale"]
|
||||
|
||||
def Mesh "model" (
|
||||
active = true
|
||||
prepend apiSchemas = ["MaterialBindingAPI"]
|
||||
)
|
||||
{
|
||||
uniform bool doubleSided = 1
|
||||
float3[] extent = [(-0.5, -0.5, 0), (0.5, 0.5, 0)]
|
||||
int[] faceVertexCounts = [)";
|
||||
|
||||
for (size_t i = 0; i < geometry->indexCount; ++i)
|
||||
{
|
||||
if ((i + 1) % 3 == 0)
|
||||
{
|
||||
if (i > 2) output << ", ";
|
||||
output << "3";
|
||||
}
|
||||
}
|
||||
|
||||
output << R"(]
|
||||
int[] faceVertexIndices = [)";
|
||||
|
||||
for (size_t i = 0; i < geometry->indexCount; ++i)
|
||||
{
|
||||
if (i > 0) output << ", ";
|
||||
output << geometry->GetIndex(i);
|
||||
}
|
||||
|
||||
output << R"(]
|
||||
rel material:binding = </root/_materials/Material0>
|
||||
normal3f[] normals = [)";
|
||||
|
||||
output << std::fixed << std::setprecision(6);
|
||||
for (size_t i = 0; i < geometry->vertexCount; ++i)
|
||||
{
|
||||
const auto& v = geometry->vertices[i];
|
||||
if (i > 0) output << ", ";
|
||||
output << "(" << v.normal.x << ", " << v.normal.y << ", " << v.normal.z << ")";
|
||||
}
|
||||
|
||||
output << R"(] (
|
||||
interpolation = "faceVarying"
|
||||
)
|
||||
point3f[] points = [)";
|
||||
|
||||
for (size_t i = 0; i < geometry->vertexCount; ++i)
|
||||
{
|
||||
const auto& v = geometry->vertices[i];
|
||||
if (i > 0) output << ", ";
|
||||
output << "(" << v.position.x << ", " << v.position.y << ", " << v.position.z << ")";
|
||||
}
|
||||
|
||||
output << R"(]
|
||||
texCoord2f[] primvars:st = [)";
|
||||
|
||||
output << std::fixed << std::setprecision(6);
|
||||
|
||||
for (size_t i = 0; i < geometry->indexCount; ++i)
|
||||
{
|
||||
const size_t vertexIndex = geometry->GetIndex(i);
|
||||
const auto& v = geometry->vertices[vertexIndex];
|
||||
if (i > 0) output << ", ";
|
||||
output << "(" << v.textureCoordinates.x << ", " << v.textureCoordinates.y << ")";
|
||||
}
|
||||
|
||||
output << R"(] (
|
||||
interpolation = "faceVarying"
|
||||
)
|
||||
uniform token subdivisionScheme = "none"
|
||||
custom string userProperties:blender:data_name = "model"
|
||||
}
|
||||
}
|
||||
|
||||
def Scope "_materials"
|
||||
{
|
||||
def Material "Material0"
|
||||
{
|
||||
token outputs:surface.connect = </root/_materials/Material0/Principled_BSDF.outputs:surface>
|
||||
custom string userProperties:blender:data_name = "Material0"
|
||||
|
||||
def Shader "Principled_BSDF"
|
||||
{
|
||||
uniform token info:id = "UsdPreviewSurface"
|
||||
float inputs:clearcoat = 0
|
||||
float inputs:clearcoatRoughness = 0.03
|
||||
color3f inputs:diffuseColor.connect = </root/_materials/Material0/Image_Texture.outputs:rgb>
|
||||
float inputs:ior = 1.5
|
||||
float inputs:metallic = 0
|
||||
float inputs:opacity = 1
|
||||
float inputs:roughness = 1
|
||||
float inputs:specular = 0
|
||||
token outputs:surface
|
||||
}
|
||||
|
||||
def Shader "Image_Texture"
|
||||
{
|
||||
uniform token info:id = "UsdUVTexture"
|
||||
asset inputs:file = @./texture.png@
|
||||
token inputs:sourceColorSpace = "sRGB"
|
||||
float2 inputs:st.connect = </root/_materials/Material0/uvmap.outputs:result>
|
||||
token inputs:wrapS = "repeat"
|
||||
token inputs:wrapT = "repeat"
|
||||
float3 outputs:rgb
|
||||
}
|
||||
|
||||
def Shader "uvmap"
|
||||
{
|
||||
uniform token info:id = "UsdPrimvarReader_float2"
|
||||
string inputs:varname = "st"
|
||||
float2 outputs:result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user