From a7fabdb824050959a313fa1b7d3f0cfe4fa7d7da Mon Sep 17 00:00:00 2001 From: Jonathan Moallem Date: Sun, 2 Feb 2025 12:08:12 +1000 Subject: [PATCH 01/10] Added skeletal mesh packer and file type with skinned vertex type --- engine/resources/SkeletalMeshData.h | 107 ++++++++++++++ examples/render/Makefile | 1 + examples/render/assets/models/cube_anim.glb | Bin 0 -> 6024 bytes examples/render/assets/models/cube_anim.sk | 3 + packer/src/main.cpp | 8 ++ packer/src/types/SkeletalMeshDataPacker.cpp | 150 ++++++++++++++++++++ packer/src/types/SkeletalMeshDataPacker.h | 16 +++ 7 files changed, 285 insertions(+) create mode 100644 engine/resources/SkeletalMeshData.h create mode 100644 examples/render/assets/models/cube_anim.glb create mode 100644 examples/render/assets/models/cube_anim.sk create mode 100644 packer/src/types/SkeletalMeshDataPacker.cpp create mode 100644 packer/src/types/SkeletalMeshDataPacker.h diff --git a/engine/resources/SkeletalMeshData.h b/engine/resources/SkeletalMeshData.h new file mode 100644 index 00000000..3b5805b6 --- /dev/null +++ b/engine/resources/SkeletalMeshData.h @@ -0,0 +1,107 @@ +// +// Copyright (c) 2020-present Caps Collective & contributors +// Originally authored by Jonathan Moallem (@jonjondev) & Aryeh Zinn (@Raelr) +// +// This code is released under an unmodified zlib license. +// For conditions of distribution and use, please see: +// https://opensource.org/licenses/Zlib +// + +#ifndef SIEGE_ENGINE_SKELETALMESHDATA_H +#define SIEGE_ENGINE_SKELETALMESHDATA_H + +#include +#include +#include +#include + +#include "StaticMeshData.h" +#include "PackFile.h" + +namespace Siege +{ + +struct SkinnedVertex +{ + BaseVertex vertex; + Vec3 position; + FColour color; + Vec3 normal; + Vec2 uv; +}; + +inline bool operator==(const SkinnedVertex& left, const SkinnedVertex& right) +{ + return left.position == right.position && left.color == right.color && + left.normal == right.normal && left.uv == right.uv; +} + +inline bool operator!=(const SkinnedVertex& left, const SkinnedVertex& right) +{ + return !(left == right); +} + +#pragma pack(push, 1) +struct SkeletalMeshData +{ + uint64_t indicesCount = 0; + uint64_t verticesCount = 0; + char data[]; + + uint32_t* GetIndices() const + { + return (uint32_t*) data; + } + + SkinnedVertex* GetVertices() const + { + return (SkinnedVertex*) (data + sizeof(uint32_t) * indicesCount); + } + + static SkeletalMeshData* Create(const std::vector& objIndices, + const std::vector& objVertices) + { + uint32_t indicesDataSize = sizeof(uint32_t) * objIndices.size(); + uint32_t verticesDataSize = sizeof(SkinnedVertex) * objVertices.size(); + uint32_t totalDataSize = sizeof(SkeletalMeshData) + indicesDataSize + verticesDataSize; + + void* mem = malloc(totalDataSize); + SkeletalMeshData* staticMeshData = new (mem) SkeletalMeshData(); + + staticMeshData->indicesCount = objIndices.size(); + staticMeshData->verticesCount = objVertices.size(); + memcpy(&staticMeshData->data[0], objIndices.data(), indicesDataSize); + memcpy(&staticMeshData->data[0] + indicesDataSize, objVertices.data(), verticesDataSize); + + return staticMeshData; + } + + static uint32_t GetDataSize(void* objectData) + { + if (!objectData) return 0; + SkeletalMeshData* staticMeshData = reinterpret_cast(objectData); + return sizeof(SkeletalMeshData) + sizeof(uint32_t) * staticMeshData->indicesCount + + sizeof(SkinnedVertex) * staticMeshData->verticesCount; + } +}; +#pragma pack(pop) + +} // namespace Siege + +namespace std +{ + +template<> +struct hash +{ + size_t operator()(const Siege::SkinnedVertex& vertex) const + { + size_t seed = 0; + Siege::Hash::HashCombine(seed, vertex.position, vertex.color, vertex.normal, vertex.uv); + return seed; + }; +}; + +} // namespace std + +#endif // SIEGE_ENGINE_SKELETALMESHDATA_H diff --git a/examples/render/Makefile b/examples/render/Makefile index f2e8ac2c..5b6973c7 100644 --- a/examples/render/Makefile +++ b/examples/render/Makefile @@ -30,6 +30,7 @@ else ifeq ($(platform), macos) endif engineRenderBuildDir := $(binDir)/engine/render/build exampleRenderAssets := $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.sm)) +exampleRenderAssets := $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.sk)) exampleRenderAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.png)) exampleRenderAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.jpg)) exampleRenderAssets += $(patsubst $(engineRenderBuildDir)/%,%, $(call rwildcard,$(engineRenderBuildDir),*.spv)) diff --git a/examples/render/assets/models/cube_anim.glb b/examples/render/assets/models/cube_anim.glb new file mode 100644 index 0000000000000000000000000000000000000000..9eea96c8e4b674685a2a3ccad1ff113247dc8501 GIT binary patch literal 6024 zcmeHJU2GIp6u$B!rGQ$X{0PW8Au-~*%+Ky@x3RsHQnjUYp+Bgt1g6WhU2J#O*=Z4L zWz}fl5s18)VB&*;5)+~rUPw!fMiU>@_>=hB2UH$>A@Kno@SHn)@6OKbPalX+&gRZN z=icv}^WAgLy|bg^{m(BUgq+?;$c@#6boC{BrX?enGaadUS{gMorfoQuEj3H~#%wEN z2`G!>1g5nYzT(v)fE(pCn*YDAW1!cr~;4y0zeAmyayp=l{& zOu(4b$L7LP#yV!=gm!r5a8VPmJyynybeqNq_;+k0lN&c27Gp>bD`ENYOn6#Kjitwr z*(L-YlF^47fLZohmIIcy<#4lS-FFjw)u;8uP7e>wb>}D2fipen#W)f{-ZPO6k>U^Ln)3S7I;LK6>pQcf8!5O!n?QB1_FsWnIb6p8h^m zpn_iN=zQTtSBuVIj*q2N=q=`w!9&G&4R0884zqqdtMRzz|$Cf!VTSXyUNQ(xHxa4KB`U$;%KCo0N7j>u6(FL`jXI_svIaf?X{77W}BRaryQcu7B4+6s8C6}d<} zm~N#;sTX&v{6VQv>b1;CXi1;u0Ty_ndgG6Ykf#f_KbA3AcpG{yJ2E zH~E*6h*smG`IoLj;;V4?vr)CG?t^fh{ZXnwKFCH(R4dtl`#7XhoR-2#@f;5|uIcb! zAruN-ttVtpXAi0SZQC3+Lh`hR-0HzplF`q+$4@yKZTWaPxz)p`n#?&Mrjk5Yh{gS2 zT))gYa6cTw$LCx=B#*sM28j(X!!f?T1T*hyoJUzd989>GyXE|F_<2rC)rs zOXznyn(5dzRp?85;*?B9g+6sY7RZ;oueeA4hFLA$JD%rapQjBMKY5Pp8}6KClb)o+=cZyyPw~KrX-a7?(^*;U_7xjMu9gn64 literal 0 HcmV?d00001 diff --git a/examples/render/assets/models/cube_anim.sk b/examples/render/assets/models/cube_anim.sk new file mode 100644 index 00000000..56dc8121 --- /dev/null +++ b/examples/render/assets/models/cube_anim.sk @@ -0,0 +1,3 @@ +SOURCE_PATH:assets/models/cube_anim.glb; +MATERIAL_PATH:assets/models/cube.mat; +NODE_PATH:/; \ No newline at end of file diff --git a/packer/src/main.cpp b/packer/src/main.cpp index 1be0c7d4..05505481 100644 --- a/packer/src/main.cpp +++ b/packer/src/main.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -19,6 +21,7 @@ #include "types/GenericFileDataPacker.h" #include "types/SceneDataPacker.h" +#include "types/SkeletalMeshDataPacker.h" #include "types/StaticMeshDataPacker.h" #include "types/Texture2DDataPacker.h" @@ -59,6 +62,11 @@ int main(int argc, char* argv[]) { data = PackStaticMeshFile(fullPath, assetsDir); } + else if (extension == ".sk") + { + data = PackSkeletalMeshFile(fullPath, assetsDir); + bodyDataSize = Siege::SkeletalMeshData::GetDataSize(data); + } else if (extension == ".jpg" || extension == ".jpeg" || extension == ".png") { data = PackTexture2DFile(fullPath); diff --git a/packer/src/types/SkeletalMeshDataPacker.cpp b/packer/src/types/SkeletalMeshDataPacker.cpp new file mode 100644 index 00000000..b34ded4c --- /dev/null +++ b/packer/src/types/SkeletalMeshDataPacker.cpp @@ -0,0 +1,150 @@ +// +// Copyright (c) 2020-present Jonathan Moallem (@J-Mo63) & Aryeh Zinn (@Raelr) +// +// This code is released under an unmodified zlib license. +// For conditions of distribution and use, please see: +// https://opensource.org/licenses/Zlib +// + +#include "SkeletalMeshDataPacker.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "StaticMeshDataPacker.h" + +static void GetMeshData(const aiScene* scene, + aiMesh* mesh, + aiMatrix4x4t matrix, + OUT std::vector& vertices, + OUT std::vector& indices) +{ + for (uint32_t i = 0; i < mesh->mNumVertices; i++) + { + Siege::SkinnedVertex vertex {}; + + aiVector3t vert = mesh->mVertices[i]; + vert *= matrix; + vertex.position = {vert.x, vert.y, vert.z}; + + aiVector3t norm = mesh->mNormals[i]; + vertex.normal = {norm.x, norm.y, norm.z}; + + aiColor4t* color = mesh->mColors[0]; + vertex.color = color ? Siege::FColour {color[i].r, color[i].g, color[i].b, color[i].a} : + Siege::FColour::White; + + if (aiVector3t* uv = mesh->mTextureCoords[0]) + { + vertex.uv = {uv->x, uv->y}; + } + + vertices.push_back(vertex); + } + + auto it = std::max_element(indices.begin(), indices.end()); + uint32_t indexOffset = it != indices.end() ? *it + 1 : 0; + for (uint32_t i = 0; i < mesh->mNumFaces; i++) + { + aiFace face = mesh->mFaces[i]; + for (uint32_t j = 0; j < face.mNumIndices; j++) + { + indices.push_back(indexOffset + face.mIndices[j]); + } + } +} + +static void GetMeshesForNode(const aiScene* scene, + aiNode* node, + const Siege::String& requestPath, + Siege::String currentPath, + aiMatrix4x4t matrix, + OUT std::vector& vertices, + OUT std::vector& indices) +{ + currentPath = currentPath + node->mName.C_Str(); + + CC_LOG_INFO("Reading node at {}", currentPath) + + matrix *= node->mTransformation; + + for (uint32_t i = 0; i < node->mNumMeshes; i++) + { + aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; + + Siege::String currentMeshPath = currentPath + '/' + mesh->mName.C_Str(); + + CC_LOG_INFO("Reading mesh at {}", currentMeshPath) + GetMeshData(scene, mesh, matrix, vertices, indices); + } + + for (uint32_t i = 0; i < node->mNumChildren; i++) + { + GetMeshesForNode(scene, + node->mChildren[i], + requestPath, + currentPath + '/', + matrix, + vertices, + indices); + } +}; + +void* PackSkeletalMeshFile(const Siege::String& filePath, const Siege::String& assetsPath) +{ + Siege::String contents = Siege::FileSystem::Read(filePath); + std::map attributes = + Siege::FileSystem::ParseAttributeFileData(contents); + + if (attributes.empty()) + { + CC_LOG_WARNING("Received empty skeletal mesh file at path \"{}\"", filePath); + return nullptr; + } + + auto it = attributes.find(TOKEN_SOURCE_PATH); + if (it == attributes.end()) + { + CC_LOG_WARNING("Failed to find SOURCE_PATH attribute in .sm file at path \"{}\"", filePath) + return nullptr; + } + + Siege::String modelPath = assetsPath + '/' + it->second; + Assimp::Importer importer; + const aiScene* scene = + importer.ReadFile(modelPath, + aiProcess_CalcTangentSpace | aiProcess_Triangulate | + aiProcess_JoinIdenticalVertices | aiProcess_SortByPType); + if (!scene) + { + CC_LOG_WARNING("Failed to read file at path \"{}\"", modelPath) + return nullptr; + } + + auto nodePathIt = attributes.find(TOKEN_NODE_PATH); + Siege::String requestedNodePath = nodePathIt != attributes.end() ? nodePathIt->second : "/"; + + CC_LOG_INFO("Reading skeletal mesh for file {} with node path {}", it->second, requestedNodePath) + + std::vector vertices; + std::vector indices; + GetMeshesForNode(scene, + scene->mRootNode, + requestedNodePath, + '/', + aiMatrix4x4t(), + vertices, + indices); + +// Siege::SkeletalMeshData* skeletalMeshData = Siege::SkeletalMeshData::Create(indices, vertices); +// return skeletalMeshData; + return nullptr; +} diff --git a/packer/src/types/SkeletalMeshDataPacker.h b/packer/src/types/SkeletalMeshDataPacker.h new file mode 100644 index 00000000..a1eeb8fa --- /dev/null +++ b/packer/src/types/SkeletalMeshDataPacker.h @@ -0,0 +1,16 @@ +// +// Copyright (c) 2020-present Jonathan Moallem (@J-Mo63) & Aryeh Zinn (@Raelr) +// +// This code is released under an unmodified zlib license. +// For conditions of distribution and use, please see: +// https://opensource.org/licenses/Zlib +// + +#ifndef SIEGE_ENGINE_SKELETALMESHDATAPACKER_H +#define SIEGE_ENGINE_SKELETALMESHDATAPACKER_H + +#include + +void* PackSkeletalMeshFile(const Siege::String& filePath, const Siege::String& assetsPath); + +#endif // SIEGE_ENGINE_SKELETALMESHDATAPACKER_H From ae527f9a3035d945d2c748236a3cdcfeca521c22 Mon Sep 17 00:00:00 2001 From: Jonathan Moallem Date: Mon, 17 Feb 2025 22:32:20 +1000 Subject: [PATCH 02/10] Implemented named bone packing for skeletal mesh data --- engine/resources/SkeletalMeshData.h | 82 +++++++++--- examples/render/Makefile | 2 +- examples/render/assets/models/cube_anim.sk | 3 +- packer/src/types/SkeletalMeshDataPacker.cpp | 135 ++++++++++++++------ 4 files changed, 157 insertions(+), 65 deletions(-) diff --git a/engine/resources/SkeletalMeshData.h b/engine/resources/SkeletalMeshData.h index 3b5805b6..bbc0228a 100644 --- a/engine/resources/SkeletalMeshData.h +++ b/engine/resources/SkeletalMeshData.h @@ -14,26 +14,28 @@ #include #include #include +#include #include "StaticMeshData.h" #include "PackFile.h" namespace Siege { +struct Bone +{ + uint32_t id; + Mat4 bindMatrix; +}; -struct SkinnedVertex +struct SkinnedVertex : public BaseVertex { - BaseVertex vertex; - Vec3 position; - FColour color; - Vec3 normal; - Vec2 uv; + Vec4 bones; + Vec4 weights; }; inline bool operator==(const SkinnedVertex& left, const SkinnedVertex& right) { - return left.position == right.position && left.color == right.color && - left.normal == right.normal && left.uv == right.uv; + return static_cast(left) == static_cast(right) && left.bones == right.bones && left.weights == right.weights; } inline bool operator!=(const SkinnedVertex& left, const SkinnedVertex& right) @@ -46,6 +48,8 @@ struct SkeletalMeshData { uint64_t indicesCount = 0; uint64_t verticesCount = 0; + uint32_t bonesCount = 0; + uint32_t boneNamesLength = 0; char data[]; uint32_t* GetIndices() const @@ -58,30 +62,66 @@ struct SkeletalMeshData return (SkinnedVertex*) (data + sizeof(uint32_t) * indicesCount); } + Bone* GetBones() const + { + return (Bone*) (data + (sizeof(uint32_t) * indicesCount + sizeof(SkinnedVertex) * verticesCount)); + } + + char* GetBoneNames() const + { + return (char*) (data + (sizeof(uint32_t) * indicesCount + sizeof(SkinnedVertex) * verticesCount) + sizeof(Bone) * bonesCount); + } + static SkeletalMeshData* Create(const std::vector& objIndices, - const std::vector& objVertices) + const std::vector& objVertices, + const std::map& objBones) { + std::vector bones; + std::vector boneNames; + size_t boneNamesLength = 0; + for (const auto& it : objBones) + { + bones.push_back(it.second); + boneNames.push_back(it.first); + boneNamesLength += it.first.Size() + 1; + } + + char boneNamesArr[boneNamesLength]; + size_t currentBoneNamesOffset = 0; + for (const Siege::String& name : boneNames) + { + memcpy(&boneNamesArr[0] + currentBoneNamesOffset, name.Str(), name.Size() + 1); + currentBoneNamesOffset += name.Size() + 1; + } + uint32_t indicesDataSize = sizeof(uint32_t) * objIndices.size(); uint32_t verticesDataSize = sizeof(SkinnedVertex) * objVertices.size(); - uint32_t totalDataSize = sizeof(SkeletalMeshData) + indicesDataSize + verticesDataSize; + uint32_t bonesDataSize = sizeof(Bone) * objBones.size(); + uint32_t totalDataSize = sizeof(SkeletalMeshData) + indicesDataSize + verticesDataSize + bonesDataSize + boneNamesLength; void* mem = malloc(totalDataSize); - SkeletalMeshData* staticMeshData = new (mem) SkeletalMeshData(); + SkeletalMeshData* skeletalMeshData = new (mem) SkeletalMeshData(); + + skeletalMeshData->indicesCount = objIndices.size(); + skeletalMeshData->verticesCount = objVertices.size(); + skeletalMeshData->bonesCount = objBones.size(); + skeletalMeshData->boneNamesLength = boneNamesLength; + memcpy(&skeletalMeshData->data[0], objIndices.data(), indicesDataSize); + memcpy(&skeletalMeshData->data[0] + indicesDataSize, objVertices.data(), verticesDataSize); - staticMeshData->indicesCount = objIndices.size(); - staticMeshData->verticesCount = objVertices.size(); - memcpy(&staticMeshData->data[0], objIndices.data(), indicesDataSize); - memcpy(&staticMeshData->data[0] + indicesDataSize, objVertices.data(), verticesDataSize); + memcpy(&skeletalMeshData->data[0] + indicesDataSize + verticesDataSize, bones.data(), bonesDataSize); + memcpy(&skeletalMeshData->data[0] + indicesDataSize + verticesDataSize + bonesDataSize, boneNamesArr, boneNamesLength); - return staticMeshData; + return skeletalMeshData; } static uint32_t GetDataSize(void* objectData) { if (!objectData) return 0; - SkeletalMeshData* staticMeshData = reinterpret_cast(objectData); - return sizeof(SkeletalMeshData) + sizeof(uint32_t) * staticMeshData->indicesCount + - sizeof(SkinnedVertex) * staticMeshData->verticesCount; + SkeletalMeshData* skeletalMeshData = reinterpret_cast(objectData); + return sizeof(SkeletalMeshData) + sizeof(uint32_t) * skeletalMeshData->indicesCount + + sizeof(SkinnedVertex) * skeletalMeshData->verticesCount + + sizeof(Bone) * skeletalMeshData->bonesCount + skeletalMeshData->boneNamesLength; } }; #pragma pack(pop) @@ -96,8 +136,8 @@ struct hash { size_t operator()(const Siege::SkinnedVertex& vertex) const { - size_t seed = 0; - Siege::Hash::HashCombine(seed, vertex.position, vertex.color, vertex.normal, vertex.uv); + size_t seed; + Siege::Hash::HashCombine(seed, vertex, vertex.bones, vertex.weights); return seed; }; }; diff --git a/examples/render/Makefile b/examples/render/Makefile index 5b6973c7..030b4b3d 100644 --- a/examples/render/Makefile +++ b/examples/render/Makefile @@ -30,7 +30,7 @@ else ifeq ($(platform), macos) endif engineRenderBuildDir := $(binDir)/engine/render/build exampleRenderAssets := $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.sm)) -exampleRenderAssets := $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.sk)) +exampleRenderAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.sk)) exampleRenderAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.png)) exampleRenderAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.jpg)) exampleRenderAssets += $(patsubst $(engineRenderBuildDir)/%,%, $(call rwildcard,$(engineRenderBuildDir),*.spv)) diff --git a/examples/render/assets/models/cube_anim.sk b/examples/render/assets/models/cube_anim.sk index 56dc8121..bfcf0fa1 100644 --- a/examples/render/assets/models/cube_anim.sk +++ b/examples/render/assets/models/cube_anim.sk @@ -1,3 +1,2 @@ SOURCE_PATH:assets/models/cube_anim.glb; -MATERIAL_PATH:assets/models/cube.mat; -NODE_PATH:/; \ No newline at end of file +MATERIAL_PATH:assets/models/cube.mat; \ No newline at end of file diff --git a/packer/src/types/SkeletalMeshDataPacker.cpp b/packer/src/types/SkeletalMeshDataPacker.cpp index b34ded4c..b1baeb3e 100644 --- a/packer/src/types/SkeletalMeshDataPacker.cpp +++ b/packer/src/types/SkeletalMeshDataPacker.cpp @@ -21,15 +21,29 @@ #include "StaticMeshDataPacker.h" +static inline Siege::Mat4 ConvertMatrix(const aiMatrix4x4& from) +{ + Siege::Mat4 to; + //the a,b,c,d in assimp is the row ; the 1,2,3,4 is the column + to[0][0] = from.a1; to[1][0] = from.a2; to[2][0] = from.a3; to[3][0] = from.a4; + to[0][1] = from.b1; to[1][1] = from.b2; to[2][1] = from.b3; to[3][1] = from.b4; + to[0][2] = from.c1; to[1][2] = from.c2; to[2][2] = from.c3; to[3][2] = from.c4; + to[0][3] = from.d1; to[1][3] = from.d2; to[2][3] = from.d3; to[3][3] = from.d4; + return to; +} + static void GetMeshData(const aiScene* scene, aiMesh* mesh, aiMatrix4x4t matrix, OUT std::vector& vertices, - OUT std::vector& indices) + OUT std::vector& indices, + OUT std::map& bones) { for (uint32_t i = 0; i < mesh->mNumVertices; i++) { Siege::SkinnedVertex vertex {}; + vertex.bones = {-1, -1, -1, -1}; + vertex.weights = {0.f, 0.f, 0.f, 0.f }; aiVector3t vert = mesh->mVertices[i]; vert *= matrix; @@ -50,59 +64,84 @@ static void GetMeshData(const aiScene* scene, vertices.push_back(vertex); } - auto it = std::max_element(indices.begin(), indices.end()); - uint32_t indexOffset = it != indices.end() ? *it + 1 : 0; for (uint32_t i = 0; i < mesh->mNumFaces; i++) { aiFace face = mesh->mFaces[i]; for (uint32_t j = 0; j < face.mNumIndices; j++) { - indices.push_back(indexOffset + face.mIndices[j]); + indices.push_back(face.mIndices[j]); + } + } + + for (uint32_t i = 0; i < mesh->mNumBones; i++) + { + aiBone* currentBone = mesh->mBones[i]; + + uint32_t boneId; + Siege::String boneName(currentBone->mName.C_Str()); + + auto it = bones.find(boneName); + if (it != bones.end()) + { + boneId = it->second.id; + } + else + { + boneId = bones.size(); + + Siege::Bone newBone; + newBone.id = boneId; + newBone.bindMatrix = ConvertMatrix(currentBone->mOffsetMatrix); + bones.emplace(boneName, newBone); + } + + CC_LOG_INFO("Reading bone {} with id {}", boneName, boneId) + + for (int32_t j = 0; j < currentBone->mNumWeights; ++j) + { + aiVertexWeight weight = currentBone->mWeights[j]; + Siege::SkinnedVertex vertex = vertices[weight.mVertexId]; + for (int32_t k = 0; k < 4; ++k) + { + if (vertex.bones[k] < 0) + { + vertex.bones[k] = static_cast(boneId); + vertex.weights[k] = weight.mWeight; + break; + } + } } } } -static void GetMeshesForNode(const aiScene* scene, - aiNode* node, - const Siege::String& requestPath, - Siege::String currentPath, - aiMatrix4x4t matrix, - OUT std::vector& vertices, - OUT std::vector& indices) +static aiMesh* FindMeshAtPath(const aiScene* scene, aiNode* node, const Siege::String& requestPath, Siege::String currentPath) { currentPath = currentPath + node->mName.C_Str(); - CC_LOG_INFO("Reading node at {}", currentPath) - matrix *= node->mTransformation; - for (uint32_t i = 0; i < node->mNumMeshes; i++) { aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; - Siege::String currentMeshPath = currentPath + '/' + mesh->mName.C_Str(); - CC_LOG_INFO("Reading mesh at {}", currentMeshPath) - GetMeshData(scene, mesh, matrix, vertices, indices); + + if (currentMeshPath != requestPath) continue; + return mesh; } for (uint32_t i = 0; i < node->mNumChildren; i++) { - GetMeshesForNode(scene, - node->mChildren[i], - requestPath, - currentPath + '/', - matrix, - vertices, - indices); + aiMesh* mesh = FindMeshAtPath(scene, node->mChildren[i], requestPath, currentPath + '/'); + if (mesh) return mesh; } -}; + + return nullptr; +} void* PackSkeletalMeshFile(const Siege::String& filePath, const Siege::String& assetsPath) { Siege::String contents = Siege::FileSystem::Read(filePath); - std::map attributes = - Siege::FileSystem::ParseAttributeFileData(contents); + std::map attributes = Siege::FileSystem::ParseAttributeFileData(contents); if (attributes.empty()) { @@ -129,22 +168,36 @@ void* PackSkeletalMeshFile(const Siege::String& filePath, const Siege::String& a return nullptr; } - auto nodePathIt = attributes.find(TOKEN_NODE_PATH); - Siege::String requestedNodePath = nodePathIt != attributes.end() ? nodePathIt->second : "/"; + if (!scene->HasMeshes()) + { + CC_LOG_WARNING("File at path \"{}\" contains no meshes", modelPath) + return nullptr; + } - CC_LOG_INFO("Reading skeletal mesh for file {} with node path {}", it->second, requestedNodePath) + aiMesh* mesh = nullptr; + auto nodePathIt = attributes.find(TOKEN_NODE_PATH); + if (nodePathIt != attributes.end()) + { + Siege::String requestedNodePath = nodePathIt->second; + CC_LOG_INFO("Reading skeletal mesh for file {} with path {}", it->second, requestedNodePath) + mesh = FindMeshAtPath(scene, scene->mRootNode, requestedNodePath, '/'); + if (!mesh) + { + CC_LOG_WARNING("Failed to find mesh at path \"{}\"", requestedNodePath) + return nullptr; + } + } + else + { + CC_LOG_INFO("Reading skeletal mesh for file {}", it->second) + mesh = scene->mMeshes[0]; + } std::vector vertices; std::vector indices; - GetMeshesForNode(scene, - scene->mRootNode, - requestedNodePath, - '/', - aiMatrix4x4t(), - vertices, - indices); - -// Siege::SkeletalMeshData* skeletalMeshData = Siege::SkeletalMeshData::Create(indices, vertices); -// return skeletalMeshData; - return nullptr; + std::map bones; + GetMeshData(scene, mesh, aiMatrix4x4t(), vertices, indices, bones); + + Siege::SkeletalMeshData* skeletalMeshData = Siege::SkeletalMeshData::Create(indices, vertices, bones); + return skeletalMeshData; } From de606e9ed6434e4d3042cd9ff8af45731519a6f9 Mon Sep 17 00:00:00 2001 From: Jonathan Moallem Date: Tue, 18 Feb 2025 08:02:47 +1000 Subject: [PATCH 03/10] Added animation packer and file type with anim data types --- engine/resources/AnimationData.h | 84 +++++++++++++ examples/render/Makefile | 1 + examples/render/assets/models/cube_anim.ska | 2 + packer/src/main.cpp | 7 ++ packer/src/types/AnimationDataPacker.cpp | 132 ++++++++++++++++++++ packer/src/types/AnimationDataPacker.h | 16 +++ 6 files changed, 242 insertions(+) create mode 100644 engine/resources/AnimationData.h create mode 100644 examples/render/assets/models/cube_anim.ska create mode 100644 packer/src/types/AnimationDataPacker.cpp create mode 100644 packer/src/types/AnimationDataPacker.h diff --git a/engine/resources/AnimationData.h b/engine/resources/AnimationData.h new file mode 100644 index 00000000..a619b9af --- /dev/null +++ b/engine/resources/AnimationData.h @@ -0,0 +1,84 @@ +// +// Copyright (c) 2020-present Caps Collective & contributors +// Originally authored by Jonathan Moallem (@jonjondev) & Aryeh Zinn (@Raelr) +// +// This code is released under an unmodified zlib license. +// For conditions of distribution and use, please see: +// https://opensource.org/licenses/Zlib +// + +#ifndef SIEGE_ENGINE_ANIMATIONDATA_H +#define SIEGE_ENGINE_ANIMATIONDATA_H + +#include +#include + +#include "PackFile.h" + +namespace Siege +{ + +struct AnimKeyPosition +{ + double timestamp = 0.f; + Vec3 position; +}; + +struct AnimKeyRotation +{ + double timestamp = 0.f; + Vec4 rotation; +}; + +struct AnimKeyScale +{ + double timestamp = 0.f; + Vec3 scale; +}; + +struct AnimChannel +{ + std::vector positionKeys; + std::vector rotationKeys; + std::vector scaleKeys; +}; + +struct Anim +{ + double length; + double speed; + std::map channels; +}; + +#pragma pack(push, 1) +struct AnimationData +{ + double length; + double speed; + uint64_t channelCount = 0; + char data[]; + + static AnimationData* Create(const Anim& animation) + { + uint32_t totalDataSize = sizeof(AnimationData); + void* mem = malloc(totalDataSize); + AnimationData* animationData = new (mem) AnimationData(); + + animationData->length = animation.length; + animationData->speed = animation.speed; + animationData->channelCount = animation.channels.size(); + return animationData; + } + + static uint32_t GetDataSize(void* objectData) + { + if (!objectData) return 0; + AnimationData* animationData = reinterpret_cast(objectData); + return sizeof(AnimationData) + animationData->channelCount; + } +}; +#pragma pack(pop) + +} // namespace Siege + +#endif // SIEGE_ENGINE_ANIMATIONDATA_H diff --git a/examples/render/Makefile b/examples/render/Makefile index 030b4b3d..61a06280 100644 --- a/examples/render/Makefile +++ b/examples/render/Makefile @@ -31,6 +31,7 @@ endif engineRenderBuildDir := $(binDir)/engine/render/build exampleRenderAssets := $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.sm)) exampleRenderAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.sk)) +exampleRenderAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.ska)) exampleRenderAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.png)) exampleRenderAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleRenderSrcDir),*.jpg)) exampleRenderAssets += $(patsubst $(engineRenderBuildDir)/%,%, $(call rwildcard,$(engineRenderBuildDir),*.spv)) diff --git a/examples/render/assets/models/cube_anim.ska b/examples/render/assets/models/cube_anim.ska new file mode 100644 index 00000000..f8a82500 --- /dev/null +++ b/examples/render/assets/models/cube_anim.ska @@ -0,0 +1,2 @@ +SOURCE_PATH:assets/models/cube_anim.glb; +ANIM_NAME:ArmatureAction; \ No newline at end of file diff --git a/packer/src/main.cpp b/packer/src/main.cpp index 05505481..52899e37 100644 --- a/packer/src/main.cpp +++ b/packer/src/main.cpp @@ -13,12 +13,14 @@ #include #include #include +#include #include #include #include #include +#include "types/AnimationDataPacker.h" #include "types/GenericFileDataPacker.h" #include "types/SceneDataPacker.h" #include "types/SkeletalMeshDataPacker.h" @@ -67,6 +69,11 @@ int main(int argc, char* argv[]) data = PackSkeletalMeshFile(fullPath, assetsDir); bodyDataSize = Siege::SkeletalMeshData::GetDataSize(data); } + else if (extension == ".ska") + { + data = PackAnimationFile(fullPath, assetsDir); + bodyDataSize = Siege::AnimationData::GetDataSize(data); + } else if (extension == ".jpg" || extension == ".jpeg" || extension == ".png") { data = PackTexture2DFile(fullPath); diff --git a/packer/src/types/AnimationDataPacker.cpp b/packer/src/types/AnimationDataPacker.cpp new file mode 100644 index 00000000..3964dc50 --- /dev/null +++ b/packer/src/types/AnimationDataPacker.cpp @@ -0,0 +1,132 @@ +// +// Copyright (c) 2020-present Jonathan Moallem (@J-Mo63) & Aryeh Zinn (@Raelr) +// +// This code is released under an unmodified zlib license. +// For conditions of distribution and use, please see: +// https://opensource.org/licenses/Zlib +// + +#include "AnimationDataPacker.h" + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "StaticMeshDataPacker.h" + +REGISTER_TOKEN(ANIM_NAME); + +static void GetAnimationData(aiAnimation* animation, OUT Siege::Anim& anim) +{ + anim.length = animation->mDuration; + anim.speed = animation->mTicksPerSecond; + + Siege::String animName = animation->mName.C_Str(); + CC_LOG_INFO("Reading animation \"{}\" with duration of {} seconds", animName, anim.length / anim.speed) + + for (uint32_t i = 0; i < animation->mNumChannels; i++) + { + aiNodeAnim* channel = animation->mChannels[i]; + Siege::String channelName = channel->mNodeName.C_Str(); + + CC_LOG_INFO("Reading channel with name \"{}\"", channelName) + + Siege::AnimChannel& animChannel = anim.channels[channelName]; + + for (uint32_t j = 0; j < channel->mNumPositionKeys; j++) + { + aiVectorKey key = channel->mPositionKeys[j]; + Siege::AnimKeyPosition& positionKey = animChannel.positionKeys.emplace_back(); + positionKey.timestamp = key.mTime; + positionKey.position = {key.mValue.x, key.mValue.y, key.mValue.z}; + } + + for (uint32_t j = 0; j < channel->mNumRotationKeys; j++) + { + aiQuatKey key = channel->mRotationKeys[j]; + Siege::AnimKeyRotation& rotationKey = animChannel.rotationKeys.emplace_back(); + rotationKey.timestamp = key.mTime; + rotationKey.rotation = {key.mValue.x, key.mValue.y, key.mValue.z, key.mValue.w}; + } + + for (uint32_t j = 0; j < channel->mNumScalingKeys; j++) + { + aiVectorKey key = channel->mScalingKeys[j]; + Siege::AnimKeyScale& scaleKey = animChannel.scaleKeys.emplace_back(); + scaleKey.timestamp = key.mTime; + scaleKey.scale = {key.mValue.x, key.mValue.y, key.mValue.z}; + } + } +} + +void* PackAnimationFile(const Siege::String& filePath, const Siege::String& assetsPath) +{ + Siege::String contents = Siege::FileSystem::Read(filePath); + std::map attributes = Siege::FileSystem::ParseAttributeFileData(contents); + + if (attributes.empty()) + { + CC_LOG_WARNING("Received empty animation file at path \"{}\"", filePath) + return nullptr; + } + + auto it = attributes.find(TOKEN_SOURCE_PATH); + if (it == attributes.end()) + { + CC_LOG_WARNING("Failed to find SOURCE_PATH attribute in .ska file at path \"{}\"", filePath) + return nullptr; + } + + auto nameIt = attributes.find(TOKEN_ANIM_NAME); + if (nameIt == attributes.end()) + { + CC_LOG_WARNING("Failed to find NAME attribute in .ska file at path \"{}\"", filePath) + } + + Siege::String modelPath = assetsPath + '/' + it->second; + Assimp::Importer importer; + const aiScene* scene = + importer.ReadFile(modelPath, + aiProcess_CalcTangentSpace | aiProcess_Triangulate | + aiProcess_JoinIdenticalVertices | aiProcess_SortByPType); + if (!scene) + { + CC_LOG_WARNING("Failed to read file at path \"{}\"", modelPath) + return nullptr; + } + + if (!scene->HasAnimations()) + { + CC_LOG_WARNING("File at path \"{}\" contains no animations", modelPath) + return nullptr; + } + + aiAnimation* animation = nullptr; + for (uint32_t i = 0; i < scene->mNumAnimations; i++) + { + if (nameIt->second == scene->mAnimations[i]->mName.C_Str()) + { + animation = scene->mAnimations[i]; + break; + } + } + + if (!animation) + { + CC_LOG_WARNING("Failed to find animation with name \"{}\"", nameIt->second) + return nullptr; + } + + Siege::Anim anim; + GetAnimationData(animation, anim); + + Siege::AnimationData* animationData = Siege::AnimationData::Create(anim); + return animationData; +} diff --git a/packer/src/types/AnimationDataPacker.h b/packer/src/types/AnimationDataPacker.h new file mode 100644 index 00000000..af42398c --- /dev/null +++ b/packer/src/types/AnimationDataPacker.h @@ -0,0 +1,16 @@ +// +// Copyright (c) 2020-present Jonathan Moallem (@J-Mo63) & Aryeh Zinn (@Raelr) +// +// This code is released under an unmodified zlib license. +// For conditions of distribution and use, please see: +// https://opensource.org/licenses/Zlib +// + +#ifndef SIEGE_ENGINE_ANIMATIONDATAPACKER_H +#define SIEGE_ENGINE_ANIMATIONDATAPACKER_H + +#include + +void* PackAnimationFile(const Siege::String& filePath, const Siege::String& assetsPath); + +#endif // SIEGE_ENGINE_ANIMATIONDATAPACKER_H From 647fc968e63d6bbc03ffbef4a04cceadbcf7c7c6 Mon Sep 17 00:00:00 2001 From: Jonathan Moallem Date: Mon, 4 Aug 2025 18:18:47 +1000 Subject: [PATCH 04/10] Added animation channel data for animation resources --- engine/resources/AnimationData.h | 83 ++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 3 deletions(-) diff --git a/engine/resources/AnimationData.h b/engine/resources/AnimationData.h index a619b9af..3ce7c344 100644 --- a/engine/resources/AnimationData.h +++ b/engine/resources/AnimationData.h @@ -51,22 +51,99 @@ struct Anim }; #pragma pack(push, 1) +struct AnimationChannelData +{ + uint64_t positionKeysCount = 0; + uint64_t rotationKeysCount = 0; + uint64_t scaleKeysCount = 0; +}; + struct AnimationData { double length; double speed; - uint64_t channelCount = 0; + uint32_t channelCount = 0; char data[]; + bool GetAnim(Anim& anim) const + { + anim.length = length; + anim.speed = speed; + + uint32_t keysStartOffset = sizeof(AnimationChannelData) * channelCount; + + std::vector channels; + for (uint32_t i = 0; i < channelCount; ++i) + { + AnimationChannelData* channelData = reinterpret_cast(data[0] + sizeof(AnimationChannelData) * i); +// AnimKeyPosition* positions = reinterpret_cast(data[0] + keysStartOffset + sizeof(AnimKeyPosition) * j); +// channels.push_back(channelData); +// channelData. + } + + return true; + } + static AnimationData* Create(const Anim& animation) { - uint32_t totalDataSize = sizeof(AnimationData); + std::vector channels; + std::vector channelNames; + size_t channelNamesLength = 0; + for (const auto& it : animation.channels) + { + channels.push_back(it.second); + + channelNames.push_back(it.first); + channelNamesLength += it.first.Size() + 1; + } + + char channelNamesArr[channelNamesLength]; + size_t currentChannelNamesOffset = 0; + for (const Siege::String& name : channelNames) + { + memcpy(&channelNamesArr[0] + currentChannelNamesOffset, name.Str(), name.Size() + 1); + currentChannelNamesOffset += name.Size() + 1; + } + + uint32_t keysDataSize = 0; + std::vector channelDataEntries; + for (const AnimChannel& channel : channels) + { + AnimationChannelData channelData; + channelData.positionKeysCount = channel.positionKeys.size(); + keysDataSize += sizeof(AnimKeyPosition) * channel.positionKeys.size(); + channelData.rotationKeysCount = channel.rotationKeys.size(); + keysDataSize += sizeof(AnimKeyRotation) * channel.rotationKeys.size(); + channelData.scaleKeysCount = channel.scaleKeys.size(); + keysDataSize += sizeof(AnimKeyScale) * channel.scaleKeys.size(); + channelDataEntries.push_back(channelData); + } + uint32_t channelsDataSize = sizeof(AnimationChannelData) * channelDataEntries.size(); + uint32_t totalDataSize = sizeof(AnimationData) + channelsDataSize + keysDataSize + channelNamesLength; + void* mem = malloc(totalDataSize); AnimationData* animationData = new (mem) AnimationData(); animationData->length = animation.length; animationData->speed = animation.speed; animationData->channelCount = animation.channels.size(); + memcpy(&animationData->data[0], channelDataEntries.data(), channelsDataSize); + + uint32_t keysWriteOffset = channelsDataSize; + for (const AnimChannel& channel : channels) + { + uint32_t positionKeysDataSize = sizeof(AnimKeyPosition) * channel.positionKeys.size(); + uint32_t rotationKeysDataSize = sizeof(AnimKeyRotation) * channel.rotationKeys.size(); + uint32_t scaleKeysDataSize = sizeof(AnimKeyScale) * channel.scaleKeys.size(); + uint32_t channelDataSize = sizeof(AnimationChannelData) + positionKeysDataSize + rotationKeysDataSize + scaleKeysDataSize; + + memcpy(&animationData->data[0] + keysWriteOffset, channel.positionKeys.data(), positionKeysDataSize); + memcpy(&animationData->data[0] + keysWriteOffset + positionKeysDataSize, channel.rotationKeys.data(), rotationKeysDataSize); + memcpy(&animationData->data[0] + keysWriteOffset + positionKeysDataSize + rotationKeysDataSize, channel.scaleKeys.data(), scaleKeysDataSize); + keysWriteOffset += channelDataSize + 1; + } + memcpy(&animationData->data[0] + keysWriteOffset, channelNamesArr, channelNamesLength); + return animationData; } @@ -74,7 +151,7 @@ struct AnimationData { if (!objectData) return 0; AnimationData* animationData = reinterpret_cast(objectData); - return sizeof(AnimationData) + animationData->channelCount; + return sizeof(AnimationData); } }; #pragma pack(pop) From 03d5f83c44bf4698d2064f2e858dcea5e07b5b6a Mon Sep 17 00:00:00 2001 From: Jonathan Moallem Date: Thu, 7 Aug 2025 22:36:24 +1000 Subject: [PATCH 05/10] Refactored skeletal mesh and animation data to support binary serialisation --- engine/resources/AnimationData.h | 141 +++++--------------- engine/resources/PackFile.h | 1 + engine/resources/SkeletalMeshData.h | 105 ++++----------- engine/resources/StaticMeshData.h | 18 +-- engine/utils/BinarySerialisation.h | 24 ++++ packer/src/main.cpp | 15 +-- packer/src/types/AnimationDataPacker.cpp | 20 ++- packer/src/types/AnimationDataPacker.h | 2 +- packer/src/types/SkeletalMeshDataPacker.cpp | 94 ++++++++----- packer/src/types/SkeletalMeshDataPacker.h | 2 +- 10 files changed, 175 insertions(+), 247 deletions(-) diff --git a/engine/resources/AnimationData.h b/engine/resources/AnimationData.h index 3ce7c344..3cbe15ec 100644 --- a/engine/resources/AnimationData.h +++ b/engine/resources/AnimationData.h @@ -36,125 +36,56 @@ struct AnimKeyScale Vec3 scale; }; -struct AnimChannel +struct AnimationChannel { std::vector positionKeys; std::vector rotationKeys; std::vector scaleKeys; }; -struct Anim +struct AnimationData { double length; double speed; - std::map channels; + std::map channels; }; -#pragma pack(push, 1) -struct AnimationChannelData +namespace BinarySerialisation { - uint64_t positionKeysCount = 0; - uint64_t rotationKeysCount = 0; - uint64_t scaleKeysCount = 0; -}; -struct AnimationData +inline void serialise(Buffer& buffer, AnimKeyPosition& value, SerialisationMode mode) { - double length; - double speed; - uint32_t channelCount = 0; - char data[]; - - bool GetAnim(Anim& anim) const - { - anim.length = length; - anim.speed = speed; - - uint32_t keysStartOffset = sizeof(AnimationChannelData) * channelCount; - - std::vector channels; - for (uint32_t i = 0; i < channelCount; ++i) - { - AnimationChannelData* channelData = reinterpret_cast(data[0] + sizeof(AnimationChannelData) * i); -// AnimKeyPosition* positions = reinterpret_cast(data[0] + keysStartOffset + sizeof(AnimKeyPosition) * j); -// channels.push_back(channelData); -// channelData. - } - - return true; - } - - static AnimationData* Create(const Anim& animation) - { - std::vector channels; - std::vector channelNames; - size_t channelNamesLength = 0; - for (const auto& it : animation.channels) - { - channels.push_back(it.second); - - channelNames.push_back(it.first); - channelNamesLength += it.first.Size() + 1; - } - - char channelNamesArr[channelNamesLength]; - size_t currentChannelNamesOffset = 0; - for (const Siege::String& name : channelNames) - { - memcpy(&channelNamesArr[0] + currentChannelNamesOffset, name.Str(), name.Size() + 1); - currentChannelNamesOffset += name.Size() + 1; - } - - uint32_t keysDataSize = 0; - std::vector channelDataEntries; - for (const AnimChannel& channel : channels) - { - AnimationChannelData channelData; - channelData.positionKeysCount = channel.positionKeys.size(); - keysDataSize += sizeof(AnimKeyPosition) * channel.positionKeys.size(); - channelData.rotationKeysCount = channel.rotationKeys.size(); - keysDataSize += sizeof(AnimKeyRotation) * channel.rotationKeys.size(); - channelData.scaleKeysCount = channel.scaleKeys.size(); - keysDataSize += sizeof(AnimKeyScale) * channel.scaleKeys.size(); - channelDataEntries.push_back(channelData); - } - uint32_t channelsDataSize = sizeof(AnimationChannelData) * channelDataEntries.size(); - uint32_t totalDataSize = sizeof(AnimationData) + channelsDataSize + keysDataSize + channelNamesLength; - - void* mem = malloc(totalDataSize); - AnimationData* animationData = new (mem) AnimationData(); - - animationData->length = animation.length; - animationData->speed = animation.speed; - animationData->channelCount = animation.channels.size(); - memcpy(&animationData->data[0], channelDataEntries.data(), channelsDataSize); - - uint32_t keysWriteOffset = channelsDataSize; - for (const AnimChannel& channel : channels) - { - uint32_t positionKeysDataSize = sizeof(AnimKeyPosition) * channel.positionKeys.size(); - uint32_t rotationKeysDataSize = sizeof(AnimKeyRotation) * channel.rotationKeys.size(); - uint32_t scaleKeysDataSize = sizeof(AnimKeyScale) * channel.scaleKeys.size(); - uint32_t channelDataSize = sizeof(AnimationChannelData) + positionKeysDataSize + rotationKeysDataSize + scaleKeysDataSize; - - memcpy(&animationData->data[0] + keysWriteOffset, channel.positionKeys.data(), positionKeysDataSize); - memcpy(&animationData->data[0] + keysWriteOffset + positionKeysDataSize, channel.rotationKeys.data(), rotationKeysDataSize); - memcpy(&animationData->data[0] + keysWriteOffset + positionKeysDataSize + rotationKeysDataSize, channel.scaleKeys.data(), scaleKeysDataSize); - keysWriteOffset += channelDataSize + 1; - } - memcpy(&animationData->data[0] + keysWriteOffset, channelNamesArr, channelNamesLength); - - return animationData; - } - - static uint32_t GetDataSize(void* objectData) - { - if (!objectData) return 0; - AnimationData* animationData = reinterpret_cast(objectData); - return sizeof(AnimationData); - } -}; -#pragma pack(pop) + serialise(buffer, value.timestamp, mode); + serialise(buffer, value.position, mode); +} + +inline void serialise(Buffer& buffer, AnimKeyRotation& value, SerialisationMode mode) +{ + serialise(buffer, value.timestamp, mode); + serialise(buffer, value.rotation, mode); +} + +inline void serialise(Buffer& buffer, AnimKeyScale& value, SerialisationMode mode) +{ + serialise(buffer, value.timestamp, mode); + serialise(buffer, value.scale, mode); +} + +inline void serialise(Buffer& buffer, AnimationChannel& value, SerialisationMode mode) +{ + serialise(buffer, value.positionKeys, mode); + serialise(buffer, value.rotationKeys, mode); + serialise(buffer, value.scaleKeys, mode); +} + +inline void serialise(Buffer& buffer, AnimationData& value, SerialisationMode mode) +{ + serialise(buffer, value.length, mode); + serialise(buffer, value.speed, mode); + serialise(buffer, value.channels, mode); +} + +} // namespace BinarySerialisation } // namespace Siege diff --git a/engine/resources/PackFile.h b/engine/resources/PackFile.h index 2772f400..d64ae200 100644 --- a/engine/resources/PackFile.h +++ b/engine/resources/PackFile.h @@ -21,6 +21,7 @@ #include "PackFileData.h" #include "SceneData.h" #include "StaticMeshData.h" +#include "SkeletalMeshData.h" #include "Texture2DData.h" #define PACKER_MAGIC_NUMBER_FILE "pck" diff --git a/engine/resources/SkeletalMeshData.h b/engine/resources/SkeletalMeshData.h index bbc0228a..f93e775a 100644 --- a/engine/resources/SkeletalMeshData.h +++ b/engine/resources/SkeletalMeshData.h @@ -23,11 +23,11 @@ namespace Siege { struct Bone { - uint32_t id; + uint32_t id = 0; Mat4 bindMatrix; }; -struct SkinnedVertex : public BaseVertex +struct SkinnedVertex : BaseVertex { Vec4 bones; Vec4 weights; @@ -43,88 +43,39 @@ inline bool operator!=(const SkinnedVertex& left, const SkinnedVertex& right) return !(left == right); } -#pragma pack(push, 1) struct SkeletalMeshData { - uint64_t indicesCount = 0; - uint64_t verticesCount = 0; - uint32_t bonesCount = 0; - uint32_t boneNamesLength = 0; - char data[]; + std::vector indices; + std::vector vertices; + std::map bones; +}; - uint32_t* GetIndices() const - { - return (uint32_t*) data; - } +namespace BinarySerialisation +{ - SkinnedVertex* GetVertices() const - { - return (SkinnedVertex*) (data + sizeof(uint32_t) * indicesCount); - } +inline void serialise(Buffer& buffer, Bone& value, SerialisationMode mode) +{ + serialise(buffer, value.id, mode); + serialise(buffer, value.bindMatrix, mode); +} - Bone* GetBones() const - { - return (Bone*) (data + (sizeof(uint32_t) * indicesCount + sizeof(SkinnedVertex) * verticesCount)); - } +inline void serialise(Buffer& buffer, SkinnedVertex& value, SerialisationMode mode) +{ + serialise(buffer, value.color, mode); + serialise(buffer, value.normal, mode); + serialise(buffer, value.uv, mode); + serialise(buffer, value.bones, mode); + serialise(buffer, value.weights, mode); +} - char* GetBoneNames() const - { - return (char*) (data + (sizeof(uint32_t) * indicesCount + sizeof(SkinnedVertex) * verticesCount) + sizeof(Bone) * bonesCount); - } +inline void serialise(Buffer& buffer, SkeletalMeshData& value, SerialisationMode mode) +{ + serialise(buffer, value.indices, mode); + serialise(buffer, value.vertices, mode); + serialise(buffer, value.bones, mode); +} - static SkeletalMeshData* Create(const std::vector& objIndices, - const std::vector& objVertices, - const std::map& objBones) - { - std::vector bones; - std::vector boneNames; - size_t boneNamesLength = 0; - for (const auto& it : objBones) - { - bones.push_back(it.second); - boneNames.push_back(it.first); - boneNamesLength += it.first.Size() + 1; - } - - char boneNamesArr[boneNamesLength]; - size_t currentBoneNamesOffset = 0; - for (const Siege::String& name : boneNames) - { - memcpy(&boneNamesArr[0] + currentBoneNamesOffset, name.Str(), name.Size() + 1); - currentBoneNamesOffset += name.Size() + 1; - } - - uint32_t indicesDataSize = sizeof(uint32_t) * objIndices.size(); - uint32_t verticesDataSize = sizeof(SkinnedVertex) * objVertices.size(); - uint32_t bonesDataSize = sizeof(Bone) * objBones.size(); - uint32_t totalDataSize = sizeof(SkeletalMeshData) + indicesDataSize + verticesDataSize + bonesDataSize + boneNamesLength; - - void* mem = malloc(totalDataSize); - SkeletalMeshData* skeletalMeshData = new (mem) SkeletalMeshData(); - - skeletalMeshData->indicesCount = objIndices.size(); - skeletalMeshData->verticesCount = objVertices.size(); - skeletalMeshData->bonesCount = objBones.size(); - skeletalMeshData->boneNamesLength = boneNamesLength; - memcpy(&skeletalMeshData->data[0], objIndices.data(), indicesDataSize); - memcpy(&skeletalMeshData->data[0] + indicesDataSize, objVertices.data(), verticesDataSize); - - memcpy(&skeletalMeshData->data[0] + indicesDataSize + verticesDataSize, bones.data(), bonesDataSize); - memcpy(&skeletalMeshData->data[0] + indicesDataSize + verticesDataSize + bonesDataSize, boneNamesArr, boneNamesLength); - - return skeletalMeshData; - } - - static uint32_t GetDataSize(void* objectData) - { - if (!objectData) return 0; - SkeletalMeshData* skeletalMeshData = reinterpret_cast(objectData); - return sizeof(SkeletalMeshData) + sizeof(uint32_t) * skeletalMeshData->indicesCount + - sizeof(SkinnedVertex) * skeletalMeshData->verticesCount + - sizeof(Bone) * skeletalMeshData->bonesCount + skeletalMeshData->boneNamesLength; - } -}; -#pragma pack(pop) +} // namespace BinarySerialisation } // namespace Siege diff --git a/engine/resources/StaticMeshData.h b/engine/resources/StaticMeshData.h index 1201e3b0..1650597c 100644 --- a/engine/resources/StaticMeshData.h +++ b/engine/resources/StaticMeshData.h @@ -40,6 +40,15 @@ inline bool operator!=(const BaseVertex& left, const BaseVertex& right) return !(left == right); } +struct StaticMeshData +{ + std::vector indices; + std::vector vertices; +}; + +namespace BinarySerialisation +{ + inline void serialise(BinarySerialisation::Buffer& buffer, BaseVertex& value, BinarySerialisation::SerialisationMode mode) @@ -50,15 +59,6 @@ inline void serialise(BinarySerialisation::Buffer& buffer, serialise(buffer, value.uv, mode); } -struct StaticMeshData -{ - std::vector indices; - std::vector vertices; -}; - -namespace BinarySerialisation -{ - inline void serialise(Buffer& buffer, StaticMeshData& value, SerialisationMode mode) { serialise(buffer, value.indices, mode); diff --git a/engine/utils/BinarySerialisation.h b/engine/utils/BinarySerialisation.h index 91f65979..c78f20d9 100644 --- a/engine/utils/BinarySerialisation.h +++ b/engine/utils/BinarySerialisation.h @@ -16,9 +16,12 @@ #include "Colour.h" #include "String.h" +#include "math/mat/Mat.h" +#include "math/mat/Mat4.h" #include "math/vec/Vec.h" #include "math/vec/Vec2.h" #include "math/vec/Vec3.h" +#include "math/vec/Vec4.h" namespace Siege::BinarySerialisation { @@ -123,6 +126,14 @@ inline void serialise(Buffer& buffer, Vec3& value, SerialisationMode mode) serialiseNative(buffer, value.z, mode); } +inline void serialise(Buffer& buffer, Vec4& value, SerialisationMode mode) +{ + serialiseNative(buffer, value.x, mode); + serialiseNative(buffer, value.y, mode); + serialiseNative(buffer, value.z, mode); + serialiseNative(buffer, value.w, mode); +} + inline void serialise(Buffer& buffer, FColour& value, SerialisationMode mode) { serialiseNative(buffer, value.r, mode); @@ -131,6 +142,14 @@ inline void serialise(Buffer& buffer, FColour& value, SerialisationMode mode) serialiseNative(buffer, value.a, mode); } +inline void serialise(Buffer& buffer, Mat4& value, SerialisationMode mode) +{ + serialise(buffer, value.values[0], mode); + serialise(buffer, value.values[1], mode); + serialise(buffer, value.values[2], mode); + serialise(buffer, value.values[3], mode); +} + inline void serialise(Buffer& buffer, String& value, SerialisationMode mode) { switch (mode) @@ -161,6 +180,11 @@ inline void serialise(Buffer& buffer, String& value, SerialisationMode mode) } } +inline void serialise(Buffer& buffer, const String& value, SerialisationMode mode) +{ + serialise(buffer, const_cast(value), mode); +} + template void serialise(Buffer& buffer, std::vector& value, SerialisationMode mode) { diff --git a/packer/src/main.cpp b/packer/src/main.cpp index 52899e37..da5ab07e 100644 --- a/packer/src/main.cpp +++ b/packer/src/main.cpp @@ -10,11 +10,6 @@ #include #include #include -#include -#include -#include -#include -#include #include #include @@ -67,25 +62,23 @@ int main(int argc, char* argv[]) else if (extension == ".sk") { data = PackSkeletalMeshFile(fullPath, assetsDir); - bodyDataSize = Siege::SkeletalMeshData::GetDataSize(data); } else if (extension == ".ska") { data = PackAnimationFile(fullPath, assetsDir); - bodyDataSize = Siege::AnimationData::GetDataSize(data); } else if (extension == ".jpg" || extension == ".jpeg" || extension == ".png") { data = PackTexture2DFile(fullPath); } - else if (extension == ".spv" || extension == ".ttf") - { - data = PackGenericFile(fullPath); - } else if (extension == ".scene") { data = PackSceneFile(fullPath); } + else if (extension == ".spv" || extension == ".ttf") + { + data = PackGenericFile(fullPath); + } if (!data) { diff --git a/packer/src/types/AnimationDataPacker.cpp b/packer/src/types/AnimationDataPacker.cpp index 3964dc50..333ddb96 100644 --- a/packer/src/types/AnimationDataPacker.cpp +++ b/packer/src/types/AnimationDataPacker.cpp @@ -23,7 +23,7 @@ REGISTER_TOKEN(ANIM_NAME); -static void GetAnimationData(aiAnimation* animation, OUT Siege::Anim& anim) +static void GetAnimationData(aiAnimation* animation, OUT Siege::AnimationData& anim) { anim.length = animation->mDuration; anim.speed = animation->mTicksPerSecond; @@ -38,7 +38,7 @@ static void GetAnimationData(aiAnimation* animation, OUT Siege::Anim& anim) CC_LOG_INFO("Reading channel with name \"{}\"", channelName) - Siege::AnimChannel& animChannel = anim.channels[channelName]; + Siege::AnimationChannel& animChannel = anim.channels[channelName]; for (uint32_t j = 0; j < channel->mNumPositionKeys; j++) { @@ -66,7 +66,7 @@ static void GetAnimationData(aiAnimation* animation, OUT Siege::Anim& anim) } } -void* PackAnimationFile(const Siege::String& filePath, const Siege::String& assetsPath) +Siege::PackFileData* PackAnimationFile(const Siege::String& filePath, const Siege::String& assetsPath) { Siege::String contents = Siege::FileSystem::Read(filePath); std::map attributes = Siege::FileSystem::ParseAttributeFileData(contents); @@ -124,9 +124,15 @@ void* PackAnimationFile(const Siege::String& filePath, const Siege::String& asse return nullptr; } - Siege::Anim anim; - GetAnimationData(animation, anim); + Siege::AnimationData animData; + GetAnimationData(animation, animData); - Siege::AnimationData* animationData = Siege::AnimationData::Create(anim); - return animationData; + Siege::BinarySerialisation::Buffer dataBuffer; + Siege::BinarySerialisation::serialise(dataBuffer, + animData, + Siege::BinarySerialisation::SERIALISE); + + char* data = reinterpret_cast(dataBuffer.data.data()); + Siege::PackFileData* fileData = Siege::PackFileData::Create(data, dataBuffer.data.size()); + return fileData; } diff --git a/packer/src/types/AnimationDataPacker.h b/packer/src/types/AnimationDataPacker.h index af42398c..2c012108 100644 --- a/packer/src/types/AnimationDataPacker.h +++ b/packer/src/types/AnimationDataPacker.h @@ -11,6 +11,6 @@ #include -void* PackAnimationFile(const Siege::String& filePath, const Siege::String& assetsPath); +Siege::PackFileData* PackAnimationFile(const Siege::String& filePath, const Siege::String& assetsPath); #endif // SIEGE_ENGINE_ANIMATIONDATAPACKER_H diff --git a/packer/src/types/SkeletalMeshDataPacker.cpp b/packer/src/types/SkeletalMeshDataPacker.cpp index b1baeb3e..7d7485fd 100644 --- a/packer/src/types/SkeletalMeshDataPacker.cpp +++ b/packer/src/types/SkeletalMeshDataPacker.cpp @@ -8,36 +8,23 @@ #include "SkeletalMeshDataPacker.h" +#include +#include #include #include #include -#include -#include -#include - #include +#include #include +#include "../PackerUtils.h" #include "StaticMeshDataPacker.h" -static inline Siege::Mat4 ConvertMatrix(const aiMatrix4x4& from) -{ - Siege::Mat4 to; - //the a,b,c,d in assimp is the row ; the 1,2,3,4 is the column - to[0][0] = from.a1; to[1][0] = from.a2; to[2][0] = from.a3; to[3][0] = from.a4; - to[0][1] = from.b1; to[1][1] = from.b2; to[2][1] = from.b3; to[3][1] = from.b4; - to[0][2] = from.c1; to[1][2] = from.c2; to[2][2] = from.c3; to[3][2] = from.c4; - to[0][3] = from.d1; to[1][3] = from.d2; to[2][3] = from.d3; to[3][3] = from.d4; - return to; -} - static void GetMeshData(const aiScene* scene, aiMesh* mesh, - aiMatrix4x4t matrix, - OUT std::vector& vertices, - OUT std::vector& indices, - OUT std::map& bones) + Siege::Mat4 mat, + OUT Siege::SkeletalMeshData& meshData) { for (uint32_t i = 0; i < mesh->mNumVertices; i++) { @@ -46,8 +33,7 @@ static void GetMeshData(const aiScene* scene, vertex.weights = {0.f, 0.f, 0.f, 0.f }; aiVector3t vert = mesh->mVertices[i]; - vert *= matrix; - vertex.position = {vert.x, vert.y, vert.z}; + vertex.position = mat * Siege::Vec4(vert.x, vert.y, vert.z, 1.f); aiVector3t norm = mesh->mNormals[i]; vertex.normal = {norm.x, norm.y, norm.z}; @@ -61,7 +47,7 @@ static void GetMeshData(const aiScene* scene, vertex.uv = {uv->x, uv->y}; } - vertices.push_back(vertex); + meshData.vertices.push_back(vertex); } for (uint32_t i = 0; i < mesh->mNumFaces; i++) @@ -69,7 +55,7 @@ static void GetMeshData(const aiScene* scene, aiFace face = mesh->mFaces[i]; for (uint32_t j = 0; j < face.mNumIndices; j++) { - indices.push_back(face.mIndices[j]); + meshData.indices.push_back(face.mIndices[j]); } } @@ -80,19 +66,19 @@ static void GetMeshData(const aiScene* scene, uint32_t boneId; Siege::String boneName(currentBone->mName.C_Str()); - auto it = bones.find(boneName); - if (it != bones.end()) + auto it = meshData.bones.find(boneName); + if (it != meshData.bones.end()) { boneId = it->second.id; } else { - boneId = bones.size(); + boneId = meshData.bones.size(); Siege::Bone newBone; newBone.id = boneId; - newBone.bindMatrix = ConvertMatrix(currentBone->mOffsetMatrix); - bones.emplace(boneName, newBone); + newBone.bindMatrix = AssimpMat4ToMat4(currentBone->mOffsetMatrix); + meshData.bones.emplace(boneName, newBone); } CC_LOG_INFO("Reading bone {} with id {}", boneName, boneId) @@ -100,7 +86,7 @@ static void GetMeshData(const aiScene* scene, for (int32_t j = 0; j < currentBone->mNumWeights; ++j) { aiVertexWeight weight = currentBone->mWeights[j]; - Siege::SkinnedVertex vertex = vertices[weight.mVertexId]; + Siege::SkinnedVertex vertex = meshData.vertices[weight.mVertexId]; for (int32_t k = 0; k < 4; ++k) { if (vertex.bones[k] < 0) @@ -138,7 +124,7 @@ static aiMesh* FindMeshAtPath(const aiScene* scene, aiNode* node, const Siege::S return nullptr; } -void* PackSkeletalMeshFile(const Siege::String& filePath, const Siege::String& assetsPath) +Siege::PackFileData* PackSkeletalMeshFile(const Siege::String& filePath, const Siege::String& assetsPath) { Siege::String contents = Siege::FileSystem::Read(filePath); std::map attributes = Siege::FileSystem::ParseAttributeFileData(contents); @@ -193,11 +179,47 @@ void* PackSkeletalMeshFile(const Siege::String& filePath, const Siege::String& a mesh = scene->mMeshes[0]; } - std::vector vertices; - std::vector indices; - std::map bones; - GetMeshData(scene, mesh, aiMatrix4x4t(), vertices, indices, bones); + Siege::Mat4 baseXform = Siege::Mat4::Identity(); + auto flipAxesIt = attributes.find(TOKEN_FLIP_AXES); + if (flipAxesIt != attributes.end()) + { + if (flipAxesIt->second.Size() < 1 || flipAxesIt->second.Size() > 3) + { + CC_LOG_WARNING("FLIP_AXES attribute in .sm file contains invalid number of axes") + } + + for (int32_t i = 0; i < flipAxesIt->second.Size(); ++i) + { + Siege::Vec4 scale = {1.f, 1.f, 1.f, 1.f}; + switch (flipAxesIt->second[i]) + { + case 'x': + scale.x *= -1; + break; + case 'y': + scale.y *= -1; + break; + case 'z': + scale.z *= -1; + break; + default: + CC_LOG_WARNING("Found invalid axis \"{}\" in FLIP_AXES attribute, ignoring", + flipAxesIt->second[i]) + break; + } + baseXform *= Siege::Mat4::Scale(scale); + } + } + + Siege::SkeletalMeshData skeletalMeshData; + GetMeshData(scene, mesh, baseXform, skeletalMeshData); + + Siege::BinarySerialisation::Buffer dataBuffer; + Siege::BinarySerialisation::serialise(dataBuffer, + skeletalMeshData, + Siege::BinarySerialisation::SERIALISE); - Siege::SkeletalMeshData* skeletalMeshData = Siege::SkeletalMeshData::Create(indices, vertices, bones); - return skeletalMeshData; + char* data = reinterpret_cast(dataBuffer.data.data()); + Siege::PackFileData* fileData = Siege::PackFileData::Create(data, dataBuffer.data.size()); + return fileData; } diff --git a/packer/src/types/SkeletalMeshDataPacker.h b/packer/src/types/SkeletalMeshDataPacker.h index a1eeb8fa..70cdbd71 100644 --- a/packer/src/types/SkeletalMeshDataPacker.h +++ b/packer/src/types/SkeletalMeshDataPacker.h @@ -11,6 +11,6 @@ #include -void* PackSkeletalMeshFile(const Siege::String& filePath, const Siege::String& assetsPath); +Siege::PackFileData* PackSkeletalMeshFile(const Siege::String& filePath, const Siege::String& assetsPath); #endif // SIEGE_ENGINE_SKELETALMESHDATAPACKER_H From dd9c330353720e6a77a460413d88a46b5ffaab72 Mon Sep 17 00:00:00 2001 From: Jonathan Moallem Date: Thu, 7 Aug 2025 22:37:13 +1000 Subject: [PATCH 06/10] Added logging and string conversion methods for float and integer colours --- engine/utils/Colour.h | 23 +++++++++++++++++++++++ engine/utils/Logging.h | 3 +++ 2 files changed, 26 insertions(+) diff --git a/engine/utils/Colour.h b/engine/utils/Colour.h index c460ce39..ea6ec329 100644 --- a/engine/utils/Colour.h +++ b/engine/utils/Colour.h @@ -14,6 +14,7 @@ #include #include "Hash.h" +#include "String.h" namespace Siege { @@ -236,6 +237,28 @@ inline constexpr FColour ToIColour(const FColour& other) static_cast(other.a * 255)}; } +/** + * Converts an integer colour to a string + * @param colour the colour to convert to a string + * @return a string representation of the colour + */ +inline String ToString(const IColour& colour) +{ + return String("%d,%d,%d,%d").Formatted(colour.r, colour.g, colour.b, colour.a); +} + + +/** + * Converts a float colour to a string + * @param colour the colour to convert to a string + * @return a string representation of the colour + */ +inline String ToString(const FColour& colour) +{ + return String("%.2f,%.2f,%.2f,%.2f").Formatted(colour.r, colour.g, colour.b, colour.a); +} + + } // namespace Siege namespace std diff --git a/engine/utils/Logging.h b/engine/utils/Logging.h index 0b92747d..e101331d 100644 --- a/engine/utils/Logging.h +++ b/engine/utils/Logging.h @@ -23,6 +23,7 @@ #include "math/mat/Mat2.h" #include "math/mat/Mat3.h" #include "math/vec/Format.h" +#include "Colour.h" // Define terminal colour wrapping macros #define _CC_LOG_COLOUR_RED "31" @@ -102,6 +103,8 @@ class VariantContainer DEFINE_VARIANT_TYPE(const Vec2& data, "Vector2(" + ToString(data) + ")"); DEFINE_VARIANT_TYPE(const Vec3& data, "Vector3(" + ToString(data) + ")"); DEFINE_VARIANT_TYPE(const Vec4& data, "Vector4(" + ToString(data) + ")"); + DEFINE_VARIANT_TYPE(const FColour& data, "FColour(" + ToString(data) + ")"); + DEFINE_VARIANT_TYPE(const IColour& data, "IColour(" + ToString(data) + ")"); DEFINE_VARIANT_TYPE(const Mat2& data, "Mat2(" + ToString(data) + ")"); DEFINE_VARIANT_TYPE(const Mat3& data, "Mat3(" + ToString(data) + ")"); DEFINE_VARIANT_TYPE(const Mat4& data, "Mat4(" + ToString(data) + ")"); From 263b4adca62c20f6746ddae71701474d49e072b5 Mon Sep 17 00:00:00 2001 From: Jonathan Moallem Date: Thu, 7 Aug 2025 22:37:35 +1000 Subject: [PATCH 07/10] Added tests for skeletal mesh data --- engine/resources/SkeletalMeshData.h | 1 - tests/Makefile | 1 + tests/assets/cube_anim.glb | Bin 0 -> 6024 bytes tests/assets/cube_anim.sk | 2 + tests/src/resources/test_ResourceSystem.cpp | 88 ++++++++++++++++++-- 5 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 tests/assets/cube_anim.glb create mode 100644 tests/assets/cube_anim.sk diff --git a/engine/resources/SkeletalMeshData.h b/engine/resources/SkeletalMeshData.h index f93e775a..cc366057 100644 --- a/engine/resources/SkeletalMeshData.h +++ b/engine/resources/SkeletalMeshData.h @@ -17,7 +17,6 @@ #include #include "StaticMeshData.h" -#include "PackFile.h" namespace Siege { diff --git a/tests/Makefile b/tests/Makefile index 3604af36..d888b788 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -17,6 +17,7 @@ testDepends := $(patsubst %.o, %.d, $(call rwildcard,$(testBinDir)/,*.o)) testBuildDir := $(testBinDir)/build testAssets := $(patsubst ./%,%, $(call rwildcard,$(testSrcDir),*.sm)) +testAssets += $(patsubst ./%,%, $(call rwildcard,$(testSrcDir),*.sk)) testAssets += $(patsubst ./%,%, $(call rwildcard,$(testSrcDir),*.png)) testAssets += $(patsubst ./%,%, $(call rwildcard,$(testSrcDir),*.ttf)) testAssets += $(patsubst ./%,%, $(call rwildcard,$(testSrcDir),*.scene)) diff --git a/tests/assets/cube_anim.glb b/tests/assets/cube_anim.glb new file mode 100644 index 0000000000000000000000000000000000000000..96fa96c14c9199f14c9544217481b384f617342f GIT binary patch literal 6024 zcmeHJU2GIp6u!0~rGQ$X{78XyLSn>qnV;R+Zex3GOVyUL3;k1D2~M}ucCov&&Q6Q9 z)~*^2JOYsy6HI(CP+~$9!wYGN(P-j>8h;XB`+&-WFC;#|1D<uP znK}d>5ebGJfO_^Bh6SFcVKKKS#86a>N}?K7V`6hSrikI7NdCTIfbvF!m^5Nq&rQ;G z6=HQxkLnO6_i_qIh#pQdIB{0(W?HkROAuAX$WObS`v(N zhG_$hPLGf4=1@vMNqHuXbQao7#u}K-!s&`gGJqzH=?s*J0&LUTtE9Drq)1p&)F?C# z83aXvp5YrJM?Fotzz8%N-)zY-;$qRr!cbSI0*!M{a5IFHT#aERcUZvc-DO{N*dV_< zrNr~&L=XGi;i&uFi_=@l9ZtR9ol>IFRni>o)b)3^_rg*1CXa?HzrCd6+5U&y2mI{e z$oRuM>|M3d4E5mpg?Sl>Au%keML#lGnRRp3xK7gi1p}R-B5Oz*E9t+Nwgkx4B0I$W z>6WULy0crQ4@#9%ujXE|)Lpyn6dQyu!M;kZd&5H&ILF-Gm}pZPowf8{Ju^XG^GY*7 zlYzweIQ=vuObXuwN;sy{*Mf~z-Is!`)?1Fi`rVPED%9p`y(Jlp-QJQYyWaiPs6!+6 zxJwa|#8s|bk|9k+(N)zGoxUE+B+gGh+6?a})|-8a_AWJMzpcxllROl0i^=rUp^SO6 zcPX)OB`%70=`tj~0(UPPS*Zx`H&^M0QW^4oHcG5g$@<`xLdw#dfg&+tmx zRKoGhvDig8%*OL9vRSq@#-k(-vnhkaeG7IBpJB0iKJLNe@EF(sbvpz9Y(B;j!jXITNH3zle_P*)cA*Vu zCtP*tezYDn!o3k~M!V5o)ByJe^Z?q39z?ZpuLXuXP!rk&_f1ehd(gvZ4cynDhtM`u z585`k_MmlW3s|j#YX{njwgb{uxOSr&v=*Y(z_k;QXkPPl(V&n4TXyH0_m#pt^C3?r zQ?J52kt_K#+dRI!xW9F9aInx{e13HP^5SAE%(Q`R8`#1g<>C1r=!+oV0Qo17Z;`zN z`+7V$*a&z5;tO&F``B@Tcz|;QF1e7Cd=X z=6Y>BiqTA%>oeyfzI@sH%J$v;Bv~!mbIkJWJoC8W!iP^XecSB?{9D~wvbXdXdSL;Z zMgu>CA5F~T-XC7#`mGzY_`GtS>u+ASuzBbj*I%hK@QJs+=lc5dDZKLK3fCWdN5|6I z-?<*Yoy5Byapu4ISjX$%{g%&b>rP-%y~_2T3kH7e_&Khh+&}H(H~U=^pTB$Ob(~*W h;_JJ%@&<0rs$BnbX#rmfcJh1--(B$J%$?t2`7ZzlmjVC) literal 0 HcmV?d00001 diff --git a/tests/assets/cube_anim.sk b/tests/assets/cube_anim.sk new file mode 100644 index 00000000..3baa9707 --- /dev/null +++ b/tests/assets/cube_anim.sk @@ -0,0 +1,2 @@ +SOURCE_PATH:assets/cube_anim.glb; +MATERIAL_PATH:assets/cube.mat; \ No newline at end of file diff --git a/tests/src/resources/test_ResourceSystem.cpp b/tests/src/resources/test_ResourceSystem.cpp index d75e8943..e226a74a 100644 --- a/tests/src/resources/test_ResourceSystem.cpp +++ b/tests/src/resources/test_ResourceSystem.cpp @@ -12,11 +12,10 @@ #include #include #include +#include #include #include -#include "utils/Logging.h" - using namespace Siege; // Define test fixture @@ -53,17 +52,18 @@ UTEST_F(test_ResourceSystem, ReadAllPackedEntries) { ResourceSystem& resourceSystem = ResourceSystem::GetInstance(); PackFile* packFile = resourceSystem.GetPackFile(); - ASSERT_EQ(6, packFile->GetEntries().size()); + ASSERT_EQ(7, packFile->GetEntries().size()); const PackFile::Header& header = packFile->GetHeader(); ASSERT_STREQ("pck", header.magic.string); - ASSERT_EQ(48703, header.bodySize); - ASSERT_EQ(48511, header.tocOffset); + ASSERT_EQ(48951, header.bodySize); + ASSERT_EQ(48727, header.tocOffset); std::vector packedFilepaths {"assets/scene2.scene", "assets/scene1.scene", "assets/garbage.scene", "assets/cube.sm", + "assets/cube_anim.sk", "assets/cappy.png", "assets/PublicPixel.ttf"}; @@ -198,4 +198,80 @@ UTEST_F(test_ResourceSystem, LoadSceneData) const String& actualEntityData = data->entities[i]; ASSERT_STREQ(expectedEntityData.Str(), actualEntityData.Str()); } -} \ No newline at end of file +} + +UTEST_F(test_ResourceSystem, LoadSkeletalMeshData) +{ + ResourceSystem& resourceSystem = ResourceSystem::GetInstance(); + PackFile* packFile = resourceSystem.GetPackFile(); + + std::shared_ptr data = + packFile->FindDataDeserialised("assets/cube_anim.sk"); + ASSERT_TRUE(data); + ASSERT_EQ(60, data->indices.size()); + ASSERT_EQ(32, data->vertices.size()); + + uint32_t expectedIndices[60] {0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, + 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23, + 24, 25, 21, 24, 21, 20, 26, 27, 17, 26, 17, 16, 28, 29, 9, 28, 9, + 8, 30, 31, 1, 30, 1, 0}; + uint32_t* actualIndices = data->indices.data(); + ASSERT_TRUE(actualIndices); + + bool indicesCorrect = true; + for (int i = 0; i < 36; i++) + { + if (expectedIndices[i] != actualIndices[i]) + { + indicesCorrect = false; + } + } + ASSERT_TRUE(indicesCorrect); + SkinnedVertex expectedVertices[32] { + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,-1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,-1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,-1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,-1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, + {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}} + }; + ASSERT_FALSE(data->vertices.empty()); + + bool verticesCorrect = true; + for (int i = 0; i < 32; i++) + { + const SkinnedVertex& expectedVertex = expectedVertices[i]; + const SkinnedVertex& actualVertex = data->vertices[i]; + if (expectedVertex != actualVertex) + { + verticesCorrect = false; + } + } + ASSERT_TRUE(verticesCorrect); +} From dc5573f6f6ed4222523caec7285d27d988fa6d19 Mon Sep 17 00:00:00 2001 From: Jonathan Moallem Date: Fri, 8 Aug 2025 23:16:08 +1000 Subject: [PATCH 08/10] Added tests for animation data --- engine/resources/AnimationData.h | 5 +- engine/resources/PackFile.h | 3 +- engine/resources/SkeletalMeshData.h | 6 +- engine/resources/StaticMeshData.h | 2 - engine/utils/Colour.h | 2 - engine/utils/Logging.h | 2 +- examples/game/Makefile | 2 + packer/src/types/AnimationDataPacker.cpp | 17 +- packer/src/types/AnimationDataPacker.h | 3 +- packer/src/types/SkeletalMeshDataPacker.cpp | 13 +- packer/src/types/SkeletalMeshDataPacker.h | 3 +- tests/Makefile | 1 + tests/assets/cube_anim.ska | 2 + tests/src/resources/test_ResourceSystem.cpp | 251 ++++++++++++++++---- 14 files changed, 240 insertions(+), 72 deletions(-) create mode 100644 tests/assets/cube_anim.ska diff --git a/engine/resources/AnimationData.h b/engine/resources/AnimationData.h index 3cbe15ec..1b1849f9 100644 --- a/engine/resources/AnimationData.h +++ b/engine/resources/AnimationData.h @@ -10,10 +10,9 @@ #ifndef SIEGE_ENGINE_ANIMATIONDATA_H #define SIEGE_ENGINE_ANIMATIONDATA_H -#include +#include #include - -#include "PackFile.h" +#include namespace Siege { diff --git a/engine/resources/PackFile.h b/engine/resources/PackFile.h index d64ae200..008c966f 100644 --- a/engine/resources/PackFile.h +++ b/engine/resources/PackFile.h @@ -18,10 +18,11 @@ #include #include +#include "AnimationData.h" #include "PackFileData.h" #include "SceneData.h" -#include "StaticMeshData.h" #include "SkeletalMeshData.h" +#include "StaticMeshData.h" #include "Texture2DData.h" #define PACKER_MAGIC_NUMBER_FILE "pck" diff --git a/engine/resources/SkeletalMeshData.h b/engine/resources/SkeletalMeshData.h index cc366057..91f43ebe 100644 --- a/engine/resources/SkeletalMeshData.h +++ b/engine/resources/SkeletalMeshData.h @@ -10,11 +10,12 @@ #ifndef SIEGE_ENGINE_SKELETALMESHDATA_H #define SIEGE_ENGINE_SKELETALMESHDATA_H +#include #include +#include #include #include #include -#include #include "StaticMeshData.h" @@ -34,7 +35,8 @@ struct SkinnedVertex : BaseVertex inline bool operator==(const SkinnedVertex& left, const SkinnedVertex& right) { - return static_cast(left) == static_cast(right) && left.bones == right.bones && left.weights == right.weights; + return static_cast(left) == static_cast(right) && + left.bones == right.bones && left.weights == right.weights; } inline bool operator!=(const SkinnedVertex& left, const SkinnedVertex& right) diff --git a/engine/resources/StaticMeshData.h b/engine/resources/StaticMeshData.h index 1650597c..e7c9ee29 100644 --- a/engine/resources/StaticMeshData.h +++ b/engine/resources/StaticMeshData.h @@ -16,8 +16,6 @@ #include #include -#include "PackFile.h" - namespace Siege { diff --git a/engine/utils/Colour.h b/engine/utils/Colour.h index ea6ec329..9e978ad5 100644 --- a/engine/utils/Colour.h +++ b/engine/utils/Colour.h @@ -247,7 +247,6 @@ inline String ToString(const IColour& colour) return String("%d,%d,%d,%d").Formatted(colour.r, colour.g, colour.b, colour.a); } - /** * Converts a float colour to a string * @param colour the colour to convert to a string @@ -258,7 +257,6 @@ inline String ToString(const FColour& colour) return String("%.2f,%.2f,%.2f,%.2f").Formatted(colour.r, colour.g, colour.b, colour.a); } - } // namespace Siege namespace std diff --git a/engine/utils/Logging.h b/engine/utils/Logging.h index e101331d..6fadbad0 100644 --- a/engine/utils/Logging.h +++ b/engine/utils/Logging.h @@ -15,6 +15,7 @@ #include #include +#include "Colour.h" #include "Macros.h" #include "String.h" #include "Token.h" @@ -23,7 +24,6 @@ #include "math/mat/Mat2.h" #include "math/mat/Mat3.h" #include "math/vec/Format.h" -#include "Colour.h" // Define terminal colour wrapping macros #define _CC_LOG_COLOUR_RED "31" diff --git a/examples/game/Makefile b/examples/game/Makefile index 6ee7e441..e7ee4a3d 100644 --- a/examples/game/Makefile +++ b/examples/game/Makefile @@ -30,6 +30,8 @@ else ifeq ($(platform), macos) endif engineRenderBuildDir := $(binDir)/engine/render/build exampleGameAssets := $(patsubst ./%,%, $(call rwildcard,$(exampleGameSrcDir),*.sm)) +exampleGameAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleGameSrcDir),*.sk)) +exampleGameAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleGameSrcDir),*.ska)) exampleGameAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleGameSrcDir),*.png)) exampleGameAssets += $(patsubst ./%,%, $(call rwildcard,$(exampleGameSrcDir),*.jpg)) exampleGameAssets += $(patsubst $(engineRenderBuildDir)/%,%, $(call rwildcard,$(engineRenderBuildDir),*.spv)) diff --git a/packer/src/types/AnimationDataPacker.cpp b/packer/src/types/AnimationDataPacker.cpp index 333ddb96..5bdb1d6e 100644 --- a/packer/src/types/AnimationDataPacker.cpp +++ b/packer/src/types/AnimationDataPacker.cpp @@ -8,15 +8,14 @@ #include "AnimationDataPacker.h" +#include +#include #include #include #include -#include -#include -#include - #include +#include #include #include "StaticMeshDataPacker.h" @@ -29,7 +28,9 @@ static void GetAnimationData(aiAnimation* animation, OUT Siege::AnimationData& a anim.speed = animation->mTicksPerSecond; Siege::String animName = animation->mName.C_Str(); - CC_LOG_INFO("Reading animation \"{}\" with duration of {} seconds", animName, anim.length / anim.speed) + CC_LOG_INFO("Reading animation \"{}\" with duration of {} seconds", + animName, + anim.length / anim.speed) for (uint32_t i = 0; i < animation->mNumChannels; i++) { @@ -66,10 +67,12 @@ static void GetAnimationData(aiAnimation* animation, OUT Siege::AnimationData& a } } -Siege::PackFileData* PackAnimationFile(const Siege::String& filePath, const Siege::String& assetsPath) +Siege::PackFileData* PackAnimationFile(const Siege::String& filePath, + const Siege::String& assetsPath) { Siege::String contents = Siege::FileSystem::Read(filePath); - std::map attributes = Siege::FileSystem::ParseAttributeFileData(contents); + std::map attributes = + Siege::FileSystem::ParseAttributeFileData(contents); if (attributes.empty()) { diff --git a/packer/src/types/AnimationDataPacker.h b/packer/src/types/AnimationDataPacker.h index 2c012108..888f4f49 100644 --- a/packer/src/types/AnimationDataPacker.h +++ b/packer/src/types/AnimationDataPacker.h @@ -11,6 +11,7 @@ #include -Siege::PackFileData* PackAnimationFile(const Siege::String& filePath, const Siege::String& assetsPath); +Siege::PackFileData* PackAnimationFile(const Siege::String& filePath, + const Siege::String& assetsPath); #endif // SIEGE_ENGINE_ANIMATIONDATAPACKER_H diff --git a/packer/src/types/SkeletalMeshDataPacker.cpp b/packer/src/types/SkeletalMeshDataPacker.cpp index 7d7485fd..8a5a3c04 100644 --- a/packer/src/types/SkeletalMeshDataPacker.cpp +++ b/packer/src/types/SkeletalMeshDataPacker.cpp @@ -30,7 +30,7 @@ static void GetMeshData(const aiScene* scene, { Siege::SkinnedVertex vertex {}; vertex.bones = {-1, -1, -1, -1}; - vertex.weights = {0.f, 0.f, 0.f, 0.f }; + vertex.weights = {0.f, 0.f, 0.f, 0.f}; aiVector3t vert = mesh->mVertices[i]; vertex.position = mat * Siege::Vec4(vert.x, vert.y, vert.z, 1.f); @@ -100,7 +100,10 @@ static void GetMeshData(const aiScene* scene, } } -static aiMesh* FindMeshAtPath(const aiScene* scene, aiNode* node, const Siege::String& requestPath, Siege::String currentPath) +static aiMesh* FindMeshAtPath(const aiScene* scene, + aiNode* node, + const Siege::String& requestPath, + Siege::String currentPath) { currentPath = currentPath + node->mName.C_Str(); CC_LOG_INFO("Reading node at {}", currentPath) @@ -124,10 +127,12 @@ static aiMesh* FindMeshAtPath(const aiScene* scene, aiNode* node, const Siege::S return nullptr; } -Siege::PackFileData* PackSkeletalMeshFile(const Siege::String& filePath, const Siege::String& assetsPath) +Siege::PackFileData* PackSkeletalMeshFile(const Siege::String& filePath, + const Siege::String& assetsPath) { Siege::String contents = Siege::FileSystem::Read(filePath); - std::map attributes = Siege::FileSystem::ParseAttributeFileData(contents); + std::map attributes = + Siege::FileSystem::ParseAttributeFileData(contents); if (attributes.empty()) { diff --git a/packer/src/types/SkeletalMeshDataPacker.h b/packer/src/types/SkeletalMeshDataPacker.h index 70cdbd71..0ad2afad 100644 --- a/packer/src/types/SkeletalMeshDataPacker.h +++ b/packer/src/types/SkeletalMeshDataPacker.h @@ -11,6 +11,7 @@ #include -Siege::PackFileData* PackSkeletalMeshFile(const Siege::String& filePath, const Siege::String& assetsPath); +Siege::PackFileData* PackSkeletalMeshFile(const Siege::String& filePath, + const Siege::String& assetsPath); #endif // SIEGE_ENGINE_SKELETALMESHDATAPACKER_H diff --git a/tests/Makefile b/tests/Makefile index d888b788..dfd47c7c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -18,6 +18,7 @@ testBuildDir := $(testBinDir)/build testAssets := $(patsubst ./%,%, $(call rwildcard,$(testSrcDir),*.sm)) testAssets += $(patsubst ./%,%, $(call rwildcard,$(testSrcDir),*.sk)) +testAssets += $(patsubst ./%,%, $(call rwildcard,$(testSrcDir),*.ska)) testAssets += $(patsubst ./%,%, $(call rwildcard,$(testSrcDir),*.png)) testAssets += $(patsubst ./%,%, $(call rwildcard,$(testSrcDir),*.ttf)) testAssets += $(patsubst ./%,%, $(call rwildcard,$(testSrcDir),*.scene)) diff --git a/tests/assets/cube_anim.ska b/tests/assets/cube_anim.ska new file mode 100644 index 00000000..508c67f9 --- /dev/null +++ b/tests/assets/cube_anim.ska @@ -0,0 +1,2 @@ +SOURCE_PATH:assets/cube_anim.glb; +ANIM_NAME:ArmatureAction; \ No newline at end of file diff --git a/tests/src/resources/test_ResourceSystem.cpp b/tests/src/resources/test_ResourceSystem.cpp index e226a74a..656973c9 100644 --- a/tests/src/resources/test_ResourceSystem.cpp +++ b/tests/src/resources/test_ResourceSystem.cpp @@ -7,12 +7,13 @@ // https://opensource.org/licenses/Zlib // +#include #include #include #include #include -#include #include +#include #include #include @@ -52,18 +53,19 @@ UTEST_F(test_ResourceSystem, ReadAllPackedEntries) { ResourceSystem& resourceSystem = ResourceSystem::GetInstance(); PackFile* packFile = resourceSystem.GetPackFile(); - ASSERT_EQ(7, packFile->GetEntries().size()); + ASSERT_EQ(8, packFile->GetEntries().size()); const PackFile::Header& header = packFile->GetHeader(); ASSERT_STREQ("pck", header.magic.string); - ASSERT_EQ(48951, header.bodySize); - ASSERT_EQ(48727, header.tocOffset); + ASSERT_EQ(49504, header.bodySize); + ASSERT_EQ(49247, header.tocOffset); std::vector packedFilepaths {"assets/scene2.scene", "assets/scene1.scene", "assets/garbage.scene", "assets/cube.sm", "assets/cube_anim.sk", + "assets/cube_anim.ska", "assets/cappy.png", "assets/PublicPixel.ttf"}; @@ -211,56 +213,114 @@ UTEST_F(test_ResourceSystem, LoadSkeletalMeshData) ASSERT_EQ(60, data->indices.size()); ASSERT_EQ(32, data->vertices.size()); - uint32_t expectedIndices[60] {0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, - 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23, - 24, 25, 21, 24, 21, 20, 26, 27, 17, 26, 17, 16, 28, 29, 9, 28, 9, - 8, 30, 31, 1, 30, 1, 0}; + uint32_t expectedIndices[60] {0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, + 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, + 20, 21, 22, 20, 22, 23, 24, 25, 21, 24, 21, 20, 26, 27, 17, + 26, 17, 16, 28, 29, 9, 28, 9, 8, 30, 31, 1, 30, 1, 0}; uint32_t* actualIndices = data->indices.data(); ASSERT_TRUE(actualIndices); - - bool indicesCorrect = true; for (int i = 0; i < 36; i++) { - if (expectedIndices[i] != actualIndices[i]) - { - indicesCorrect = false; - } + ASSERT_EQ(expectedIndices[i], actualIndices[i]); } - ASSERT_TRUE(indicesCorrect); + SkinnedVertex expectedVertices[32] { - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,-1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,-1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,-1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,-1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,0.00,1.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {0.00,-1.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}}, - {{{0.00,0.00,0.00}, {1.00,1.00,1.00,1.00}, {-1.00,0.00,0.00}, {1.00,1.00}},{-1.00,-1.00,-1.00,-1.00},{0.00,0.00,0.00,0.00}} - }; + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, -1.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, -1.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, -1.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, -1.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, 1.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, 1.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, 1.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, 1.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}, + {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, + {-1.00, -1.00, -1.00, -1.00}, + {0.00, 0.00, 0.00, 0.00}}}; ASSERT_FALSE(data->vertices.empty()); bool verticesCorrect = true; @@ -275,3 +335,98 @@ UTEST_F(test_ResourceSystem, LoadSkeletalMeshData) } ASSERT_TRUE(verticesCorrect); } + +UTEST_F(test_ResourceSystem, LoadAnimationData) +{ + ResourceSystem& resourceSystem = ResourceSystem::GetInstance(); + PackFile* packFile = resourceSystem.GetPackFile(); + + std::shared_ptr data = + packFile->FindDataDeserialised("assets/cube_anim.ska"); + ASSERT_TRUE(data); + ASSERT_NEAR(833.333, data->length, 0.001); + ASSERT_NEAR(1000.000, data->speed, 0.001); + ASSERT_EQ(2, data->channels.size()); + + { + auto it = data->channels.find("Bone.Root"); + ASSERT_TRUE(it != data->channels.end()); + + ASSERT_EQ(2, it->second.positionKeys.size()); + ASSERT_EQ(2, it->second.scaleKeys.size()); + + ASSERT_EQ(20, it->second.rotationKeys.size()); + + AnimKeyRotation expectedRotationKeys[20] { + {41.66666793823242188, {-0.71, 0.00, 0.00, 0.71}}, + {83.33333587646484375, {-0.70, 0.00, 0.00, 0.71}}, + {125.00000000000000000, {-0.69, 0.00, 0.00, 0.72}}, + {166.66667175292968750, {-0.67, 0.00, 0.00, 0.74}}, + {208.33332824707031250, {-0.64, 0.00, 0.00, 0.77}}, + {250.00000000000000000, {-0.61, 0.00, 0.00, 0.79}}, + {291.66665649414062500, {-0.58, 0.00, 0.00, 0.82}}, + {333.33334350585937500, {-0.54, 0.00, 0.00, 0.84}}, + {375.00000000000000000, {-0.52, 0.00, 0.00, 0.85}}, + {416.66665649414062500, {-0.51, 0.00, 0.00, 0.86}}, + {458.33334350585937500, {-0.52, 0.00, 0.00, 0.85}}, + {500.00000000000000000, {-0.54, 0.00, 0.00, 0.84}}, + {541.66668701171875000, {-0.58, 0.00, 0.00, 0.82}}, + {583.33331298828125000, {-0.61, 0.00, 0.00, 0.79}}, + {625.00000000000000000, {-0.64, 0.00, 0.00, 0.77}}, + {666.66668701171875000, {-0.67, 0.00, 0.00, 0.74}}, + {708.33331298828125000, {-0.69, 0.00, 0.00, 0.73}}, + {750.00000000000000000, {-0.70, 0.00, 0.00, 0.72}}, + {791.66668701171875000, {-0.70, 0.00, 0.00, 0.71}}, + {833.33331298828125000, {-0.71, 0.00, 0.00, 0.71}}}; + for (int i = 0; i < 20; i++) + { + const AnimKeyRotation& expectedKey = expectedRotationKeys[i]; + const AnimKeyRotation& actualKey = it->second.rotationKeys[i]; + ASSERT_NEAR(expectedKey.timestamp, actualKey.timestamp, 0.0001); + ASSERT_NEAR(expectedKey.rotation.x, actualKey.rotation.x, 0.01); + ASSERT_NEAR(expectedKey.rotation.y, actualKey.rotation.y, 0.01); + ASSERT_NEAR(expectedKey.rotation.z, actualKey.rotation.z, 0.01); + ASSERT_NEAR(expectedKey.rotation.w, actualKey.rotation.w, 0.01); + } + } + + { + auto it = data->channels.find("Bone.Leaf"); + ASSERT_TRUE(it != data->channels.end()); + + ASSERT_EQ(2, it->second.positionKeys.size()); + ASSERT_EQ(2, it->second.scaleKeys.size()); + + AnimKeyRotation expectedRotationKeys[20] { + {41.66666793823242188, {0.00, 0.00, 0.00, 1.00}}, + {83.33333587646484375, {0.02, 0.00, 0.00, 1.00}}, + {125.00000000000000000, {0.07, 0.00, 0.00, 1.00}}, + {166.66667175292968750, {0.14, 0.00, 0.00, 0.99}}, + {208.33332824707031250, {0.22, 0.00, 0.00, 0.98}}, + {250.00000000000000000, {0.29, 0.00, 0.00, 0.96}}, + {291.66665649414062500, {0.36, 0.00, 0.00, 0.93}}, + {333.33334350585937500, {0.41, 0.00, 0.00, 0.91}}, + {375.00000000000000000, {0.44, 0.00, 0.00, 0.90}}, + {416.66665649414062500, {0.46, 0.00, 0.00, 0.89}}, + {458.33334350585937500, {0.44, 0.00, 0.00, 0.90}}, + {500.00000000000000000, {0.41, 0.00, 0.00, 0.91}}, + {541.66668701171875000, {0.35, 0.00, 0.00, 0.94}}, + {583.33331298828125000, {0.29, 0.00, 0.00, 0.96}}, + {625.00000000000000000, {0.22, 0.00, 0.00, 0.98}}, + {666.66668701171875000, {0.15, 0.00, 0.00, 0.99}}, + {708.33331298828125000, {0.09, 0.00, 0.00, 1.00}}, + {750.00000000000000000, {0.04, 0.00, 0.00, 1.00}}, + {791.66668701171875000, {0.01, 0.00, 0.00, 1.00}}, + {833.33331298828125000, {0.00, 0.00, 0.00, 1.00}}}; + for (int i = 0; i < 20; i++) + { + const AnimKeyRotation& expectedKey = expectedRotationKeys[i]; + const AnimKeyRotation& actualKey = it->second.rotationKeys[i]; + ASSERT_NEAR(expectedKey.timestamp, actualKey.timestamp, 0.0001); + ASSERT_NEAR(expectedKey.rotation.x, actualKey.rotation.x, 0.01); + ASSERT_NEAR(expectedKey.rotation.y, actualKey.rotation.y, 0.01); + ASSERT_NEAR(expectedKey.rotation.z, actualKey.rotation.z, 0.01); + ASSERT_NEAR(expectedKey.rotation.w, actualKey.rotation.w, 0.01); + } + } +} From 7650911bdda659e8708afa4d4d6f53af34c0517b Mon Sep 17 00:00:00 2001 From: Jonathan Moallem Date: Mon, 11 Aug 2025 00:29:20 +1000 Subject: [PATCH 09/10] Folded base vertex fields into skeletal mesh data --- engine/resources/SkeletalMeshData.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/engine/resources/SkeletalMeshData.h b/engine/resources/SkeletalMeshData.h index 91f43ebe..c02df02e 100644 --- a/engine/resources/SkeletalMeshData.h +++ b/engine/resources/SkeletalMeshData.h @@ -27,16 +27,21 @@ struct Bone Mat4 bindMatrix; }; -struct SkinnedVertex : BaseVertex +struct SkinnedVertex { + Vec3 position; + FColour color; + Vec3 normal; + Vec2 uv; Vec4 bones; Vec4 weights; }; inline bool operator==(const SkinnedVertex& left, const SkinnedVertex& right) { - return static_cast(left) == static_cast(right) && - left.bones == right.bones && left.weights == right.weights; + return left.position == right.position && left.color == right.color && + left.normal == right.normal && left.uv == right.uv && left.bones == right.bones && + left.weights == right.weights; } inline bool operator!=(const SkinnedVertex& left, const SkinnedVertex& right) @@ -62,6 +67,7 @@ inline void serialise(Buffer& buffer, Bone& value, SerialisationMode mode) inline void serialise(Buffer& buffer, SkinnedVertex& value, SerialisationMode mode) { + serialise(buffer, value.position, mode); serialise(buffer, value.color, mode); serialise(buffer, value.normal, mode); serialise(buffer, value.uv, mode); From 7140aaf76fb1f80d2278891899f8f26e73782e34 Mon Sep 17 00:00:00 2001 From: Jonathan Moallem Date: Mon, 11 Aug 2025 00:30:19 +1000 Subject: [PATCH 10/10] Fixed skeletal mesh data packing to apply bone weights, fixed tests --- packer/src/types/SkeletalMeshDataPacker.cpp | 4 +- tests/src/resources/test_ResourceSystem.cpp | 367 +++++++++++++------- 2 files changed, 251 insertions(+), 120 deletions(-) diff --git a/packer/src/types/SkeletalMeshDataPacker.cpp b/packer/src/types/SkeletalMeshDataPacker.cpp index 8a5a3c04..e3bd5819 100644 --- a/packer/src/types/SkeletalMeshDataPacker.cpp +++ b/packer/src/types/SkeletalMeshDataPacker.cpp @@ -28,7 +28,7 @@ static void GetMeshData(const aiScene* scene, { for (uint32_t i = 0; i < mesh->mNumVertices; i++) { - Siege::SkinnedVertex vertex {}; + Siege::SkinnedVertex vertex; vertex.bones = {-1, -1, -1, -1}; vertex.weights = {0.f, 0.f, 0.f, 0.f}; @@ -86,7 +86,7 @@ static void GetMeshData(const aiScene* scene, for (int32_t j = 0; j < currentBone->mNumWeights; ++j) { aiVertexWeight weight = currentBone->mWeights[j]; - Siege::SkinnedVertex vertex = meshData.vertices[weight.mVertexId]; + Siege::SkinnedVertex& vertex = meshData.vertices[weight.mVertexId]; for (int32_t k = 0; k < 4; ++k) { if (vertex.bones[k] < 0) diff --git a/tests/src/resources/test_ResourceSystem.cpp b/tests/src/resources/test_ResourceSystem.cpp index 656973c9..df40b2f4 100644 --- a/tests/src/resources/test_ResourceSystem.cpp +++ b/tests/src/resources/test_ResourceSystem.cpp @@ -57,8 +57,8 @@ UTEST_F(test_ResourceSystem, ReadAllPackedEntries) const PackFile::Header& header = packFile->GetHeader(); ASSERT_STREQ("pck", header.magic.string); - ASSERT_EQ(49504, header.bodySize); - ASSERT_EQ(49247, header.tocOffset); + ASSERT_EQ(49643, header.bodySize); + ASSERT_EQ(49386, header.tocOffset); std::vector packedFilepaths {"assets/scene2.scene", "assets/scene1.scene", @@ -110,16 +110,10 @@ UTEST_F(test_ResourceSystem, LoadStaticMeshData) 0, 18, 1, 3, 19, 4, 6, 20, 7, 9, 21, 10, 12, 22, 13, 15, 23, 16}; uint32_t* actualIndices = data->indices.data(); ASSERT_TRUE(actualIndices); - - bool indicesCorrect = true; for (int i = 0; i < 36; i++) { - if (expectedIndices[i] != actualIndices[i]) - { - indicesCorrect = false; - } + ASSERT_EQ(expectedIndices[i], actualIndices[i]); } - ASSERT_TRUE(indicesCorrect); BaseVertex expectedVertices[24] {{{-1, -1, 1}, {1, 1, 1, 1}, {0, -1, 0}, {0.875, 0.5}}, {{1, -1, -1}, {1, 1, 1, 1}, {0, -1, 0}, {0.625, 0.75}}, @@ -147,17 +141,23 @@ UTEST_F(test_ResourceSystem, LoadStaticMeshData) {{1, -1, 1}, {1, 1, 1, 1}, {0, 0, 1}, {0.625, 0.5}}}; ASSERT_FALSE(data->vertices.empty()); - bool verticesCorrect = true; for (int i = 0; i < 24; i++) { const BaseVertex& expectedVertex = expectedVertices[i]; const BaseVertex& actualVertex = data->vertices[i]; - if (expectedVertex != actualVertex) - { - verticesCorrect = false; - } + ASSERT_NEAR(expectedVertex.position.x, actualVertex.position.x, 0.0001); + ASSERT_NEAR(expectedVertex.position.y, actualVertex.position.y, 0.0001); + ASSERT_NEAR(expectedVertex.position.z, actualVertex.position.z, 0.0001); + ASSERT_NEAR(expectedVertex.color.r, actualVertex.color.r, 0.0001); + ASSERT_NEAR(expectedVertex.color.g, actualVertex.color.g, 0.0001); + ASSERT_NEAR(expectedVertex.color.b, actualVertex.color.b, 0.0001); + ASSERT_NEAR(expectedVertex.color.a, actualVertex.color.a, 0.0001); + ASSERT_NEAR(expectedVertex.normal.x, actualVertex.normal.x, 0.0001); + ASSERT_NEAR(expectedVertex.normal.y, actualVertex.normal.y, 0.0001); + ASSERT_NEAR(expectedVertex.normal.z, actualVertex.normal.z, 0.0001); + ASSERT_NEAR(expectedVertex.uv.x, actualVertex.uv.x, 0.0001); + ASSERT_NEAR(expectedVertex.uv.y, actualVertex.uv.y, 0.0001); } - ASSERT_TRUE(verticesCorrect); } UTEST_F(test_ResourceSystem, LoadTextureData) @@ -224,116 +224,247 @@ UTEST_F(test_ResourceSystem, LoadSkeletalMeshData) ASSERT_EQ(expectedIndices[i], actualIndices[i]); } - SkinnedVertex expectedVertices[32] { - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, -1.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, -1.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, -1.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, -1.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, 1.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, 1.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, 1.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 0.00, 1.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, 1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {0.00, -1.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}, - {{{0.00, 0.00, 0.00}, {1.00, 1.00, 1.00, 1.00}, {-1.00, 0.00, 0.00}, {1.00, 1.00}}, - {-1.00, -1.00, -1.00, -1.00}, - {0.00, 0.00, 0.00, 0.00}}}; + SkinnedVertex expectedVertices[32] {{{-0.15, -0.15, 0.00}, + {1.00, 1.00, 1.00, 1.00}, + {-1.00, 0.00, 0.00}, + {1.00, 1.00}, + {0.00, 1.00, -1.00, -1.00}, + {0.50, 0.50, 0.00, 0.00}}, + {{-0.15, 0.15, 0.00}, + {1.00, 1.00, 1.00, 1.00}, + {-1.00, 0.00, 0.00}, + {1.00, 1.00}, + {0.00, 1.00, -1.00, -1.00}, + {0.50, 0.50, 0.00, 0.00}}, + {{-0.15, 0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {-1.00, 0.00, 0.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{-0.15, -0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {-1.00, 0.00, 0.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{-0.15, -0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 0.00, -1.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{-0.15, 0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 0.00, -1.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, 0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 0.00, -1.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, -0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 0.00, -1.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, -0.15, 0.00}, + {1.00, 1.00, 1.00, 1.00}, + {1.00, 0.00, 0.00}, + {1.00, 1.00}, + {0.00, 1.00, -1.00, -1.00}, + {0.50, 0.50, 0.00, 0.00}}, + {{0.15, 0.15, 0.00}, + {1.00, 1.00, 1.00, 1.00}, + {1.00, 0.00, 0.00}, + {1.00, 1.00}, + {0.00, 1.00, -1.00, -1.00}, + {0.50, 0.50, 0.00, 0.00}}, + {{0.15, 0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {1.00, 0.00, 0.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, -0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {1.00, 0.00, 0.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, -0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 0.00, 1.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, 0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 0.00, 1.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{-0.15, 0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 0.00, 1.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{-0.15, -0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 0.00, 1.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{-0.15, -0.15, 0.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, -1.00, 0.00}, + {1.00, 1.00}, + {0.00, 1.00, -1.00, -1.00}, + {0.50, 0.50, 0.00, 0.00}}, + {{0.15, -0.15, 0.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, -1.00, 0.00}, + {1.00, 1.00}, + {0.00, 1.00, -1.00, -1.00}, + {0.50, 0.50, 0.00, 0.00}}, + {{0.15, -0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, -1.00, 0.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{-0.15, -0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, -1.00, 0.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, 0.15, 0.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 1.00, 0.00}, + {1.00, 1.00}, + {0.00, 1.00, -1.00, -1.00}, + {0.50, 0.50, 0.00, 0.00}}, + {{-0.15, 0.15, 0.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 1.00, 0.00}, + {1.00, 1.00}, + {0.00, 1.00, -1.00, -1.00}, + {0.50, 0.50, 0.00, 0.00}}, + {{-0.15, 0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 1.00, 0.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, 0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 1.00, 0.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, 0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 1.00, 0.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{-0.15, 0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, 1.00, 0.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{-0.15, -0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, -1.00, 0.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, -0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {0.00, -1.00, 0.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, -0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {1.00, 0.00, 0.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{0.15, 0.15, -1.00}, + {1.00, 1.00, 1.00, 1.00}, + {1.00, 0.00, 0.00}, + {1.00, 1.00}, + {1.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{-0.15, -0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {-1.00, 0.00, 0.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}, + {{-0.15, 0.15, 1.00}, + {1.00, 1.00, 1.00, 1.00}, + {-1.00, 0.00, 0.00}, + {1.00, 1.00}, + {0.00, -1.00, -1.00, -1.00}, + {1.00, 0.00, 0.00, 0.00}}}; ASSERT_FALSE(data->vertices.empty()); - bool verticesCorrect = true; for (int i = 0; i < 32; i++) { const SkinnedVertex& expectedVertex = expectedVertices[i]; const SkinnedVertex& actualVertex = data->vertices[i]; - if (expectedVertex != actualVertex) - { - verticesCorrect = false; - } + ASSERT_NEAR(expectedVertex.position.x, actualVertex.position.x, 0.01); + ASSERT_NEAR(expectedVertex.position.y, actualVertex.position.y, 0.01); + ASSERT_NEAR(expectedVertex.position.z, actualVertex.position.z, 0.01); + ASSERT_NEAR(expectedVertex.color.r, actualVertex.color.r, 0.0001); + ASSERT_NEAR(expectedVertex.color.g, actualVertex.color.g, 0.0001); + ASSERT_NEAR(expectedVertex.color.b, actualVertex.color.b, 0.0001); + ASSERT_NEAR(expectedVertex.color.a, actualVertex.color.a, 0.0001); + ASSERT_NEAR(expectedVertex.normal.x, actualVertex.normal.x, 0.0001); + ASSERT_NEAR(expectedVertex.normal.y, actualVertex.normal.y, 0.0001); + ASSERT_NEAR(expectedVertex.normal.z, actualVertex.normal.z, 0.0001); + ASSERT_NEAR(expectedVertex.uv.x, actualVertex.uv.x, 0.0001); + ASSERT_NEAR(expectedVertex.uv.y, actualVertex.uv.y, 0.0001); + ASSERT_NEAR(expectedVertex.bones.x, actualVertex.bones.x, 0.0001); + ASSERT_NEAR(expectedVertex.bones.y, actualVertex.bones.y, 0.0001); + ASSERT_NEAR(expectedVertex.bones.z, actualVertex.bones.z, 0.0001); + ASSERT_NEAR(expectedVertex.bones.w, actualVertex.bones.w, 0.0001); + ASSERT_NEAR(expectedVertex.weights.x, actualVertex.weights.x, 0.0001); + ASSERT_NEAR(expectedVertex.weights.y, actualVertex.weights.y, 0.0001); + ASSERT_NEAR(expectedVertex.weights.z, actualVertex.weights.z, 0.0001); + ASSERT_NEAR(expectedVertex.weights.w, actualVertex.weights.w, 0.0001); + } + + ASSERT_EQ(2, data->bones.size()); + { + auto it = data->bones.find("Bone.Root"); + ASSERT_TRUE(it != data->bones.end()); + ASSERT_EQ(0, it->second.id); + Mat4 expectedMatrix = {{1.00, -0.00, 0.00, -0.00}, + {-0.00, 0.00, 1.00, 0.00}, + {0.00, -1.00, 0.00, -0.00}, + {-0.00, 1.00, -0.00, 1.00}}; + ASSERT_TRUE(expectedMatrix == it->second.bindMatrix); + } + + { + auto it = data->bones.find("Bone.Leaf"); + ASSERT_TRUE(it != data->bones.end()); + Mat4 expectedMatrix = {{1.00, -0.00, 0.00, -0.00}, + {-0.00, 0.00, 1.00, 0.00}, + {0.00, -1.00, 0.00, -0.00}, + {-0.00, 0.00, -0.00, 1.00}}; + ASSERT_TRUE(expectedMatrix == it->second.bindMatrix); } - ASSERT_TRUE(verticesCorrect); } UTEST_F(test_ResourceSystem, LoadAnimationData)