diff --git a/src/playsim/actor.h b/src/playsim/actor.h index a2a525089..2b8b3f84e 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 56d61451d..eea576fb2 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 f9e306dc3..ae5e3dc88 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 3bc5e2248..effe9a884 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 003bf3604..40c261cc9 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 e95963cca..1fc8f14d1 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) {