- fixed destructive logic and memory allocation issues in RenderFrameModels.

This completely avoids modification of the model data, instead using local variables.
This commit is contained in:
Christoph Oelckers 2022-07-23 10:10:15 +02:00
commit 76c8214d67
13 changed files with 66 additions and 57 deletions

View file

@ -230,13 +230,11 @@ 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 && !(tempsmf->flags & MDL_NOINTERPOLATION))
if (gl_interpolate_model_frames && !(smf->flags & MDL_NOINTERPOLATION))
{
FState *nextState = curState->GetNextState();
if (curState != nextState && nextState)
@ -258,7 +256,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 ((tempsmf->flags & MDL_INTERPOLATEDOUBLEDFRAMES))
if ((smf->flags & MDL_INTERPOLATEDOUBLEDFRAMES))
{
const FState *prevState = curState - 1;
if ((curState->sprite == prevState->sprite) && (curState->Frame == prevState->Frame))
@ -278,61 +276,75 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
}
}
int modelsamount = smf->modelsAmount;
//[SM] - if we added any models for the frame to also render, then we also need to update modelsAmount for this smf
if (actor->modelData != nullptr)
{
if (tempsmf->modelsAmount < actor->modelData->modelIDs.Size())
{
tempsmf->modelsAmount = actor->modelData->modelIDs.Size();
while (tempsmf->modelframes.Size() < actor->modelData->modelIDs.Size()) tempsmf->modelframes.Push(-1);
while (smfNext->modelframes.Size() < actor->modelData->modelIDs.Size()) smfNext->modelframes.Push(-1);
}
modelsamount = actor->modelData->modelIDs.Size();
}
for (int i = 0; i < tempsmf->modelsAmount; i++)
TArray<FTextureID> surfaceskinids;
for (int i = 0; i < modelsamount; i++)
{
int modelid = -1;
int modelframe = -1;
int modelframenext = -1;
FTextureID skinid; skinid.SetInvalid();
surfaceskinids.Clear();
bool surfaceskinsswapped = false;
if (actor->modelData != nullptr)
{
if (i < (int)actor->modelData->modelIDs.Size())
{
if (actor->modelData->modelIDs[i] != -1)
tempsmf->modelIDs[i] = actor->modelData->modelIDs[i];
}
modelid = actor->modelData->modelIDs[i];
if (i < (int)actor->modelData->modelFrameGenerators.Size())
{
//[SM] - We will use this little snippet to allow a modder to specify a model index to clone. It's also pointless to clone something that clones something else in this case. And causes me headaches.
if (actor->modelData->modelFrameGenerators[i] >= 0 && smf->modelframes[i] != -1)
if (actor->modelData->modelFrameGenerators[i] >= 0 && smf->modelframes.Size() < (unsigned)i && smf->modelframes[i] != -1)
{
tempsmf->modelframes[i] = tempsmf->modelframes[actor->modelData->modelFrameGenerators[i]];
if (smfNext) smfNext->modelframes[i] = smfNext->modelframes[actor->modelData->modelFrameGenerators[i]];
modelframe = smf->modelframes[actor->modelData->modelFrameGenerators[i]];
if (smfNext) modelframenext = smfNext->modelframes[actor->modelData->modelFrameGenerators[i]];
}
}
if (i < (int)actor->modelData->skinIDs.Size())
{
if (actor->modelData->skinIDs[i].isValid())
tempsmf->skinIDs[i] = actor->modelData->skinIDs[i];
skinid = 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];
{
// only make a copy of the surfaceskinIDs array if really needed
if (surfaceskinids.Size() == 0) surfaceskinids = smf->surfaceskinIDs;
surfaceskinids[surface] = actor->modelData->surfaceSkinIDs[surface];
}
}
}
}
if (tempsmf->modelIDs[i] >= 0)
if (i < smf->modelsAmount)
{
FModel * mdl = Models[tempsmf->modelIDs[i]];
auto tex = tempsmf->skinIDs[i].isValid() ? TexMan.GetGameTexture(tempsmf->skinIDs[i], true) : nullptr;
if (modelid == -1) modelid = smf->modelIDs[i];
if (modelframe == -1) modelframe = smf->modelframes[i];
if (modelframenext == -1 && smfNext) modelframenext = smfNext->modelframes[i];
if (!skinid.isValid()) skinid = smf->skinIDs[i];
}
if (modelid >= 0)
{
FModel * mdl = Models[modelid];
auto tex = skinid.isValid() ? TexMan.GetGameTexture(skinid, true) : nullptr;
mdl->BuildVertexBuffer(renderer);
mdl->PushSpriteMDLFrame(tempsmf, i);
mdl->PushSpriteMDLFrame(smf, i);
if (smfNext && tempsmf->modelframes[i] != smfNext->modelframes[i])
mdl->RenderFrame(renderer, tex, tempsmf->modelframes[i], smfNext->modelframes[i], inter, translation);
if (smfNext && modelframe != modelframenext)
mdl->RenderFrame(renderer, tex, modelframe, modelframenext, inter, translation, surfaceskinids.Size() > 0? surfaceskinids : smf->surfaceskinIDs);
else
mdl->RenderFrame(renderer, tex, tempsmf->modelframes[i], tempsmf->modelframes[i], 0.f, translation);
mdl->RenderFrame(renderer, tex, modelframe, modelframe, 0.f, translation, surfaceskinids.Size() > 0 ? surfaceskinids : smf->surfaceskinIDs);
}
}
}