From b55ffdbfd3e0045a05af7b6fb017543b629966e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Lu=C3=ADs=20Vaz=20Silva?= Date: Thu, 20 Apr 2023 22:06:55 -0300 Subject: [PATCH] major A_ChangeModel cleanup --- src/playsim/actor.h | 1 + src/playsim/p_actionfunctions.cpp | 323 ++++++++++++++++++------------ src/playsim/p_mobj.cpp | 9 + src/r_data/models.cpp | 32 ++- 4 files changed, 229 insertions(+), 136 deletions(-) diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 5db2754df0..2521cd2a21 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -691,6 +691,7 @@ public: DActorModelData() = default; virtual void Serialize(FSerializer& arc) override; + virtual void OnDestroy() override; }; class DViewPosition : public DObject diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index 48d2b2249b..49f33342ed 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -5107,7 +5107,196 @@ enum ChangeModelFlags CMDL_USESURFACESKIN = 1 << 2, }; -DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) +void ChangeModelNative( + AActor * self, + AActor * invoker, + FStateParamInfo * stateinfo, + FName modeldef, + int i_modelindex, + const FString &p_modelpath, + FName model, + int i_skinindex, + const FString &p_skinpath, + FName skin, + int flags, + int generatorindex, + int i_animationindex, + const FString &p_animationpath, + FName animation +) { + if(!self) ThrowAbortException(X_READ_NIL, "In function parameter self"); + + if (modeldef != NAME_None && PClass::FindClass(modeldef.GetChars()) == nullptr) + { + Printf("Attempt to pass invalid modeldef name %s in %s.", modeldef.GetChars(), self->GetCharacterName()); + return; + } + + unsigned modelindex = i_modelindex < 0 ? 0 : i_modelindex; + unsigned skinindex = i_skinindex < 0 ? 0 : i_skinindex; + unsigned animationindex = i_animationindex < 0 ? 0 : i_animationindex; + + AActor* mobj = (ACTION_CALL_FROM_PSPRITE() && (flags & CMDL_WEAPONTOPLAYER)) || ACTION_CALL_FROM_INVENTORY() ? self : invoker; + + FString modelpath = p_modelpath; + FString skinpath = p_skinpath; + FString animationpath = p_animationpath; + + if (modelpath.Len() != 0 && modelpath[(int)modelpath.Len() - 1] != '/') modelpath += '/'; + if (skinpath.Len() != 0 && skinpath[(int)skinpath.Len() - 1] != '/') skinpath += '/'; + if (animationpath.Len() != 0 && animationpath[(int)animationpath.Len() - 1] != '/') animationpath += '/'; + + if (mobj->modelData == nullptr) + { + auto ptr = Create(); + + ptr->hasModel = mobj->hasmodel; + ptr->modelDef = NAME_None; + + mobj->modelData = ptr; + mobj->hasmodel = true; + GC::WriteBarrier(mobj, ptr); + }; + + unsigned skinPosition = skinindex + modelindex * MD3_MAX_SURFACES; + + int queryModel = !(flags & CMDL_HIDEMODEL) ? model != NAME_None ? FindModel(modelpath.GetChars(), model.GetChars()) : -1 : -2; + int queryAnimation = animation != NAME_None ? FindModel(animationpath.GetChars(), animation.GetChars()) : -1; + + mobj->modelData->modelDef = modeldef; + + assert(mobj->modelData->modelIDs.Size() == mobj->modelData->modelFrameGenerators.Size()); + + if(mobj->modelData->modelIDs.Size() < modelindex) + { + mobj->modelData->modelIDs.AppendFill(-1, modelindex - mobj->modelData->modelIDs.Size()); + mobj->modelData->modelFrameGenerators.AppendFill(-1, modelindex - mobj->modelData->modelFrameGenerators.Size()); + } + + if(mobj->modelData->animationIDs.Size() < animationindex) + { + mobj->modelData->animationIDs.AppendFill(-1, animationindex - mobj->modelData->animationIDs.Size()); + } + + if(flags & CMDL_USESURFACESKIN) + { + if(mobj->modelData->surfaceSkinIDs.Size() < skinPosition) + { + mobj->modelData->surfaceSkinIDs.AppendFill(FNullTextureID(), skinPosition - mobj->modelData->surfaceSkinIDs.Size()); + } + } + else if(mobj->modelData->skinIDs.Size() < skinindex) + { + mobj->modelData->skinIDs.AppendFill(FNullTextureID(), skinindex - mobj->modelData->skinIDs.Size()); + } + + if(mobj->modelData->modelIDs.Size() == modelindex) + { + mobj->modelData->modelIDs.Push(queryModel); + mobj->modelData->modelFrameGenerators.Push(generatorindex); + } + else + { + mobj->modelData->modelIDs[modelindex] = queryModel; + mobj->modelData->modelFrameGenerators[modelindex] = generatorindex; + } + + if(mobj->modelData->animationIDs.Size() == animationindex) + { + mobj->modelData->animationIDs.Push(queryAnimation); + } + else + { + mobj->modelData->animationIDs[animationindex] = queryAnimation; + } + + auto skindata = skin != NAME_None ? LoadSkin(skinpath.GetChars(), skin.GetChars()) : FNullTextureID(); + + if (flags & CMDL_USESURFACESKIN) + { + if(mobj->modelData->surfaceSkinIDs.Size() == skinPosition) + { + mobj->modelData->surfaceSkinIDs.Push(skindata); + } + else + { + mobj->modelData->surfaceSkinIDs[skinPosition] = skindata; + } + } + else + { + if(mobj->modelData->skinIDs.Size() == skinindex) + { + mobj->modelData->skinIDs.Push(skindata); + } + else + { + mobj->modelData->skinIDs[skinindex] = skindata; + } + } + + //[SM] - We need to serialize file paths and model names so that they are pushed on loading save files. Likewise, let's not include models that were already parsed when initialized. + if (queryModel >= 0) + { + FString fullName; + fullName.Format("%s%s", modelpath.GetChars(), model.GetChars()); + bool found = false; + + for (auto &m : savedModelFiles) + { + if(m.CompareNoCase(fullName) == 0) + { + found = true; + break; + } + } + if(!found) for (auto &m : Models) + { + if (m->mFileName.CompareNoCase(fullName) == 0) + { + found = true; + break; + } + } + if(!found) savedModelFiles.Push(fullName); + } + //Same for animations + if (queryAnimation >= 0) + { + FString fullName; + fullName.Format("%s%s", animationpath.GetChars(), animation.GetChars()); + bool found = false; + + for (auto &m : savedModelFiles) + { + if(m.CompareNoCase(fullName) == 0) + { + found = true; + break; + } + } + if(!found) for (auto &m : Models) + { + if (m->mFileName.CompareNoCase(fullName) == 0) + { + found = true; + break; + } + } + if(!found) savedModelFiles.Push(fullName); + } + + if (mobj->modelData->modelIDs.Size() == 0 && mobj->modelData->modelFrameGenerators.Size() == 0 && mobj->modelData->skinIDs.Size() == 0 && mobj->modelData->surfaceSkinIDs.Size() == 0 && mobj->modelData->animationIDs.Size() == 0 && modeldef == NAME_None) + { + mobj->hasmodel = mobj->modelData->hasModel; + mobj->modelData->Destroy(); + mobj->modelData = nullptr; + } + + return; +} + +DEFINE_ACTION_FUNCTION_NATIVE(AActor, A_ChangeModel, ChangeModelNative) { PARAM_ACTION_PROLOGUE(AActor); PARAM_NAME(modeldef); @@ -5122,138 +5311,8 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) PARAM_INT(animationindex); PARAM_STRING_VAL(animationpath); PARAM_NAME(animation); - - if (self == nullptr) - ACTION_RETURN_BOOL(false); - else if (modeldef != NAME_None && PClass::FindClass(modeldef.GetChars()) == nullptr) - { - Printf("Attempt to pass invalid modeldef name %s in %s.", modeldef.GetChars(), self->GetCharacterName()); - ACTION_RETURN_BOOL(false); - } - else if (modelindex < 0) - { - Printf("Attempt to pass invalid model index %d in %s, index must be non-negative.", modelindex, self->GetCharacterName()); - ACTION_RETURN_BOOL(false); - } - else if (skinindex < 0) - { - Printf("Attempt to pass invalid skin index %d in %s, index must be non-negative.", skinindex, self->GetCharacterName()); - ACTION_RETURN_BOOL(false); - } - else if (animationindex < 0) - { - Printf("Attempt to pass invalid animation index %d in %s, index must be non-negative.", animationindex, self->GetCharacterName()); - ACTION_RETURN_BOOL(false); - } - - AActor* mobj = (ACTION_CALL_FROM_PSPRITE() && (flags & CMDL_WEAPONTOPLAYER)) || ACTION_CALL_FROM_INVENTORY() ? self : stateowner; - - if (modelpath[(int)modelpath.Len() - 1] != '/') modelpath += '/'; - if (skinpath[(int)skinpath.Len() - 1] != '/') skinpath += '/'; - if (animationpath[(int)animationpath.Len() - 1] != '/') animationpath += '/'; - - if (mobj->modelData == nullptr) - { - auto ptr = Create(); - - ptr->hasModel = mobj->hasmodel ? 1 : 0; - ptr->modelDef = NAME_None; - - mobj->modelData = ptr; - mobj->hasmodel = 1; - GC::WriteBarrier(mobj, ptr); - } - - int maxModels = mobj->modelData->modelIDs.Size(); - int maxSkins = mobj->modelData->skinIDs.Size(); - int maxSurfaceSkins = mobj->modelData->surfaceSkinIDs.Size(); - int maxAnimations = mobj->modelData->animationIDs.Size(); - int maxGenerators = mobj->modelData->modelFrameGenerators.Size(); - - int skinPosition = skinindex + modelindex * MD3_MAX_SURFACES; - - int queryModel = !(flags & CMDL_HIDEMODEL) ? model != NAME_None ? FindModel(modelpath.GetChars(), model.GetChars()) : -1 : -2; - int queryAnimation = animation != NAME_None ? FindModel(animationpath.GetChars(), animation.GetChars()) : -1; - - //[SM] - Let's clear out any potential entries at the specified indices - mobj->modelData->modelDef = modeldef; - if(maxModels > modelindex) mobj->modelData->modelIDs.Pop(mobj->modelData->modelIDs[modelindex]); - if(maxAnimations > animationindex) mobj->modelData->animationIDs.Pop(mobj->modelData->animationIDs[animationindex]); - if(maxGenerators > modelindex) mobj->modelData->modelFrameGenerators.Pop(mobj->modelData->modelFrameGenerators[modelindex]); - - if (flags & CMDL_USESURFACESKIN) - { - if (maxSurfaceSkins > skinPosition) - mobj->modelData->surfaceSkinIDs.Delete(skinPosition); //[SM] - It seems the only way to make sure this does what it's told is from Delete, not Pop - } - else - { - if (maxSkins > skinindex) - mobj->modelData->skinIDs.Pop(mobj->modelData->skinIDs[skinindex]); - } - - //[SM] - We need to fill up any holes this new index will make so that it doesn't leave behind any undefined behavior - while ((int)mobj->modelData->modelIDs.Size() < modelindex) mobj->modelData->modelIDs.Push(-1); - while ((int)mobj->modelData->modelFrameGenerators.Size() < modelindex) mobj->modelData->modelFrameGenerators.Push(-1); - while ((int)mobj->modelData->animationIDs.Size() < modelindex) mobj->modelData->animationIDs.Push(-1); - if (flags & CMDL_USESURFACESKIN) - while ((int)mobj->modelData->surfaceSkinIDs.Size() < skinPosition) mobj->modelData->surfaceSkinIDs.Push(FNullTextureID()); - else - while ((int)mobj->modelData->skinIDs.Size() < skinindex) mobj->modelData->skinIDs.Push(FNullTextureID()); - mobj->modelData->modelIDs.Insert(modelindex, queryModel); - mobj->modelData->modelFrameGenerators.Insert(modelindex, generatorindex); - mobj->modelData->animationIDs.Insert(animationindex, queryAnimation); - if (flags & CMDL_USESURFACESKIN) - mobj->modelData->surfaceSkinIDs.Insert(skinPosition, skin != NAME_None ? LoadSkin(skinpath.GetChars(), skin.GetChars()) : FNullTextureID()); - else - mobj->modelData->skinIDs.Insert(skinindex, skin != NAME_None ? LoadSkin(skinpath.GetChars(), skin.GetChars()) : FNullTextureID()); - - //[SM] - We need to serialize file paths and model names so that they are pushed on loading save files. Likewise, let's not include models that were already parsed when initialized. - if (queryModel >= 0) - { - FString fullName; - fullName.Format("%s%s", modelpath.GetChars(), model.GetChars()); - bool allowPush = true; - for (unsigned i = 0; i < savedModelFiles.Size(); i++) if (!savedModelFiles[i].CompareNoCase(fullName)) allowPush = false; - for (unsigned i = 0; i < Models.Size()-1; i++) if (!Models[i]->mFileName.CompareNoCase(fullName)) allowPush = false; - - if(allowPush) savedModelFiles.Push(fullName); - } - //Same for animations - if (queryAnimation >= 0) - { - FString fullName; - fullName.Format("%s%s", animationpath.GetChars(), animation.GetChars()); - bool allowPush = true; - for (unsigned i = 0; i < savedModelFiles.Size(); i++) if (!savedModelFiles[i].CompareNoCase(fullName)) allowPush = false; - for (unsigned i = 0; i < Models.Size() - 1; i++) if (!Models[i]->mFileName.CompareNoCase(fullName)) allowPush = false; - - if (allowPush) savedModelFiles.Push(fullName); - } - - //[SM] - if an indice of modelIDs or skinIDs comes up blank and it's the last one, just delete it. For using very large amounts of indices, common sense says to just not run this repeatedly. - while (mobj->modelData->modelIDs.Size() > 0 && mobj->modelData->modelIDs.Last() == -1) - mobj->modelData->modelIDs.Pop(mobj->modelData->modelIDs.Last()); - while (mobj->modelData->modelFrameGenerators.Size() > 0 && mobj->modelData->modelFrameGenerators.Last() == -1) - mobj->modelData->modelFrameGenerators.Pop(mobj->modelData->modelFrameGenerators.Last()); - while (mobj->modelData->skinIDs.Size() > 0 && mobj->modelData->skinIDs.Last() == FNullTextureID()) - mobj->modelData->skinIDs.Pop(mobj->modelData->skinIDs.Last()); - while (mobj->modelData->surfaceSkinIDs.Size() > 0 && mobj->modelData->surfaceSkinIDs.Last() == FNullTextureID()) - mobj->modelData->surfaceSkinIDs.Pop(mobj->modelData->surfaceSkinIDs.Last()); - while (mobj->modelData->animationIDs.Size() > 0 && mobj->modelData->animationIDs.Last() == -1) - mobj->modelData->animationIDs.Pop(mobj->modelData->animationIDs.Last()); - - if (mobj->modelData->modelIDs.Size() == 0 && mobj->modelData->modelFrameGenerators.Size() == 0 && mobj->modelData->skinIDs.Size() == 0 && mobj->modelData->surfaceSkinIDs.Size() == 0 && mobj->modelData->animationIDs.Size() == 0 && modeldef == NAME_None) - { - mobj->hasmodel = mobj->modelData->hasModel; - mobj->modelData->modelIDs.Reset(); - mobj->modelData->modelFrameGenerators.Reset(); - mobj->modelData->skinIDs.Reset(); - mobj->modelData->surfaceSkinIDs.Reset(); - mobj->modelData->animationIDs.Reset(); - mobj->modelData->Destroy(); - } + ChangeModelNative(self,stateowner,stateinfo,modeldef,modelindex,modelpath,model,skinindex,skinpath,skin,flags,generatorindex,animationindex,animationpath,animation); return 0; } diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index e6321139a1..987436424f 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -1336,6 +1336,15 @@ void DActorModelData::Serialize(FSerializer& arc) ("hasModel", hasModel); } +void DActorModelData::OnDestroy() +{ + modelIDs.Reset(); + modelFrameGenerators.Reset(); + skinIDs.Reset(); + surfaceSkinIDs.Reset(); + animationIDs.Reset(); +} + //---------------------------------------------------------------------------- // // PROC P_ExplodeMissile diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index fd7d0c06cf..03d1def7e1 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -344,16 +344,40 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr if (actor->modelData->skinIDs[i].isValid()) skinid = actor->modelData->skinIDs[i]; } - for (int surface = i * MD3_MAX_SURFACES; surface < (i + 1) * MD3_MAX_SURFACES; surface++) + + unsigned sz1 = smf->surfaceskinIDs.Size(); + unsigned sz2 = actor->modelData->surfaceSkinIDs.Size(); + unsigned end = (i + 1) * MD3_MAX_SURFACES; + + if(actor->modelData->surfaceSkinIDs.Size() > 0) { - if (surface < (int)actor->modelData->surfaceSkinIDs.Size()) + long last_ok = -1; + surfaceskinids.Resize(actor->modelData->surfaceSkinIDs.Size()); + for (unsigned surface = i * MD3_MAX_SURFACES; surface < end; surface++) { + if (surface >= sz2) break; + if (actor->modelData->surfaceSkinIDs[surface].isValid()) { - // only make a copy of the surfaceskinIDs array if really needed - if (surfaceskinids.Size() == 0) surfaceskinids = smf->surfaceskinIDs; surfaceskinids[surface] = actor->modelData->surfaceSkinIDs[surface]; + last_ok = surface; } + else if(surface < sz1) + { + surfaceskinids[surface] = smf->surfaceskinIDs[surface]; + } + else + { + surfaceskinids[surface].SetInvalid(); + } + } + if(last_ok >= 0) + { + surfaceskinids.Resize(max(last_ok + 1, (long)sz1)); // clear out + } + else + { + surfaceskinids.Clear(); } } }