Working commit, write load with tinygltf

This commit is contained in:
Xiao Qi 2023-02-20 19:56:26 +08:00
parent 7cba537fc9
commit 5cd0b272c9
8 changed files with 3160 additions and 358 deletions

View File

@ -1,2 +1,2 @@
rmdir "Dependencies/assimp" /S /Q rmdir "Dependencies/tinygltf" /S /Q
git clone https://github.com/SHADE-DP/assimp.git "Dependencies/assimp" git clone https://github.com/XiaoQiDigipen/tinygltf.git "Dependencies/tinygltf"

View File

@ -1,14 +1,15 @@
AssimpInclude = "%{prj.location}\\Dependencies\\assimp" -- AssimpInclude = "%{prj.location}\\Dependencies\\assimp"
TinyGLTFInclude = "%{prj.location}\\Dependencies\\tinygltf"
-- outputdir = "%{wks.location}/bin/%{cfg.buildcfg}" outputdir = "%{wks.location}/bin/%{cfg.buildcfg}"
-- interdir = "%{wks.location}/bin_int" interdir = "%{wks.location}/bin_int"
-- workspace "ModelCompile" workspace "ModelCompile"
-- architecture "x64" architecture "x64"
-- configurations configurations
-- { {
-- "Release", "Release",
-- "Debug" "Debug"
-- } }
project "ModelCompiler" project "ModelCompiler"
kind "ConsoleApp" kind "ConsoleApp"
@ -26,7 +27,7 @@ project "ModelCompiler"
externalincludedirs externalincludedirs
{ {
"%{AssimpInclude}\\include" "%{TinyGLTFInclude}"
} }
includedirs includedirs
@ -47,37 +48,37 @@ project "ModelCompiler"
"MultiProcessorCompile" "MultiProcessorCompile"
} }
filter "configurations:Debug" -- filter "configurations:Debug"
postbuildcommands -- postbuildcommands
{ -- {
"xcopy /r /y /q \"%{AssimpInclude}\\bin\\Debug\\assimp-vc142-mtd.dll\" \"$(OutDir)\"" -- "xcopy /r /y /q \"%{AssimpInclude}\\bin\\Debug\\assimp-vc142-mtd.dll\" \"$(OutDir)\""
} -- }
filter "configurations:Release" -- filter "configurations:Release"
postbuildcommands -- postbuildcommands
{ -- {
"xcopy /r /y /q \"%{AssimpInclude}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\"" -- "xcopy /r /y /q \"%{AssimpInclude}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\""
} -- }
filter "configurations:Publish" -- filter "configurations:Publish"
postbuildcommands -- postbuildcommands
{ -- {
"xcopy /r /y /q \"%{AssimpInclude}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\"" -- "xcopy /r /y /q \"%{AssimpInclude}\\bin\\Release\\assimp-vc142-mt.dll\" \"$(OutDir)\""
} -- }
warnings 'Extra' warnings 'Extra'
filter "configurations:Debug" filter "configurations:Debug"
symbols "On" symbols "On"
defines {"_DEBUG"} defines {"_DEBUG"}
links{"assimp-vc142-mtd.lib"} -- links{"assimp-vc142-mtd.lib"}
filter "configurations:Release" filter "configurations:Release"
optimize "On" optimize "On"
defines{"_RELEASE"} defines{"_RELEASE"}
links{"assimp-vc142-mt.lib"} -- links{"assimp-vc142-mt.lib"}
filter "configurations:Publish" filter "configurations:Publish"
optimize "On" optimize "On"
defines{"_RELEASE, _PUBLISH"} defines{"_RELEASE, _PUBLISH"}
links{"assimp-vc142-mt.lib"} -- links{"assimp-vc142-mt.lib"}

3009
racoon.gltf Normal file

File diff suppressed because one or more lines are too long

View File

@ -14,6 +14,46 @@
// Typedefs // Typedefs
typedef std::filesystem::path AssetPath; typedef std::filesystem::path AssetPath;
enum class BUFFER_TARGET : int
{
ARRAY_BUFFER = 3496,
ELEMENT_ARRAY_BUFFER = 34963
};
enum class ACCESSOR_DATA_TYPE : int
{
BYTE = 5120,
U_BYTE = 5121,
SHORT = 5122,
U_SHORT = 5123,
U_INT = 5125,
FLOAT = 5126
};
enum class ACCESSOR_COMPONENT_TYPE : int
{
SCALAR = 64 + 1,
VEC2 = 2,
VEC3 = 3,
VEC4 = 4,
MAT2 = 32 + 2,
MAT3 = 32 + 3,
MAT4 = 32 + 4,
VECTOR = 64 + 4,
MATRIX = 64 + 16
};
enum class PRIMITIVE_MODE : int
{
POINT = 0,
LINE = 1,
LINE_LOOP = 2,
LINE_STRIP = 3,
TRIANGLE = 4,
TRIANGLE_STRIP = 5,
TRIANLE_FAN = 6
};
//Directory //Directory
#ifdef _PUBLISH #ifdef _PUBLISH
constexpr std::string_view ASSET_ROOT{ "Assets" }; constexpr std::string_view ASSET_ROOT{ "Assets" };
@ -30,6 +70,15 @@ constexpr std::string_view MODEL_EXTENSION {".shmodel"};
constexpr std::string_view FBX_EXTENSION{ ".fbx" }; constexpr std::string_view FBX_EXTENSION{ ".fbx" };
constexpr std::string_view GLTF_EXTENSION{ ".gltf" }; constexpr std::string_view GLTF_EXTENSION{ ".gltf" };
// ATTRIBUTE NAMES
constexpr std::string_view ATT_POSITION {"POSITION"};
constexpr std::string_view ATT_NORMAL { "NORMAL" };
constexpr std::string_view ATT_TANGENT { "TANGENT" };
constexpr std::string_view ATT_JOINT { "JOINTS_0" };
constexpr std::string_view ATT_COLOUR { "COLOR_0" };
constexpr std::string_view ATT_TEXCOORD { "TEXCOORD_0" };
constexpr std::string_view ATT_WEIGHTS { "WEIGHTS_0" };
constexpr std::string_view EXTERNALS[] = { constexpr std::string_view EXTERNALS[] = {
FBX_EXTENSION, FBX_EXTENSION,
GLTF_EXTENSION GLTF_EXTENSION

View File

@ -10,336 +10,78 @@
* disclosure of this file or its contents without the prior written consent * disclosure of this file or its contents without the prior written consent
* of DigiPen Institute of Technology is prohibited. * of DigiPen Institute of Technology is prohibited.
*****************************************************************************/ *****************************************************************************/
#include "MeshCompiler.h" #include "MeshCompiler.h"
#include "MeshWriter.h" #include "MeshWriter.h"
#include <assimp/postprocess.h>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include "tiny_gltf.h"
#include <map>
#include <stack> #include <stack>
namespace SH_COMP namespace SH_COMP
{ {
SHMat4 aiTransformToMat4(aiMatrix4x4 const& inMatrx)
{
SHMat4 result;
std::memcpy(
&result,
&inMatrx,
sizeof(result)
);
return result;
}
Assimp::Importer MeshCompiler::aiImporter;
uint32_t MeshCompiler::rigNodeIDCounter { 0 }; uint32_t MeshCompiler::rigNodeIDCounter { 0 };
void MeshCompiler::ProcessNode(AiNodeConstPtr node, aiScene const& scene, MeshVectorRef meshes, RigData& rig) noexcept
{
if (node->mNumMeshes > 0)
{
aiMesh* mesh = scene.mMeshes[node->mMeshes[0]];
meshes.emplace_back();
GetMesh(*mesh, meshes.back());
meshes.back().name = node->mName.C_Str();
}
else if (node->mParent != nullptr)
{
BuildArmature(node, rig);
return;
}
for (auto i{ 0 }; i < node->mNumChildren; ++i)
{
ProcessNode(node->mChildren[i], scene, meshes, rig);
}
}
void MeshCompiler::GetMesh(aiMesh const& mesh, MeshData& meshData) noexcept
{
meshData.vertexPosition.reserve(mesh.mNumVertices);
meshData.vertexNormal.reserve(mesh.mNumVertices);
meshData.vertexTangent.reserve(mesh.mNumVertices);
meshData.texCoords.reserve(mesh.mNumVertices);
meshData.bonesInfo.resize(mesh.mNumBones);
meshData.bones.resize(mesh.mNumBones);
for (auto i{ 0 }; i < mesh.mNumBones; ++i)
{
auto const& bone = *mesh.mBones[i];
auto& newBone = meshData.bones[i];
auto& newBoneInfo = meshData.bonesInfo[i];
newBone.name = bone.mName.C_Str();
newBoneInfo.charCount = newBone.name.length();
std::memcpy(&newBone.offset, &bone.mOffsetMatrix, sizeof(SHMat4));
newBone.weights.resize(bone.mNumWeights);
for (auto j{ 0 }; j < bone.mNumWeights; ++j)
{
newBone.weights[j].index = bone.mWeights[j].mVertexId;
newBone.weights[j].weight = bone.mWeights[j].mWeight;
}
newBoneInfo.weightCount = bone.mNumWeights;
}
for (size_t i{ 0 }; i < mesh.mNumVertices; ++i)
{
// Vertex position
SHVec3 vertex;
vertex.x = mesh.mVertices[i].x;
vertex.y = mesh.mVertices[i].y;
vertex.z = mesh.mVertices[i].z;
meshData.vertexPosition.push_back(vertex);
// Tex coords
SHVec2 texCoord{ 0.f, 0.f };
if (mesh.mTextureCoords[0])
{
texCoord.x = mesh.mTextureCoords[0][i].x;
texCoord.y = mesh.mTextureCoords[0][i].y;
}
meshData.texCoords.push_back(texCoord);
// Normals
SHVec3 normal{ 0.f, 0.f, 0.f };
if (mesh.mNormals)
{
normal.x = mesh.mNormals[i].x;
normal.y = mesh.mNormals[i].y;
normal.z = mesh.mNormals[i].z;
}
meshData.vertexNormal.push_back(normal);
// Tangent
SHVec3 tangent{ 0.f, 0.f, 0.f };
if (mesh.mTangents)
{
tangent.x = mesh.mTangents[i].x;
tangent.y = mesh.mTangents[i].y;
tangent.z = mesh.mTangents[i].z;
}
meshData.vertexTangent.push_back(tangent);
}
for (size_t i{ 0 }; i < mesh.mNumFaces; ++i)
{
aiFace face = mesh.mFaces[i];
for (size_t j{ 0 }; j < face.mNumIndices; ++j)
{
meshData.indices.push_back(face.mIndices[j]);
}
}
}
void MeshCompiler::BuildHeaders(ModelRef asset) noexcept
{
// Mesh Headers
asset.meshHeaders.resize(asset.meshes.size());
asset.header.meshCount = asset.meshes.size();
for (auto i{ 0 }; i < asset.header.meshCount; ++i)
{
auto const& mesh = asset.meshes[i];
auto& head = asset.meshHeaders[i];
head.charCount = mesh.name.size();
head.indexCount = mesh.indices.size();
head.vertexCount = mesh.vertexPosition.size();
head.boneCount = mesh.bonesInfo.size();
}
// Anim Headers
asset.animHeaders.resize(asset.anims.size());
asset.header.animCount = asset.anims.size();
for (auto i{ 0 }; i < asset.header.animCount; ++i)
{
auto const& anim = asset.anims[i];
auto& head = asset.animHeaders[i];
head.charCount = anim.name.size();
head.animNodeCount = anim.nodeChannels.size();
head.nodeHeaders.resize(head.animNodeCount);
for (auto j{ 0 }; j < head.animNodeCount; ++j)
{
auto const& animNode = anim.nodeChannels[j];
auto& nodeHeader = head.nodeHeaders[j];
nodeHeader.charCount = animNode.name.size();
nodeHeader.posKeyCount = animNode.positionKeys.size();
nodeHeader.rotKeyCount = animNode.rotationKeys.size();
nodeHeader.scaKeyCount = animNode.scaleKeys.size();
}
}
}
void MeshCompiler::BoneOffsetCopy(ModelRef asset) noexcept
{
if (asset.meshHeaders[0].boneCount == 0)
{
return;
}
auto const& boneVec {asset.meshes[0].bones};
std::stack<RigNodeData*> nodeStack;
nodeStack.push(asset.rig.root);
while(!nodeStack.empty())
{
auto& node = *nodeStack.top();
nodeStack.pop();
for (auto const& bone : boneVec)
{
if (node.name == bone.name)
{
node.offset = bone.offset;
break;
}
}
for (auto const& child : node.children)
{
nodeStack.push(child);
}
}
}
void MeshCompiler::ParseAnimations(aiScene const& scene, std::vector<AnimData>& anims) noexcept
{
// Size and read for number of animation clips
anims.resize(scene.mNumAnimations);
for (auto i{ 0 }; i < scene.mNumAnimations; ++i)
{
auto const& animData = *scene.mAnimations[i];
auto& anim = anims[i];
anim.name = animData.mName.C_Str();
anim.duration = animData.mDuration;
anim.ticksPerSecond = animData.mTicksPerSecond;
// Size and read for number of animation frames
anim.nodeChannels.resize(animData.mNumChannels);
for (auto j{ 0 }; j < animData.mNumChannels; ++j)
{
auto const& channelData = *animData.mChannels[j];
auto& node = anim.nodeChannels[j];
node.name = channelData.mNodeName.C_Str();
// Position Keys
node.positionKeys.resize(channelData.mNumPositionKeys);
for (auto k{ 0 }; k < channelData.mNumPositionKeys; ++k)
{
auto const& posKeyData = channelData.mPositionKeys[k];
auto& posKey = node.positionKeys[k];
posKey.time = posKeyData.mTime;
posKey.value.x = posKeyData.mValue.x;
posKey.value.y = posKeyData.mValue.y;
posKey.value.z = posKeyData.mValue.z;
}
// Rotation Keys
node.rotationKeys.resize(channelData.mNumRotationKeys);
for (auto k{ 0 }; k < channelData.mNumRotationKeys; ++k)
{
auto const& rotKeyData = channelData.mRotationKeys[k];
auto& rotKey = node.rotationKeys[k];
rotKey.time = rotKeyData.mTime;
rotKey.value.x = rotKeyData.mValue.x;
rotKey.value.y = rotKeyData.mValue.y;
rotKey.value.z = rotKeyData.mValue.z;
rotKey.value.w = rotKeyData.mValue.w;
}
// Scale Keys
node.scaleKeys.resize(channelData.mNumScalingKeys);
for (auto k{ 0 }; k < channelData.mNumScalingKeys; ++k)
{
auto const& scaKeyData = channelData.mScalingKeys[k];
auto& scaKey = node.scaleKeys[k];
scaKey.time = scaKeyData.mTime;
scaKey.value.x = scaKeyData.mValue.x;
scaKey.value.y = scaKeyData.mValue.y;
scaKey.value.z = scaKeyData.mValue.z;
}
}
}
}
std::pair<RigNodeData*, aiNode const*> MeshCompiler::PairHelper(AiNodeConstPtr node)
{
return std::make_pair(NewNode(node), node);
}
RigNodeData* MeshCompiler::NewNode(AiNodeConstPtr inNode)
{
return new RigNodeData(inNode->mName.C_Str(), aiTransformToMat4(inNode->mTransformation));
}
void MeshCompiler::LoadFromFile(AssetPath path, ModelRef asset) noexcept void MeshCompiler::LoadFromFile(AssetPath path, ModelRef asset) noexcept
{ {
const aiScene* scene = aiImporter.ReadFile(path.string().c_str(), ModelData model;
aiProcess_Triangulate // Make sure we get triangles rather than nvert polygons tinygltf::TinyGLTF loader;
| aiProcess_GenUVCoords // Convert any type of mapping to uv mapping std::string warn, error;
| aiProcess_TransformUVCoords // preprocess UV transformations (scaling, translation ...)
//| aiProcess_FindInstances // search for instanced meshes and remove them by references to one master bool result = loader.LoadASCIIFromFile(&model, &error, &warn, path.string());
| aiProcess_CalcTangentSpace // calculate tangents and bitangents if possible
//| aiProcess_JoinIdenticalVertices // join identical vertices/ optimize indexing if (!warn.empty())
//| aiProcess_FindInvalidData // detect invalid model data, such as invalid normal vector std::cout << "[TinyGLTF Warning]: " << warn << std::endl;
| aiProcess_FlipUVs // flip the V to match the Vulkans way of doing UVs
//| aiProcess_ValidateDataStructure // checks all bones, animations and vertices are linked correctly if (!error.empty())
//| aiProcess_LimitBoneWeights // Limit number of bones effect vertices to 4 std::cout << "[TinyGLTF Error]: " << error << std::endl;
if (!result)
{
std::cout << "TinyGLTF failed to parse.\n";
std::exit(1);
}
ProcessModel(model, asset);
}
void MeshCompiler::ProcessModel(ModelData const& data, ModelRef asset) noexcept
{
auto const& accessors { data.accessors };
auto const& bufferViews { data.bufferViews };
auto const& bufferData { data.buffers[0].data.data() };
for (auto const& mesh : data.meshes)
{
auto const& primitive { mesh.primitives[0] };
auto& meshIn {asset.meshes.emplace_back()};
meshIn.name = mesh.name;
try
{
// Get Accessors
auto const& positionAccessor { accessors[primitive.attributes.at(ATT_POSITION.data())]};
auto const& normalAccessor { accessors[primitive.attributes.at(ATT_NORMAL.data())]};
auto const& tangentAccessor { accessors[primitive.attributes.at(ATT_TANGENT.data())]};
meshIn.vertexPosition.resize(positionAccessor.count);
auto const& positionView { bufferViews[positionAccessor.bufferView] };
std::memcpy(
meshIn.vertexPosition.data(),
bufferData + positionView.byteOffset,
positionView.byteLength
); );
if (!scene || !scene->HasMeshes()) meshIn.vertexNormal.resize(normalAccessor.count);
{
std::cout << "ERROR in GLTF::ASSIMP: " << aiImporter.GetErrorString() << "\nFile: " << path.string() << std::endl;
return;
} }
catch (std::out_of_range e)
ProcessNode(scene->mRootNode, *scene, asset.meshes, asset.rig);
ParseAnimations(*scene, asset.anims);
aiImporter.FreeScene();
}
void MeshCompiler::BuildArmature(AiNodeConstPtr baseNode, RigData& rig) noexcept
{ {
// Build implementation copy of armature tree std::cout << "[Model Compiler] Failed to load gltf\n";
// node collection write done later when writing to file
std::stack<std::pair<RigNodeData*, AiNodeConstPtr>> nodeStack;
nodeStack.emplace(PairHelper(baseNode));
rig.root = nodeStack.top().first;
while(!nodeStack.empty())
{
auto currPair = nodeStack.top();
nodeStack.pop();
auto currNode = currPair.first;
auto const& currAiNode = currPair.second;
int const iStart {static_cast<int>(currAiNode->mNumChildren - 1)};
rig.header.nodeCount++;
rig.header.charCounts.push_back(currNode->name.length());
for (int i {iStart}; i >= 0 ; --i)
{
auto newPair = PairHelper(currAiNode->mChildren[i]);
currNode->children.push_back(newPair.first);
nodeStack.push(newPair);
} }
} }
} }
@ -349,8 +91,6 @@ namespace SH_COMP
auto const asset = new ModelAsset(); auto const asset = new ModelAsset();
LoadFromFile(path, *asset); LoadFromFile(path, *asset);
BuildHeaders(*asset);
BoneOffsetCopy(*asset);
MeshWriter::CompileMeshBinary(path, *asset); MeshWriter::CompileMeshBinary(path, *asset);
delete asset; delete asset;

View File

@ -12,16 +12,30 @@
*****************************************************************************/ *****************************************************************************/
#pragma once #pragma once
#include <assimp/Importer.hpp> #define TINYGLTF_IMPLEMENTATION
#include <assimp/scene.h> #define TINYGLTF_NO_EXTERNAL_IMAGE
#define TINYGLTF_USE_CPP14
#define TINYGLTF_NO_INCLUDE_STB_IMAGE
#define TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
#define TINYGLTF_NO_STB_IMAGE_WRITE
#define TINYGLTF_NO_STB_IMAGE
#define TINYGLTF_USE_CPP14
#include <vector> #include <vector>
#include "Types/AnimationAsset.h" #include "Types/AnimationAsset.h"
#include "Types/ModelAsset.h" #include "Types/ModelAsset.h"
#include "AssetMacros.h" #include "AssetMacros.h"
//Forward Declare
namespace tinygltf
{
class Model;
}
namespace SH_COMP namespace SH_COMP
{ {
class tinygltf::Model;
class MeshCompiler class MeshCompiler
{ {
@ -29,26 +43,15 @@ namespace SH_COMP
using AnimVectorRef = std::vector<AnimData>&; using AnimVectorRef = std::vector<AnimData>&;
using ModelRef = ModelAsset&; using ModelRef = ModelAsset&;
using ModelData = tinygltf::Model;
using AiNodeConstPtr = aiNode const*;
static Assimp::Importer aiImporter;
static uint32_t rigNodeIDCounter; static uint32_t rigNodeIDCounter;
static void ProcessNode(AiNodeConstPtr node, aiScene const& scene, MeshVectorRef meshes, RigData& rig) noexcept;
static void GetMesh(aiMesh const& mesh, MeshData& meshData) noexcept;
static void BuildHeaders(ModelRef asset) noexcept;
static void BoneOffsetCopy(ModelRef asset) noexcept;
static void BuildArmature(AiNodeConstPtr node, RigData& rig) noexcept;
static void ParseAnimations(aiScene const& scene, std::vector<AnimData>& anims) noexcept;
static std::pair<RigNodeData*, AiNodeConstPtr> PairHelper(AiNodeConstPtr node);
static RigNodeData* NewNode(AiNodeConstPtr inNode);
static void LoadFromFile(AssetPath path, ModelRef asset) noexcept; static void LoadFromFile(AssetPath path, ModelRef asset) noexcept;
static void ProcessModel(ModelData const&, ModelRef asset) noexcept;
public: public:
static void LoadAndCompile(AssetPath path) noexcept; static void LoadAndCompile(AssetPath path) noexcept;
}; };

View File

@ -12,7 +12,7 @@
#include "PseudoMath.h" #include "PseudoMath.h"
#include <vector> #include <vector>
#include <assimp/anim.h> #include <string>
namespace SH_COMP namespace SH_COMP
{ {

View File

@ -18,7 +18,7 @@ int main(int argc, char* argv[])
{ {
std::vector<std::string> paths; std::vector<std::string> paths;
#if 1 #if 0
if (argc == 1) if (argc == 1)
{ {
@ -57,7 +57,7 @@ int main(int argc, char* argv[])
} }
#else #else
SH_COMP::MeshCompiler::LoadAndCompile("MD_Homeowner-NoRig.gltf"); SH_COMP::MeshCompiler::LoadAndCompile("racoon.gltf");
#endif #endif
return 0; return 0;