From 69ee1eb0a2404fefb62436a7dc89312347b24397 Mon Sep 17 00:00:00 2001 From: Shiny Metagross <30511800+ShinyMetagross@users.noreply.github.com> Date: Tue, 5 Jul 2022 13:29:47 -0700 Subject: [PATCH] Surfaceskin modification - Made it so when rendering a model, it clones an smf to use so that data isn't overwritten - Reimplemented the skin index property. This changes the behavior of this index if CMDL_USESURFACESKIN is activated --- src/common/models/model.h | 2 +- src/playsim/actor.h | 1 + src/playsim/p_actionfunctions.cpp | 43 +++++++++++++++++++++------ src/playsim/p_mobj.cpp | 1 + src/r_data/models.cpp | 39 ++++++++++++++---------- wadsrc/static/zscript/actors/actor.zs | 2 +- wadsrc/static/zscript/constants.zs | 1 + 7 files changed, 62 insertions(+), 27 deletions(-) diff --git a/src/common/models/model.h b/src/common/models/model.h index df4b0e6a62..ec13914989 100644 --- a/src/common/models/model.h +++ b/src/common/models/model.h @@ -70,7 +70,7 @@ public: IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; } void DestroyVertexBuffer(); - const FSpriteModelFrame *curSpriteMDLFrame; + const FSpriteModelFrame* curSpriteMDLFrame; int curMDLIndex; void PushSpriteMDLFrame(const FSpriteModelFrame *smf, int index) { curSpriteMDLFrame = smf; curMDLIndex = index; }; diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 80ac8cfbe2..2780f856f7 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -681,6 +681,7 @@ public: bool hasModel; TArray modelIDs; TArray skinIDs; + TArray surfaceSkinIDs; DActorModelData() = default; virtual void Serialize(FSerializer& arc) override; diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index 3d36f03c58..6e5fb28aaf 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -5032,15 +5032,17 @@ enum ChangeModelFlags { CMDL_WEAPONTOPLAYER = 1, CMDL_HIDEMODEL = 1 << 1, + CMDL_USESURFACESKIN = 1 << 2, }; DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) { PARAM_ACTION_PROLOGUE(AActor); PARAM_NAME(modeldef) - PARAM_INT(index) + PARAM_INT(modelindex) PARAM_STRING_VAL(modelpath) PARAM_NAME(model) + PARAM_INT(skinindex) PARAM_STRING_VAL(skinpath) PARAM_NAME(skin) PARAM_INT(flags) @@ -5059,6 +5061,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) ptr->hasModel = mobj->hasmodel ? 1 : 0; ptr->modelIDs = *new TArray(); ptr->skinIDs = *new TArray(); + ptr->surfaceSkinIDs = *new TArray(); ptr->modelDef = NAME_None; mobj->modelData = ptr; mobj->hasmodel = 1; @@ -5067,19 +5070,38 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) int maxModels = mobj->modelData->modelIDs.Size(); int maxSkins = mobj->modelData->skinIDs.Size(); + int maxSurfaceSkins = mobj->modelData->surfaceSkinIDs.Size(); + + int skinPosition = skinindex + modelindex * MD3_MAX_SURFACES; int queryModel = !(flags & CMDL_HIDEMODEL) ? model != NAME_None ? FindModel(modelpath.GetChars(), model.GetChars()) : -1 : -2; + //[SM] - Let's clear out any potential entries at the specified indices mobj->modelData->modelDef = modeldef; - if(maxModels > index) mobj->modelData->modelIDs.Pop(mobj->modelData->modelIDs[index]); - if(maxSkins > index) mobj->modelData->skinIDs.Pop(mobj->modelData->skinIDs[index]); + if(maxModels > modelindex) mobj->modelData->modelIDs.Pop(mobj->modelData->modelIDs[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() < index) mobj->modelData->modelIDs.Push(-1); - while ((int)mobj->modelData->skinIDs.Size() < index) mobj->modelData->skinIDs.Push(FNullTextureID()); - - mobj->modelData->modelIDs.Insert(index, queryModel); - mobj->modelData->skinIDs.Insert(index, skin != NAME_None ? LoadSkin(skinpath.GetChars(), skin.GetChars()) : FNullTextureID()); + while ((int)mobj->modelData->modelIDs.Size() < modelindex) mobj->modelData->modelIDs.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); + 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) @@ -5098,12 +5120,15 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel) mobj->modelData->modelIDs.Pop(mobj->modelData->modelIDs.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()); - if (mobj->modelData->modelIDs.Size() == 0 && mobj->modelData->skinIDs.Size() == 0 && modeldef == NAME_None) + if (mobj->modelData->modelIDs.Size() == 0 && mobj->modelData->skinIDs.Size() == 0 && mobj->modelData->surfaceSkinIDs.Size() == 0 && modeldef == NAME_None) { mobj->hasmodel = mobj->modelData->hasModel; mobj->modelData->modelIDs.Reset(); mobj->modelData->skinIDs.Reset(); + mobj->modelData->surfaceSkinIDs.Reset(); mobj->modelData->Destroy(); } diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index 1107608138..400f007484 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -1368,6 +1368,7 @@ void DActorModelData::Serialize(FSerializer& arc) arc("modelDef", modelDef) ("modelIDs", modelIDs) ("skinIDs", skinIDs) + ("surfaceSkinIDs", surfaceSkinIDs) ("hasModel", hasModel); } diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index 825a7d0cf7..b87970689a 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -55,7 +55,7 @@ EXTERN_CVAR (Bool, r_drawvoxels) extern TDeletingArray Voxels; extern TDeletingArray VoxelDefs; -void RenderFrameModels(FModelRenderer* renderer, FLevelLocals* Level, const FSpriteModelFrame* smf, const FState* curState, const int curTics, const PClass* ti, int translation, AActor* actor); +void RenderFrameModels(FModelRenderer* renderer, FLevelLocals* Level, const FSpriteModelFrame *smf, const FState* curState, const int curTics, const PClass* ti, int translation, AActor* actor); void RenderModel(FModelRenderer *renderer, float x, float y, float z, FSpriteModelFrame *smf, AActor *actor, double ticFrac) @@ -230,11 +230,13 @@ void RenderHUDModel(FModelRenderer *renderer, DPSprite *psp, float ofsX, float o void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, const PClass *ti, int translation, AActor* actor) { + //[SM] - Create a temporary sprite model frame, which prevents data from being overwritten + FSpriteModelFrame* tempsmf = new FSpriteModelFrame(*smf); // [BB] Frame interpolation: Find the FSpriteModelFrame smfNext which follows after smf in the animation // and the scalar value inter ( element of [0,1) ), both necessary to determine the interpolated frame. FSpriteModelFrame * smfNext = nullptr; double inter = 0.; - if (gl_interpolate_model_frames && !(smf->flags & MDL_NOINTERPOLATION)) + if (gl_interpolate_model_frames && !(tempsmf->flags & MDL_NOINTERPOLATION)) { FState *nextState = curState->GetNextState(); if (curState != nextState && nextState) @@ -256,7 +258,7 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr { // [BB] Workaround for actors that use the same frame twice in a row. // Most of the standard Doom monsters do this in their see state. - if ((smf->flags & MDL_INTERPOLATEDOUBLEDFRAMES)) + if ((tempsmf->flags & MDL_INTERPOLATEDOUBLEDFRAMES)) { const FState *prevState = curState - 1; if ((curState->sprite == prevState->sprite) && (curState->Frame == prevState->Frame)) @@ -276,36 +278,41 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr } } - //[SM] - these temporary arrays prevent actual smf data from being overwritten, which was the source of my problems - TArray tempModelIDs = smf->modelIDs; - TArray tempSkinIDs = smf->skinIDs; - for (int i = 0; i < smf->modelsAmount; i++) + for (int i = 0; i < tempsmf->modelsAmount; i++) { if (actor->modelData != nullptr) { if (i < (int)actor->modelData->modelIDs.Size()) { if(actor->modelData->modelIDs[i] != -1) - tempModelIDs[i] = actor->modelData->modelIDs[i]; + tempsmf->modelIDs[i] = actor->modelData->modelIDs[i]; } if (i < (int)actor->modelData->skinIDs.Size()) { if (actor->modelData->skinIDs[i].isValid()) - tempSkinIDs[i] = actor->modelData->skinIDs[i]; + tempsmf->skinIDs[i] = actor->modelData->skinIDs[i]; + } + for (int surface = i * MD3_MAX_SURFACES; surface < (i + 1) * MD3_MAX_SURFACES; surface++) + { + if (surface < (int)actor->modelData->surfaceSkinIDs.Size()) + { + if (actor->modelData->surfaceSkinIDs[surface].isValid()) + tempsmf->surfaceskinIDs[surface] = actor->modelData->surfaceSkinIDs[surface]; + } } } - if (tempModelIDs[i] >= 0) + if (tempsmf->modelIDs[i] >= 0) { - FModel * mdl = Models[tempModelIDs[i]]; - auto tex = tempSkinIDs[i].isValid() ? TexMan.GetGameTexture(tempSkinIDs[i], true) : nullptr; + FModel * mdl = Models[tempsmf->modelIDs[i]]; + auto tex = tempsmf->skinIDs[i].isValid() ? TexMan.GetGameTexture(tempsmf->skinIDs[i], true) : nullptr; mdl->BuildVertexBuffer(renderer); - mdl->PushSpriteMDLFrame(smf, i); + mdl->PushSpriteMDLFrame(tempsmf, i); - if (smfNext && smf->modelframes[i] != smfNext->modelframes[i]) - mdl->RenderFrame(renderer, tex, smf->modelframes[i], smfNext->modelframes[i], inter, translation); + if (smfNext && tempsmf->modelframes[i] != smfNext->modelframes[i]) + mdl->RenderFrame(renderer, tex, tempsmf->modelframes[i], smfNext->modelframes[i], inter, translation); else - mdl->RenderFrame(renderer, tex, smf->modelframes[i], smf->modelframes[i], 0.f, translation); + mdl->RenderFrame(renderer, tex, tempsmf->modelframes[i], tempsmf->modelframes[i], 0.f, translation); } } } diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 1fc8f14d1c..5da2866957 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, int index = 0, string modelpath = "", name model = "", string skinpath = "", name skin = "", int flags = 0); + action native void A_ChangeModel(name modeldef, int modelindex = 0, string modelpath = "", name model = "", int skinindex = 0, string skinpath = "", name skin = "", int flags = 0); void A_SetFriendly (bool set) { diff --git a/wadsrc/static/zscript/constants.zs b/wadsrc/static/zscript/constants.zs index 15a1514bfa..73ae5b385a 100644 --- a/wadsrc/static/zscript/constants.zs +++ b/wadsrc/static/zscript/constants.zs @@ -370,6 +370,7 @@ enum ChangeModelFlags { CMDL_WEAPONTOPLAYER = 1, CMDL_HIDEMODEL = 1 << 1, + CMDL_USESURFACESKIN = 1 << 2, }; // Activation flags