Some of the to-dos

- Implemented Animation parameters for A_ChangeModel
- Made a modeldef flag to treat additional model indices as just attachments, meaning they will use armature data from index 0
- Fixed an issue with A_ChangeModel where generated indices lower than smf frame amounts could not actually generate anything
This commit is contained in:
Shiny Metagross 2022-08-15 14:19:33 -07:00 committed by Christoph Oelckers
parent e9e919b54c
commit 5670e6f54c
19 changed files with 119 additions and 59 deletions

View File

@ -70,11 +70,12 @@ public:
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) = 0;
virtual int FindFrame(const char * name, bool nodefault = false) = 0;
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) = 0;
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData) = 0;
virtual void BuildVertexBuffer(FModelRenderer *renderer) = 0;
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) = 0;
virtual float getAspectFactor(float vscale) { return 1.f; }
virtual const TArray<VSMatrix>* AttachAnimationData() { return nullptr; };
virtual const TArray<VSMatrix> CalculateBones(int frame1, int frame2, double inter, const TArray<VSMatrix>& animationData) { return {}; };
void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; }
IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; }

View File

@ -109,10 +109,11 @@ public:
bool Load(const char* fn, int lumpnum, const char* buffer, int length) override;
int FindFrame(const char* name, bool nodefault) override;
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData) override;
void BuildVertexBuffer(FModelRenderer* renderer) override;
void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override;
const TArray<VSMatrix>* AttachAnimationData() override;
const TArray<VSMatrix> CalculateBones(int frame1, int frame2, double inter, const TArray<VSMatrix>& animationData) override;
private:
void LoadGeometry();

View File

@ -59,7 +59,7 @@ public:
bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
void Initialize();
virtual int FindFrame(const char* name, bool nodefault) override;
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData) override;
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;
FTextureID GetPaletteTexture() const { return mPalette; }
void BuildVertexBuffer(FModelRenderer *renderer) override;

View File

@ -113,7 +113,7 @@ public:
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
virtual int FindFrame(const char* name, bool nodefault) override;
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData) override;
virtual void LoadGeometry();
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;

View File

@ -67,7 +67,7 @@ public:
virtual bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
virtual int FindFrame(const char* name, bool nodefault) override;
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData) override;
void LoadGeometry();
void BuildVertexBuffer(FModelRenderer *renderer);
virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;

View File

@ -98,7 +98,7 @@ public:
~FOBJModel();
bool Load(const char* fn, int lumpnum, const char* buffer, int length) override;
int FindFrame(const char* name, bool nodefault) override;
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData) override;
void BuildVertexBuffer(FModelRenderer* renderer) override;
void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override;
};

View File

@ -26,7 +26,7 @@ public:
bool Load(const char * fn, int lumpnum, const char * buffer, int length) override;
int FindFrame(const char* name, bool nodefault) override;
void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData) override;
void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData) override;
void BuildVertexBuffer(FModelRenderer *renderer) override;
void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override;
void LoadGeometry();

View File

@ -490,46 +490,9 @@ int IQMModel::FindFrame(const char* name, bool nodefault)
return FErr_NotFound;
}
void IQMModel::RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame1, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData)
void IQMModel::RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame1, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData)
{
const TArray<VSMatrix>& animationFrames = &animationData ? animationData : FrameTransforms;
int numbones = Joints.Size();
frame1 = clamp(frame1, 0, ((int)animationFrames.Size() - 1)/numbones);
frame2 = clamp(frame2, 0, ((int)animationFrames.Size() - 1)/numbones);
int offset1 = frame1 * numbones;
int offset2 = frame2 * numbones;
float t = (float)inter;
float invt = 1.0f - t;
TArray<VSMatrix> bones(numbones, true);
for (int i = 0; i < numbones; i++)
{
const float* from = animationFrames[offset1 + i].get();
const float* to = animationFrames[offset2 + i].get();
// Interpolate bone between the two frames
float bone[16];
for (int i = 0; i < 16; i++)
{
bone[i] = from[i] * invt + to[i] * t;
}
// Apply parent bone
if (Joints[i].Parent >= 0)
{
bones[i] = bones[Joints[i].Parent];
bones[i].multMatrix(bone);
}
else
{
bones[i].loadMatrix(bone);
}
}
renderer->SetupFrame(this, 0, 0, NumVertices, bones);
renderer->SetupFrame(this, 0, 0, NumVertices, boneData);
FGameTexture* lastSkin = nullptr;
for (int i = 0; i < Meshes.Size(); i++)
@ -597,3 +560,45 @@ const TArray<VSMatrix>* IQMModel::AttachAnimationData()
{
return &FrameTransforms;
}
const TArray<VSMatrix> IQMModel::CalculateBones(int frame1, int frame2, double inter, const TArray<VSMatrix>& animationData)
{
const TArray<VSMatrix>& animationFrames = &animationData ? animationData : FrameTransforms;
int numbones = Joints.Size();
frame1 = clamp(frame1, 0, ((int)animationFrames.Size() - 1) / numbones);
frame2 = clamp(frame2, 0, ((int)animationFrames.Size() - 1) / numbones);
int offset1 = frame1 * numbones;
int offset2 = frame2 * numbones;
float t = (float)inter;
float invt = 1.0f - t;
TArray<VSMatrix> bones(numbones, true);
for (int i = 0; i < numbones; i++)
{
const float* from = animationFrames[offset1 + i].get();
const float* to = animationFrames[offset2 + i].get();
// Interpolate bone between the two frames
float bone[16];
for (int i = 0; i < 16; i++)
{
bone[i] = from[i] * invt + to[i] * t;
}
// Apply parent bone
if (Joints[i].Parent >= 0)
{
bones[i] = bones[Joints[i].Parent];
bones[i].multMatrix(bone);
}
else
{
bones[i].loadMatrix(bone);
}
}
return bones;
}

View File

@ -363,7 +363,7 @@ int FDMDModel::FindFrame(const char* name, bool nodefault)
//
//===========================================================================
void FDMDModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID*, const TArray<VSMatrix>& animationData)
void FDMDModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID*, const TArray<VSMatrix>& boneData)
{
if (frameno >= info.numFrames || frameno2 >= info.numFrames) return;

View File

@ -343,7 +343,7 @@ int FMD3Model::FindFrame(const char* name, bool nodefault)
//
//===========================================================================
void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData)
void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData)
{
if ((unsigned)frameno >= Frames.Size() || (unsigned)frameno2 >= Frames.Size()) return;

View File

@ -628,7 +628,7 @@ int FOBJModel::FindFrame(const char* name, bool nodefault)
* @param inter The amount to interpolate the two frames.
* @param translation The translation for the skin
*/
void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData)
void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData)
{
// Prevent the model from rendering if the frame number is < 0
if (frameno < 0 || frameno2 < 0) return;

View File

@ -229,7 +229,7 @@ int FUE1Model::FindFrame(const char* name, bool nodefault)
return index;
}
void FUE1Model::RenderFrame( FModelRenderer *renderer, FGameTexture *skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& animationData)
void FUE1Model::RenderFrame( FModelRenderer *renderer, FGameTexture *skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray<VSMatrix>& boneData)
{
// the moment of magic
if ( (frame < 0) || (frame2 < 0) || (frame >= numFrames) || (frame2 >= numFrames) ) return;

View File

@ -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 FTextureID*, const TArray<VSMatrix>& animationData)
void FVoxelModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID*, const TArray<VSMatrix>& boneData)
{
renderer->SetMaterial(skin, true, translation);
renderer->SetupFrame(this, 0, 0, 0, {});

View File

@ -682,6 +682,7 @@ public:
TArray<int> modelIDs;
TArray<FTextureID> skinIDs;
TArray<FTextureID> surfaceSkinIDs;
TArray<int> animationIDs;
TArray<int> modelFrameGenerators;
DActorModelData() = default;

View File

@ -5047,6 +5047,9 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel)
PARAM_NAME(skin);
PARAM_INT(flags);
PARAM_INT(generatorindex);
PARAM_INT(animationindex);
PARAM_STRING_VAL(animationpath);
PARAM_NAME(animation);
if (self == nullptr)
ACTION_RETURN_BOOL(false);
@ -5060,11 +5063,17 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel)
Printf("Attempt to pass invalid skin index %d in %s, index must be non-negative.", skinindex, self->GetCharacterName());
ACTION_RETURN_BOOL(false);
}
else if (animationindex < 0)
{
Printf("Attempt to pass invalid animation index %d in %s, index must be non-negative.", animationindex, self->GetCharacterName());
ACTION_RETURN_BOOL(false);
}
AActor* mobj = (ACTION_CALL_FROM_PSPRITE() && (flags & CMDL_WEAPONTOPLAYER)) || ACTION_CALL_FROM_INVENTORY() ? self : stateowner;
if (modelpath[(int)modelpath.Len() - 1] != '/') modelpath += '/';
if (skinpath[(int)skinpath.Len() - 1] != '/') skinpath += '/';
if (animationpath[(int)animationpath.Len() - 1] != '/') animationpath += '/';
if (mobj->modelData == nullptr)
{
@ -5073,6 +5082,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel)
ptr->modelIDs = *new TArray<int>();
ptr->skinIDs = *new TArray<FTextureID>();
ptr->surfaceSkinIDs = *new TArray<FTextureID>();
ptr->animationIDs = *new TArray<int>();
ptr->modelFrameGenerators = *new TArray<int>();
ptr->modelDef = NAME_None;
mobj->modelData = ptr;
@ -5083,15 +5093,18 @@ 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 maxAnimations = mobj->modelData->animationIDs.Size();
int maxGenerators = mobj->modelData->modelFrameGenerators.Size();
int skinPosition = skinindex + modelindex * MD3_MAX_SURFACES;
int queryModel = !(flags & CMDL_HIDEMODEL) ? model != NAME_None ? FindModel(modelpath.GetChars(), model.GetChars()) : -1 : -2;
int queryAnimation = animation != NAME_None ? FindModel(animationpath.GetChars(), animation.GetChars()) : -1;
//[SM] - Let's clear out any potential entries at the specified indices
mobj->modelData->modelDef = modeldef;
if(maxModels > modelindex) mobj->modelData->modelIDs.Pop(mobj->modelData->modelIDs[modelindex]);
if(maxAnimations > animationindex) mobj->modelData->animationIDs.Pop(mobj->modelData->animationIDs[animationindex]);
if(maxGenerators > modelindex) mobj->modelData->modelFrameGenerators.Pop(mobj->modelData->modelFrameGenerators[modelindex]);
if (flags & CMDL_USESURFACESKIN)
@ -5108,13 +5121,15 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel)
//[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() < modelindex) mobj->modelData->modelIDs.Push(-1);
while ((int)mobj->modelData->modelFrameGenerators.Size() < modelindex) mobj->modelData->modelFrameGenerators.Push(-1);
while ((int)mobj->modelData->animationIDs.Size() < modelindex) mobj->modelData->animationIDs.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);
mobj->modelData->modelFrameGenerators.Insert(modelindex, generatorindex);
mobj->modelData->animationIDs.Insert(animationindex, queryAnimation);
if (flags & CMDL_USESURFACESKIN)
mobj->modelData->surfaceSkinIDs.Insert(skinPosition, skin != NAME_None ? LoadSkin(skinpath.GetChars(), skin.GetChars()) : FNullTextureID());
else
@ -5131,6 +5146,17 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel)
if(allowPush) savedModelFiles.Push(fullName);
}
//Same for animations
if (queryAnimation >= 0)
{
FString fullName;
fullName.Format("%s%s", animationpath.GetChars(), animation.GetChars());
bool allowPush = true;
for (unsigned i = 0; i < savedModelFiles.Size(); i++) if (!savedModelFiles[i].CompareNoCase(fullName)) allowPush = false;
for (unsigned i = 0; i < Models.Size() - 1; i++) if (!Models[i]->mFileName.CompareNoCase(fullName)) allowPush = false;
if (allowPush) savedModelFiles.Push(fullName);
}
//[SM] - if an indice of modelIDs or skinIDs comes up blank and it's the last one, just delete it. For using very large amounts of indices, common sense says to just not run this repeatedly.
while (mobj->modelData->modelIDs.Size() > 0 && mobj->modelData->modelIDs.Last() == -1)
@ -5141,14 +5167,17 @@ DEFINE_ACTION_FUNCTION(AActor, A_ChangeModel)
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());
while (mobj->modelData->animationIDs.Size() > 0 && mobj->modelData->animationIDs.Last() == -1)
mobj->modelData->animationIDs.Pop(mobj->modelData->animationIDs.Last());
if (mobj->modelData->modelIDs.Size() == 0 && mobj->modelData->modelFrameGenerators.Size() == 0 && mobj->modelData->skinIDs.Size() == 0 && mobj->modelData->surfaceSkinIDs.Size() == 0 && modeldef == NAME_None)
if (mobj->modelData->modelIDs.Size() == 0 && mobj->modelData->modelFrameGenerators.Size() == 0 && mobj->modelData->skinIDs.Size() == 0 && mobj->modelData->surfaceSkinIDs.Size() == 0 && mobj->modelData->animationIDs.Size() == 0 && modeldef == NAME_None)
{
mobj->hasmodel = mobj->modelData->hasModel;
mobj->modelData->modelIDs.Reset();
mobj->modelData->modelFrameGenerators.Reset();
mobj->modelData->skinIDs.Reset();
mobj->modelData->surfaceSkinIDs.Reset();
mobj->modelData->animationIDs.Reset();
mobj->modelData->Destroy();
}

View File

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

View File

@ -286,9 +286,12 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
TArray<FTextureID> surfaceskinids;
TArray<VSMatrix> boneData = TArray<VSMatrix>();
for (int i = 0; i < modelsamount; i++)
{
int modelid = -1;
int animationid = -1;
int modelframe = -1;
int modelframenext = -1;
FTextureID skinid; skinid.SetInvalid();
@ -299,10 +302,13 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
if (i < (int)actor->modelData->modelIDs.Size())
modelid = actor->modelData->modelIDs[i];
if (i < (int)actor->modelData->animationIDs.Size())
animationid = actor->modelData->animationIDs[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.Size() < (unsigned)i && smf->modelframes[i] != -1)
if (actor->modelData->modelFrameGenerators[i] >= 0 && actor->modelData->modelFrameGenerators[i] <= modelsamount && smf->modelframes[actor->modelData->modelFrameGenerators[i]] != -1)
{
modelframe = smf->modelframes[actor->modelData->modelFrameGenerators[i]];
if (smfNext) modelframenext = smfNext->modelframes[actor->modelData->modelFrameGenerators[i]];
@ -329,6 +335,7 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
if (i < smf->modelsAmount)
{
if (modelid == -1) modelid = smf->modelIDs[i];
if (animationid == -1) animationid = smf->animationIDs[i];
if (modelframe == -1) modelframe = smf->modelframes[i];
if (modelframenext == -1 && smfNext) modelframenext = smfNext->modelframes[i];
if (!skinid.isValid()) skinid = smf->skinIDs[i];
@ -345,16 +352,26 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr
const TArray<VSMatrix>* animationData = nullptr;
if (smf->animationIDs[i] >= 0)
bool attachments = smf->flags & MDL_MODELSAREATTACHMENTS;
bool nextFrame = smfNext && modelframe != modelframenext;
bool hasExternAnimation = false;
if (animationid >= 0)
{
FModel* animation = Models[smf->animationIDs[i]];
FModel* animation = Models[animationid];
animationData = animation->AttachAnimationData();
if(!attachments || boneData.Size() == 0)
boneData = animation->CalculateBones(modelframe, nextFrame ? modelframenext : modelframe, nextFrame ? inter : 0.f, *animationData);
hasExternAnimation = true;
}
if(!hasExternAnimation) boneData = mdl->CalculateBones(modelframe, nextFrame ? modelframenext : modelframe, nextFrame ? inter : 0.f, *animationData);
if (smfNext && modelframe != modelframenext)
mdl->RenderFrame(renderer, tex, modelframe, modelframenext, inter, translation, ssidp, *animationData);
mdl->RenderFrame(renderer, tex, modelframe, modelframenext, inter, translation, ssidp, boneData);
else
mdl->RenderFrame(renderer, tex, modelframe, modelframe, 0.f, translation, ssidp, *animationData);
mdl->RenderFrame(renderer, tex, modelframe, modelframe, 0.f, translation, ssidp, boneData);
}
}
}
@ -629,6 +646,10 @@ static void ParseModelDefLump(int Lump)
{
smf.flags |= MDL_SCALEWEAPONFOV;
}
else if (sc.Compare("modelsareattachments"))
{
smf.flags |= MDL_MODELSAREATTACHMENTS;
}
else if (sc.Compare("rotating"))
{
smf.flags |= MDL_ROTATING;

View File

@ -57,6 +57,7 @@ enum
MDL_USEROTATIONCENTER = 512,
MDL_NOPERPIXELLIGHTING = 1024, // forces a model to not use per-pixel lighting. useful for voxel-converted-to-model objects.
MDL_SCALEWEAPONFOV = 2048, // scale weapon view model with higher user FOVs
MDL_MODELSAREATTACHMENTS = 4096, // any model index after 0 is treated as an attachment, and therefore will use the bone results of index 0
};
FSpriteModelFrame * FindModelFrame(const PClass * ti, int sprite, int frame, bool dropped);

View File

@ -1133,7 +1133,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 modelindex = 0, string modelpath = "", name model = "", int skinindex = 0, string skinpath = "", name skin = "", int flags = 0, int generatorindex = -1);
action native void A_ChangeModel(name modeldef, int modelindex = 0, string modelpath = "", name model = "", int skinindex = 0, string skinpath = "", name skin = "", int flags = 0, int generatorindex = -1, int animationindex = 0, string animationpath = "", name animation = "");
void A_SetFriendly (bool set)
{