diff --git a/src/playsim/actor.h b/src/playsim/actor.h index a2a5250894..2b8b3f84e5 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -673,6 +673,18 @@ enum EViewPosFlags // [MC] Flags for SetViewPos. VPSF_ABSOLUTEPOS = 1 << 2, // Use absolute position. }; +class DActorModelData : public DObject +{ + DECLARE_CLASS(DActorModelData, DObject); +public: + bool hasModel; + TArray modelIDs; + TArray skinIDs; + + DActorModelData() = default; + virtual void Serialize(FSerializer& arc) override; +}; + class DViewPosition : public DObject { DECLARE_CLASS(DViewPosition, DObject); @@ -1068,9 +1080,8 @@ public: DVector3 WorldOffset; double Speed; double FloatSpeed; - FName modelDef; - TArray models; - TArray skins; + FName modelDef; + TObjPtr modelData; // interaction info FBlockNode *BlockNode; // links in blocks (if needed) diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index 56d61451d2..eea576fb20 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -5037,12 +5037,11 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) { PARAM_ACTION_PROLOGUE(AActor); PARAM_NAME(modeldef) + PARAM_INT(index) PARAM_STRING_VAL(modelpath) PARAM_NAME(model) - PARAM_INT(modelindex) PARAM_STRING_VAL(skinpath) PARAM_NAME(skin) - PARAM_INT(skinindex) PARAM_INT(flags) if (self == nullptr) @@ -5053,15 +5052,42 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) if (modelpath[(int)modelpath.Len() - 1] != '/') modelpath += '/'; if (skinpath[(int)skinpath.Len() - 1] != '/') skinpath += '/'; - mobj->hasmodel = modeldef == nullptr && !mobj->hasmodel ? 1 : 0; - mobj->modelDef = modeldef; + if (mobj->modelData == nullptr) + { + auto ptr = Create(); + ptr->hasModel = modeldef != NAME_None && !mobj->hasmodel ? 0 : 1; + ptr->modelIDs = *new TArray(); + ptr->skinIDs = *new TArray(); + mobj->modelData = ptr; + mobj->hasmodel = mobj->modelData->hasModel; + GC::WriteBarrier(mobj, ptr); + } - mobj->models.Delete(modelindex); - mobj->skins.Delete(skinindex); - if(model != nullptr) mobj->models.Insert(modelindex, FindModel(modelpath.GetChars(), model.GetChars())); - else mobj->models.Insert(modelindex, -1); - if(skin != nullptr) mobj->skins.Insert(skinindex, LoadSkin(skinpath.GetChars(), skin.GetChars())); - else mobj->skins.Insert(skinindex, FNullTextureID()); + mobj->modelDef = modeldef; + mobj->modelData->modelIDs.Delete(index); + mobj->modelData->skinIDs.Delete(index); + mobj->modelData->modelIDs.Insert(index, model != NAME_None ? FindModel(modelpath.GetChars(), model.GetChars()) : -1); + mobj->modelData->skinIDs.Insert(index, skin != NAME_None ? LoadSkin(skinpath.GetChars(), skin.GetChars()) : FNullTextureID()); + + //[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. + int i = 0; + int maxModels = mobj->modelData->modelIDs.Size(); + int maxSkins = mobj->modelData->modelIDs.Size(); + for (i = 0; i < maxModels; i++) + { + if (mobj->modelData->modelIDs[mobj->modelData->modelIDs.Size()-1] == -1) + mobj->modelData->modelIDs.Delete(mobj->modelData->modelIDs.Size()-1); + } + for (i = 0; i < maxSkins; i++) + { + if (mobj->modelData->skinIDs[mobj->modelData->skinIDs.Size()-1] == FNullTextureID()) + mobj->modelData->skinIDs.Delete(mobj->modelData->skinIDs.Size()-1); + } + if (mobj->modelData->modelIDs.Size() == 0 && mobj->modelData->skinIDs.Size() == 0 && modeldef == NAME_None) + { + mobj->hasmodel = mobj->modelData->hasModel; + mobj->modelData = nullptr; + } return 0; } diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index f9e306dc3b..ae5e3dc883 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -158,6 +158,7 @@ CVAR (Int, cl_bloodtype, 0, CVAR_ARCHIVE); // CODE -------------------------------------------------------------------- +IMPLEMENT_CLASS(DActorModelData, false, false); IMPLEMENT_CLASS(AActor, false, true) IMPLEMENT_POINTERS_START(AActor) @@ -374,7 +375,9 @@ void AActor::Serialize(FSerializer &arc) ("viewpos", ViewPos) A("lightlevel", LightLevel) A("userlights", UserLights) - A("WorldOffset", WorldOffset); + A("WorldOffset", WorldOffset) + A("modelData", modelData) + A("modelDef", modelDef); SerializeTerrain(arc, "floorterrain", floorterrain, &def->floorterrain); SerializeArgs(arc, "args", args, def->args, special); @@ -1353,6 +1356,20 @@ bool AActor::Massacre () return false; } +//---------------------------------------------------------------------------- +// +// Serialize DActorModelData +// +//---------------------------------------------------------------------------- + +void DActorModelData::Serialize(FSerializer& arc) +{ + Super::Serialize(arc); + arc ("modelIDs", modelIDs) + ("skinIDs", skinIDs) + ("hasModel", hasModel); +} + //---------------------------------------------------------------------------- // // PROC P_ExplodeMissile diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index 3bc5e22487..effe9a8842 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -176,14 +176,14 @@ void RenderModel(FModelRenderer *renderer, float x, float y, float z, FSpriteMod float orientation = scaleFactorX * scaleFactorY * scaleFactorZ; renderer->BeginDrawModel(actor->RenderStyle, smf, objectToWorldMatrix, orientation < 0); - RenderFrameModels(renderer, actor->Level, smf, actor->state, actor->tics, actor->modelDef != nullptr ? PClass::FindActor(actor->modelDef) : actor->GetClass(), translation, actor); + RenderFrameModels(renderer, actor->Level, smf, actor->state, actor->tics, actor->modelDef != NAME_None ? PClass::FindActor(actor->modelDef) : actor->GetClass(), translation, actor); renderer->EndDrawModel(actor->RenderStyle, smf); } void RenderHUDModel(FModelRenderer *renderer, DPSprite *psp, float ofsX, float ofsY) { AActor * playermo = players[consoleplayer].camera; - FSpriteModelFrame *smf = psp->Caller != nullptr ? FindModelFrame(psp->Caller->modelDef != nullptr ? PClass::FindActor(psp->Caller->modelDef) : psp->Caller->GetClass(), psp->GetSprite(), psp->GetFrame(), false) : nullptr; + FSpriteModelFrame *smf = psp->Caller != nullptr ? FindModelFrame(psp->Caller->modelDef != NAME_None ? PClass::FindActor(psp->Caller->modelDef) : psp->Caller->GetClass(), psp->GetSprite(), psp->GetFrame(), false) : nullptr; // [BB] No model found for this sprite, so we can't render anything. if (smf == nullptr) @@ -224,7 +224,7 @@ void RenderHUDModel(FModelRenderer *renderer, DPSprite *psp, float ofsX, float o renderer->BeginDrawHUDModel(playermo->RenderStyle, objectToWorldMatrix, orientation < 0); uint32_t trans = psp->GetTranslation() != 0 ? psp->GetTranslation() : 0; if ((psp->Flags & PSPF_PLAYERTRANSLATED)) trans = psp->Owner->mo->Translation; - RenderFrameModels(renderer, playermo->Level, smf, psp->GetState(), psp->GetTics(), psp->Caller->modelDef != nullptr ? PClass::FindActor(psp->Caller->modelDef) : psp->Caller->GetClass(), trans, psp->Caller); + RenderFrameModels(renderer, playermo->Level, smf, psp->GetState(), psp->GetTics(), psp->Caller->modelDef != NAME_None ? PClass::FindActor(psp->Caller->modelDef) : psp->Caller->GetClass(), trans, psp->Caller); renderer->EndDrawHUDModel(playermo->RenderStyle); } @@ -281,19 +281,22 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr TArray tempSkinIDs = smf->skinIDs; for (int i = 0; i < smf->modelsAmount; i++) { - if (i < actor->models.Size()) + if (actor->modelData != nullptr) { - if (actor->models[i] >= 0) - tempModelIDs[i] = actor->models[i]; + if (i < (int)actor->modelData->modelIDs.Size()) + { + if(actor->modelData->modelIDs[i] >= 0) + tempModelIDs[i] = actor->modelData->modelIDs[i]; + } + if (i < (int)actor->modelData->skinIDs.Size()) + { + if (actor->modelData->skinIDs[i].isValid()) + tempSkinIDs[i] = actor->modelData->skinIDs[i]; + } } if (tempModelIDs[i] != -1) { FModel * mdl = Models[tempModelIDs[i]]; - if (i < actor->skins.Size()) - { - if (actor->skins[i].isValid()) - tempSkinIDs[i] = actor->skins[i]; - } auto tex = tempSkinIDs[i].isValid() ? TexMan.GetGameTexture(tempSkinIDs[i], true) : nullptr; mdl->BuildVertexBuffer(renderer); diff --git a/src/rendering/hwrenderer/scene/hw_sprites.cpp b/src/rendering/hwrenderer/scene/hw_sprites.cpp index 003bf3604e..40c261cc93 100644 --- a/src/rendering/hwrenderer/scene/hw_sprites.cpp +++ b/src/rendering/hwrenderer/scene/hw_sprites.cpp @@ -834,7 +834,7 @@ void HWSprite::Process(HWDrawInfo *di, AActor* thing, sector_t * sector, area_t z += fz; } - modelframe = isPicnumOverride ? nullptr : FindModelFrame(thing->modelDef != nullptr ? PClass::FindActor(thing->modelDef) : thing->GetClass(), spritenum, thing->frame, !!(thing->flags & MF_DROPPED)); + modelframe = isPicnumOverride ? nullptr : FindModelFrame(thing->modelDef != NAME_None ? PClass::FindActor(thing->modelDef) : thing->GetClass(), spritenum, thing->frame, !!(thing->flags & MF_DROPPED)); // don't bother drawing sprite shadows if this is a model (it will never look right) if (modelframe && isSpriteShadow) diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index e95963cca7..1fc8f14d1c 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -1132,7 +1132,7 @@ class Actor : Thinker native native void A_SetBlend(color color1, double alpha, int tics, color color2 = 0, double alpha2 = 0.); deprecated("2.3", "Use 'b = [true/false]' instead") native void A_ChangeFlag(string flagname, bool value); native void A_ChangeCountFlags(int kill = FLAG_NO_CHANGE, int item = FLAG_NO_CHANGE, int secret = FLAG_NO_CHANGE); - action native void A_ChangeModel(name modeldef, string modelpath = "", name model = "", int modelindex = 0, string skinpath = "", name skin = "", int skinindex = 0, int flags = 0); + action native void A_ChangeModel(name modeldef, int index = 0, string modelpath = "", name model = "", string skinpath = "", name skin = "", int flags = 0); void A_SetFriendly (bool set) {