Decompile 4 functions in Source/Resource/Models/ModelResource.cpp, Source/Resource/Models/ModelResource.h, ...
Batch #1726 by ragnarok-clanker: 0 byte-identical, 4 confirmed by peer review. - fn_08004 C3dGrannyModelRes::PlayAnimation: logic confirmed by peer review - fn_08005 GrannyAnimationLibrary::Load: logic confirmed by peer review - fn_08007 AnimatedModelNode::Shade: logic confirmed by peer review - fn_08008 C3dGrannyModelRes::ProjectBounds: logic confirmed by peer review Peer-reviewed-by: Yommy Reviewed-by: ragnarok-clanker
This commit is contained in:
parent
eba26963c9
commit
731d1b60ce
4 changed files with 475 additions and 6 deletions
|
|
@ -1,4 +1,9 @@
|
|||
#include "Client/Application/ErrorDialog.h"
|
||||
#include "Contents/Jobs/JobNames.h"
|
||||
#include "File/File.h"
|
||||
#include "File/ResourcePath.h"
|
||||
#include "Render/DeviceContext/DCSurface.h"
|
||||
#include "Render/Renderer.h"
|
||||
#include "Render/Textures/Texture.h"
|
||||
#include "Render/Textures/TextureCache.h"
|
||||
#include "Resource/Bitmap/BitmapResource.h"
|
||||
|
|
@ -6,9 +11,34 @@
|
|||
#include "Resource/Models/GrannyModelResource.h"
|
||||
#include "Resource/Models/ModelResource.h"
|
||||
#include "Resource/ResourceByteReader.h"
|
||||
#include "Utility/Math/AffineMatrix.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <list>
|
||||
#include <string>
|
||||
|
||||
// GLOBAL: gl_24708
|
||||
char g_modelPath[MAX_PATH];
|
||||
// GLOBAL: gl_24734
|
||||
char g_animationPath[MAX_PATH];
|
||||
// GLOBAL: gl_24707
|
||||
unsigned g_animationJobs[10] = {1288, 722, 1324, 1000, 1000, 1000, 1000, 1286, 1285, 1287};
|
||||
// GLOBAL: gl_24706
|
||||
const char* g_animationNames[5] = {"stand", "move", "attack", "dead", "damage"};
|
||||
|
||||
struct ShadeVector {
|
||||
ShadeVector() : x(0.0f), y(0.0f), z(0.0f) {
|
||||
}
|
||||
|
||||
/* 0000 */ float x;
|
||||
/* 0004 */ float y;
|
||||
/* 0008 */ float z;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ShadeVector) == 12);
|
||||
|
||||
// OFFSET: 0x00756D60
|
||||
// FUNCTION: fn_07997
|
||||
|
|
@ -432,3 +462,383 @@ void C3dGrannyModelRes::FreeControl(granny_control* control) {
|
|||
GrannyFreeControl(control);
|
||||
}
|
||||
}
|
||||
|
||||
// OFFSET: 0x007598B0
|
||||
// FUNCTION: fn_08019
|
||||
granny_file* GrannyAnimationLibrary::ReadAnimationFile(const char* name, const char* suffix) {
|
||||
CFile file;
|
||||
std::sprintf(g_animationPath, "%s%s.gr2", name, suffix);
|
||||
|
||||
if (!file.Open(g_animationPath, 0, true, false)) {
|
||||
const std::string path(g_animationPath);
|
||||
const std::string message = std::string("Cannot find File : ") + path;
|
||||
ShowErrorDialog(GetRenderSystemHolder()->system, message.c_str(), 0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
granny_file* result = GrannyReadEntireFileFromMemory(file.GetSize(), file.GetData());
|
||||
file.Close();
|
||||
return result;
|
||||
}
|
||||
|
||||
// OFFSET: 0x007594B0
|
||||
// FUNCTION: fn_08013
|
||||
void GrannyAnimationLibrary::FreeInstance() {
|
||||
if (GrannyModelInstance* instance = this->instance) {
|
||||
for (int index = 0; index < instance->meshCount; ++index) {
|
||||
GrannyMeshEntry& entry = instance->meshes[index];
|
||||
GrannyFreeMeshBinding(entry.binding);
|
||||
GrannyFreeMeshDeformer(entry.deformer);
|
||||
delete entry.indices;
|
||||
}
|
||||
|
||||
GrannyFreeModelInstance(instance->instance);
|
||||
GrannyFreeWorldPose(instance->worldPose);
|
||||
delete[] instance->meshes;
|
||||
}
|
||||
|
||||
delete this->instance;
|
||||
this->instance = nullptr;
|
||||
}
|
||||
|
||||
// OFFSET: 0x00757390
|
||||
// FUNCTION: fn_08005
|
||||
bool GrannyAnimationLibrary::Load(const char* name, int job, int action) {
|
||||
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->modelCount = 0;
|
||||
this->maxBoneCount = 0;
|
||||
this->textureCount = 0;
|
||||
this->unknown_0170 = 0;
|
||||
CFile file;
|
||||
|
||||
if (!file.Open(name, 0, true, false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->file = GrannyReadEntireFileFromMemory(file.GetSize(), file.GetData());
|
||||
file.Close();
|
||||
|
||||
if (!this->file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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->modelCount = info->modelCount;
|
||||
|
||||
if (this->instance) {
|
||||
this->FreeInstance();
|
||||
}
|
||||
|
||||
GrannyModelInstance* instance = new GrannyModelInstance;
|
||||
this->instance = instance;
|
||||
granny_model* model = info->models[0];
|
||||
const int boneCount = model->skeleton->boneCount;
|
||||
|
||||
if (this->maxBoneCount < boneCount) {
|
||||
this->maxBoneCount = boneCount;
|
||||
}
|
||||
|
||||
instance->instance = GrannyInstantiateModel(model);
|
||||
instance->worldPose = GrannyNewWorldPose(boneCount);
|
||||
instance->meshCount = model->meshBindingCount;
|
||||
instance->meshes = new GrannyMeshEntry[instance->meshCount];
|
||||
|
||||
for (int index = 0; index < instance->meshCount; ++index) {
|
||||
GrannyMeshEntry& entry = instance->meshes[index];
|
||||
granny_mesh* mesh = model->meshBindings[index].mesh;
|
||||
entry.mesh = mesh;
|
||||
granny_skeleton* skeleton = GrannyGetSourceSkeleton(instance->instance);
|
||||
entry.binding = GrannyNewMeshBinding(mesh, skeleton, skeleton);
|
||||
|
||||
const unsigned int indexCount = GrannyGetMeshIndexCount(mesh);
|
||||
entry.indices = new std::uint16_t[indexCount];
|
||||
if (indexCount != 0) {
|
||||
GrannyCopyMeshIndices(mesh, 2, entry.indices);
|
||||
}
|
||||
|
||||
const int vertexBytes = GrannyGetMeshVertexCount(mesh) * 32;
|
||||
if (GrannyMeshIsRigid(mesh)) {
|
||||
entry.deformer = nullptr;
|
||||
} else {
|
||||
if (this->deformBufferSize < vertexBytes) {
|
||||
this->deformBufferSize = vertexBytes;
|
||||
}
|
||||
|
||||
entry.deformer = GrannyNewMeshDeformer(GrannyGetMeshVertexType(mesh), GrannyPNT332VertexType, 2);
|
||||
if (!entry.deformer) {
|
||||
ShowErrorDialog(GetRenderSystemHolder()->system, "Granny didn't find a matching deformer for the vertex format used by mesh", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (info->animationCount != 0) {
|
||||
granny_animation* animation = nullptr;
|
||||
|
||||
if (char* extension = std::strchr(this->fileName, '.')) {
|
||||
*extension = '\0';
|
||||
}
|
||||
|
||||
if (granny_file* animationFile = this->ReadAnimationFile(this->fileName, "")) {
|
||||
if (granny_file_info* animationInfo = GrannyGetFileInfo(animationFile)) {
|
||||
if (!instance->instance || animationInfo->animationCount != 1) {
|
||||
ShowErrorDialog(GetRenderSystemHolder()->system, "This sample only supports single-animation files", 0);
|
||||
this->animations[job][action] = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
animation = animationInfo->animations[0];
|
||||
}
|
||||
}
|
||||
|
||||
this->animations[job][action] = animation;
|
||||
|
||||
if (!animation) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this->localPose = GrannyNewLocalPose(this->maxBoneCount);
|
||||
|
||||
if (this->deformBufferSize != 0) {
|
||||
this->unknown_0170 = 4;
|
||||
}
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
||||
GrannyFreeFileSection(this->file, 1);
|
||||
GrannyFreeFileSection(this->file, 2);
|
||||
GrannyFreeFileSection(this->file, 4);
|
||||
GrannyFreeFileSection(this->file, 5);
|
||||
this->unknown_0000 = 1920.0f;
|
||||
return result;
|
||||
}
|
||||
|
||||
// OFFSET: 0x00757270
|
||||
// FUNCTION: fn_08004
|
||||
granny_control* C3dGrannyModelRes::PlayAnimation(unsigned action, granny_model_instance* instance, granny_control* control, int loopCount) {
|
||||
if (control) {
|
||||
GrannySetControlActive(control, true);
|
||||
return control;
|
||||
}
|
||||
|
||||
const unsigned job = this->job;
|
||||
GrannyAnimationLibrary* library = this->animations;
|
||||
granny_animation* const& animation = library->animations[job][action];
|
||||
|
||||
if (!animation) {
|
||||
g_modelPath[0] = '\0';
|
||||
|
||||
if (job > 9 || action > 4) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (action == 0) {
|
||||
std::sprintf(g_modelPath, "model\\3dmob\\%s", this->m_context->jobNames.Lookup(g_animationJobs[job], -1));
|
||||
} else {
|
||||
std::sprintf(g_modelPath, "model\\3dmob_bone\\%d_%s.gr2", job, g_animationNames[action]);
|
||||
}
|
||||
|
||||
if (!g_modelPath[0] || !ResourceExists(g_modelPath, 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (job != 1) {
|
||||
library->Load(g_modelPath, job, action);
|
||||
}
|
||||
|
||||
if (!animation) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
granny_control* result = GrannyPlayControlledAnimation(0.0f, animation, instance);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GrannySetControlLoopCount(result, loopCount);
|
||||
return result;
|
||||
}
|
||||
|
||||
// OFFSET: 0x00758710
|
||||
// FUNCTION: fn_08008
|
||||
bool C3dGrannyModelRes::ProjectBounds(float* rect, const AffineMatrix& view, const AffineMatrix& world, const float* eye) const {
|
||||
AffineMatrix matrix;
|
||||
matrix.SetZRotation(3.141592f);
|
||||
const float* m = matrix.values;
|
||||
const float x = this->position[1] * m[3] + this->position[0] * m[0] + this->position[2] * m[6] + m[9];
|
||||
const float y = this->position[0] * m[1] + this->position[1] * m[4] + this->position[2] * m[7] + m[10];
|
||||
const float z = this->position[0] * m[2] + this->position[1] * m[5] + this->position[2] * m[8] + m[11];
|
||||
matrix.Compose(world, view);
|
||||
|
||||
const float screenX = y * m[3] + x * m[0] + z * m[6] + m[9];
|
||||
const float screenY = x * m[1] + y * m[4] + z * m[7] + m[10];
|
||||
const float depth = y * m[5] + x * m[2] + z * m[8] + m[11];
|
||||
const float deltaY = eye[1] - y;
|
||||
const float deltaX = eye[0] - x;
|
||||
const float deltaZ = eye[2] - z;
|
||||
const float distance = SquareRoot(deltaY * deltaY + deltaX * deltaX + deltaZ * deltaZ);
|
||||
const float slope = std::min(std::fabs(1.0f / distance * deltaY), 1.0f);
|
||||
const float height = this->unknown_03A4 - (this->unknown_03A4 - this->unknown_03A0) * slope;
|
||||
const float halfWidth = SquareRoot(this->unknown_03A0 * this->unknown_03A0 * 0.5f);
|
||||
const float halfHeight = SquareRoot(height * height * 0.5f);
|
||||
const float inverseDepth = 1.0f / depth;
|
||||
|
||||
rect[4] = inverseDepth;
|
||||
rect[0] = (screenX - halfWidth) * g_renderer->viewport.scaleX * inverseDepth + static_cast<float>(g_renderer->viewport.clipCenterX);
|
||||
rect[1] = (screenY - halfHeight) * g_renderer->viewport.scaleY * inverseDepth + static_cast<float>(g_renderer->viewport.clipCenterY);
|
||||
rect[2] = (halfWidth + screenX) * g_renderer->viewport.scaleX * inverseDepth + static_cast<float>(g_renderer->viewport.clipCenterX);
|
||||
rect[3] = (halfHeight + screenY) * g_renderer->viewport.scaleY * inverseDepth + static_cast<float>(g_renderer->viewport.clipCenterY);
|
||||
|
||||
const unsigned width = g_renderer->viewport.width;
|
||||
const unsigned screenHeight = g_renderer->viewport.height;
|
||||
return !(static_cast<unsigned>(rect[0]) > width && static_cast<unsigned>(rect[2]) > width && static_cast<unsigned>(rect[1]) > screenHeight &&
|
||||
static_cast<unsigned>(rect[3]) > screenHeight);
|
||||
}
|
||||
|
||||
// OFFSET: 0x00757850
|
||||
// FUNCTION: fn_08007
|
||||
void AnimatedModelNode::Shade(const float* matrix, int frame, const float* light, const float* diffuse, const float* ambient, int shadeType, int alpha) {
|
||||
// GLOBAL: gl_24735
|
||||
static ShadeVector faceNormals[30000];
|
||||
// GLOBAL: gl_24736
|
||||
static ShadeVector vertexNormals[30000];
|
||||
std::list<int> groups[32];
|
||||
auto* colors = reinterpret_cast<std::array<std::uint32_t, 3>*>(this->faceNormals);
|
||||
|
||||
if (shadeType == 0) {
|
||||
for (int index = 0; index < this->faceCount; ++index) {
|
||||
const std::uint32_t color = (alpha << 24) + 0xFFFFFF;
|
||||
colors[index][0] = color;
|
||||
colors[index][1] = color;
|
||||
colors[index][2] = color;
|
||||
}
|
||||
} else if (shadeType == 1) {
|
||||
for (int index = 0; index < this->faceCount; ++index) {
|
||||
const AnimatedModelFace& face = this->faces[index];
|
||||
const std::array<float, 3>* vertices = this->frames[frame].vertices;
|
||||
const std::array<float, 3>& v0 = vertices[face.vertices[0]];
|
||||
const std::array<float, 3>& v1 = vertices[face.vertices[1]];
|
||||
const std::array<float, 3>& v2 = vertices[face.vertices[2]];
|
||||
const float x2 = v2[0] * matrix[0] + v2[1] * matrix[3] + v2[2] * matrix[6] + matrix[9];
|
||||
const float y2 = v2[0] * matrix[1] + v2[1] * matrix[4] + v2[2] * matrix[7] + matrix[10];
|
||||
const float z2 = v2[0] * matrix[2] + v2[1] * matrix[5] + v2[2] * matrix[8] + matrix[11];
|
||||
const float ax = x2 - (matrix[0] * v0[0] + v0[1] * matrix[3] + v0[2] * matrix[6] + matrix[9]);
|
||||
const float ay = y2 - (matrix[1] * v0[0] + matrix[4] * v0[1] + matrix[7] * v0[2] + matrix[10]);
|
||||
const float az = z2 - (matrix[2] * v0[0] + matrix[5] * v0[1] + matrix[8] * v0[2] + matrix[11]);
|
||||
const float bx = x2 - (v1[0] * matrix[0] + v1[1] * matrix[3] + v1[2] * matrix[6] + matrix[9]);
|
||||
const float by = y2 - (v1[0] * matrix[1] + v1[1] * matrix[4] + v1[2] * matrix[7] + matrix[10]);
|
||||
const float bz = z2 - (v1[0] * matrix[2] + v1[1] * matrix[5] + v1[2] * matrix[8] + matrix[11]);
|
||||
const float nx = by * az - bz * ay;
|
||||
const float ny = bz * ax - bx * az;
|
||||
const float nz = bx * ay - by * ax;
|
||||
const float inverse = 1.0f / SquareRoot(ny * ny + nx * nx + nz * nz);
|
||||
const float intensity = std::max(0.0f, nx * inverse * light[0] + ny * inverse * light[1] + nz * inverse * light[2]);
|
||||
const float red = std::min(intensity * diffuse[0] + ambient[0], 1.0f);
|
||||
const float green = std::min(intensity * diffuse[1] + ambient[1], 1.0f);
|
||||
const float blue = std::min(intensity * diffuse[2] + ambient[2], 1.0f);
|
||||
|
||||
for (int corner = 0; corner < 3; ++corner) {
|
||||
const int source = this->sharedTextureVertices ? 0 : frame;
|
||||
const float scale = this->frames[source].textureVertices[face.textureVertices[corner]].shaded ? 128.0f : 255.0f;
|
||||
colors[index][corner] = ((((alpha << 8) | static_cast<int>(scale * red)) << 8 | static_cast<int>(scale * green)) << 8) | static_cast<int>(scale * blue);
|
||||
}
|
||||
}
|
||||
} else if (shadeType == 2) {
|
||||
for (int index = 0; index < this->faceCount; ++index) {
|
||||
const AnimatedModelFace& face = this->faces[index];
|
||||
const std::array<float, 3>* vertices = this->frames[frame].vertices;
|
||||
const std::array<float, 3>& v0 = vertices[face.vertices[0]];
|
||||
const std::array<float, 3>& v1 = vertices[face.vertices[1]];
|
||||
const std::array<float, 3>& v2 = vertices[face.vertices[2]];
|
||||
const float x2 = v2[0] * matrix[0] + v2[1] * matrix[3] + v2[2] * matrix[6] + matrix[9];
|
||||
const float y2 = v2[0] * matrix[1] + v2[1] * matrix[4] + v2[2] * matrix[7] + matrix[10];
|
||||
const float z2 = v2[0] * matrix[2] + v2[1] * matrix[5] + v2[2] * matrix[8] + matrix[11];
|
||||
const float ax = x2 - (v0[0] * matrix[0] + v0[1] * matrix[3] + v0[2] * matrix[6] + matrix[9]);
|
||||
const float ay = y2 - (matrix[1] * v0[0] + matrix[4] * v0[1] + matrix[7] * v0[2] + matrix[10]);
|
||||
const float az = z2 - (matrix[2] * v0[0] + matrix[5] * v0[1] + matrix[8] * v0[2] + matrix[11]);
|
||||
const float bx = x2 - (v1[0] * matrix[0] + v1[1] * matrix[3] + v1[2] * matrix[6] + matrix[9]);
|
||||
const float by = y2 - (v1[0] * matrix[1] + v1[1] * matrix[4] + v1[2] * matrix[7] + matrix[10]);
|
||||
const float bz = z2 - (v1[0] * matrix[2] + v1[1] * matrix[5] + v1[2] * matrix[8] + matrix[11]);
|
||||
ShadeVector& normal = faceNormals[index];
|
||||
normal.x = by * az - bz * ay;
|
||||
normal.y = ax * bz - bx * az;
|
||||
normal.z = bx * ay - ax * by;
|
||||
const float inverse = 1.0f / SquareRoot(normal.y * normal.y + normal.x * normal.x + normal.z * normal.z);
|
||||
normal.x = inverse * normal.x;
|
||||
normal.y = inverse * normal.y;
|
||||
normal.z = inverse * normal.z;
|
||||
groups[face.smoothGroup].push_back(index);
|
||||
}
|
||||
|
||||
for (int group = 0; group < 32; ++group) {
|
||||
if (groups[group].empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int vertex = 0; vertex < this->vertexCount; ++vertex) {
|
||||
ShadeVector& normal = vertexNormals[vertex];
|
||||
normal.x = 0.0f;
|
||||
normal.y = 0.0f;
|
||||
normal.z = 0.0f;
|
||||
|
||||
for (int index = 0; index < this->faceCount; ++index) {
|
||||
const AnimatedModelFace& face = this->faces[index];
|
||||
if (face.smoothGroup == group && (face.vertices[0] == vertex || face.vertices[1] == vertex || face.vertices[2] == vertex)) {
|
||||
normal.x = faceNormals[index].x + normal.x;
|
||||
normal.y = faceNormals[index].y + normal.y;
|
||||
normal.z = normal.z + faceNormals[index].z;
|
||||
}
|
||||
}
|
||||
|
||||
if (SquareRoot(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z) != 0.0f) {
|
||||
const float inverse = 1.0f / SquareRoot(normal.x * normal.x + normal.y * normal.y + normal.z * normal.z);
|
||||
normal.x = inverse * normal.x;
|
||||
normal.y = inverse * normal.y;
|
||||
normal.z = inverse * normal.z;
|
||||
}
|
||||
}
|
||||
|
||||
for (int index : groups[group]) {
|
||||
const AnimatedModelFace& face = this->faces[index];
|
||||
|
||||
for (int corner = 0; corner < 3; ++corner) {
|
||||
const ShadeVector& normal = vertexNormals[face.vertices[corner]];
|
||||
const int source = this->sharedTextureVertices ? 0 : frame;
|
||||
const float intensity = std::max(0.0f, normal.y * light[1] + light[0] * normal.x + normal.z * light[2]);
|
||||
const float red = std::min(intensity * diffuse[0] + ambient[0], 1.0f);
|
||||
const float green = std::min(intensity * diffuse[1] + ambient[1], 1.0f);
|
||||
const float blue = std::min(intensity * diffuse[2] + ambient[2], 1.0f);
|
||||
const float scale = this->frames[source].textureVertices[face.textureVertices[corner]].shaded ? 128.0f : 255.0f;
|
||||
colors[index][corner] = ((((alpha << 8) | static_cast<int>(scale * red)) << 8 | static_cast<int>(green * scale)) << 8) | static_cast<int>(blue * scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int index = 0; index < this->faceCount; ++index) {
|
||||
const std::uint32_t color = (alpha << 24) + 0xFFFFFF;
|
||||
colors[index][0] = color;
|
||||
colors[index][1] = color;
|
||||
colors[index][2] = color;
|
||||
}
|
||||
|
||||
ShowErrorDialog(GetRenderSystemHolder()->system, "Invalid Shade Type", 0);
|
||||
}
|
||||
|
||||
for (AnimatedModelNode* child : this->children) {
|
||||
child->Shade(matrix, frame, light, diffuse, ambient, shadeType, alpha);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue