mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-26 22:11:43 +00:00
- got rid of the state variables in the model class entirely.
This is *not* the right way to reduce function parameter count. The data in question is far easier evaluated at the calling site and passed as a parameter.
This commit is contained in:
parent
76c8214d67
commit
9875850c19
13 changed files with 43 additions and 53 deletions
|
@ -61,19 +61,15 @@ public:
|
||||||
|
|
||||||
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) = 0;
|
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) = 0;
|
||||||
virtual int FindFrame(const char * name) = 0;
|
virtual int FindFrame(const char * name) = 0;
|
||||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const TArray<FTextureID>& surfaceskinids) = 0;
|
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) = 0;
|
||||||
virtual void BuildVertexBuffer(FModelRenderer *renderer) = 0;
|
virtual void BuildVertexBuffer(FModelRenderer *renderer) = 0;
|
||||||
virtual void AddSkins(uint8_t *hitlist) = 0;
|
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) = 0;
|
||||||
virtual float getAspectFactor(float vscale) { return 1.f; }
|
virtual float getAspectFactor(float vscale) { return 1.f; }
|
||||||
|
|
||||||
void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; }
|
void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; }
|
||||||
IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; }
|
IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; }
|
||||||
void DestroyVertexBuffer();
|
void DestroyVertexBuffer();
|
||||||
|
|
||||||
const FSpriteModelFrame* curSpriteMDLFrame;
|
|
||||||
int curMDLIndex;
|
|
||||||
void PushSpriteMDLFrame(const FSpriteModelFrame *smf, int index) { curSpriteMDLFrame = smf; curMDLIndex = index; };
|
|
||||||
|
|
||||||
FString mFileName;
|
FString mFileName;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -59,8 +59,8 @@ public:
|
||||||
bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
||||||
void Initialize();
|
void Initialize();
|
||||||
virtual int FindFrame(const char * name) override;
|
virtual int FindFrame(const char * name) override;
|
||||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const TArray<FTextureID>& surfaceskinids) override;
|
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) override;
|
||||||
virtual void AddSkins(uint8_t *hitlist) override;
|
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;
|
||||||
FTextureID GetPaletteTexture() const { return mPalette; }
|
FTextureID GetPaletteTexture() const { return mPalette; }
|
||||||
void BuildVertexBuffer(FModelRenderer *renderer) override;
|
void BuildVertexBuffer(FModelRenderer *renderer) override;
|
||||||
float getAspectFactor(float vscale) override;
|
float getAspectFactor(float vscale) override;
|
||||||
|
|
|
@ -111,11 +111,11 @@ public:
|
||||||
}
|
}
|
||||||
virtual ~FDMDModel();
|
virtual ~FDMDModel();
|
||||||
|
|
||||||
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length);
|
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
||||||
virtual int FindFrame(const char * name);
|
virtual int FindFrame(const char * name) override;
|
||||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const TArray<FTextureID>& surfaceskinids);
|
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) override;
|
||||||
virtual void LoadGeometry();
|
virtual void LoadGeometry();
|
||||||
virtual void AddSkins(uint8_t *hitlist);
|
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;
|
||||||
|
|
||||||
void UnloadGeometry();
|
void UnloadGeometry();
|
||||||
void BuildVertexBuffer(FModelRenderer *renderer);
|
void BuildVertexBuffer(FModelRenderer *renderer);
|
||||||
|
|
|
@ -65,11 +65,11 @@ class FMD3Model : public FModel
|
||||||
public:
|
public:
|
||||||
FMD3Model() = default;
|
FMD3Model() = default;
|
||||||
|
|
||||||
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length);
|
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
||||||
virtual int FindFrame(const char * name);
|
virtual int FindFrame(const char * name) override;
|
||||||
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const TArray<FTextureID>& surfaceskinids);
|
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) override;
|
||||||
void LoadGeometry();
|
void LoadGeometry();
|
||||||
void BuildVertexBuffer(FModelRenderer *renderer);
|
void BuildVertexBuffer(FModelRenderer *renderer);
|
||||||
virtual void AddSkins(uint8_t *hitlist);
|
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -98,9 +98,9 @@ public:
|
||||||
~FOBJModel();
|
~FOBJModel();
|
||||||
bool Load(const char* fn, int lumpnum, const char* buffer, int length) override;
|
bool Load(const char* fn, int lumpnum, const char* buffer, int length) override;
|
||||||
int FindFrame(const char* name) override;
|
int FindFrame(const char* name) override;
|
||||||
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const TArray<FTextureID>& surfaceskinids) override;
|
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) override;
|
||||||
void BuildVertexBuffer(FModelRenderer* renderer) override;
|
void BuildVertexBuffer(FModelRenderer* renderer) override;
|
||||||
void AddSkins(uint8_t* hitlist) override;
|
void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,9 +26,9 @@ public:
|
||||||
|
|
||||||
bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
|
||||||
int FindFrame(const char * name) override;
|
int FindFrame(const char * name) override;
|
||||||
void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const TArray<FTextureID>& surfaceskinids) override;
|
void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids) override;
|
||||||
void BuildVertexBuffer(FModelRenderer *renderer) override;
|
void BuildVertexBuffer(FModelRenderer *renderer) override;
|
||||||
void AddSkins(uint8_t *hitlist) override;
|
void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;
|
||||||
void LoadGeometry();
|
void LoadGeometry();
|
||||||
void UnloadGeometry();
|
void UnloadGeometry();
|
||||||
FUE1Model()
|
FUE1Model()
|
||||||
|
|
|
@ -332,7 +332,7 @@ void FDMDModel::BuildVertexBuffer(FModelRenderer *renderer)
|
||||||
//
|
//
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
void FDMDModel::AddSkins(uint8_t *hitlist)
|
void FDMDModel::AddSkins(uint8_t *hitlist, const FTextureID*)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < info.numSkins; i++)
|
for (int i = 0; i < info.numSkins; i++)
|
||||||
{
|
{
|
||||||
|
@ -363,7 +363,7 @@ int FDMDModel::FindFrame(const char * name)
|
||||||
//
|
//
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
void FDMDModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const TArray<FTextureID>&)
|
void FDMDModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID*)
|
||||||
{
|
{
|
||||||
if (frameno >= info.numFrames || frameno2 >= info.numFrames) return;
|
if (frameno >= info.numFrames || frameno2 >= info.numFrames) return;
|
||||||
|
|
||||||
|
|
|
@ -302,14 +302,13 @@ void FMD3Model::BuildVertexBuffer(FModelRenderer *renderer)
|
||||||
//
|
//
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
void FMD3Model::AddSkins(uint8_t *hitlist)
|
void FMD3Model::AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
for (unsigned i = 0; i < Surfaces.Size(); i++)
|
||||||
{
|
{
|
||||||
int ssIndex = i + curMDLIndex * MD3_MAX_SURFACES;
|
if (surfaceskinids && surfaceskinids[i].isValid())
|
||||||
if (curSpriteMDLFrame && curSpriteMDLFrame->surfaceskinIDs[ssIndex].isValid())
|
|
||||||
{
|
{
|
||||||
hitlist[curSpriteMDLFrame->surfaceskinIDs[ssIndex].GetIndex()] |= FTextureManager::HIT_Flat;
|
hitlist[surfaceskinids[i].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||||
}
|
}
|
||||||
|
|
||||||
MD3Surface * surf = &Surfaces[i];
|
MD3Surface * surf = &Surfaces[i];
|
||||||
|
@ -344,7 +343,7 @@ int FMD3Model::FindFrame(const char * name)
|
||||||
//
|
//
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const TArray<FTextureID>& surfaceskinids)
|
void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids)
|
||||||
{
|
{
|
||||||
if ((unsigned)frameno >= Frames.Size() || (unsigned)frameno2 >= Frames.Size()) return;
|
if ((unsigned)frameno >= Frames.Size() || (unsigned)frameno2 >= Frames.Size()) return;
|
||||||
|
|
||||||
|
@ -358,10 +357,9 @@ void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int f
|
||||||
FGameTexture *surfaceSkin = skin;
|
FGameTexture *surfaceSkin = skin;
|
||||||
if (!surfaceSkin)
|
if (!surfaceSkin)
|
||||||
{
|
{
|
||||||
int ssIndex = i + curMDLIndex * MD3_MAX_SURFACES;
|
if (surfaceskinids && surfaceskinids[i].isValid())
|
||||||
if (surfaceskinids[ssIndex].isValid())
|
|
||||||
{
|
{
|
||||||
surfaceSkin = TexMan.GetGameTexture(surfaceskinids[ssIndex], true);
|
surfaceSkin = TexMan.GetGameTexture(surfaceskinids[i], true);
|
||||||
}
|
}
|
||||||
else if (surf->numSkins > 0 && surf->Skins[0].isValid())
|
else if (surf->numSkins > 0 && surf->Skins[0].isValid())
|
||||||
{
|
{
|
||||||
|
|
|
@ -628,7 +628,7 @@ int FOBJModel::FindFrame(const char* name)
|
||||||
* @param inter The amount to interpolate the two frames.
|
* @param inter The amount to interpolate the two frames.
|
||||||
* @param translation The translation for the skin
|
* @param translation The translation for the skin
|
||||||
*/
|
*/
|
||||||
void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const TArray<FTextureID>& surfaceskinids)
|
void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids)
|
||||||
{
|
{
|
||||||
// Prevent the model from rendering if the frame number is < 0
|
// Prevent the model from rendering if the frame number is < 0
|
||||||
if (frameno < 0 || frameno2 < 0) return;
|
if (frameno < 0 || frameno2 < 0) return;
|
||||||
|
@ -640,10 +640,9 @@ void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int f
|
||||||
FGameTexture *userSkin = skin;
|
FGameTexture *userSkin = skin;
|
||||||
if (!userSkin)
|
if (!userSkin)
|
||||||
{
|
{
|
||||||
int ssIndex = i + curMDLIndex * MD3_MAX_SURFACES;
|
if (surfaceskinids && surfaceskinids[i].isValid())
|
||||||
if (i < MD3_MAX_SURFACES && surfaceskinids[ssIndex].isValid())
|
|
||||||
{
|
{
|
||||||
userSkin = TexMan.GetGameTexture(surfaceskinids[ssIndex], true);
|
userSkin = TexMan.GetGameTexture(surfaceskinids[i], true);
|
||||||
}
|
}
|
||||||
else if (surf->skin.isValid())
|
else if (surf->skin.isValid())
|
||||||
{
|
{
|
||||||
|
@ -668,18 +667,17 @@ void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int f
|
||||||
*
|
*
|
||||||
* @param hitlist The list of textures
|
* @param hitlist The list of textures
|
||||||
*/
|
*/
|
||||||
void FOBJModel::AddSkins(uint8_t* hitlist)
|
void FOBJModel::AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < surfaces.Size(); i++)
|
for (size_t i = 0; i < surfaces.Size(); i++)
|
||||||
{
|
{
|
||||||
size_t ssIndex = i + curMDLIndex * MD3_MAX_SURFACES;
|
if (surfaceskinids && i < MD3_MAX_SURFACES && surfaceskinids[i].isValid())
|
||||||
if (curSpriteMDLFrame && i < MD3_MAX_SURFACES && curSpriteMDLFrame->surfaceskinIDs[ssIndex].isValid())
|
|
||||||
{
|
{
|
||||||
// Precache skins manually reassigned by the user.
|
// Precache skins manually reassigned by the user.
|
||||||
// On OBJs with lots of skins, such as Doom map OBJs exported from GZDB,
|
// On OBJs with lots of skins, such as Doom map OBJs exported from GZDB,
|
||||||
// there may be too many skins for the user to manually change, unless
|
// there may be too many skins for the user to manually change, unless
|
||||||
// the limit is bumped or surfaceskinIDs is changed to a TArray<FTextureID>.
|
// the limit is bumped or surfaceskinIDs is changed to a TArray<FTextureID>.
|
||||||
hitlist[curSpriteMDLFrame->surfaceskinIDs[ssIndex].GetIndex()] |= FTextureManager::HIT_Flat;
|
hitlist[surfaceskinids[i].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||||
return; // No need to precache skin that was replaced
|
return; // No need to precache skin that was replaced
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -227,7 +227,7 @@ int FUE1Model::FindFrame( const char *name )
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FUE1Model::RenderFrame( FModelRenderer *renderer, FGameTexture *skin, int frame, int frame2, double inter, int translation, const TArray<FTextureID>& surfaceskinids)
|
void FUE1Model::RenderFrame( FModelRenderer *renderer, FGameTexture *skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids)
|
||||||
{
|
{
|
||||||
// the moment of magic
|
// the moment of magic
|
||||||
if ( (frame >= numFrames) || (frame2 >= numFrames) ) return;
|
if ( (frame >= numFrames) || (frame2 >= numFrames) ) return;
|
||||||
|
@ -246,9 +246,8 @@ void FUE1Model::RenderFrame( FModelRenderer *renderer, FGameTexture *skin, int f
|
||||||
FGameTexture *sskin = skin;
|
FGameTexture *sskin = skin;
|
||||||
if ( !sskin )
|
if ( !sskin )
|
||||||
{
|
{
|
||||||
int ssIndex = groups[i].texNum + curMDLIndex * MD3_MAX_SURFACES;
|
if (surfaceskinids && surfaceskinids[i].isValid())
|
||||||
if (surfaceskinids[ssIndex].isValid())
|
sskin = TexMan.GetGameTexture(surfaceskinids[i], true);
|
||||||
sskin = TexMan.GetGameTexture(surfaceskinids[ssIndex], true);
|
|
||||||
if ( !sskin )
|
if ( !sskin )
|
||||||
{
|
{
|
||||||
vofs += vsize;
|
vofs += vsize;
|
||||||
|
@ -305,13 +304,12 @@ void FUE1Model::BuildVertexBuffer( FModelRenderer *renderer )
|
||||||
vbuf->UnlockVertexBuffer();
|
vbuf->UnlockVertexBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FUE1Model::AddSkins( uint8_t *hitlist )
|
void FUE1Model::AddSkins( uint8_t *hitlist, const FTextureID* surfaceskinids)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < numGroups; i++)
|
for (int i = 0; i < numGroups; i++)
|
||||||
{
|
{
|
||||||
int ssIndex = groups[i].texNum + curMDLIndex * MD3_MAX_SURFACES;
|
if (surfaceskinids && surfaceskinids[i].isValid())
|
||||||
if (curSpriteMDLFrame && curSpriteMDLFrame->surfaceskinIDs[ssIndex].isValid())
|
hitlist[surfaceskinids[i].GetIndex()] |= FTextureManager::HIT_Flat;
|
||||||
hitlist[curSpriteMDLFrame->surfaceskinIDs[ssIndex].GetIndex()] |= FTextureManager::HIT_Flat;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -356,7 +356,7 @@ void FVoxelModel::BuildVertexBuffer(FModelRenderer *renderer)
|
||||||
//
|
//
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
void FVoxelModel::AddSkins(uint8_t *hitlist)
|
void FVoxelModel::AddSkins(uint8_t *hitlist, const FTextureID*)
|
||||||
{
|
{
|
||||||
hitlist[mPalette.GetIndex()] |= FTextureManager::HIT_Flat;
|
hitlist[mPalette.GetIndex()] |= FTextureManager::HIT_Flat;
|
||||||
}
|
}
|
||||||
|
@ -400,7 +400,7 @@ float FVoxelModel::getAspectFactor(float stretch)
|
||||||
//
|
//
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
void FVoxelModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const TArray<FTextureID>&)
|
void FVoxelModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID*)
|
||||||
{
|
{
|
||||||
renderer->SetMaterial(skin, true, translation);
|
renderer->SetMaterial(skin, true, translation);
|
||||||
renderer->SetupFrame(this, 0, 0, 0);
|
renderer->SetupFrame(this, 0, 0, 0);
|
||||||
|
|
|
@ -339,12 +339,13 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
|
||||||
auto tex = skinid.isValid() ? TexMan.GetGameTexture(skinid, true) : nullptr;
|
auto tex = skinid.isValid() ? TexMan.GetGameTexture(skinid, true) : nullptr;
|
||||||
mdl->BuildVertexBuffer(renderer);
|
mdl->BuildVertexBuffer(renderer);
|
||||||
|
|
||||||
mdl->PushSpriteMDLFrame(smf, i);
|
auto& ssids = surfaceskinids.Size() > 0 ? surfaceskinids : smf->surfaceskinIDs;
|
||||||
|
auto ssidp = (unsigned)(i * MD3_MAX_SURFACES) < ssids.Size() ? &ssids[i * MD3_MAX_SURFACES] : nullptr;
|
||||||
|
|
||||||
if (smfNext && modelframe != modelframenext)
|
if (smfNext && modelframe != modelframenext)
|
||||||
mdl->RenderFrame(renderer, tex, modelframe, modelframenext, inter, translation, surfaceskinids.Size() > 0? surfaceskinids : smf->surfaceskinIDs);
|
mdl->RenderFrame(renderer, tex, modelframe, modelframenext, inter, translation, ssidp);
|
||||||
else
|
else
|
||||||
mdl->RenderFrame(renderer, tex, modelframe, modelframe, 0.f, translation, surfaceskinids.Size() > 0 ? surfaceskinids : smf->surfaceskinIDs);
|
mdl->RenderFrame(renderer, tex, modelframe, modelframe, 0.f, translation, ssidp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,8 +170,7 @@ void hw_PrecacheTexture(uint8_t *texhitlist, TMap<PClassActor*, bool> &actorhitl
|
||||||
}
|
}
|
||||||
else if (smf->modelIDs[i] != -1)
|
else if (smf->modelIDs[i] != -1)
|
||||||
{
|
{
|
||||||
Models[smf->modelIDs[i]]->PushSpriteMDLFrame(smf, i);
|
Models[smf->modelIDs[i]]->AddSkins(texhitlist, (unsigned)(i * MD3_MAX_SURFACES) < smf->surfaceskinIDs.Size()? &smf->surfaceskinIDs[i * MD3_MAX_SURFACES] : nullptr);
|
||||||
Models[smf->modelIDs[i]]->AddSkins(texhitlist);
|
|
||||||
}
|
}
|
||||||
if (smf->modelIDs[i] != -1)
|
if (smf->modelIDs[i] != -1)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue