Moved InitFromFile() from Geometry to MeshLoader, added MeshLoader function for USD files, added tinyusdz library

This commit is contained in:
Vladyslav Baranovskyi
2024-08-29 21:01:42 +03:00
parent f70adcbd7f
commit 380e0f323d
7 changed files with 215 additions and 85 deletions

View File

@@ -40,3 +40,4 @@ if(ENABLE_TEST)
add_subdirectory(catch2)
endif()
add_subdirectory(tinyusdz)

22
3rdParty/tinyusdz/CMakeLists.txt vendored Normal file
View File

@@ -0,0 +1,22 @@
include(Utils)
include(FetchContent)
message("-- Building TinyUSDZ")
FetchContent_Declare(
tinyusdz
GIT_REPOSITORY https://github.com/syoyo/tinyusdz.git
GIT_TAG v0.8.0rc8
GIT_SHALLOW TRUE
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR}/INSTALL
-DCMAKE_BUILD_TYPE:STRING=Release
-DTINYUSDZ_BUILD_TESTS:BOOL=OFF
-DTINYUSDZ_BUILD_EXAMPLES:BOOL=OFF
)
FetchContent_MakeAvailable(tinyusdz)
function (LinkTinyUSDZ TARGET)
target_include_directories(${TARGET} PRIVATE ${tinyusdz_SOURCE_DIR}/src)
target_link_libraries(${TARGET} PRIVATE tinyusdz_static)
endfunction ()

View File

@@ -53,6 +53,7 @@ add_subdirectory(3rdParty)
# Then generate the OpenVulkano Library
add_subdirectory(openVulkanoCpp)
LinkTinyUSDZ(openVulkanoCpp)
if(ENABLE_TEST AND NOT IOS)
enable_testing()

View File

@@ -5,16 +5,8 @@
*/
#include "Geometry.hpp"
#include "Vertex.hpp"
#include "Base/Utils.hpp"
#include "Base/Logger.hpp"
#if __has_include("assimp/Importer.hpp")
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#define ASSIMP_AVAILABLE
#endif
#include <stdexcept>
namespace OpenVulkano::Scene
@@ -126,68 +118,6 @@ namespace OpenVulkano::Scene
renderGeo = nullptr;
}
void Geometry::InitFromFile(const std::string& file)
{
#ifdef ASSIMP_AVAILABLE
Assimp::Importer importer;
const uint32_t flags = aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_GenNormals |
aiProcess_ImproveCacheLocality | aiProcess_RemoveRedundantMaterials | aiProcess_GenUVCoords | aiProcess_TransformUVCoords |
aiProcess_ConvertToLeftHanded | aiProcess_PreTransformVertices | aiProcess_OptimizeGraph;
const aiScene* scene = importer.ReadFile(file, flags);
if (!scene) throw std::runtime_error("Failed to load file \"" + file + "\" Error: " + importer.GetErrorString());
if (!scene->HasMeshes()) throw std::runtime_error("File \"" + file + "\" does not have any meshes");
if (scene->mNumMeshes > 1) Logger::DATA->warn("File {0} contains more than one mesh. Only first one will be loaded", file);
Init(scene->mMeshes[0]);
importer.FreeScene();
#else
throw std::runtime_error("ASSIMP not available!");
#endif
}
void Geometry::Init(aiMesh* mesh)
{
#ifdef ASSIMP_AVAILABLE
aabb.Reset();
Init(mesh->mNumVertices, mesh->mNumFaces * 3); // Reserve the space for the data
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
vertices[i].Set(mesh->mVertices[i]);
if (mesh->HasNormals()) vertices[i].SetNormal(mesh->mNormals[i]);
if (mesh->HasTangentsAndBitangents())
{
vertices[i].SetTangentAndBiTangent(mesh->mTangents[i], mesh->mBitangents[i]);
}
if (mesh->HasTextureCoords(0)) vertices[i].SetTextureCoordinates(mesh->mTextureCoords[0][i]);
if (mesh->HasVertexColors(0)) vertices[i].SetColor(mesh->mColors[0][i]);
aabb.Grow(vertices[i].position);
}
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
{
const aiFace face = mesh->mFaces[i];
if (face.mNumIndices != 3) throw std::runtime_error("Mesh is not a triangle mesh!");
for (unsigned int j = 0; j < face.mNumIndices; j++)
{
if (indexType == VertexIndexType::UINT16)
{
static_cast<uint16_t*>(indices)[i * face.mNumIndices + j] = static_cast<uint16_t>(face.mIndices[j]);
}
else
{
static_cast<uint32_t*>(indices)[i * face.mNumIndices + j] = face.mIndices[j];
}
}
}
//TODO load bones
//TODO load materials
#else
throw std::runtime_error("ASSIMP not available!");
#endif
}
void Geometry::SetIndices(const uint32_t* data, uint32_t size, uint32_t dstOffset) const
{
for(uint32_t i = 0; i < size; i++)

View File

@@ -9,14 +9,11 @@
#include "Base/ICloseable.hpp"
#include "Math/AABB.hpp"
#include "Base/Utils.hpp"
#include "Vertex.hpp"
#include <string>
class aiMesh;
namespace OpenVulkano
{
struct Vertex;
namespace Scene
{
enum class VertexIndexType
@@ -26,6 +23,7 @@ namespace OpenVulkano
class Geometry : public ICloseable
{
friend class MeshLoader;
public:
uint32_t vertexCount = 0, indexCount = 0;
Vertex* vertices = nullptr;
@@ -42,15 +40,6 @@ namespace OpenVulkano
Geometry& operator=(Geometry&& other) noexcept;
~Geometry();
static Geometry* LoadFromFile(const std::string& file)
{
Geometry* mesh = new Geometry();
mesh->InitFromFile(file);
return mesh;
}
void InitFromFile(const std::string& file);
/**
* \brief Creates the arrays for the vertices and indices. They will not be filled!
* \param vertexCount The amount of vertices that will be used
@@ -58,8 +47,6 @@ namespace OpenVulkano
*/
void Init(uint32_t vertexCount, uint32_t indexCount);
void Init(aiMesh* mesh);
void SetIndices(const uint32_t* data, uint32_t size, uint32_t dstOffset = 0) const;
void Close() override;

View File

@@ -0,0 +1,168 @@
/*
* 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 "MeshLoader.hpp"
#include "Base/Logger.hpp"
#if __has_include("assimp/Importer.hpp")
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#define ASSIMP_AVAILABLE
#endif
#include "tinyusdz.hh"
#include "tydra/attribute-eval.hh"
#include "tydra/render-data.hh"
#include "tydra/scene-access.hh"
#include "tydra/shader-network.hh"
#include "usdShade.hh"
#include "pprinter.hh"
#include "prim-pprint.hh"
#include "value-pprint.hh"
#include "value-types.hh"
namespace
{
static bool ends_with(std::string_view str, std::string_view suffix)
{
return str.size() >= suffix.size() && str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
}
}
namespace OpenVulkano::Scene
{
void MeshLoader::parseAssimpFile(Geometry *geometry, const std::string& file)
{
#ifdef ASSIMP_AVAILABLE
Assimp::Importer importer;
const uint32_t flags = aiProcess_CalcTangentSpace | aiProcess_Triangulate | aiProcess_JoinIdenticalVertices | aiProcess_GenNormals |
aiProcess_ImproveCacheLocality | aiProcess_RemoveRedundantMaterials | aiProcess_GenUVCoords | aiProcess_TransformUVCoords |
aiProcess_ConvertToLeftHanded | aiProcess_PreTransformVertices | aiProcess_OptimizeGraph;
const aiScene* scene = importer.ReadFile(file, flags);
if (!scene) throw std::runtime_error("Failed to load file \"" + file + "\" Error: " + importer.GetErrorString());
if (!scene->HasMeshes()) throw std::runtime_error("File \"" + file + "\" does not have any meshes");
if (scene->mNumMeshes > 1) Logger::DATA->warn("File {0} contains more than one mesh. Only first one will be loaded", file);
aiMesh *mesh = scene->mMeshes[0];
geometry->aabb.Reset();
geometry->Init(mesh->mNumVertices, mesh->mNumFaces * 3); // Reserve the space for the data
for (unsigned int i = 0; i < mesh->mNumVertices; i++)
{
geometry->vertices[i].Set(mesh->mVertices[i]);
if (mesh->HasNormals()) geometry->vertices[i].SetNormal(mesh->mNormals[i]);
if (mesh->HasTangentsAndBitangents())
{
geometry->vertices[i].SetTangentAndBiTangent(mesh->mTangents[i], mesh->mBitangents[i]);
}
if (mesh->HasTextureCoords(0)) geometry->vertices[i].SetTextureCoordinates(mesh->mTextureCoords[0][i]);
if (mesh->HasVertexColors(0)) geometry->vertices[i].SetColor(mesh->mColors[0][i]);
geometry->aabb.Grow(geometry->vertices[i].position);
}
for (unsigned int i = 0; i < mesh->mNumFaces; i++)
{
const aiFace face = mesh->mFaces[i];
if (face.mNumIndices != 3) throw std::runtime_error("Mesh is not a triangle mesh!");
for (unsigned int j = 0; j < face.mNumIndices; j++)
{
if (geometry->indexType == VertexIndexType::UINT16)
{
static_cast<uint16_t*>(geometry->indices)[i * face.mNumIndices + j] = static_cast<uint16_t>(face.mIndices[j]);
}
else
{
static_cast<uint32_t*>(geometry->indices)[i * face.mNumIndices + j] = face.mIndices[j];
}
}
}
//TODO load bones
//TODO load materials
importer.FreeScene();
#else
throw std::runtime_error("ASSIMP not available!");
#endif
}
void MeshLoader::parseUSDFile(Geometry *geometry, const std::string& file)
{
tinyusdz::Stage stage;
std::string warning, err;
auto result = tinyusdz::LoadUSDFromFile(file, &stage, &warning, &err);
if (!result)
{
throw std::runtime_error("Failed to load USD file: " + file);
}
for (auto &prim : stage.root_prims())
{
if (prim.type_name() == "Xform")
{
for (auto& child : prim.children())
{
auto mesh = child.as<tinyusdz::GeomMesh>();
if (child.prim_type_name() == "Mesh" && mesh)
{
const auto& points = mesh->get_points();
const auto& indices = mesh->get_faceVertexIndices();
geometry->Init(static_cast<uint32_t>(points.size()), static_cast<uint32_t>(indices.size()));
for (uint32_t i = 0; i < geometry->vertexCount; ++i)
{
auto point = points[i];
OpenVulkano::Math::Vector3f p;
p.x = point.x;
p.y = point.y;
p.z = point.z;
geometry->vertices[i].position = p;
geometry->aabb.Grow(p);
}
if (geometry->indexType == VertexIndexType::UINT16)
{
uint16_t* indices16 = static_cast<uint16_t*>(geometry->indices);
for (uint32_t i = 0; i < geometry->indexCount; ++i)
{
indices16[i] = static_cast<uint16_t>(indices[i]);
}
}
else
{
uint32_t* indices32 = static_cast<uint32_t*>(geometry->indices);
for (uint32_t i = 0; i < geometry->indexCount; ++i)
{
indices32[i] = indices[i];
}
}
return;
}
}
}
}
throw std::runtime_error("No mesh found inside a xform in USD file: " + file);
}
Geometry* MeshLoader::LoadFromFile(const std::string& file)
{
Geometry* geometry = new Geometry();
if (ends_with(file, ".usd") || ends_with(file, ".usda") || ends_with(file, ".usdc") || ends_with(file, ".usdz"))
{
parseUSDFile(geometry, file);
}
else
{
parseAssimpFile(geometry, file);
}
return geometry;
}
}

View File

@@ -0,0 +1,21 @@
/*
* 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>
namespace OpenVulkano::Scene
{
class MeshLoader
{
static void parseAssimpFile(Geometry *geometry, const std::string& file);
static void parseUSDFile(Geometry *geometry, const std::string& file);
public:
static Geometry* LoadFromFile(const std::string& file);
};
}