diff --git a/src/common/models/model.h b/src/common/models/model.h index f77574e9a..250f231e7 100644 --- a/src/common/models/model.h +++ b/src/common/models/model.h @@ -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& animationData) = 0; + virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& 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* AttachAnimationData() { return nullptr; }; + virtual const TArray CalculateBones(int frame1, int frame2, double inter, const TArray& animationData) { return {}; }; void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; } IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; } diff --git a/src/common/models/model_iqm.h b/src/common/models/model_iqm.h index 6c347d0e2..ba349cb10 100644 --- a/src/common/models/model_iqm.h +++ b/src/common/models/model_iqm.h @@ -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& animationData) override; + void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData) override; void BuildVertexBuffer(FModelRenderer* renderer) override; void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override; const TArray* AttachAnimationData() override; + const TArray CalculateBones(int frame1, int frame2, double inter, const TArray& animationData) override; private: void LoadGeometry(); diff --git a/src/common/models/model_kvx.h b/src/common/models/model_kvx.h index e6f569bd6..6cea51380 100644 --- a/src/common/models/model_kvx.h +++ b/src/common/models/model_kvx.h @@ -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& animationData) override; + virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData) override; virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override; FTextureID GetPaletteTexture() const { return mPalette; } void BuildVertexBuffer(FModelRenderer *renderer) override; diff --git a/src/common/models/model_md2.h b/src/common/models/model_md2.h index f6c4ce050..4b828b78d 100644 --- a/src/common/models/model_md2.h +++ b/src/common/models/model_md2.h @@ -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& animationData) override; + virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData) override; virtual void LoadGeometry(); virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override; diff --git a/src/common/models/model_md3.h b/src/common/models/model_md3.h index 84fba7504..96425649c 100644 --- a/src/common/models/model_md3.h +++ b/src/common/models/model_md3.h @@ -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& animationData) override; + virtual void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData) override; void LoadGeometry(); void BuildVertexBuffer(FModelRenderer *renderer); virtual void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override; diff --git a/src/common/models/model_obj.h b/src/common/models/model_obj.h index 4eb4883d9..7e15c8735 100644 --- a/src/common/models/model_obj.h +++ b/src/common/models/model_obj.h @@ -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& animationData) override; + void RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData) override; void BuildVertexBuffer(FModelRenderer* renderer) override; void AddSkins(uint8_t* hitlist, const FTextureID* surfaceskinids) override; }; diff --git a/src/common/models/model_ue1.h b/src/common/models/model_ue1.h index d59526ea6..67efbdbca 100644 --- a/src/common/models/model_ue1.h +++ b/src/common/models/model_ue1.h @@ -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& animationData) override; + void RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData) override; void BuildVertexBuffer(FModelRenderer *renderer) override; void AddSkins(uint8_t *hitlist, const FTextureID* surfaceskinids) override; void LoadGeometry(); diff --git a/src/common/models/models_iqm.cpp b/src/common/models/models_iqm.cpp index 5d1985fc0..d468855ed 100644 --- a/src/common/models/models_iqm.cpp +++ b/src/common/models/models_iqm.cpp @@ -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& animationData) +void IQMModel::RenderFrame(FModelRenderer* renderer, FGameTexture* skin, int frame1, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData) { - const TArray& 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 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* IQMModel::AttachAnimationData() { return &FrameTransforms; } + +const TArray IQMModel::CalculateBones(int frame1, int frame2, double inter, const TArray& animationData) +{ + const TArray& 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 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; +} \ No newline at end of file diff --git a/src/common/models/models_md2.cpp b/src/common/models/models_md2.cpp index c55bc4260..001ddc855 100644 --- a/src/common/models/models_md2.cpp +++ b/src/common/models/models_md2.cpp @@ -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& animationData) +void FDMDModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID*, const TArray& boneData) { if (frameno >= info.numFrames || frameno2 >= info.numFrames) return; diff --git a/src/common/models/models_md3.cpp b/src/common/models/models_md3.cpp index 7c8119efd..e3090f0a3 100644 --- a/src/common/models/models_md3.cpp +++ b/src/common/models/models_md3.cpp @@ -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& animationData) +void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData) { if ((unsigned)frameno >= Frames.Size() || (unsigned)frameno2 >= Frames.Size()) return; diff --git a/src/common/models/models_obj.cpp b/src/common/models/models_obj.cpp index 9c52e1d4b..3e94eda09 100644 --- a/src/common/models/models_obj.cpp +++ b/src/common/models/models_obj.cpp @@ -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& animationData) +void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frameno, int frameno2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData) { // Prevent the model from rendering if the frame number is < 0 if (frameno < 0 || frameno2 < 0) return; diff --git a/src/common/models/models_ue1.cpp b/src/common/models/models_ue1.cpp index f6936b168..d32bd154e 100644 --- a/src/common/models/models_ue1.cpp +++ b/src/common/models/models_ue1.cpp @@ -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& animationData) +void FUE1Model::RenderFrame( FModelRenderer *renderer, FGameTexture *skin, int frame, int frame2, double inter, int translation, const FTextureID* surfaceskinids, const TArray& boneData) { // the moment of magic if ( (frame < 0) || (frame2 < 0) || (frame >= numFrames) || (frame2 >= numFrames) ) return; diff --git a/src/common/models/models_voxel.cpp b/src/common/models/models_voxel.cpp index 4832757bd..6fe27d16e 100644 --- a/src/common/models/models_voxel.cpp +++ b/src/common/models/models_voxel.cpp @@ -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& animationData) +void FVoxelModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int frame, int frame2, double inter, int translation, const FTextureID*, const TArray& boneData) { renderer->SetMaterial(skin, true, translation); renderer->SetupFrame(this, 0, 0, 0, {}); diff --git a/src/playsim/actor.h b/src/playsim/actor.h index 7502078ec..5ba4d5dbb 100644 --- a/src/playsim/actor.h +++ b/src/playsim/actor.h @@ -682,6 +682,7 @@ public: TArray modelIDs; TArray skinIDs; TArray surfaceSkinIDs; + TArray animationIDs; TArray modelFrameGenerators; DActorModelData() = default; diff --git a/src/playsim/p_actionfunctions.cpp b/src/playsim/p_actionfunctions.cpp index 4e42cc0cc..851b72b83 100644 --- a/src/playsim/p_actionfunctions.cpp +++ b/src/playsim/p_actionfunctions.cpp @@ -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(); ptr->skinIDs = *new TArray(); ptr->surfaceSkinIDs = *new TArray(); + ptr->animationIDs = *new TArray(); ptr->modelFrameGenerators = *new TArray(); 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(); } diff --git a/src/playsim/p_mobj.cpp b/src/playsim/p_mobj.cpp index b32829696..46d6c2667 100644 --- a/src/playsim/p_mobj.cpp +++ b/src/playsim/p_mobj.cpp @@ -1370,6 +1370,7 @@ void DActorModelData::Serialize(FSerializer& arc) ("modelIDs", modelIDs) ("skinIDs", skinIDs) ("surfaceSkinIDs", surfaceSkinIDs) + ("animationIDs", animationIDs) ("modelFrameGenerators", modelFrameGenerators) ("hasModel", hasModel); } diff --git a/src/r_data/models.cpp b/src/r_data/models.cpp index 94358a078..5b828efc6 100644 --- a/src/r_data/models.cpp +++ b/src/r_data/models.cpp @@ -286,9 +286,12 @@ void RenderFrameModels(FModelRenderer *renderer, FLevelLocals *Level, const FSpr TArray surfaceskinids; + TArray boneData = TArray(); + 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* 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; diff --git a/src/r_data/models.h b/src/r_data/models.h index 6a2623027..ca832a7ca 100644 --- a/src/r_data/models.h +++ b/src/r_data/models.h @@ -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); diff --git a/wadsrc/static/zscript/actors/actor.zs b/wadsrc/static/zscript/actors/actor.zs index 46cbfa75f..68fc53dd6 100644 --- a/wadsrc/static/zscript/actors/actor.zs +++ b/wadsrc/static/zscript/actors/actor.zs @@ -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 = [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) {