4.14.2 model refactor cherry pick
Some checks failed
Continuous Integration / macOS | Debug (push) Has been cancelled
Continuous Integration / Linux Clang 11 | Debug (push) Has been cancelled
Continuous Integration / Visual Studio 2022 | Debug (push) Has been cancelled
Continuous Integration / Linux GCC 12 | MinSizeRel (push) Has been cancelled
Continuous Integration / Linux GCC 9 | RelWithDebInfo (push) Has been cancelled
Continuous Integration / macOS | Release (push) Has been cancelled
Continuous Integration / Linux Clang 15 | Release (push) Has been cancelled
Continuous Integration / Visual Studio 2019 | Release (push) Has been cancelled
Continuous Integration / Visual Studio 2022 | Release (push) Has been cancelled

* split frame info calculation from RenderFrameModels

* clean up FindModelFrame

* refactor model overrides into its own function

* refactor frame rendering into RenderModelFrame

* split frame processing into ProcessModelFrame
This commit is contained in:
Ricardo Luís Vaz Silva 2025-01-29 01:31:37 -03:00 committed by Rachael Alexanderson
commit 99aa489d09
5 changed files with 276 additions and 179 deletions

View file

@ -45,7 +45,7 @@
TDeletingArray<FModel*> Models;
TArray<FSpriteModelFrame> SpriteModelFrames;
TMap<void*, FSpriteModelFrame> BaseSpriteModelFrames;
TMap<const PClass*, FSpriteModelFrame> BaseSpriteModelFrames;
/////////////////////////////////////////////////////////////////////////////

View file

@ -16,14 +16,16 @@ class FModelRenderer;
class FGameTexture;
class IModelVertexBuffer;
class FModel;
class PClass;
struct FSpriteModelFrame;
FTextureID LoadSkin(const char* path, const char* fn);
void FlushModels();
extern TDeletingArray<FModel*> Models;
extern TArray<FSpriteModelFrame> SpriteModelFrames;
extern TMap<void*, FSpriteModelFrame> BaseSpriteModelFrames;
extern TMap<const PClass*, FSpriteModelFrame> BaseSpriteModelFrames;
#define MD3_MAX_SURFACES 32
#define MIN_MODELS 4

View file

@ -313,19 +313,18 @@ void calcFrames(const ModelAnim &curAnim, double tic, ModelAnimFrameInterp &to,
}
}
void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, FTranslationID translation, AActor* actor)
CalcModelFrameInfo CalcModelFrame(FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, DActorModelData* data, AActor* actor, bool is_decoupled, double tic)
{
// [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.
int smf_flags = smf->getFlags(actor->modelData);
int smf_flags = smf->getFlags(data);
const FSpriteModelFrame * smfNext = nullptr;
float inter = 0.;
bool is_decoupled = (actor->flags9 & MF9_DECOUPLEDANIMATIONS);
ModelAnimFrameInterp decoupled_frame;
ModelAnimFrame * decoupled_frame_prev = nullptr;
// if prev_frame == -1: interpolate(main_frame, next_frame, inter), else: interpolate(interpolate(main_prev_frame, main_frame, inter_main), interpolate(next_prev_frame, next_frame, inter_next), inter)
// 4-way interpolation is needed to interpolate animation switches between animations that aren't 35hz
@ -333,15 +332,10 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
if(is_decoupled)
{
smfNext = smf = &BaseSpriteModelFrames[actor->GetClass()];
if(actor->modelData && !(actor->modelData->curAnim.flags & MODELANIM_NONE))
if(data && !(data->curAnim.flags & MODELANIM_NONE))
{
double tic = actor->Level->totaltime;
if ((ConsoleState == c_up || ConsoleState == c_rising) && (menuactive == MENU_Off || menuactive == MENU_OnNoPause) && !actor->isFrozen())
{
tic += I_GetTimeFrac();
}
calcFrames(actor->modelData->curAnim, tic, decoupled_frame, inter);
calcFrames(data->curAnim, tic, decoupled_frame, inter);
decoupled_frame_prev = &data->prevAnim;
}
}
else if (gl_interpolate_model_frames && !(smf_flags & MDL_NOINTERPOLATION))
@ -388,173 +382,226 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
unsigned 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 (data != nullptr)
{
if (actor->modelData->models.Size() > modelsamount)
modelsamount = actor->modelData->models.Size();
if (data->models.Size() > modelsamount)
modelsamount = data->models.Size();
}
TArray<FTextureID> surfaceskinids;
return
{
smf_flags,
smfNext,
inter,
is_decoupled,
decoupled_frame,
decoupled_frame_prev,
modelsamount
};
}
bool CalcModelOverrides(int i, const FSpriteModelFrame *smf, DActorModelData* data, const CalcModelFrameInfo &info, ModelDrawInfo &out, bool is_decoupled)
{
//reset drawinfo
out.modelid = -1;
out.animationid = -1;
out.modelframe = -1;
out.modelframenext = -1;
out.skinid.SetNull();
out.surfaceskinids.Clear();
if (data)
{
//modelID
if (data->models.Size() > i && data->models[i].modelID >= 0)
{
out.modelid = data->models[i].modelID;
}
else if(data->models.Size() > i && data->models[i].modelID == -2)
{
return false;
}
else if(smf->modelsAmount > i)
{
out.modelid = smf->modelIDs[i];
}
//animationID
if (data->animationIDs.Size() > i && data->animationIDs[i] >= 0)
{
out.animationid = data->animationIDs[i];
}
else if(smf->modelsAmount > i)
{
out.animationid = smf->animationIDs[i];
}
if(!is_decoupled)
{
//modelFrame
if (data->modelFrameGenerators.Size() > i
&& (unsigned)data->modelFrameGenerators[i] < info.modelsamount
&& smf->modelframes[data->modelFrameGenerators[i]] >= 0
) {
out.modelframe = smf->modelframes[data->modelFrameGenerators[i]];
if (info.smfNext)
{
if(info.smfNext->modelframes[data->modelFrameGenerators[i]] >= 0)
{
out.modelframenext = info.smfNext->modelframes[data->modelFrameGenerators[i]];
}
else
{
out.modelframenext = info.smfNext->modelframes[i];
}
}
}
else if(smf->modelsAmount > i)
{
out.modelframe = smf->modelframes[i];
if (info.smfNext) out.modelframenext = info.smfNext->modelframes[i];
}
}
//skinID
if (data->skinIDs.Size() > i && data->skinIDs[i].isValid())
{
out.skinid = data->skinIDs[i];
}
else if(smf->modelsAmount > i)
{
out.skinid = smf->skinIDs[i];
}
//surfaceSkinIDs
if(data->models.Size() > i && data->models[i].surfaceSkinIDs.Size() > 0)
{
unsigned sz1 = smf->surfaceskinIDs.Size();
unsigned sz2 = data->models[i].surfaceSkinIDs.Size();
unsigned start = i * MD3_MAX_SURFACES;
out.surfaceskinids = data->models[i].surfaceSkinIDs;
out.surfaceskinids.Resize(MD3_MAX_SURFACES);
for (unsigned surface = 0; surface < MD3_MAX_SURFACES; surface++)
{
if (sz2 > surface && (data->models[i].surfaceSkinIDs[surface].isValid()))
{
continue;
}
if((surface + start) < sz1)
{
out.surfaceskinids[surface] = smf->surfaceskinIDs[surface + start];
}
else
{
out.surfaceskinids[surface].SetNull();
}
}
}
}
else
{
out.modelid = smf->modelIDs[i];
out.animationid = smf->animationIDs[i];
out.modelframe = smf->modelframes[i];
if (info.smfNext) out.modelframenext = info.smfNext->modelframes[i];
out.skinid = smf->skinIDs[i];
}
return (out.modelid >= 0 && out.modelid < Models.size());
}
const TArray<VSMatrix> * ProcessModelFrame(FModel * animation, bool nextFrame, int i, const FSpriteModelFrame *smf, DActorModelData* modelData, const CalcModelFrameInfo &frameinfo, ModelDrawInfo &drawinfo, bool is_decoupled, double tic)
{
const TArray<TRS>* animationData = nullptr;
if (drawinfo.animationid >= 0)
{
animation = Models[drawinfo.animationid];
animationData = animation->AttachAnimationData();
}
const TArray<VSMatrix> *boneData = nullptr;
if(is_decoupled)
{
if(frameinfo.decoupled_frame.frame1 >= 0)
{
boneData = animation->CalculateBones(
frameinfo.decoupled_frame_prev ? *frameinfo.decoupled_frame_prev : nullptr,
frameinfo.decoupled_frame,
frameinfo.inter,
animationData);
}
}
else
{
boneData = animation->CalculateBones(
nullptr,
{
nextFrame ? frameinfo.inter : -1.0f,
drawinfo.modelframe,
drawinfo.modelframenext
},
-1.0f,
animationData);
}
return boneData;
}
static inline void RenderModelFrame(FModelRenderer *renderer, int i, const FSpriteModelFrame *smf, DActorModelData* modelData, const CalcModelFrameInfo &frameinfo, ModelDrawInfo &drawinfo, bool is_decoupled, double tic, FTranslationID translation, int &boneStartingPosition, bool &evaluatedSingle)
{
FModel * mdl = Models[drawinfo.modelid];
auto tex = drawinfo.skinid.isValid() ? TexMan.GetGameTexture(drawinfo.skinid, true) : nullptr;
mdl->BuildVertexBuffer(renderer);
auto ssidp = drawinfo.surfaceskinids.Size() > 0
? drawinfo.surfaceskinids.Data()
: (((i * MD3_MAX_SURFACES) < smf->surfaceskinIDs.Size()) ? &smf->surfaceskinIDs[i * MD3_MAX_SURFACES] : nullptr);
bool nextFrame = frameinfo.smfNext && drawinfo.modelframe != drawinfo.modelframenext;
// [Jay] while per-model animations aren't done, DECOUPLEDANIMATIONS does the same as MODELSAREATTACHMENTS
if(!evaluatedSingle)
{ // [Jay] TODO per-model decoupled animations
const TArray<VSMatrix> *boneData = ProcessModelFrame(mdl, nextFrame, i, smf, modelData, frameinfo, drawinfo, is_decoupled, tic);
if(frameinfo.smf_flags & MDL_MODELSAREATTACHMENTS || is_decoupled)
{
boneStartingPosition = boneData ? screen->mBones->UploadBones(*boneData) : -1;
evaluatedSingle = true;
}
}
mdl->RenderFrame(renderer, tex, drawinfo.modelframe, nextFrame ? drawinfo.modelframenext : drawinfo.modelframe, nextFrame ? frameinfo.inter : -1.f, translation, ssidp, boneStartingPosition);
}
void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, FTranslationID translation, AActor* actor)
{
double tic = actor->Level->totaltime;
if ((ConsoleState == c_up || ConsoleState == c_rising) && (menuactive == MENU_Off || menuactive == MENU_OnNoPause) && !actor->isFrozen())
{
tic += I_GetTimeFrac();
}
bool is_decoupled = (actor->flags9 & MF9_DECOUPLEDANIMATIONS);
DActorModelData* modelData = actor ? actor->modelData.ForceGet() : nullptr;
CalcModelFrameInfo frameinfo = CalcModelFrame(Level, smf, curState, curTics, modelData, actor, is_decoupled, tic);
ModelDrawInfo drawinfo;
int boneStartingPosition = -1;
bool evaluatedSingle = false;
for (unsigned i = 0; i < modelsamount; i++)
for (unsigned i = 0; i < frameinfo.modelsamount; i++)
{
int modelid = -1;
int animationid = -1;
int modelframe = -1;
int modelframenext = -1;
FTextureID skinid(nullptr);
surfaceskinids.Clear();
if (actor->modelData != nullptr)
if (CalcModelOverrides(i, smf, modelData, frameinfo, drawinfo, is_decoupled))
{
//modelID
if (actor->modelData->models.Size() > i && actor->modelData->models[i].modelID >= 0)
{
modelid = actor->modelData->models[i].modelID;
}
else if(actor->modelData->models.Size() > i && actor->modelData->models[i].modelID == -2)
{
continue;
}
else if(smf->modelsAmount > i)
{
modelid = smf->modelIDs[i];
}
//animationID
if (actor->modelData->animationIDs.Size() > i && actor->modelData->animationIDs[i] >= 0)
{
animationid = actor->modelData->animationIDs[i];
}
else if(smf->modelsAmount > i)
{
animationid = smf->animationIDs[i];
}
if(!is_decoupled)
{
//modelFrame
if (actor->modelData->modelFrameGenerators.Size() > i
&& (unsigned)actor->modelData->modelFrameGenerators[i] < modelsamount
&& smf->modelframes[actor->modelData->modelFrameGenerators[i]] >= 0
) {
modelframe = smf->modelframes[actor->modelData->modelFrameGenerators[i]];
if (smfNext)
{
if(smfNext->modelframes[actor->modelData->modelFrameGenerators[i]] >= 0)
{
modelframenext = smfNext->modelframes[actor->modelData->modelFrameGenerators[i]];
}
else
{
modelframenext = smfNext->modelframes[i];
}
}
}
else if(smf->modelsAmount > i)
{
modelframe = smf->modelframes[i];
if (smfNext) modelframenext = smfNext->modelframes[i];
}
}
//skinID
if (actor->modelData->skinIDs.Size() > i && actor->modelData->skinIDs[i].isValid())
{
skinid = actor->modelData->skinIDs[i];
}
else if(smf->modelsAmount > i)
{
skinid = smf->skinIDs[i];
}
//surfaceSkinIDs
if(actor->modelData->models.Size() > i && actor->modelData->models[i].surfaceSkinIDs.Size() > 0)
{
unsigned sz1 = smf->surfaceskinIDs.Size();
unsigned sz2 = actor->modelData->models[i].surfaceSkinIDs.Size();
unsigned start = i * MD3_MAX_SURFACES;
surfaceskinids = actor->modelData->models[i].surfaceSkinIDs;
surfaceskinids.Resize(MD3_MAX_SURFACES);
for (unsigned surface = 0; surface < MD3_MAX_SURFACES; surface++)
{
if (sz2 > surface && (actor->modelData->models[i].surfaceSkinIDs[surface].isValid()))
{
continue;
}
if((surface + start) < sz1)
{
surfaceskinids[surface] = smf->surfaceskinIDs[surface + start];
}
else
{
surfaceskinids[surface].SetNull();
}
}
}
}
else
{
modelid = smf->modelIDs[i];
animationid = smf->animationIDs[i];
modelframe = smf->modelframes[i];
if (smfNext) modelframenext = smfNext->modelframes[i];
skinid = smf->skinIDs[i];
}
if (modelid >= 0 && modelid < Models.size())
{
FModel * mdl = Models[modelid];
auto tex = skinid.isValid() ? TexMan.GetGameTexture(skinid, true) : nullptr;
mdl->BuildVertexBuffer(renderer);
auto ssidp = surfaceskinids.Size() > 0
? surfaceskinids.Data()
: (((i * MD3_MAX_SURFACES) < smf->surfaceskinIDs.Size()) ? &smf->surfaceskinIDs[i * MD3_MAX_SURFACES] : nullptr);
bool nextFrame = smfNext && modelframe != modelframenext;
// [RL0] while per-model animations aren't done, DECOUPLEDANIMATIONS does the same as MODELSAREATTACHMENTS
if(!evaluatedSingle)
{
const TArray<VSMatrix> *boneData = nullptr;
FModel* animation = mdl;
const TArray<TRS>* animationData = nullptr;
if (animationid >= 0)
{
animation = Models[animationid];
animationData = animation->AttachAnimationData();
}
if(is_decoupled)
{
if(decoupled_frame.frame1 >= 0)
{
boneData = animation->CalculateBones(actor->modelData->prevAnim, decoupled_frame, inter, animationData);
}
}
else
{
boneData = animation->CalculateBones(nullptr, {nextFrame ? inter : -1.0f, modelframe, modelframenext}, -1.0f, animationData);
}
if(smf_flags & MDL_MODELSAREATTACHMENTS || is_decoupled)
{
boneStartingPosition = boneData ? screen->mBones->UploadBones(*boneData) : -1;
evaluatedSingle = true;
}
}
mdl->RenderFrame(renderer, tex, modelframe, nextFrame ? modelframenext : modelframe, nextFrame ? inter : -1.f, translation, ssidp, boneStartingPosition);
RenderModelFrame(renderer, i, smf, modelData, frameinfo, drawinfo, is_decoupled, tic, translation, boneStartingPosition, evaluatedSingle);
}
}
}
@ -1068,33 +1115,24 @@ void ParseModelDefLump(int Lump)
//
//===========================================================================
FSpriteModelFrame * FindModelFrameRaw(const PClass * ti, int sprite, int frame, bool dropped)
FSpriteModelFrame * FindModelFrameRaw(const AActor * actorDefaults, const PClass * ti, int sprite, int frame, bool dropped)
{
auto def = GetDefaultByType(ti);
if (def->hasmodel)
if(actorDefaults->hasmodel)
{
if(def->flags9 & MF9_DECOUPLEDANIMATIONS)
FSpriteModelFrame smf;
memset(&smf, 0, sizeof(smf));
smf.type = ti;
smf.sprite = sprite;
smf.frame = frame;
int hash = SpriteModelHash[ModelFrameHash(&smf) % SpriteModelFrames.Size()];
while (hash>=0)
{
FSpriteModelFrame * smf = BaseSpriteModelFrames.CheckKey((void*)ti);
if(smf) return smf;
}
else
{
FSpriteModelFrame smf;
memset(&smf, 0, sizeof(smf));
smf.type=ti;
smf.sprite=sprite;
smf.frame=frame;
int hash = SpriteModelHash[ModelFrameHash(&smf) % SpriteModelFrames.Size()];
while (hash>=0)
{
FSpriteModelFrame * smff = &SpriteModelFrames[hash];
if (smff->type==ti && smff->sprite==sprite && smff->frame==frame) return smff;
hash=smff->hashnext;
}
FSpriteModelFrame * smff = &SpriteModelFrames[hash];
if (smff->type == ti && smff->sprite == sprite && smff->frame == frame) return smff;
hash = smff->hashnext;
}
}
@ -1108,28 +1146,52 @@ FSpriteModelFrame * FindModelFrameRaw(const PClass * ti, int sprite, int frame,
if (sprframe->Voxel != nullptr)
{
int index = sprframe->Voxel->VoxeldefIndex;
if (dropped && sprframe->Voxel->DroppedSpin !=sprframe->Voxel->PlacedSpin) index++;
if (dropped && sprframe->Voxel->DroppedSpin != sprframe->Voxel->PlacedSpin) index++;
return &SpriteModelFrames[index];
}
}
}
return nullptr;
}
FSpriteModelFrame * FindModelFrame(const AActor * thing, int sprite, int frame, bool dropped)
FSpriteModelFrame * FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped)
{
if(!thing) return nullptr;
auto def = GetDefaultByType(ti);
if(thing->flags9 & MF9_DECOUPLEDANIMATIONS)
if (def->hasmodel)
{
return BaseSpriteModelFrames.CheckKey((thing->modelData != nullptr && thing->modelData->modelDef != nullptr) ? thing->modelData->modelDef : thing->GetClass());
if(def->flags9 & MF9_DECOUPLEDANIMATIONS)
{
FSpriteModelFrame * smf = BaseSpriteModelFrames.CheckKey(ti);
if(smf) return smf;
}
}
return FindModelFrameRaw(def, ti, sprite, frame, dropped);
}
FSpriteModelFrame * FindModelFrame(const PClass * ti, bool is_decoupled, int sprite, int frame, bool dropped)
{
if(!ti) return nullptr;
if(is_decoupled)
{
return BaseSpriteModelFrames.CheckKey(ti);
}
else
{
return FindModelFrameRaw((thing->modelData != nullptr && thing->modelData->modelDef != nullptr) ? thing->modelData->modelDef : thing->GetClass(), sprite, frame, dropped);
return FindModelFrameRaw(GetDefaultByType(ti), ti, sprite, frame, dropped);
}
}
FSpriteModelFrame * FindModelFrame(AActor * thing, int sprite, int frame, bool dropped)
{
if(!thing) return nullptr;
return FindModelFrame((thing->modelData != nullptr && thing->modelData->modelDef != nullptr) ? thing->modelData->modelDef : thing->GetClass(), (thing->flags9 & MF9_DECOUPLEDANIMATIONS), sprite, frame, dropped);
}
//===========================================================================
//
// IsHUDModelForPlayerAvailable

View file

@ -62,8 +62,11 @@ enum
MDL_FORCECULLBACKFACES = 1<<14,
};
FSpriteModelFrame * FindModelFrame(const AActor * thing, int sprite, int frame, bool dropped);
FSpriteModelFrame * FindModelFrameRaw(const PClass * ti, int sprite, int frame, bool dropped);
FSpriteModelFrame * FindModelFrame(AActor * thing, int sprite, int frame, bool dropped);
FSpriteModelFrame * FindModelFrame(const PClass * ti, bool is_decoupled, int sprite, int frame, bool dropped);
FSpriteModelFrame * FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped);
//FSpriteModelFrame * FindModelFrameRaw(const AActor * actorDefaults, const PClass * ti, int sprite, int frame, bool dropped);
bool IsHUDModelForPlayerAvailable(player_t * player);
// Check if circle potentially intersects with node AABB
@ -114,6 +117,36 @@ void BSPWalkCircle(FLevelLocals *Level, float x, float y, float radiusSquared, c
void RenderModel(FModelRenderer* renderer, float x, float y, float z, FSpriteModelFrame* smf, AActor* actor, double ticFrac);
void RenderHUDModel(FModelRenderer* renderer, DPSprite* psp, FVector3 translation, FVector3 rotation, FVector3 rotation_pivot, FSpriteModelFrame *smf);
struct CalcModelFrameInfo
{
int smf_flags;
const FSpriteModelFrame * smfNext;
float inter;
bool is_decoupled;
ModelAnimFrameInterp decoupled_frame;
ModelAnimFrame * decoupled_frame_prev;
unsigned modelsamount;
};
struct ModelDrawInfo
{
TArray<FTextureID> surfaceskinids;
int modelid;
int animationid;
int modelframe;
int modelframenext;
FTextureID skinid;
};
class DActorModelData;
CalcModelFrameInfo CalcModelFrame(FLevelLocals *Level, const FSpriteModelFrame *smf, const FState *curState, const int curTics, DActorModelData* modelData, AActor* actor, bool is_decoupled, double tic);
// returns true if the model isn't removed
bool CalcModelOverrides(int modelindex, const FSpriteModelFrame *smf, DActorModelData* modelData, const CalcModelFrameInfo &frameinfo, ModelDrawInfo &drawinfo, bool is_decoupled);
const TArray<VSMatrix> * ProcessModelFrame(FModel * animation, bool nextFrame, int i, const FSpriteModelFrame *smf, DActorModelData* modelData, const CalcModelFrameInfo &frameinfo, ModelDrawInfo &drawinfo, bool is_decoupled, double tic);
EXTERN_CVAR(Float, cl_scaleweaponfov)
#endif

View file

@ -159,7 +159,7 @@ void hw_PrecacheTexture(uint8_t *texhitlist, TMap<PClassActor*, bool> &actorhitl
{
auto &state = cls->GetStates()[i];
spritelist[state.sprite].Insert(gltrans, true);
FSpriteModelFrame * smf = FindModelFrameRaw(cls, state.sprite, state.Frame, false);
FSpriteModelFrame * smf = FindModelFrame(cls, state.sprite, state.Frame, false);
if (smf != NULL)
{
for (int i = 0; i < smf->modelsAmount; i++)