rodecomp/Source/Resource/Models/AnimatedModelResource.cpp
ragnarok-clanker a47d20f8be Reconstruct readable C++ and organize cohesive source subsystems
Administrative source import: native x86 compilation passed for 423 authored units. Source placement and submission states are migrated separately; this commit does not imply peer acceptance.

Previous head: 35d2a8cb36
Payload SHA256: 592f016d2730c5593bb653aa054c1e5f76a6df72c8f5a09e66f74876e2e33dee
2026-10-25 07:42:41 +00:00

183 lines
5.2 KiB
C++

#include "Client/Application/ErrorDialog.h"
#include "Render/Textures/Texture.h"
#include "Render/Textures/TextureCache.h"
#include "Resource/Models/AnimatedModelResource.h"
#include "Resource/Bitmap/BitmapResource.h"
#include "Resource/ResourceByteReader.h"
#include <cstring>
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();
}
// 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<const unsigned char*>(file.Data()), static_cast<unsigned>(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<std::string> 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<AnimatedModelNode>(this->m_context->textures);
node->model = this;
node->frames.resize(static_cast<unsigned>(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<unsigned>(node->vertexCount));
input.Read(frame.vertices.data(), static_cast<unsigned>(node->vertexCount) * 12u);
}
input.Read(&node->textureVertexCount, 4);
input.Read(&node->sharedTextureVertices, 4);
if (node->sharedTextureVertices) {
node->frames[0].textureVertices.resize(static_cast<unsigned>(node->textureVertexCount));
input.Read(node->frames[0].textureVertices.data(), static_cast<unsigned>(node->textureVertexCount) * 12u);
} else {
for (AnimatedModelFrame& frame : node->frames) {
frame.textureVertices.resize(static_cast<unsigned>(node->textureVertexCount));
input.Read(frame.textureVertices.data(), static_cast<unsigned>(node->textureVertexCount) * 12u);
}
}
input.Read(&node->faceCount, 4);
node->faceNormals.resize(static_cast<unsigned>(node->faceCount));
node->faces.resize(static_cast<unsigned>(node->faceCount));
input.Read(node->faces.data(), static_cast<unsigned>(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;
}