rodecomp/Source/Contents/Models/ModelActor.cpp
ragnarok-clanker e168b4e4a6 Restore translation-unit ownership and publish verified source corrections
Owner-authorized source migration: 31 helper-only translation units were merged into their callers or existing inline headers; both build manifests updated. Includes the preserved source payload, Lua configuration matching wowdecomp, and exact malloca expansion without diagnostic pragmas.

449 native units compiled locally and 449 staged units compiled by the pinned server toolchain. No previously byte-matched function regressed. Compiler mapping diagnostics remain recorded as needs_cleanup, not accepted or peer-confirmed. Existing independent review records are preserved. README.md is intentionally unchanged.
2026-10-25 15:42:21 +00:00

280 lines
7.2 KiB
C++

#include "Contents/Models/ModelActor.h"
#include <cstring>
#include <windows.h>
namespace {
// INLINED
float Radians(float degrees) {
return (degrees / 180.0f) * 3.141592f;
}
}
// OFFSET: 0x00AB59A0
// FUNCTION: fn_17461
C3dActor::C3dActor()
: position{}, rotation{}, scale{1, 1, 1}, offset{}, dirty(1), oriented{}, corners{}, minimum{}, maximum{}, enabled(1), renderFrame(0), occlusionEnabled(0),
occluded(0), alpha(255), commandMode(0), color(0xFFFFFFFFu), playback(2), speed(0), shade(0) {
}
// OFFSET: 0x00AB6420
// FUNCTION: fn_17470
C3dActor::~C3dActor() {
for (ModelVolume*& volume : this->volumes) {
delete volume;
volume = nullptr;
}
}
// OFFSET: 0x00AB5BB0
// FUNCTION: fn_17462
C3dActorV1::C3dActorV1() : root(nullptr), frame(0), animationLength(0), lastTick(0) {
}
// INLINED
C3dActorV1::~C3dActorV1() {
delete this->root;
this->root = nullptr;
}
// OFFSET: 0x00AB5C30
// FUNCTION: fn_17463
C3dActorV2::C3dActorV2() : framesPerSecond(0), duration(0), startTick(GetTickCount()) {
}
// INLINED
C3dActorV2::~C3dActorV2() {
for (C3dNodeV2*& root : this->roots) {
delete root;
root = nullptr;
}
}
// OFFSET: 0x00AB6E90
// FUNCTION: fn_17479
void C3dActorV1::Assign(const C3dModelRes& resource) {
this->shade = resource.shade;
this->animationLength = static_cast<const C3dModelResV1&>(resource).animationLength;
this->root = new C3dNodeV1;
this->root->model = this;
this->root->Initialize(*resource.nodes.front());
this->Animate(0);
this->UpdateBounds();
}
// OFFSET: 0x00AB6F50
// FUNCTION: fn_17480
void C3dActorV2::Assign(const C3dModelRes& resource) {
const auto& modern = static_cast<const C3dModelResV2&>(resource);
this->shade = resource.shade;
this->framesPerSecond = modern.framesPerSecond;
const float inverse = 1.0f / modern.framesPerSecond;
const int milliseconds = 1000 * modern.animationLength;
this->duration = static_cast<float>(milliseconds) * inverse;
for (const std::string& name : resource.rootNames) {
auto* root = new C3dNodeV2;
root->model = this;
root->Initialize(*resource.FindNode(name.c_str()));
this->roots.push_back(root);
}
this->Animate(0);
this->UpdateBounds();
}
// OFFSET: 0x00ABB890
// FUNCTION: fn_17516
void C3dActor::UpdateTransform() {
this->transform.SetTranslation(this->offset[0], this->offset[1], this->offset[2]);
this->transform.Scale(this->scale[0], this->scale[1], this->scale[2]);
this->transform.RotateY(Radians(this->rotation[1]));
this->transform.RotateX(Radians(this->rotation[0]));
this->transform.RotateZ(Radians(this->rotation[2]));
this->transform.Translate(this->position);
this->inverseTransform.Inverse(this->transform);
this->dirty = 0;
}
// OFFSET: 0x00ABB970
// FUNCTION: fn_17517
void C3dActorV2::UpdateTransform() {
this->transform.Identity();
this->transform.values[4] = -1.0f;
this->transform.Scale(this->scale[0], this->scale[1], this->scale[2]);
this->transform.RotateY(Radians(this->rotation[1]));
this->transform.RotateX(Radians(this->rotation[0]));
this->transform.RotateZ(Radians(this->rotation[2]));
this->transform.Translate(this->position);
this->inverseTransform.Inverse(this->transform);
this->dirty = 0;
}
// OFFSET: 0x00ABBA60
// FUNCTION: fn_17519
void C3dActorV1::Prepare(ModelLightingContext& context) {
if (this->dirty == 1) {
this->UpdateTransform();
}
const float* previousPosition = context.position;
const std::uint32_t* previousColor = context.color;
const int* previousShade = context.shade;
context.position = this->position;
context.color = &this->color;
context.shade = &this->shade;
this->root->Prepare(this->transform.values, context);
context.position = previousPosition;
context.color = previousColor;
context.shade = previousShade;
}
// OFFSET: 0x00ABBAA0
// FUNCTION: fn_17520
void C3dActorV2::Prepare(ModelLightingContext& context) {
if (this->dirty == 1) {
this->UpdateTransform();
}
const float* previousPosition = context.position;
const std::uint32_t* previousColor = context.color;
const int* previousShade = context.shade;
context.position = this->position;
context.color = &this->color;
context.shade = &this->shade;
for (C3dNodeV2* root : this->roots) {
root->Prepare(this->transform.values, context);
}
context.position = previousPosition;
context.color = previousColor;
context.shade = previousShade;
}
// OFFSET: 0x00AB9070
// FUNCTION: fn_17504
void C3dActorV1::Animate(double frame) {
this->root->Animate(frame);
this->dirty = 1;
}
// OFFSET: 0x00AB90A0
// FUNCTION: fn_17505
void C3dActorV2::Animate(double frame) {
const float selected = static_cast<float>(static_cast<double>(this->framesPerSecond) * frame / 1000.0);
for (C3dNodeV2* root : this->roots) {
root->Animate(static_cast<double>(selected));
}
this->dirty = 1;
}
// OFFSET: 0x00AB9010
// FUNCTION: fn_17501
void C3dActor::SetSpeed(float speed) {
this->speed = speed;
if (speed != 0.0f) {
this->speed = speed / 3.0f;
}
}
// OFFSET: 0x00AB9040
// FUNCTION: fn_17502
void C3dActorV2::SetSpeed(float speed) {
this->speed = speed;
}
// OFFSET: 0x00AB7860
// FUNCTION: fn_17489
C3dNode* C3dActorV1::Find(const char* name) {
return this->root->Find(name);
}
// OFFSET: 0x00AB7870
// FUNCTION: fn_17490
C3dNode* C3dActorV2::Find(const char* name) {
for (C3dNodeV2* root : this->roots) {
if (C3dNode* found = root->Find(name)) {
return found;
}
}
return nullptr;
}
// OFFSET: 0x00AB94D0
// FUNCTION: fn_17510
void C3dActor::CopyVolumes(const std::list<ModelVolume*>& source) {
for (const ModelVolume* volume : source) {
this->volumes.push_back(new ModelVolume(*volume));
}
}
// INLINED
bool C3dActor::Reversed() const {
bool result = this->scale[0] >= 0.0f;
if (this->scale[1] >= 0.0f) {
result = this->scale[0] < 0.0f;
}
return this->scale[2] < 0.0f ? !result : result;
}
// INLINED
void C3dActor::Fade() {
if (this->occluded == 1) {
if (this->alpha > 90) {
this->alpha = static_cast<unsigned char>(this->alpha - 5);
} else {
this->occluded = 0;
}
} else if (this->alpha != 255) {
this->alpha = static_cast<unsigned char>(this->alpha + 5);
}
}
// OFFSET: 0x00AB7B50
// FUNCTION: fn_17496
void C3dActorV1::Draw(const float* matrix, bool clip, ModelRenderContext& context) {
if (this->dirty == 1) {
this->UpdateTransform();
}
AffineMatrix camera;
std::memcpy(camera.values, matrix, sizeof(camera.values));
AffineMatrix combined;
combined.Compose(this->transform, camera);
this->root->Draw(combined.values, this->Reversed(), clip, this->commandMode, context);
if (this->occlusionEnabled == 1) {
this->Fade();
}
}
// OFFSET: 0x00AB7C40
// FUNCTION: fn_17497
void C3dActorV2::Draw(const float* matrix, bool clip, ModelRenderContext& context) {
if (this->dirty == 1) {
this->UpdateTransform();
}
AffineMatrix camera;
std::memcpy(camera.values, matrix, sizeof(camera.values));
AffineMatrix combined;
combined.Compose(this->transform, camera);
const bool reversed = this->Reversed();
for (C3dNodeV2* root : this->roots) {
root->Draw(combined.values, reversed, clip, this->commandMode, context);
if (this->occlusionEnabled == 1) {
this->Fade();
}
}
}