From 04c3dac38250c1bea8a0f49ede28efa53d3924e7 Mon Sep 17 00:00:00 2001 From: fallenoak Date: Fri, 13 Feb 2026 21:10:02 -0600 Subject: [PATCH] feat(object): handle creature geosets and skins in CGUnit_C::PostInit --- src/component/CCharacterComponent.cpp | 72 +++++++++++++++++++++++++++ src/component/CCharacterComponent.hpp | 4 ++ src/model/CM2Model.cpp | 4 ++ src/model/CM2Model.hpp | 1 + src/object/client/CGUnit_C.cpp | 13 +++++ 5 files changed, 94 insertions(+) diff --git a/src/component/CCharacterComponent.cpp b/src/component/CCharacterComponent.cpp index 63e2a06..db3b5fe 100644 --- a/src/component/CCharacterComponent.cpp +++ b/src/component/CCharacterComponent.cpp @@ -121,6 +121,10 @@ bool ComponentCompressCallback(CVar* var, const char* oldValue, const char* valu return true; } +void ReplaceParticleColor(CM2Model* model, int32_t particleColorID) { + // TODO +} + int32_t CCharacterComponent::AddHandItem(CM2Model* model, const ItemDisplayInfoRec* displayRec, INVENTORY_SLOTS invSlot, SHEATHE_TYPE sheatheType, bool sheathed, bool shield, bool a7, int32_t visualID) { if (!model || !displayRec || invSlot > INVSLOT_TABARD) { return -1; @@ -247,6 +251,26 @@ CCharacterComponent* CCharacterComponent::AllocComponent() { return component; } +void CCharacterComponent::ApplyMonsterGeosets(CM2Model* model, const CreatureDisplayInfoRec* displayInfoRec) { + if (!model || !displayInfoRec || !displayInfoRec->m_creatureGeosetData) { + return; + } + + for (int32_t group = 100, dataOfs = 0; group < 900; group += 100, dataOfs += 4) { + auto section = (displayInfoRec->m_creatureGeosetData >> dataOfs) & 0xF; + + if (section) { + // Hide all sections in group + model->SetGeometryVisible(group, group + 99, false); + + // Show matching section + model->SetGeometryVisible(group + section, group + section, true); + } + } + + model->OptimizeVisibleGeometry(); +} + void CCharacterComponent::ComponentCloseFingers(CM2Model* model, COMP_HAND_SLOT handSlot) { uint32_t firstBone; uint32_t lastBone; @@ -789,6 +813,54 @@ void CCharacterComponent::RemoveLinkpt(CM2Model* model, GEOCOMPONENTLINKS link) } } +void CCharacterComponent::ReplaceMonsterSkin(CM2Model* model, const CreatureDisplayInfoRec* displayInfoRec, const CreatureModelDataRec* modelDataRec) { + if (!model || !displayInfoRec || !modelDataRec) { + return; + } + + CStatus status; + char texturePath[STORM_MAX_PATH]; + + // Copy model path to use as base path for texture + auto src = modelDataRec->m_modelName; + auto dst = texturePath; + while (*src) { + *dst++ = *src++; + } + *dst = '\0'; + + // Locate start of model file name + auto lastSlash = strrchr(texturePath, '\\'); + auto modelFileName = lastSlash ? lastSlash + 1 : texturePath; + + auto textureFlags = CGxTexFlags(GxTex_LinearMipLinear, 1, 1, 0, 0, 0, 1); + + for (uint32_t i = 0; i < 3; i++) { + auto textureName = displayInfoRec->m_textureVariation[i]; + + if (textureName[0] == '\0') { + continue; + } + + // Replace model file name with texture name + src = textureName; + dst = modelFileName; + while (*src) { + *dst++ = *src++; + } + *dst = '\0'; + + auto texture = TextureCreate(texturePath, textureFlags, &status, 0); + + if (texture) { + model->ReplaceTexture(11 + i, texture); + HandleClose(texture); + } + } + + ReplaceParticleColor(model, displayInfoRec->m_particleColorID); +} + void CCharacterComponent::UpdateBaseTexture(EGxTexCommand cmd, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevel, void* userArg, uint32_t& texelStrideInBytes, const void*& texels) { auto component = static_cast(userArg); diff --git a/src/component/CCharacterComponent.hpp b/src/component/CCharacterComponent.hpp index 04e12f7..6b6637c 100644 --- a/src/component/CCharacterComponent.hpp +++ b/src/component/CCharacterComponent.hpp @@ -10,6 +10,8 @@ class CACHEENTRY; class CCharacterComponent; class CharSectionsRec; +class CreatureDisplayInfoRec; +class CreatureModelDataRec; class ItemDisplayInfoRec; struct BlpPalPixel; @@ -51,6 +53,7 @@ class CCharacterComponent { static int32_t AddHandItem(CM2Model* model, const ItemDisplayInfoRec* displayRec, INVENTORY_SLOTS invSlot, SHEATHE_TYPE sheatheType, bool sheathed, bool shield, bool a7, int32_t a8); static void AddLink(CM2Model* parent, GEOCOMPONENTLINKS link, char const* modelPath, char const* texturePath, int32_t visualID, const ItemDisplayInfoRec* displayRec); static CCharacterComponent* AllocComponent(); + static void ApplyMonsterGeosets(CM2Model* model, const CreatureDisplayInfoRec* displayInfoRec); static void ComponentCloseFingers(CM2Model* model, COMP_HAND_SLOT handSlot); static void ComponentOpenFingers(CM2Model* model, COMP_HAND_SLOT handSlot); static HTEXTURE CreateTexture(const char* fileName, CStatus* status); @@ -68,6 +71,7 @@ class CCharacterComponent { static void PasteTransparent4Bit(void* srcTexture, const BlpPalPixel* srcPal, MipBits* dstMips, const C2iVector& dstPos, uint32_t dstWidth, const C2iVector& srcPos, const C2iVector& srcSize, TCTEXTUREINFO& srcInfo, int32_t srcMipLevel, int32_t dstMipLevelOfs); static void PasteTransparent8Bit(void* srcTexture, const BlpPalPixel* srcPal, MipBits* dstMips, const C2iVector& dstPos, uint32_t dstWidth, const C2iVector& srcPos, const C2iVector& srcSize, TCTEXTUREINFO& srcInfo, int32_t srcMipLevel, int32_t dstMipLevelOfs); static void RemoveLinkpt(CM2Model* model, GEOCOMPONENTLINKS link); + static void ReplaceMonsterSkin(CM2Model* model, const CreatureDisplayInfoRec* displayInfoRec, const CreatureModelDataRec* modelDataRec); static void UpdateBaseTexture(EGxTexCommand cmd, uint32_t width, uint32_t height, uint32_t depth, uint32_t mipLevel, void* userArg, uint32_t& texelStrideInBytes, const void*& texels); // Member variables diff --git a/src/model/CM2Model.cpp b/src/model/CM2Model.cpp index 6f081f8..49ace78 100644 --- a/src/model/CM2Model.cpp +++ b/src/model/CM2Model.cpp @@ -1604,6 +1604,10 @@ void CM2Model::LinkToCallbackListTail() { this->m_shared->m_callbackListTail = &this->m_callbackNext; } +void CM2Model::OptimizeVisibleGeometry() { + // TODO +} + int32_t CM2Model::ProcessCallbacks() { // TODO return 1; diff --git a/src/model/CM2Model.hpp b/src/model/CM2Model.hpp index 821b498..9ca72cb 100644 --- a/src/model/CM2Model.hpp +++ b/src/model/CM2Model.hpp @@ -178,6 +178,7 @@ class CM2Model { int32_t IsDrawable(int32_t a2, int32_t a3); int32_t IsLoaded(int32_t a2, int32_t attachments); void LinkToCallbackListTail(); + void OptimizeVisibleGeometry(); int32_t ProcessCallbacks(); void ProcessCallbacksRecursive(); uint32_t Release(); diff --git a/src/object/client/CGUnit_C.cpp b/src/object/client/CGUnit_C.cpp index d167e26..d040676 100644 --- a/src/object/client/CGUnit_C.cpp +++ b/src/object/client/CGUnit_C.cpp @@ -1,4 +1,6 @@ #include "object/client/CGUnit_C.hpp" +#include "component/CCharacterComponent.hpp" +#include "model/Model2.hpp" #include "object/client/ObjMgr.hpp" #include "db/Db.hpp" #include "ui/Game.hpp" @@ -175,6 +177,17 @@ void CGUnit_C::PostInit(uint32_t time, const CClientObjCreate& init, bool a4) { this->CGObject_C::PostInit(time, init, a4); // TODO + + if (this->m_displayInfo) { + CCharacterComponent::ApplyMonsterGeosets(this->m_model, this->m_displayInfo); + CCharacterComponent::ReplaceMonsterSkin(this->m_model, this->m_displayInfo, this->m_modelData); + + if (this->m_modelData) { + this->m_model->m_flag4 = (this->m_modelData->m_flags & 0x200) ? true : false; + } + } + + // TODO } void CGUnit_C::PostMovementUpdate(const CClientMoveUpdate& move, int32_t activeMover) {