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
This commit is contained in:
Shiny Metagross 2022-07-05 13:29:47 -07:00 committed by Christoph Oelckers
parent ec3d81a34f
commit 69ee1eb0a2
7 changed files with 62 additions and 27 deletions

View file

@ -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; };

View file

@ -681,6 +681,7 @@ public:
bool hasModel;
TArray<int> modelIDs;
TArray<FTextureID> skinIDs;
TArray<FTextureID> surfaceSkinIDs;
DActorModelData() = default;
virtual void Serialize(FSerializer& arc) override;

View file

@ -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<int>();
ptr->skinIDs = *new TArray<FTextureID>();
ptr->surfaceSkinIDs = *new TArray<FTextureID>();
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();
}

View file

@ -1368,6 +1368,7 @@ void DActorModelData::Serialize(FSerializer& arc)
arc("modelDef", modelDef)
("modelIDs", modelIDs)
("skinIDs", skinIDs)
("surfaceSkinIDs", surfaceSkinIDs)
("hasModel", hasModel);
}

View file

@ -55,7 +55,7 @@ EXTERN_CVAR (Bool, r_drawvoxels)
extern TDeletingArray<FVoxel *> Voxels;
extern TDeletingArray<FVoxelDef *> 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<int> tempModelIDs = smf->modelIDs;
TArray<FTextureID> 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);
}
}
}

View file

@ -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<FlagName> = [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)
{

View file

@ -370,6 +370,7 @@ enum ChangeModelFlags
{
CMDL_WEAPONTOPLAYER = 1,
CMDL_HIDEMODEL = 1 << 1,
CMDL_USESURFACESKIN = 1 << 2,
};
// Activation flags