#include "Client/ErrorDialog.h" #include "Render/Texture.h" #include "Render/TextureCache.h" #include "Resource/AnimatedModelResource.h" #include "Resource/BitmapResource.h" #include "Resource/ResourceByteReader.h" #include AnimatedModelNode::AnimatedModelNode(TextureCache& textures) : model(nullptr), parent(nullptr), unknown_0040(0), vertexCount(0), textureVertexCount(0), faceCount(0), sharedTextureVertices(0), m_textures(textures) { } AnimatedModelNode::~AnimatedModelNode() { for (CTexture* texture : this->textures) { texture->Release(); this->m_textures.Remove(texture); } } // OFFSET: 0x007521F0 // FUNCTION: fn_07956 C3dAnimModelRes::C3dAnimModelRes(ModelResourceContext& context) : shade(2), frameCount(0), animationLength(0), speed(1.0f), unknown_0150(0), unknown_0154(0.0f), unknown_0158(0.0f), unknown_015C(0.0f), alpha(255), m_context(&context) { } // OFFSET: 0x00753510 // FUNCTION: fn_07960 C3dAnimModelRes::~C3dAnimModelRes() { this->Unload(); } // OFFSET: 0x007521F0 // FUNCTION: fn_07956 C3dAnimModelRes* C3dAnimModelRes::Create() { return new C3dAnimModelRes(*this->m_context); } // OFFSET: 0x00752380 // FUNCTION: fn_07957 // OFFSET: 0x0075C710 // FUNCTION: fn_08023 void C3dAnimModelRes::Unload() { for (AnimatedModelNode* node : this->nodes) { delete node; } this->nodes.clear(); // The original retains root-name strings until object destruction. } // OFFSET: 0x00752390 // FUNCTION: fn_07958 bool C3dAnimModelRes::Load(const char* name) { auto& file = this->m_context->file; if (!file.Open(name)) { file.Close(); return false; } ResourceByteReader input{static_cast(file.Data()), static_cast(file.Size())}; char magic[5]; unsigned char major; unsigned char minor; input.Read(magic, 4); magic[4] = 0; input.Read(&major, 1); input.Read(&minor, 1); if (major == 1 && minor >= 2) { input.Read(&this->speed, 4); input.Read(&this->unknown_0154, 12); } if (major == 1 && minor == 3) { input.Read(&this->alpha, 4); } input.Read(&this->shade, 4); input.Read(&this->frameCount, 4); input.Read(&this->animationLength, 4); unsigned char reserved[16]; input.Read(reserved, sizeof(reserved)); unsigned textureCount; input.Read(&textureCount, 4); std::vector names; char field[40]; for (unsigned index = 0; index < textureCount; ++index) { input.Read(field, sizeof(field)); names.emplace_back(field); } input.Read(field, sizeof(field)); const std::string rootName(field); int nodeCount; input.Read(&nodeCount, 4); for (int index = 0; index < nodeCount; ++index) { auto node = std::make_unique(this->m_context->textures); node->model = this; node->frames.resize(static_cast(this->frameCount)); std::uint32_t marker; input.Read(&marker, 4); if (marker != 0x12345678u) { ShowErrorDialog(this->m_context->renderSystem, "data corrupt", 0); file.Close(); return false; } input.Read(field, sizeof(field)); node->name = field; input.Read(field, sizeof(field)); node->parentName = field; int count; input.Read(&count, 4); for (int texture = 0; texture < count; ++texture) { int id; input.Read(&id, 4); CTexture* value = this->m_context->textures.Load(names[id].c_str(), 0, false, true, false); value->Retain(); node->textures.push_back(value); } input.Read(&node->vertexCount, 4); for (AnimatedModelFrame& frame : node->frames) { frame.vertices.resize(static_cast(node->vertexCount)); input.Read(frame.vertices.data(), static_cast(node->vertexCount) * 12u); } input.Read(&node->textureVertexCount, 4); input.Read(&node->sharedTextureVertices, 4); if (node->sharedTextureVertices) { node->frames[0].textureVertices.resize(static_cast(node->textureVertexCount)); input.Read(node->frames[0].textureVertices.data(), static_cast(node->textureVertexCount) * 12u); } else { for (AnimatedModelFrame& frame : node->frames) { frame.textureVertices.resize(static_cast(node->textureVertexCount)); input.Read(frame.textureVertices.data(), static_cast(node->textureVertexCount) * 12u); } } input.Read(&node->faceCount, 4); node->faceNormals.resize(static_cast(node->faceCount)); node->faces.resize(static_cast(node->faceCount)); input.Read(node->faces.data(), static_cast(node->faceCount) * 24u); this->nodes.push_back(node.release()); input.Read(&marker, 4); if (marker != 0x12345678u) { ShowErrorDialog(this->m_context->renderSystem, "data corrupt", 0); file.Close(); return false; } } file.Close(); for (AnimatedModelNode* node : this->nodes) { if (!node->parentName.empty()) { AnimatedModelNode* parent = nullptr; for (AnimatedModelNode* candidate : this->nodes) { if (candidate->name == node->parentName) { parent = candidate; break; } } node->parent = parent; parent->children.push_back(node); } } this->rootNames.push_back(rootName); file.Close(); return true; }