rodecomp/Source/Resource/Models/ModelResourceV2.cpp
ragnarok-clanker 5ad8e0c58c Maintenance: Resubmission of 16340 over main 047268c0 (conflict in UICashEmotionPack::GetPriceText resolved keeping main's new body).
Batch #16351 by ragnarok-clanker: no function's compiled code changes; checked with 784 compiled unit(s), including every includer of Source/Archive/GPak.h, Source/Client/Browser/BrowserCommandTarget.h, Source/Client/Browser/BrowserDocumentUI.h, Source/Client/Browser/BrowserInPlace.h, Source/Client/Browser/WebBrowser.h, Source/Client/Input/Language.h, Source/Client/Input/LanguageTC.h, Source/Client/Input/Mouse.h, Source/Client/Mode/CGameMode.h, Source/Client/Mode/CLoginMode.h, Source/Client/Replay.h, Source/Client/Session.h, Source/Client/Stove/StovePCSDK.h, Source/Contents/AchievementMgr.h, Source/Contents/Actors/CharacterRender.h, Source/Contents/Actors/EffectActors.h, Source/Contents/Actors/GameActor.h, Source/Contents/Actors/GndRiser.h, Source/Contents/Actors/GrannyGameActor.h, Source/Contents/Actors/MousePointer.h, Source/Contents/Actors/Npc.h, Source/Contents/Actors/Pc.h, Source/Contents/Actors/RenderObjectState.h, Source/Contents/Actors/Skill.h, Source/Contents/AdventureGuideMgr.h, Source/Contents/CashEmotionMgr.h, Source/Contents/ClanInfo.h, Source/Contents/Configuration/GameSettingsMgr.h, Source/Contents/EffectResourceSwitcher.h, Source/Contents/Effects/AcidifiedThrow.h, Source/Contents/Effects/ArrowEffect.h, Source/Contents/Effects/AxeBoomerang.h, Source/Contents/Effects/BlastForge.h, Source/Contents/Effects/CharacterParticleBundle.h, Source/Contents/Effects/DummyEffectLauncher.h, Source/Contents/Effects/EZ2STREffectEx.h, Source/Contents/Effects/Effect.h, Source/Contents/Effects/EffectActor.h, Source/Contents/Effects/EffectEmitter.h, Source/Contents/Effects/EffectPrimitiveState.h, Source/Contents/Effects/EffectTypes.h, Source/Contents/Effects/ElectricEffect.h, Source/Contents/Effects/EmitterEffect.h, Source/Contents/Effects/FortuneKissEffect.h, Source/Contents/Effects/MagicFloorEffect.h, Source/Contents/Effects/MandraAttackEffect.h, Source/Contents/Effects/MonsterSkillEffects.h, Source/Contents/Effects/MsgEffect.h, Source/Contents/Effects/Particle.h, Source/Contents/Effects/RagEffectState.h, Source/Contents/Effects/RichManKim.h, Source/Contents/Effects/SpriteParticle.h, Source/Contents/Effects/StarEffect.h, Source/Contents/Effects/WhirlwindEffect.h, Source/Contents/EnchantMgr.h, Source/Contents/FlyingShipRouteInfo.h, Source/Contents/Ground/GroundSpatial.h, Source/Contents/GuildInfo.h, Source/Contents/Hotkeys/InterfaceHotkey.h, Source/Contents/Items/ItemInfo.h, Source/Contents/Items/RuneSystem.h, Source/Contents/MapSkyMgr.h, Source/Contents/Models/ModelActor.h, Source/Contents/Movies/BinkMovie.h, Source/Contents/NavigationManager.h, Source/Contents/Party/PartyBookingMgr.h, Source/Contents/PetEvolutionMgr.h, Source/Contents/RenewQuestMgr.h, Source/Contents/RodexMailStore.h, Source/Contents/Schedule/Schedule.h, Source/Contents/Schedule/ShutDownSchedule.h, Source/Contents/SignBoard/SignBoard.h, Source/Contents/Skills/PlayerSkillBundle.h, Source/Contents/Sound/SoundMgr.h, Source/Contents/SpriteLayerDrawing.h, Source/Contents/Store/MCStoreInfoBackupAsyncWork.h, Source/Contents/UserData/AccountLinkedUserDataLoadAsyncWork.h, Source/Contents/UserData/NormalItemSlots.h, Source/Contents/WorldView/WorldViewData.h, Source/Network/Connection/Connection.h, Source/Render/DX7/DeviceOwnerDX7.h, Source/Render/DX7/DirectDrawEnumeration.h, Source/Render/DX9/DeviceOwnerDX9.h, Source/Render/Primitives/LightmappedPrimitive.h, Source/Render/Primitives/RenderPrimitive.h, Source/Render/Renderer.h, Source/Render/RendererDX7.h, Source/Render/Text/EmbedFont.h, Source/Render/Text/TextContext.h, Source/Render/Textures/Texture.h, Source/Resource/BinkRuntime.h, Source/Resource/GifResource.h, Source/Resource/Models/AnimatedModelResource.h, Source/Resource/Models/GrannyModelResource.h, Source/Resource/Models/ModelMesh.h, Source/Resource/ResourceMgr.h, Source/Resource/World/GroundResource.h, Source/Resource/World/WorldResource.h, Source/Security/NProtectGameGuardMgr.h, Source/Security/ReassemblyPacketMgr.h, Source/UI/Buttons/UIBmpRadioBtn.h, Source/UI/Buttons/UIBmpTextButton2.h, Source/UI/Buttons/UIDescriptRace.h, Source/UI/Buttons/UIItemTagButton.h, Source/UI/Buttons/UITVTextButton.h, Source/UI/Buttons/UITextureButton.h, Source/UI/Chat/UINewChatWnd.h, Source/UI/Chat/UISubChatHisWnd.h, Source/UI/Dialogs/UILoadingWnd.h, Source/UI/Dialogs/UIMiniMapWnd.h, Source/UI/Dialogs/UIPasswordWnd.h, Source/UI/Dialogs/UIRestartWnd.h, Source/UI/Dialogs/UISayDialogWnd.h, Source/UI/Edit/UIEditCtrl.h, Source/UI/Enchant/EnchantUI.h, Source/UI/GameSettings/GameSettingsUI.h, Source/UI/Guild/UIGuildWnd.h, Source/UI/Item/UIItemStoreFindWnd.h, Source/UI/Item/UIItemStoreSubWnd.h, Source/UI/Item/UIItemStoreWnd.h, Source/UI/Items/UIEquipWnd.h, Source/UI/Items/UIExchangeWnd.h, Source/UI/Items/UIItemCollectionWnd.h, Source/UI/Items/UIItemRepairWnd.h, Source/UI/Items/UISwapEquipmentWnd.h, Source/UI/Lists/TableBox.h, Source/UI/Lists/UIListBox.h, Source/UI/Lists/UIListBoxEx.h, Source/UI/Lists/UIListBoxID.h, Source/UI/Lists/UIListBoxTemplate.h, Source/UI/Lists/UITabControl.h, Source/UI/Lists/UITreeView.h, Source/UI/Lists/UIVScrollBar.h, Source/UI/Navigation/UINavigationV4Wnd.h, Source/UI/Navigation/UIPrivateAirplaneWnd.h, Source/UI/Party/UIAdvenPartyBoardWnd.h, Source/UI/Party/UIRequestJoinPartyWnd.h, Source/UI/ProbabilityTable/UIProbabilityTable.h, Source/UI/Quest/UIRenewQuestWnd.h, Source/UI/Rodex/UIRodexComposeWnd.h, Source/UI/RuneSystem/UIRuneSystemWindows.h, Source/UI/RuneSystem/UIRuneSystemWnd.h, Source/UI/Shop/UIMerchantItemMyShopWnd.h, Source/UI/Shop/UIMerchantShopMakeWnd.h, Source/UI/Shop/UIMerchantShopTitle.h, Source/UI/StylingShop/UIStylingShopWnd.h, Source/UI/Text/CreateLineInfo.h, Source/UI/Text/TextMarkup.h, Source/UI/Text/UIString.h, Source/UI/Text/UITextViewer.h, Source/UI/Text/UITransBalloonText.h, Source/UI/UIChangeMaterialWnd.h, Source/UI/UIMacroDetectorWnd.h, Source/UI/UIMacroUserInfoWnd.h, Source/UI/Window/UIAccountLimitedToolWnd.h, Source/UI/Window/UIBalloonOpenBtn.h, Source/UI/Window/UIBasicInfoWnd.h, Source/UI/Window/UIBattleMsgOptionWnd.h, Source/UI/Window/UICollectionSystemWnd.h, Source/UI/Window/UIControl.h, Source/UI/Window/UIHotKeyWnd.h, Source/UI/Window/UIJobListWnd.h, Source/UI/Window/UILoginWnd.h, Source/UI/Window/UIMakeCharWnd.h, Source/UI/Window/UIMerInfoWnd.h, Source/UI/Window/UIMessageBox.h, Source/UI/Window/UIMonsterDisplayWnd.h, Source/UI/Window/UINewMakeCharWnd.h, Source/UI/Window/UINewSkillListWnd.h, Source/UI/Window/UINoticeWnd.h, Source/UI/Window/UIReplayRECControlWnd.h, Source/UI/Window/UIRoMapWnd.h, Source/UI/Window/UISecondCostumeWnd.h, Source/UI/Window/UISeekPartyListWnd.h, Source/UI/Window/UISeekPartyMBWnd.h, Source/UI/Window/UISeekPartyWnd.h, Source/UI/Window/UISerachStoreInfoWnd.h, Source/UI/Window/UIWindow.h, Source/UI/Window/UIWindowMgr.h, Source/UI/Window/WindowId.h, Source/Utility/Async/NAsyncWork.h, Source/Utility/Async/NRecycleObjectPoolMgr.h, Source/Utility/JsonCppLoader.h, Source/Utility/Lexicon/LexiconSerializer.h, Source/Utility/NFileLog.h, Source/Utility/Threading/NThread.h.
2026-11-10 17:38:25 +00:00

482 lines
14 KiB
C++

#include "Render/DeviceContext/DCSurface.h"
#include "File/File.h"
#include "Client/Application/ErrorDialog.h"
#include "Render/Textures/Texture.h"
#include "Render/Textures/TextureCache.h"
#include "Resource/Bitmap/BitmapResource.h"
#include "Resource/Models/ModelResource.h"
#include "Resource/ResourceMgr.h"
#include <algorithm>
#include <cstring>
struct ModelFaceRecord {
/* 0000 */ std::uint16_t vertices[3];
/* 0006 */ std::uint16_t textureVertices[3];
/* 000C */ std::uint16_t texture;
/* 000E */ std::uint16_t padding_000E;
/* 0010 */ std::uint32_t twoSided;
/* 0014 */ std::uint32_t smoothing;
};
// OFFSET: 0x007555C0
// FUNCTION: fn_07984
C3dModelResV2::C3dModelResV2() : framesPerSecond(0.0f), animationLength(0) {
}
// INLINED
C3dModelResV2::~C3dModelResV2() = default;
// OFFSET: 0x00751ED0
// FUNCTION: fn_07953
C3dModelResV2* C3dModelResV2::Create() {
return new C3dModelResV2;
}
// INLINED
inline void C3dModelResV2::ReadVersion(CFile& input, unsigned minor) {
switch (minor) {
case 1:
this->ReadVersion1(input);
break;
case 2:
this->ReadVersion2(input);
break;
case 3:
this->ReadVersion3(input);
break;
}
}
// OFFSET: 0x00751F70
// FUNCTION: fn_07954
bool C3dModelResV2::Load(const char* name) {
CFile file;
if (!file.Open(name, 0, true, false)) {
return false;
}
char magic[5]{};
file.Read(magic, 4);
if (std::strcmp(magic, "GRSM") != 0) {
ShowErrorDialog("모델파일이 아닙니다. 형식을 다시한번 확인해주세요.", 0);
return false;
}
unsigned char major = 0;
unsigned char minor = 0;
file.Read(&major, 1);
this->major = major;
file.Read(&minor, 1);
this->ReadVersion(file, minor);
file.Close();
this->sourceName = name;
return true;
}
// OFFSET: 0x00759ED0
// FUNCTION: fn_08020
void C3dModelResV2::ReadVersion1(CFile& input) {
input.Read(&this->animationLength, 4);
input.Read(&this->shade, 4);
input.Read(&this->alpha, 1);
input.Read(&this->framesPerSecond, 4);
int textureCount = 0;
input.Read(&textureCount, 4);
std::vector<std::string> textureNames;
for (int index = 0; index < textureCount; ++index) {
unsigned bytes = 0;
input.Read(&bytes, 4);
char name[40]{};
input.Read(name, bytes);
textureNames.push_back(name);
}
int rootCount = 0;
input.Read(&rootCount, 4);
for (int index = 0; index < rootCount; ++index) {
unsigned bytes = 0;
input.Read(&bytes, 4);
char name[40]{};
input.Read(name, bytes);
this->rootNames.push_back(name);
}
int nodeCount = 0;
input.Read(&nodeCount, 4);
for (int index = 0; index < nodeCount; ++index) {
auto* node = new ModelNodeV2;
node->model = this;
node->mesh.reset(new ModelMesh);
int bytes = 0;
input.Read(&bytes, 4);
char name[40]{};
input.Read(name, bytes);
node->name = name;
bytes = 0;
input.Read(&bytes, 4);
if (bytes > 0) {
char parentName[40]{};
input.Read(parentName, bytes);
node->parentName = parentName;
}
int nodeTextureCount = 0;
input.Read(&nodeTextureCount, 4);
for (int texture = 0; texture < nodeTextureCount; ++texture) {
int textureIndex = 0;
input.Read(&textureIndex, 4);
CTexture* loaded = g_texMgr.Load(textureNames[textureIndex].c_str(), 0, false, true, false);
node->textures.push_back(loaded);
if (loaded) {
loaded->Retain();
}
}
input.Read(node->transform, sizeof(node->transform));
int vertexCount = 0;
input.Read(&vertexCount, 4);
node->mesh->vertexCount = vertexCount;
auto* vertices = new std::array<float, 3>[vertexCount]{};
input.Read(vertices, static_cast<unsigned>(vertexCount) * 12u);
node->mesh->vertices = vertices;
int textureVertexCount = 0;
input.Read(&textureVertexCount, 4);
node->mesh->textureVertexCount = textureVertexCount;
ModelTextureVertex* textureVertices = new ModelTextureVertex[textureVertexCount];
input.Read(textureVertices, static_cast<unsigned>(textureVertexCount) * 12u);
node->mesh->textureVertices = textureVertices;
int faceCount = 0;
input.Read(&faceCount, 4);
node->mesh->faceCount = faceCount;
ModelFace* faces = new ModelFace[faceCount];
for (int face = 0; face < faceCount; ++face) {
ModelFace& target = faces[face];
ModelFaceRecord record;
input.Read(&record, sizeof(record));
for (int corner = 0; corner < 3; ++corner) {
target.vertices[corner] = record.vertices[corner];
}
for (int corner = 0; corner < 3; ++corner) {
target.textureVertices[corner] = record.textureVertices[corner];
}
target.texture = record.texture;
target.twoSided = record.twoSided;
if (target.smoothing.empty()) {
target.smoothing.push_back(record.smoothing);
}
}
node->mesh->faces = faces;
std::sort(node->mesh->faces, node->mesh->faces + node->mesh->faceCount, [](const ModelFace& a, const ModelFace& b) { return a.texture < b.texture; });
node->mesh->CalculateNormals();
node->scaleKeys.Read(input);
node->rotationKeys.Read(input);
node->positionKeys.Read(input);
this->nodes.push_back(node);
this->meshes[node->name] = node->mesh.get();
}
for (ModelNode* node : this->nodes) {
if (!node->parentName.empty()) {
ModelNode* parent = this->FindNode(node->parentName.c_str());
if (parent) {
node->parent = parent;
parent->children.push_back(node);
}
}
}
{
int volumeCount = 0;
input.Read(&volumeCount, 4);
for (int index = 0; index < volumeCount; ++index) {
auto* volume = new ModelVolume;
std::memset(volume, 0, 132);
input.Read(volume, sizeof(ModelVolume));
this->volumes.push_back(volume);
}
}
}
// OFFSET: 0x0075AB20
// FUNCTION: fn_08021
void C3dModelResV2::ReadVersion2(CFile& input) {
input.Read(&this->animationLength, 4);
input.Read(&this->shade, 4);
input.Read(&this->alpha, 1);
input.Read(&this->framesPerSecond, 4);
int textureCount = 0;
input.Read(&textureCount, 4);
std::vector<std::string> textureNames;
for (int index = 0; index < textureCount; ++index) {
unsigned bytes = 0;
input.Read(&bytes, 4);
char name[40]{};
input.Read(name, bytes);
textureNames.push_back(name);
}
int rootCount = 0;
input.Read(&rootCount, 4);
for (int index = 0; index < rootCount; ++index) {
unsigned bytes = 0;
input.Read(&bytes, 4);
char name[40]{};
input.Read(name, bytes);
this->rootNames.push_back(name);
}
int nodeCount = 0;
input.Read(&nodeCount, 4);
for (int index = 0; index < nodeCount; ++index) {
auto* node = new ModelNodeV2;
node->model = this;
node->mesh.reset(new ModelMesh);
unsigned bytes = 0;
input.Read(&bytes, 4);
char name[40]{};
input.Read(name, bytes);
node->name = name;
bytes = 0;
input.Read(&bytes, 4);
if (static_cast<int>(bytes) > 0) {
char parentName[40]{};
input.Read(parentName, bytes);
node->parentName = parentName;
}
int nodeTextureCount = 0;
input.Read(&nodeTextureCount, 4);
for (int texture = 0; texture < nodeTextureCount; ++texture) {
int textureIndex = 0;
input.Read(&textureIndex, 4);
CTexture* loaded = g_texMgr.Load(textureNames[textureIndex].c_str(), 0, false, true, false);
node->textures.push_back(loaded);
if (loaded) {
loaded->Retain();
}
}
input.Read(node->transform, sizeof(node->transform));
ModelMesh& mesh = *node->mesh;
input.Read(&mesh.vertexCount, 4);
mesh.vertices = new std::array<float, 3>[mesh.vertexCount]{};
input.Read(mesh.vertices, static_cast<unsigned>(mesh.vertexCount) * 12u);
input.Read(&mesh.textureVertexCount, 4);
mesh.textureVertices = new ModelTextureVertex[mesh.textureVertexCount];
input.Read(mesh.textureVertices, static_cast<unsigned>(mesh.textureVertexCount) * 12u);
input.Read(&mesh.faceCount, 4);
mesh.faces = new ModelFace[mesh.faceCount];
for (int face = 0; face < mesh.faceCount; ++face) {
ModelFace& target = mesh.faces[face];
unsigned size = 0;
input.Read(&size, 4);
std::vector<unsigned char> record(size);
input.Read(record.data(), size);
std::memcpy(&target, record.data(), 20);
const unsigned groups = (size - 20u) / 4u;
target.smoothing.resize(groups);
std::memcpy(target.smoothing.data(), record.data() + 20, groups * 4u);
}
std::sort(mesh.faces, mesh.faces + mesh.faceCount, [](const ModelFace& a, const ModelFace& b) { return a.texture < b.texture; });
mesh.CalculateNormals();
node->scaleKeys.Read(input);
node->rotationKeys.Read(input);
node->positionKeys.Read(input);
this->nodes.push_back(node);
this->meshes[node->name] = node->mesh.get();
}
for (ModelNode* node : this->nodes) {
if (!node->parentName.empty()) {
ModelNode* parent = this->FindNode(node->parentName.c_str());
if (parent) {
node->parent = parent;
parent->children.push_back(node);
}
}
}
int volumeCount = 0;
input.Read(&volumeCount, 4);
for (int index = 0; index < volumeCount; ++index) {
auto* volume = new ModelVolume;
std::memset(volume, 0, 132);
input.Read(volume, sizeof(ModelVolume));
this->volumes.push_back(volume);
}
}
// OFFSET: 0x0075B8E0
// FUNCTION: fn_08022
void C3dModelResV2::ReadVersion3(CFile& input) {
input.Read(&this->animationLength, 4);
input.Read(&this->shade, 4);
input.Read(&this->alpha, 1);
input.Read(&this->framesPerSecond, 4);
int rootCount = 0;
input.Read(&rootCount, 4);
for (int index = 0; index < rootCount; ++index) {
unsigned bytes = 0;
input.Read(&bytes, 4);
char name[40]{};
input.Read(name, bytes);
this->rootNames.push_back(name);
}
int nodeCount = 0;
input.Read(&nodeCount, 4);
for (int index = 0; index < nodeCount; ++index) {
auto* node = new ModelNodeV2;
node->model = this;
node->mesh.reset(new ModelMesh);
{
int bytes = 0;
input.Read(&bytes, 4);
char name[40]{};
input.Read(name, bytes);
node->name = name;
bytes = 0;
input.Read(&bytes, 4);
if (bytes > 0) {
char parentName[40]{};
input.Read(parentName, bytes);
node->parentName = parentName;
}
}
int nodeTextureCount = 0;
input.Read(&nodeTextureCount, 4);
for (int texture = 0; texture < nodeTextureCount; ++texture) {
unsigned textureBytes = 0;
input.Read(&textureBytes, 4);
char textureName[40]{};
input.Read(textureName, textureBytes);
CTexture* loaded = g_texMgr.Load(textureName, 0, false, true, false);
node->textures.push_back(loaded);
if (loaded) {
loaded->Retain();
}
}
input.Read(node->transform, sizeof(node->transform));
int vertexCount = 0;
input.Read(&vertexCount, 4);
node->mesh->vertexCount = vertexCount;
auto* vertices = new std::array<float, 3>[vertexCount]{};
input.Read(vertices, static_cast<unsigned>(vertexCount) * 12u);
node->mesh->vertices = vertices;
int textureVertexCount = 0;
input.Read(&textureVertexCount, 4);
node->mesh->textureVertexCount = textureVertexCount;
ModelTextureVertex* textureVertices = new ModelTextureVertex[textureVertexCount];
input.Read(textureVertices, static_cast<unsigned>(textureVertexCount) * 12u);
node->mesh->textureVertices = textureVertices;
int faceCount = 0;
input.Read(&faceCount, 4);
node->mesh->faceCount = faceCount;
ModelFace* faces = new ModelFace[faceCount];
for (int face = 0; face < faceCount; ++face) {
ModelFace& target = faces[face];
unsigned size = 0;
input.Read(&size, 4);
std::vector<unsigned char> buffer;
buffer.resize(size);
input.Read(buffer.data(), size);
ModelFaceRecord record;
std::memcpy(&record, buffer.data(), 20);
for (int corner = 0; corner < 3; ++corner) {
target.vertices[corner] = record.vertices[corner];
}
for (int corner = 0; corner < 3; ++corner) {
target.textureVertices[corner] = record.textureVertices[corner];
}
target.texture = record.texture;
target.twoSided = record.twoSided;
const unsigned groups = (buffer.size() - 20u) / 4u;
target.smoothing.resize(groups);
std::memcpy(target.smoothing.data(), buffer.data() + 20, groups * 4u);
}
node->mesh->faces = faces;
std::sort(node->mesh->faces, node->mesh->faces + node->mesh->faceCount, [](const ModelFace& a, const ModelFace& b) { return a.texture < b.texture; });
node->mesh->CalculateNormals();
node->scaleKeys.Read(input);
node->rotationKeys.Read(input);
node->positionKeys.Read(input);
int textureAnimations = 0;
input.Read(&textureAnimations, 4);
for (int animation = 0; animation < textureAnimations; ++animation) {
int texture = 0;
input.Read(&texture, 4);
ModelTextureChannelMap channels;
channels.Read(input);
node->textureKeys[texture] = channels;
}
this->nodes.push_back(node);
this->meshes[node->name] = node->mesh.get();
}
for (ModelNode* node : this->nodes) {
if (!node->parentName.empty()) {
ModelNode* parent = this->FindNode(node->parentName.c_str());
if (parent) {
node->parent = parent;
parent->children.push_back(node);
}
}
}
int volumeCount = 0;
input.Read(&volumeCount, 4);
for (int index = 0; index < volumeCount; ++index) {
auto* volume = new ModelVolume;
std::memset(volume, 0, 132);
input.Read(volume, sizeof(ModelVolume));
this->volumes.push_back(volume);
}
}
// INLINED
C3dModelResV2::C3dModelResV2(const char* extension, const char* prefix) {
ResourceMgr::Instance().Register(extension, prefix, this);
}
// GLOBAL: gl_25561
C3dModelResV2 g_rsm2Prototype("rsm2", "model\\");