diff --git a/Source/Resource/Models/AnimatedModelResource.cpp b/Source/Resource/Models/AnimatedModelResource.cpp index 1f3decc2..19484b30 100644 --- a/Source/Resource/Models/AnimatedModelResource.cpp +++ b/Source/Resource/Models/AnimatedModelResource.cpp @@ -1,10 +1,20 @@ #include "Client/Application/ErrorDialog.h" +#include "File/File.h" +#include "Render/DeviceContext/DCSurface.h" #include "Render/Textures/Texture.h" #include "Render/Textures/TextureCache.h" #include "Resource/Models/AnimatedModelResource.h" +#include "Resource/Models/GrannyModelResource.h" #include "Resource/Bitmap/BitmapResource.h" #include "Resource/ResourceByteReader.h" +#include +#include +#include + +// GLOBAL: gl_24742 +GrannyAnimationLibrary g_grannyAnimationLibrary; + // INLINED AnimatedModelFrame::AnimatedModelFrame() : vertices(nullptr), textureVertices(nullptr) { } @@ -213,3 +223,148 @@ bool C3dAnimModelRes::Load(const char* name) { file.Close(); return true; } + +// INLINED +GrannyAnimationLibrary::~GrannyAnimationLibrary() { + if (this->instance) { + this->FreeInstance(); + + for (auto& job : this->animations) { + for (granny_animation*& animation : job) { + if (animation) { + animation = nullptr; + } + } + } + + GrannyFreeLocalPose(this->localPose); + GrannyFreeFile(this->file); + } +} + +// INLINED +C3dGrannyModelRes::C3dGrannyModelRes(ModelResourceContext& context) : m_context(&context) { + this->position[0] = 0.0f; + this->position[1] = 0.0f; + this->position[2] = 0.0f; + this->instance = nullptr; + this->model = nullptr; + this->job = 0; + this->unknown_03B0 = 0; +} + +// OFFSET: 0x00753540 +// FUNCTION: fn_07961 +C3dGrannyModelRes* C3dGrannyModelRes::Create() { + return new C3dGrannyModelRes(*this->m_context); +} + +// OFFSET: 0x00753620 +// FUNCTION: fn_07963 +bool C3dGrannyModelRes::Load(const char* name) { + bool result = false; + + if (!GrannyVersionsMatch_(2, 1, 0, 5)) { + ShowErrorDialog(GetRenderSystemHolder()->system, "doesn't match the .h file used during compilation", 0); + return false; + } + + this->animations = &g_grannyAnimationLibrary; + this->modelCount = 0; + this->maxBoneCount = 0; + this->textureCount = 0; + this->unknown_0280 = 0; + const char* cursor = std::strchr(name, '_'); + + if (!cursor) { + return false; + } + + ++cursor; + char number[10]; + int length = 0; + + for (; *cursor != '.'; ++cursor) { + if (length >= 4) { + break; + } + + number[length++] = *cursor; + } + + number[length] = 0; + this->job = std::atoi(number); + CFile file; + + if (!file.Open(name, 0, true, false)) { + const std::string path(name); + const std::string message = std::string("Cannot find File : ") + path; + ShowErrorDialog(GetRenderSystemHolder()->system, message.c_str(), 0); + return false; + } + + this->file = GrannyReadEntireFileFromMemory(file.GetSize(), file.GetData()); + file.Close(); + + if (!this->file) { + ShowErrorDialog(GetRenderSystemHolder()->system, "Unable to load file .gr2 (C3dGrannyModelRes :: Load)", 0); + } else { + granny_file_info* info = GrannyGetFileInfo(this->file); + std::strcpy(this->fileName, name); + + if (!info) { + ShowErrorDialog(GetRenderSystemHolder()->system, "Unable to access file data .gr2", 0); + } else { + this->textureCount = info->textureCount; + this->textures = new GrannyTextureEntry[this->textureCount]; + + for (int index = 0; index < this->textureCount; ++index) { + this->textures[index].texture = nullptr; + const granny_texture* texture = info->textures[index]; + GrannyTextureEntry& entry = this->textures[index]; + entry.name = texture->fromFileName; + + if (texture->textureType == 0 && texture->imageCount == 1) { + entry.texture = this->CreateTexture(texture, 0); + } else { + ShowErrorDialog(GetRenderSystemHolder()->system, "This sample only works with single-image : ColorMapTextureType textures", 0); + } + } + + this->modelCount = info->modelCount; + this->instance = new GrannyModelInstance; + const granny_model* model = info->models[0]; + this->InstantiateModel(this->instance, model); + this->skeleton = info->skeletons[0]; + this->model = model; + this->localPose = GrannyNewLocalPose(this->maxBoneCount); + + if (this->deformBufferSize) { + this->unknown_0280 = 4; + } + + result = true; + } + + GrannyFreeFileSection(this->file, 1); + GrannyFreeFileSection(this->file, 2); + GrannyFreeFileSection(this->file, 4); + GrannyFreeFileSection(this->file, 5); + } + + this->unknown_0110 = 1920.0f; + + for (int index = 0; index < this->instance->meshCount; ++index) { + if (GrannyGetMeshIndexCount(this->instance->meshes[index].mesh) / 3 > 10000) { + ShowErrorDialog(GetRenderSystemHolder()->system, "too many vertex granny model!", 0); + } + } + + this->position[0] = 0.0f; + this->position[1] = 0.0f; + this->position[2] = 0.0f; + this->unknown_03A0 = 10.0f; + this->unknown_03A4 = 10.0f; + file.Close(); + return result; +} diff --git a/Source/Resource/Models/GrannyModelResource.h b/Source/Resource/Models/GrannyModelResource.h index f72136fb..28f146f0 100644 --- a/Source/Resource/Models/GrannyModelResource.h +++ b/Source/Resource/Models/GrannyModelResource.h @@ -40,6 +40,10 @@ struct granny_texture { /* 0004 */ std::int32_t textureType; /* 0008 */ std::int32_t width; /* 000C */ std::int32_t height; + /* 0010 */ std::int32_t encoding; + /* 0014 */ std::int32_t subFormat; + /* 0018 */ granny_pixel_layout layout; + /* 003C */ std::int32_t imageCount; }; struct granny_skeleton { @@ -72,7 +76,11 @@ struct granny_model { struct granny_file_info { /* 0000 */ unsigned char unknown_0000[0x10]; /* 0010 */ std::int32_t textureCount; - /* 0014 */ unsigned char unknown_0014[0x2C]; + /* 0014 */ granny_texture** textures; + /* 0018 */ unsigned char unknown_0018[8]; + /* 0020 */ std::int32_t skeletonCount; + /* 0024 */ granny_skeleton** skeletons; + /* 0028 */ unsigned char unknown_0028[0x18]; /* 0040 */ std::int32_t modelCount; /* 0044 */ granny_model** models; /* 0048 */ unsigned char unknown_0048[8]; @@ -116,6 +124,8 @@ __declspec(dllimport) void WINAPI GrannyFreeModelInstance(granny_model_instance* __declspec(dllimport) void WINAPI GrannyFreeWorldPose(granny_world_pose* pose); __declspec(dllimport) granny_control* WINAPI GrannyPlayControlledAnimation(float startTime, const granny_animation* animation, granny_model_instance* instance); __declspec(dllimport) void WINAPI GrannySetControlLoopCount(granny_control* control, std::int32_t loopCount); +__declspec(dllimport) void WINAPI GrannyFreeLocalPose(granny_local_pose* pose); +__declspec(dllimport) void WINAPI GrannyFreeFile(granny_file* file); } struct GrannyTextureEntry { @@ -142,6 +152,7 @@ struct GrannyModelInstance { class GrannyAnimationLibrary { public: + ~GrannyAnimationLibrary(); bool Load(const char* name, int job, int action); void Initialize(); void FreeInstance(); @@ -165,6 +176,9 @@ public: class C3dGrannyModelRes : public CRes { public: + explicit C3dGrannyModelRes(ModelResourceContext& context); + C3dGrannyModelRes* Create() override; + bool Load(const char* name) override; void RebindMeshes(const C3dGrannyModelRes& target); void InstantiateModel(GrannyModelInstance* target, const granny_model* model); CTexture* CreateTexture(const granny_texture* texture, int option); @@ -174,8 +188,9 @@ public: int GetTriangleCount(int mesh) const; unsigned char* GetRigidVertices(int mesh) const; - /* 0110 */ unsigned char unknown_0110[0x150]; - /* 0260 */ void* file; + /* 0110 */ float unknown_0110; + /* 0114 */ unsigned char unknown_0114[0x14C]; + /* 0260 */ granny_file* file; /* 0264 */ int textureCount; /* 0268 */ GrannyTextureEntry* textures; /* 026C */ int modelCount; @@ -199,7 +214,11 @@ private: ModelResourceContext* m_context; }; +extern GrannyAnimationLibrary g_grannyAnimationLibrary; + #if defined(_WIN32) && !defined(_WIN64) +static_assert(offsetof(granny_texture, imageCount) == 0x3C); +static_assert(offsetof(granny_file_info, skeletons) == 0x24); static_assert(offsetof(granny_texture, width) == 0x08); static_assert(offsetof(granny_mesh, materialBindings) == 0x18); static_assert(offsetof(granny_model, meshBindingCount) == 0x4C);