Bone Manip part 1 - bone setters
* split frame info calculation from RenderFrameModels
* clean up fvec4 being used as quat in iqms
* initial work on bone setting
* implement bone setters
* clean up FindModelFrame
* refactor model overrides into its own function
* refactor frame rendering into RenderModelFrame
* split frame processing into ProcessModelFrame
* refactor BoneOverride in preparation for translation/scale overrides
* clean up macros
* SetBoneTranslation/SetBoneScaling
* GetBoneOffset
* fix compilation on linux/mac (fuck you MSVC)
* fix typo 😅
* make sure bone overrides are cleared on model switches, add ClearBoneOffsets to clear them manually
* bone info getters
* fix joint lengths, add joint dirs
* serialize bone overrides
* fix bone dir return type
* GetBoneIndex/GetBoneCount
* helper functions for working with non-quat angles
* add mode enum, add more helper functions
* fix up formatting
This commit is contained in:
parent
c0d17bbda3
commit
3780c5910a
18 changed files with 1396 additions and 253 deletions
|
|
@ -22,6 +22,7 @@ union FRenderStyle;
|
|||
class DObject;
|
||||
class FTextureID;
|
||||
struct FTranslationID;
|
||||
struct BoneOverride;
|
||||
|
||||
inline bool nullcmp(const void *buffer, size_t length)
|
||||
{
|
||||
|
|
@ -253,6 +254,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, struct AnimModelOverri
|
|||
FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnim &ao, ModelAnim *def);
|
||||
FSerializer &Serialize(FSerializer &arc, const char *key, ModelAnimFrame &ao, ModelAnimFrame *def);
|
||||
FSerializer &Serialize(FSerializer& arc, const char* key, FTranslationID& value, FTranslationID* defval);
|
||||
FSerializer &Serialize(FSerializer& arc, const char* key, BoneOverride& value, BoneOverride* defval);
|
||||
|
||||
void SerializeFunctionPointer(FSerializer &arc, const char *key, FunctionPointerValue *&p);
|
||||
|
||||
|
|
@ -347,6 +349,16 @@ inline FSerializer &Serialize(FSerializer &arc, const char *key, DVector3 &p, DV
|
|||
return arc.Array<double>(key, &p[0], def? &(*def)[0] : nullptr, 3, true);
|
||||
}
|
||||
|
||||
inline FSerializer &Serialize(FSerializer &arc, const char *key, DVector4 &p, DVector4 *def)
|
||||
{
|
||||
return arc.Array<double>(key, &p[0], def? &(*def)[0] : nullptr, 4, true);
|
||||
}
|
||||
|
||||
inline FSerializer& Serialize(FSerializer& arc, const char* key, DQuaternion& p, DQuaternion* def)
|
||||
{
|
||||
return arc.Array<double>(key, &p[0], def ? &(*def)[0] : nullptr, 4, true);
|
||||
}
|
||||
|
||||
inline FSerializer &Serialize(FSerializer &arc, const char *key, DRotator &p, DRotator *def)
|
||||
{
|
||||
return arc.Array<DAngle>(key, &p[0], def? &(*def)[0] : nullptr, 3, true);
|
||||
|
|
@ -362,6 +374,11 @@ inline FSerializer& Serialize(FSerializer& arc, const char* key, FVector4& p, FV
|
|||
return arc.Array<float>(key, &p[0], def ? &(*def)[0] : nullptr, 4, true);
|
||||
}
|
||||
|
||||
inline FSerializer& Serialize(FSerializer& arc, const char* key, FQuaternion& p, FQuaternion* def)
|
||||
{
|
||||
return arc.Array<float>(key, &p[0], def ? &(*def)[0] : nullptr, 4, true);
|
||||
}
|
||||
|
||||
inline FSerializer& Serialize(FSerializer& arc, const char* key, FVector3& p, FVector3* def)
|
||||
{
|
||||
return arc.Array<float>(key, &p[0], def ? &(*def)[0] : nullptr, 3, true);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,113 @@ enum EModelAnimFlags
|
|||
MODELANIM_LOOP = 1 << 1, // animation loops, otherwise it stays on the last frame once it ends
|
||||
};
|
||||
|
||||
FQuaternion InterpolateQuat(const FQuaternion &from, const FQuaternion &to, float t, float invt);
|
||||
|
||||
template<typename T>
|
||||
inline constexpr T DefVal()
|
||||
{ // [Jay] can't use T DefVal as a template parameter to BoneOverrideComponent without C++20 so we're stuck with this for now, TODO replace with template parameter once gzdoom switches to C++20
|
||||
if constexpr(std::is_same_v<T, FQuaternion>)
|
||||
{
|
||||
return FQuaternion(0,0,0,1);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
template<typename T, T(*Lerp)(const T &from, const T &to, float t, float invt), T(*Add)(const T &from, const T &to)>
|
||||
struct BoneOverrideComponent
|
||||
{
|
||||
int mode = 0; // 0 = no override, 1 = rotate, 2 = replace
|
||||
int prev_mode = 0;
|
||||
double switchtic = 0.0;
|
||||
double interplen = 0.0;
|
||||
T prev = DefVal<T>();
|
||||
T cur = DefVal<T>();
|
||||
|
||||
void Set(const T &newValue, double tic, double newInterplen, int newMode)
|
||||
{
|
||||
double prev_interp_amt = interplen > 0.0 ? std::clamp(((tic - switchtic) / interplen), 0.0, 1.0) : 1.0;
|
||||
double prev_interp_amt_inv = 1.0 - prev_interp_amt;
|
||||
|
||||
prev = mode > 0 ? Lerp(prev, cur, prev_interp_amt, prev_interp_amt_inv) : DefVal<T>();
|
||||
|
||||
// might break slightly if value is switched from absolute to additive before interpolation finishes,
|
||||
// but shouldn't matter too much, since people will probably mostly stick to one single mode per value
|
||||
// so not worth the extra complexity (and adding the requirement of needing bone calculation for setters to work) to properly support it
|
||||
prev_mode = (mode == 0 && prev_mode != 0 && prev_interp_amt_inv > 0.0) ? 1 : mode;
|
||||
|
||||
cur = (newMode == 0 ? DefVal<T>() : newValue);
|
||||
switchtic = tic;
|
||||
interplen = newInterplen;
|
||||
mode = newMode;
|
||||
}
|
||||
|
||||
void Modify(T &value, double tic) const
|
||||
{
|
||||
|
||||
double lerp_amt = interplen > 0.0 ? std::clamp(((tic - switchtic) / interplen), 0.0, 1.0) : 1.0;
|
||||
|
||||
if(mode > 0 || (prev_mode > 0 && lerp_amt < 1.0))
|
||||
{
|
||||
T from = ModifyValue(value, prev, prev_mode);
|
||||
T to = ModifyValue(value, cur, mode);
|
||||
value = Lerp(from, to, lerp_amt, 1.0 - lerp_amt);
|
||||
}
|
||||
}
|
||||
|
||||
T Get(const T &value, double tic) const
|
||||
{
|
||||
T newVal = value;
|
||||
Modify(newVal, tic);
|
||||
return newVal;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
inline static T ModifyValue(const T &orig, const T &cur, int mode)
|
||||
{
|
||||
if(mode == 0) return orig;
|
||||
if(mode == 1) return Add(orig, cur);
|
||||
return cur;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct BoneOverride
|
||||
{
|
||||
static inline FQuaternion AddQuat(const FQuaternion &from, const FQuaternion &to)
|
||||
{
|
||||
return (from * to).Unit();
|
||||
}
|
||||
|
||||
static inline FVector3 LerpVec3(const FVector3 &from, const FVector3 &to, float t, float invt)
|
||||
{
|
||||
return from * invt + to * t;
|
||||
}
|
||||
|
||||
static inline FVector3 AddVec3(const FVector3 &from, const FVector3 &to)
|
||||
{
|
||||
return from + to;
|
||||
}
|
||||
|
||||
BoneOverrideComponent<FVector3, &LerpVec3, &AddVec3> translation;
|
||||
BoneOverrideComponent<FQuaternion, &InterpolateQuat, &AddQuat> rotation;
|
||||
BoneOverrideComponent<FVector3, &LerpVec3, &AddVec3> scaling;
|
||||
|
||||
void Modify(TRS &trs, double tic) const
|
||||
{
|
||||
translation.Modify(trs.translation, tic);
|
||||
rotation.Modify(trs.rotation, tic);
|
||||
scaling.Modify(trs.scaling, tic);
|
||||
}
|
||||
};
|
||||
|
||||
struct BoneInfo
|
||||
{
|
||||
TArray<TRS> bones_anim_only;
|
||||
TArray<TRS> bones_with_override;
|
||||
TArray<VSMatrix> positions;
|
||||
};
|
||||
|
||||
struct ModelAnim
|
||||
{
|
||||
int firstFrame = 0;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
|
||||
TDeletingArray<FModel*> Models;
|
||||
TArray<FSpriteModelFrame> SpriteModelFrames;
|
||||
TMap<void*, FSpriteModelFrame> BaseSpriteModelFrames;
|
||||
TMap<const PClass*, FSpriteModelFrame> BaseSpriteModelFrames;
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -86,6 +88,18 @@ public:
|
|||
|
||||
virtual int FindFrame(const char * name, bool nodefault = false) = 0;
|
||||
|
||||
virtual int NumJoints() { return 0; }
|
||||
virtual int FindJoint(FName name) { return -1; }
|
||||
|
||||
virtual int GetJointParent(int joint) { return -1; }
|
||||
virtual double GetJointLength(int joint) { return 0.0; }
|
||||
virtual FName GetJointName(int joint) { return NAME_None; }
|
||||
virtual FVector3 GetJointDir(int joint) { return FVector3(0.0f,0.0f,0.0f); }
|
||||
|
||||
virtual void GetJointChildren(int joint, TArray<int> &out) {}
|
||||
|
||||
virtual void GetRootJoints(TArray<int> &out) {}
|
||||
|
||||
// [RL0] these are used for decoupled iqm animations
|
||||
virtual int FindFirstFrame(FName name) { return FErr_NotFound; }
|
||||
virtual int FindLastFrame(FName name) { return FErr_NotFound; }
|
||||
|
|
@ -99,7 +113,7 @@ public:
|
|||
|
||||
virtual ModelAnimFrame PrecalculateFrame(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData) { return nullptr; };
|
||||
|
||||
virtual const TArray<VSMatrix>* CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData) { return nullptr; };
|
||||
virtual const TArray<VSMatrix>* CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData, TArray<BoneOverride> *in, BoneInfo *out, double time) { return nullptr; };
|
||||
|
||||
void SetVertexBuffer(int type, IModelVertexBuffer *buffer) { mVBuf[type] = buffer; }
|
||||
IModelVertexBuffer *GetVertexBuffer(int type) const { return mVBuf[type]; }
|
||||
|
|
|
|||
|
|
@ -67,10 +67,11 @@ struct IQMAdjacency
|
|||
|
||||
struct IQMJoint
|
||||
{
|
||||
FString Name;
|
||||
FName Name;
|
||||
int32_t Parent; // parent < 0 means this is a root bone
|
||||
TArray<int> Children; // indices of children
|
||||
FVector3 Translate;
|
||||
FVector4 Quaternion;
|
||||
FQuaternion Quaternion;
|
||||
FVector3 Scale;
|
||||
};
|
||||
|
||||
|
|
@ -122,10 +123,10 @@ public:
|
|||
const TArray<TRS>* AttachAnimationData() override;
|
||||
|
||||
ModelAnimFrame PrecalculateFrame(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData) override;
|
||||
const TArray<VSMatrix>* CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData) override;
|
||||
const TArray<VSMatrix>* CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData, TArray<BoneOverride> *in, BoneInfo *out, double time) override;
|
||||
|
||||
ModelAnimFramePrecalculatedIQM CalculateFrameIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray<TRS>* animationData);
|
||||
const TArray<VSMatrix>* CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray<TRS>* animationData);
|
||||
const TArray<VSMatrix>* CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray<TRS>* animationData, TArray<BoneOverride> *in, BoneInfo *out, double time);
|
||||
|
||||
private:
|
||||
void LoadGeometry();
|
||||
|
|
@ -140,11 +141,15 @@ private:
|
|||
int mLumpNum = -1;
|
||||
|
||||
TMap<FName, int> NamedAnimations;
|
||||
TMap<FName, int> NamedJoints;
|
||||
|
||||
TArray<IQMMesh> Meshes;
|
||||
TArray<IQMTriangle> Triangles;
|
||||
TArray<IQMAdjacency> Adjacency;
|
||||
TArray<IQMJoint> Joints;
|
||||
|
||||
TArray<int> RootJoints;
|
||||
|
||||
TArray<IQMPose> Poses;
|
||||
TArray<IQMAnim> Anims;
|
||||
TArray<IQMBounds> Bounds;
|
||||
|
|
@ -158,6 +163,47 @@ private:
|
|||
TArray<VSMatrix> baseframe;
|
||||
TArray<VSMatrix> inversebaseframe;
|
||||
TArray<TRS> TRSData;
|
||||
public:
|
||||
int NumJoints() override { return Joints.Size(); }
|
||||
int FindJoint(FName name) override
|
||||
{
|
||||
int *j = NamedJoints.CheckKey(name);
|
||||
|
||||
return j ? *j : -1;
|
||||
}
|
||||
|
||||
int GetJointParent(int joint) override
|
||||
{
|
||||
return (joint >= 0 && joint < Joints.Size()) ? Joints[joint].Parent : -1;
|
||||
}
|
||||
|
||||
FName GetJointName(int joint) override
|
||||
{
|
||||
return (joint >= 0 && joint < Joints.Size()) ? Joints[joint].Name : FName(NAME_None);
|
||||
}
|
||||
|
||||
void GetRootJoints(TArray<int> &out) override
|
||||
{
|
||||
out = RootJoints;
|
||||
}
|
||||
|
||||
void GetJointChildren(int joint, TArray<int> &out) override
|
||||
{
|
||||
if(joint >= 0 && joint < Joints.Size())
|
||||
{
|
||||
out = Joints[joint].Children;
|
||||
}
|
||||
}
|
||||
|
||||
double GetJointLength(int joint) override
|
||||
{
|
||||
return (joint >= 0 && joint < Joints.Size()) ? Joints[joint].Translate.Length() : 0.0;
|
||||
}
|
||||
|
||||
FVector3 GetJointDir(int joint) override
|
||||
{
|
||||
return (joint >= 0 && joint < Joints.Size()) ? Joints[joint].Translate.Unit() : FVector3(0.0f,0.0f,0.0f);
|
||||
}
|
||||
};
|
||||
|
||||
struct IQMReadErrorException { };
|
||||
|
|
|
|||
|
|
@ -109,13 +109,26 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
|
|||
}
|
||||
|
||||
reader.SeekTo(ofs_joints);
|
||||
for (IQMJoint& joint : Joints)
|
||||
for (int i = 0; i < Joints.Size(); i++)
|
||||
{
|
||||
joint.Name = reader.ReadName(text);
|
||||
IQMJoint& joint = Joints[i];
|
||||
|
||||
FString name = reader.ReadName(text);
|
||||
|
||||
joint.Name = name;
|
||||
|
||||
if(!name.IsEmpty())
|
||||
{
|
||||
NamedJoints.Insert(joint.Name, i);
|
||||
}
|
||||
|
||||
joint.Parent = reader.ReadInt32();
|
||||
joint.Translate.X = reader.ReadFloat();
|
||||
joint.Translate.Y = reader.ReadFloat();
|
||||
joint.Translate.Z = reader.ReadFloat();
|
||||
|
||||
int len = joint.Translate.Length();
|
||||
|
||||
joint.Quaternion.X = reader.ReadFloat();
|
||||
joint.Quaternion.Y = reader.ReadFloat();
|
||||
joint.Quaternion.Z = reader.ReadFloat();
|
||||
|
|
@ -124,6 +137,19 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
|
|||
joint.Scale.X = reader.ReadFloat();
|
||||
joint.Scale.Y = reader.ReadFloat();
|
||||
joint.Scale.Z = reader.ReadFloat();
|
||||
|
||||
if(joint.Parent < 0)
|
||||
{
|
||||
RootJoints.Push(i);
|
||||
}
|
||||
else if(joint.Parent >= Joints.Size())
|
||||
{
|
||||
I_FatalError("Joint parent index out of bounds in IQM Model");
|
||||
}
|
||||
else
|
||||
{
|
||||
Joints[joint.Parent].Children.Push(i);
|
||||
}
|
||||
}
|
||||
|
||||
reader.SeekTo(ofs_poses);
|
||||
|
|
@ -188,7 +214,7 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
|
|||
translate.Y = p.ChannelOffset[1]; if (p.ChannelMask & 0x02) translate.Y += reader.ReadUInt16() * p.ChannelScale[1];
|
||||
translate.Z = p.ChannelOffset[2]; if (p.ChannelMask & 0x04) translate.Z += reader.ReadUInt16() * p.ChannelScale[2];
|
||||
|
||||
FVector4 quaternion;
|
||||
FQuaternion quaternion;
|
||||
quaternion.X = p.ChannelOffset[3]; if (p.ChannelMask & 0x08) quaternion.X += reader.ReadUInt16() * p.ChannelScale[3];
|
||||
quaternion.Y = p.ChannelOffset[4]; if (p.ChannelMask & 0x10) quaternion.Y += reader.ReadUInt16() * p.ChannelScale[4];
|
||||
quaternion.Z = p.ChannelOffset[5]; if (p.ChannelMask & 0x20) quaternion.Z += reader.ReadUInt16() * p.ChannelScale[5];
|
||||
|
|
@ -211,30 +237,7 @@ bool IQMModel::Load(const char* path, int lumpnum, const char* buffer, int lengt
|
|||
{
|
||||
num_frames = 1;
|
||||
TRSData.Resize(num_joints);
|
||||
|
||||
for (uint32_t j = 0; j < num_joints; j++)
|
||||
{
|
||||
FVector3 translate;
|
||||
translate.X = Joints[j].Translate.X;
|
||||
translate.Y = Joints[j].Translate.Y;
|
||||
translate.Z = Joints[j].Translate.Z;
|
||||
|
||||
FVector4 quaternion;
|
||||
quaternion.X = Joints[j].Quaternion.X;
|
||||
quaternion.Y = Joints[j].Quaternion.Y;
|
||||
quaternion.Z = Joints[j].Quaternion.Z;
|
||||
quaternion.W = Joints[j].Quaternion.W;
|
||||
quaternion.MakeUnit();
|
||||
|
||||
FVector3 scale;
|
||||
scale.X = Joints[j].Scale.X;
|
||||
scale.Y = Joints[j].Scale.Y;
|
||||
scale.Z = Joints[j].Scale.Z;
|
||||
|
||||
TRSData[j].translation = translate;
|
||||
TRSData[j].rotation = quaternion;
|
||||
TRSData[j].scaling = scale;
|
||||
}
|
||||
std::copy(Joints.begin(), Joints.end(), TRSData.begin());
|
||||
}
|
||||
|
||||
reader.SeekTo(ofs_bounds);
|
||||
|
|
@ -541,20 +544,29 @@ const TArray<TRS>* IQMModel::AttachAnimationData()
|
|||
return &TRSData;
|
||||
}
|
||||
|
||||
FQuaternion InterpolateQuat(const FQuaternion &from, const FQuaternion &to, float t, float invt)
|
||||
{
|
||||
FQuaternion rot = from * invt;
|
||||
|
||||
if ((rot | to * t) < 0)
|
||||
{
|
||||
rot.X *= -1;
|
||||
rot.Y *= -1;
|
||||
rot.Z *= -1;
|
||||
rot.W *= -1;
|
||||
}
|
||||
|
||||
rot += to * t;
|
||||
|
||||
return rot.Unit();
|
||||
}
|
||||
|
||||
static TRS InterpolateBone(const TRS &from, const TRS &to, float t, float invt)
|
||||
{
|
||||
TRS bone;
|
||||
|
||||
bone.translation = from.translation * invt + to.translation * t;
|
||||
bone.rotation = from.rotation * invt;
|
||||
|
||||
if ((bone.rotation | to.rotation * t) < 0)
|
||||
{
|
||||
bone.rotation.X *= -1; bone.rotation.Y *= -1; bone.rotation.Z *= -1; bone.rotation.W *= -1;
|
||||
}
|
||||
|
||||
bone.rotation += to.rotation * t;
|
||||
bone.rotation.MakeUnit();
|
||||
bone.rotation = InterpolateQuat(from.rotation, to.rotation, t, invt);
|
||||
bone.scaling = from.scaling * invt + to.scaling * t;
|
||||
|
||||
return bone;
|
||||
|
|
@ -585,28 +597,34 @@ ModelAnimFrame IQMModel::PrecalculateFrame(const ModelAnimFrame &from, const Mod
|
|||
}
|
||||
}
|
||||
|
||||
const TArray<VSMatrix>* IQMModel::CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData)
|
||||
const TArray<VSMatrix>* IQMModel::CalculateBones(const ModelAnimFrame &from, const ModelAnimFrameInterp &to, float inter, const TArray<TRS>* animationData, TArray<BoneOverride> *in, BoneInfo *out, double time)
|
||||
{
|
||||
if(inter <= 0)
|
||||
{
|
||||
return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, nullptr, animationData);
|
||||
return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, nullptr, animationData, in, out, time);
|
||||
}
|
||||
else if(std::holds_alternative<ModelAnimFrameInterp>(from))
|
||||
{
|
||||
auto &from_interp = std::get<ModelAnimFrameInterp>(from);
|
||||
|
||||
return CalculateBonesIQM(from_interp.frame2, to.frame2, inter, from_interp.frame1, from_interp.inter, to.frame1, to.inter, nullptr, animationData);
|
||||
return CalculateBonesIQM(from_interp.frame2, to.frame2, inter, from_interp.frame1, from_interp.inter, to.frame1, to.inter, nullptr, animationData, in, out, time);
|
||||
}
|
||||
else if(std::holds_alternative<ModelAnimFramePrecalculatedIQM>(from))
|
||||
{
|
||||
return CalculateBonesIQM(0, to.frame2, inter, 0, -1.f, to.frame1, to.inter, &std::get<ModelAnimFramePrecalculatedIQM>(from), animationData);
|
||||
return CalculateBonesIQM(0, to.frame2, inter, 0, -1.f, to.frame1, to.inter, &std::get<ModelAnimFramePrecalculatedIQM>(from), animationData, in, out, time);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, nullptr, animationData);
|
||||
return CalculateBonesIQM(to.frame1, to.frame2, to.inter, 0, -1.f, 0, -1.f, nullptr, animationData, in, out, time);
|
||||
}
|
||||
}
|
||||
|
||||
inline void ModifyBone(const BoneOverride& mod, TRS &bone, double time)
|
||||
{
|
||||
mod.Modify(bone, time);
|
||||
}
|
||||
|
||||
// explicitly don't pass modelBoneOverrides when precalculating animation for interpolation, as it's applied _after_ animation
|
||||
ModelAnimFramePrecalculatedIQM IQMModel::CalculateFrameIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray<TRS>* animationData)
|
||||
{
|
||||
ModelAnimFramePrecalculatedIQM out;
|
||||
|
|
@ -614,7 +632,7 @@ ModelAnimFramePrecalculatedIQM IQMModel::CalculateFrameIQM(int frame1, int frame
|
|||
|
||||
out.precalcBones.Resize(Joints.Size());
|
||||
|
||||
if (Joints.Size() > 0)
|
||||
if (Joints.Size() > 0 && animationFrames.Size() > 0)
|
||||
{
|
||||
int numbones = Joints.SSize();
|
||||
|
||||
|
|
@ -661,12 +679,25 @@ ModelAnimFramePrecalculatedIQM IQMModel::CalculateFrameIQM(int frame1, int frame
|
|||
return out;
|
||||
}
|
||||
|
||||
const TArray<VSMatrix>* IQMModel::CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray<TRS>* animationData)
|
||||
const TArray<VSMatrix>* IQMModel::CalculateBonesIQM(int frame1, int frame2, float inter, int frame1_prev, float inter1_prev, int frame2_prev, float inter2_prev, const ModelAnimFramePrecalculatedIQM* precalculated, const TArray<TRS>* animationData, TArray<BoneOverride> *in, BoneInfo *out, double time)
|
||||
{
|
||||
const TArray<TRS>& animationFrames = animationData ? *animationData : TRSData;
|
||||
if (Joints.Size() > 0)
|
||||
|
||||
TArray<VSMatrix>* outMatrix = out ? &out->positions : &boneData;
|
||||
|
||||
int numbones = Joints.SSize();
|
||||
outMatrix->Resize(numbones);
|
||||
|
||||
if(out)
|
||||
{
|
||||
out->bones_anim_only.Resize(numbones);
|
||||
out->bones_with_override.Resize(numbones);
|
||||
}
|
||||
|
||||
if(in && in->size() != Joints.Size()) in = nullptr;
|
||||
|
||||
if (numbones > 0 && animationFrames.Size() > 0)
|
||||
{
|
||||
int numbones = Joints.SSize();
|
||||
|
||||
frame1 = clamp(frame1, 0, (animationFrames.SSize() - 1) / numbones);
|
||||
frame2 = clamp(frame2, 0, (animationFrames.SSize() - 1) / numbones);
|
||||
|
|
@ -689,8 +720,6 @@ const TArray<VSMatrix>* IQMModel::CalculateBonesIQM(int frame1, int frame2, floa
|
|||
0.0f, 0.0f, 0.0f, 1.0f
|
||||
};
|
||||
|
||||
boneData.Resize(numbones);
|
||||
|
||||
for (int i = 0; i < numbones; i++)
|
||||
{
|
||||
TRS prev;
|
||||
|
|
@ -721,16 +750,33 @@ const TArray<VSMatrix>* IQMModel::CalculateBonesIQM(int frame1, int frame2, floa
|
|||
bone = inter < 0 ? animationFrames[offset1 + i] : InterpolateBone(prev, next , inter, invt);
|
||||
}
|
||||
|
||||
if(out)
|
||||
{
|
||||
out->bones_anim_only[i] = bone;
|
||||
|
||||
if(in)
|
||||
{
|
||||
(*in)[i].Modify(bone, time);
|
||||
}
|
||||
|
||||
out->bones_with_override[i] = bone;
|
||||
}
|
||||
else if(in)
|
||||
{
|
||||
(*in)[i].Modify(bone, time);
|
||||
}
|
||||
|
||||
VSMatrix m;
|
||||
m.loadIdentity();
|
||||
m.translate(bone.translation.X, bone.translation.Y, bone.translation.Z);
|
||||
m.multQuaternion(bone.rotation);
|
||||
m.scale(bone.scaling.X, bone.scaling.Y, bone.scaling.Z);
|
||||
|
||||
VSMatrix& result = boneData[i];
|
||||
VSMatrix& result = (*outMatrix)[i];
|
||||
if (Joints[i].Parent >= 0)
|
||||
{
|
||||
result = boneData[Joints[i].Parent];
|
||||
assert(Joints[i].Parent < i);
|
||||
result = (*outMatrix)[Joints[i].Parent];
|
||||
result.multMatrix(swapYZ);
|
||||
result.multMatrix(baseframe[Joints[i].Parent]);
|
||||
result.multMatrix(m);
|
||||
|
|
|
|||
|
|
@ -33,24 +33,27 @@
|
|||
|
||||
#pragma once
|
||||
#include "vectors.h"
|
||||
#include "quaternion.h"
|
||||
|
||||
class TRS
|
||||
{
|
||||
public:
|
||||
FVector3 translation;
|
||||
FVector4 rotation;
|
||||
FVector3 scaling;
|
||||
FVector3 translation = FVector3(0,0,0);
|
||||
FQuaternion rotation = FQuaternion::Identity();
|
||||
FVector3 scaling = FVector3(0,0,0);
|
||||
|
||||
TRS()
|
||||
bool operator==(const TRS& other) const
|
||||
{
|
||||
translation = FVector3(0,0,0);
|
||||
rotation = FVector4(0,0,0,1);
|
||||
scaling = FVector3(0,0,0);
|
||||
return other.translation == translation && other.rotation == rotation && other.scaling == scaling;
|
||||
}
|
||||
|
||||
bool Equals(TRS& compare)
|
||||
{
|
||||
return compare.translation == this->translation && compare.rotation == this->rotation && compare.scaling == this->scaling;
|
||||
template<typename T>
|
||||
TRS& operator=(const T& other)
|
||||
{ // templated because IQMJoint is defined in model_iqm.h
|
||||
translation = other.Translate;
|
||||
rotation = other.Quaternion;
|
||||
scaling = other.Scale;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -114,6 +114,22 @@ void VSMatrix::multQuaternion(const TVector4<FLOATTYPE>& q)
|
|||
multMatrix(m);
|
||||
}
|
||||
|
||||
void VSMatrix::multQuaternion(const TQuaternion<FLOATTYPE>& q)
|
||||
{
|
||||
FLOATTYPE m[16] = { FLOATTYPE(0.0) };
|
||||
m[0 * 4 + 0] = FLOATTYPE(1.0) - FLOATTYPE(2.0) * q.Y * q.Y - FLOATTYPE(2.0) * q.Z * q.Z;
|
||||
m[1 * 4 + 0] = FLOATTYPE(2.0) * q.X * q.Y - FLOATTYPE(2.0) * q.W * q.Z;
|
||||
m[2 * 4 + 0] = FLOATTYPE(2.0) * q.X * q.Z + FLOATTYPE(2.0) * q.W * q.Y;
|
||||
m[0 * 4 + 1] = FLOATTYPE(2.0) * q.X * q.Y + FLOATTYPE(2.0) * q.W * q.Z;
|
||||
m[1 * 4 + 1] = FLOATTYPE(1.0) - FLOATTYPE(2.0) * q.X * q.X - FLOATTYPE(2.0) * q.Z * q.Z;
|
||||
m[2 * 4 + 1] = FLOATTYPE(2.0) * q.Y * q.Z - FLOATTYPE(2.0) * q.W * q.X;
|
||||
m[0 * 4 + 2] = FLOATTYPE(2.0) * q.X * q.Z - FLOATTYPE(2.0) * q.W * q.Y;
|
||||
m[1 * 4 + 2] = FLOATTYPE(2.0) * q.Y * q.Z + FLOATTYPE(2.0) * q.W * q.X;
|
||||
m[2 * 4 + 2] = FLOATTYPE(1.0) - FLOATTYPE(2.0) * q.X * q.X - FLOATTYPE(2.0) * q.Y * q.Y;
|
||||
m[3 * 4 + 3] = FLOATTYPE(1.0);
|
||||
multMatrix(m);
|
||||
}
|
||||
|
||||
|
||||
// gl LoadMatrix implementation
|
||||
void
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include <stdlib.h>
|
||||
#include "vectors.h"
|
||||
#include "quaternion.h"
|
||||
|
||||
#ifdef USE_DOUBLE
|
||||
typedef double FLOATTYPE;
|
||||
|
|
@ -54,6 +55,7 @@ class VSMatrix {
|
|||
multMatrix(aMatrix.mMatrix);
|
||||
}
|
||||
void multQuaternion(const TVector4<FLOATTYPE>& q);
|
||||
void multQuaternion(const TQuaternion<FLOATTYPE>& q);
|
||||
void loadMatrix(const FLOATTYPE *aMatrix);
|
||||
#ifdef USE_DOUBLE
|
||||
void loadMatrix(const float *aMatrix);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public:
|
|||
|
||||
TQuaternion() = default;
|
||||
|
||||
TQuaternion(vec_t x, vec_t y, vec_t z, vec_t w)
|
||||
constexpr TQuaternion(vec_t x, vec_t y, vec_t z, vec_t w)
|
||||
: X(x), Y(y), Z(z), W(w)
|
||||
{
|
||||
}
|
||||
|
|
@ -40,6 +40,17 @@ public:
|
|||
Z = Y = X = W = 0;
|
||||
}
|
||||
|
||||
void MakeIdentity()
|
||||
{
|
||||
Z = Y = X = 0;
|
||||
W = 1;
|
||||
}
|
||||
|
||||
static constexpr TQuaternion Identity()
|
||||
{
|
||||
return {0,0,0,1};
|
||||
}
|
||||
|
||||
bool isZero() const
|
||||
{
|
||||
return X == 0 && Y == 0 && Z == 0 && W == 0;
|
||||
|
|
@ -346,6 +357,12 @@ public:
|
|||
return (from * scale0 + to * scale1).Unit();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
explicit operator TVector4<U>()
|
||||
{
|
||||
return TVector4<U>(U(X),U(Y),U(Z),U(W));
|
||||
}
|
||||
};
|
||||
|
||||
typedef TQuaternion<float> FQuaternion;
|
||||
|
|
|
|||
|
|
@ -733,14 +733,15 @@ class DActorModelData : public DObject
|
|||
{
|
||||
DECLARE_CLASS(DActorModelData, DObject);
|
||||
public:
|
||||
PClass * modelDef;
|
||||
TArray<ModelOverride> models;
|
||||
TArray<FTextureID> skinIDs;
|
||||
TArray<AnimModelOverride> animationIDs;
|
||||
TArray<int> modelFrameGenerators;
|
||||
int flags;
|
||||
int overrideFlagsSet;
|
||||
int overrideFlagsClear;
|
||||
PClass * modelDef;
|
||||
TArray<ModelOverride> models;
|
||||
TArray<FTextureID> skinIDs;
|
||||
TArray<AnimModelOverride> animationIDs;
|
||||
TArray<int> modelFrameGenerators;
|
||||
TArray<TArray<BoneOverride>> modelBoneOverrides;
|
||||
int flags;
|
||||
int overrideFlagsSet;
|
||||
int overrideFlagsClear;
|
||||
|
||||
ModelAnim curAnim;
|
||||
ModelAnimFrame prevAnim; // used for interpolation when switching anims
|
||||
|
|
|
|||
|
|
@ -5113,6 +5113,7 @@ static void CleanupModelData(AActor * mobj)
|
|||
&& mobj->modelData->modelFrameGenerators.Size() == 0
|
||||
&& mobj->modelData->skinIDs.Size() == 0
|
||||
&& mobj->modelData->animationIDs.Size() == 0
|
||||
&& mobj->modelData->modelBoneOverrides.Size() == 0
|
||||
&& mobj->modelData->modelDef == nullptr
|
||||
&&(mobj->modelData->flags & ~MODELDATA_HADMODEL) == 0 )
|
||||
{
|
||||
|
|
@ -5122,6 +5123,626 @@ static void CleanupModelData(AActor * mobj)
|
|||
}
|
||||
}
|
||||
|
||||
FQuaternion InterpolateQuat(const FQuaternion &from, const FQuaternion &to, float t, float invt);
|
||||
|
||||
static void SetModelBoneRotationInternal(AActor * self, FModel * mdl, int model_index, int index, FQuaternion rotation, int mode, double interpolation_duration, double switchTic)
|
||||
{
|
||||
if(self->modelData->modelBoneOverrides.Size() <= model_index) self->modelData->modelBoneOverrides.Resize(model_index + 1);
|
||||
|
||||
self->modelData->modelBoneOverrides[model_index].Resize(mdl->NumJoints());
|
||||
|
||||
self->modelData->modelBoneOverrides[model_index][index].rotation.Set(rotation, switchTic, interpolation_duration, mode);
|
||||
}
|
||||
|
||||
template<bool isSet, bool isOffset>
|
||||
FModel * SetGetBoneShared(AActor * self, int model_index)
|
||||
{
|
||||
if(!(self->flags9 & MF9_DECOUPLEDANIMATIONS))
|
||||
{
|
||||
ThrowAbortException(X_OTHER, isSet ? "Cannot set bone offset for non-decoupled actors" : (isOffset ? "Cannot get bone for non-decoupled actors" : "Cannot get bone offset for non-decoupled actors"));
|
||||
}
|
||||
|
||||
if(!BaseSpriteModelFrames.CheckKey(self->GetClass()))
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Actor class is missing a MODELDEF definition or a MODELDEF BaseFrame");
|
||||
}
|
||||
|
||||
EnsureModelData(self);
|
||||
|
||||
if(self->modelData->models.Size() > model_index && self->modelData->models[model_index].modelID >= 0 && self->modelData->models[model_index].modelID < Models.Size())
|
||||
{
|
||||
return Models[self->modelData->models[model_index].modelID];
|
||||
}
|
||||
else if(BaseSpriteModelFrames[self->GetClass()].modelIDs.Size() > model_index)
|
||||
{
|
||||
return Models[BaseSpriteModelFrames[self->GetClass()].modelIDs[model_index]];
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Model Index out of range");
|
||||
}
|
||||
}
|
||||
|
||||
template<bool isSet, bool isOffset>
|
||||
FModel * SetGetBoneSharedIndex(AActor * self, int model_index, int &bone_index, FName * bone_name)
|
||||
{
|
||||
FModel * mdl = SetGetBoneShared<isSet, isOffset>(self, model_index);
|
||||
|
||||
if(bone_name)
|
||||
{
|
||||
bone_index = mdl->FindJoint(*bone_name);
|
||||
if(bone_index < 0 || bone_index >= mdl->NumJoints())
|
||||
{
|
||||
Printf(PRINT_NONOTIFY, "Could not find bone '%s'", bone_name->GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else if(bone_index < 0 || bone_index >= mdl->NumJoints())
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "bone index out of range");
|
||||
}
|
||||
|
||||
if(self->modelData->modelBoneOverrides.Size() <= model_index) self->modelData->modelBoneOverrides.Resize(model_index + 1);
|
||||
|
||||
self->modelData->modelBoneOverrides[model_index].Resize(mdl->NumJoints());
|
||||
|
||||
return mdl;
|
||||
}
|
||||
|
||||
FModel * SetBoneOffsetShared(AActor * self, int model_index, int &bone_index, FName * bone_name, int mode, double &interpolation_duration)
|
||||
{
|
||||
if(interpolation_duration < 0) interpolation_duration = 0;
|
||||
|
||||
if(mode < 0 || mode > 2)
|
||||
{
|
||||
ThrowAbortException(X_OTHER, "Invalid mode for setbone");
|
||||
}
|
||||
|
||||
return SetGetBoneSharedIndex<true, true>(self, model_index, bone_index, bone_name);
|
||||
}
|
||||
|
||||
FModel * GetBoneOffsetShared(AActor * self, int model_index, int &bone_index, FName * bone_name)
|
||||
{
|
||||
return SetGetBoneSharedIndex<false, true>(self, model_index, bone_index, bone_name);
|
||||
}
|
||||
|
||||
FModel * GetBoneShared(AActor * self, int model_index, int &bone_index, FName * bone_name)
|
||||
{
|
||||
return SetGetBoneSharedIndex<false, false>(self, model_index, bone_index, bone_name);
|
||||
}
|
||||
|
||||
//================================================
|
||||
//
|
||||
// SetBoneRotation
|
||||
//
|
||||
//================================================
|
||||
|
||||
static void SetModelBoneRotationNative(AActor * self, int model_index, int bone_index, double rot_x, double rot_y, double rot_z, double rot_w, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
FModel * mdl = SetBoneOffsetShared(self, model_index, bone_index, nullptr, mode, interpolation_duration);
|
||||
|
||||
if(!mdl) return;
|
||||
|
||||
self->modelData->modelBoneOverrides[model_index][bone_index].rotation.Set(FQuaternion(rot_x, rot_y, rot_z, rot_w), self->Level->totaltime + ticFrac, interpolation_duration, mode);
|
||||
}
|
||||
|
||||
static void SetBoneRotationNative(AActor * self, int bone_index, double rot_x, double rot_y, double rot_z, double rot_w, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
SetModelBoneRotationNative(self, 0, bone_index, rot_x, rot_y, rot_z, rot_w, mode, interpolation_duration, ticFrac);
|
||||
}
|
||||
|
||||
static void SetModelNamedBoneRotationNative(AActor * self, int model_index, int boneName_i, double rot_x, double rot_y, double rot_z, double rot_w, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
FName bone_name {ENamedName(boneName_i)};
|
||||
|
||||
int bone_index;
|
||||
|
||||
FModel * mdl = SetBoneOffsetShared(self, model_index, bone_index, &bone_name, mode, interpolation_duration);
|
||||
|
||||
if(!mdl) return;
|
||||
|
||||
self->modelData->modelBoneOverrides[model_index][bone_index].rotation.Set(FQuaternion(rot_x, rot_y, rot_z, rot_w), self->Level->totaltime + ticFrac, interpolation_duration, mode);
|
||||
}
|
||||
|
||||
static void SetNamedBoneRotationNative(AActor * self, int boneName_i, double rot_x, double rot_y, double rot_z, double rot_w, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
SetModelNamedBoneRotationNative(self, 0, boneName_i, rot_x, rot_y, rot_z, rot_w, mode, interpolation_duration, ticFrac);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetBoneRotation, SetBoneRotationNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(boneindex);
|
||||
PARAM_FLOAT(rot_x);
|
||||
PARAM_FLOAT(rot_y);
|
||||
PARAM_FLOAT(rot_z);
|
||||
PARAM_FLOAT(rot_w);
|
||||
PARAM_INT(mode);
|
||||
PARAM_FLOAT(interplen);
|
||||
|
||||
SetBoneRotationNative(self, boneindex, rot_x, rot_y, rot_z, rot_w, mode, interplen, 1.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetNamedBoneRotation, SetNamedBoneRotationNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_NAME(bonename);
|
||||
PARAM_FLOAT(rot_x);
|
||||
PARAM_FLOAT(rot_y);
|
||||
PARAM_FLOAT(rot_z);
|
||||
PARAM_FLOAT(rot_w);
|
||||
PARAM_INT(mode);
|
||||
PARAM_FLOAT(interplen);
|
||||
|
||||
SetNamedBoneRotationNative(self, bonename.GetIndex(), rot_x, rot_y, rot_z, rot_w, mode, interplen, 1.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//================================================
|
||||
//
|
||||
// SetBoneTranslation
|
||||
//
|
||||
//================================================
|
||||
|
||||
static void SetModelBoneTranslationNative(AActor * self, int model_index, int bone_index, double rot_x, double rot_y, double rot_z, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
FModel * mdl = SetBoneOffsetShared(self, model_index, bone_index, nullptr, mode, interpolation_duration);
|
||||
|
||||
if(!mdl) return;
|
||||
|
||||
self->modelData->modelBoneOverrides[model_index][bone_index].translation.Set(FVector3(rot_x, rot_y, rot_z), self->Level->totaltime + ticFrac, interpolation_duration, mode);
|
||||
}
|
||||
|
||||
static void SetBoneTranslationNative(AActor * self, int bone_index, double rot_x, double rot_y, double rot_z, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
SetModelBoneTranslationNative(self, 0, bone_index, rot_x, rot_y, rot_z, mode, interpolation_duration, ticFrac);
|
||||
}
|
||||
|
||||
static void SetModelNamedBoneTranslationNative(AActor * self, int model_index, int boneName_i, double rot_x, double rot_y, double rot_z, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
FName bone_name {ENamedName(boneName_i)};
|
||||
|
||||
int bone_index;
|
||||
|
||||
FModel * mdl = SetBoneOffsetShared(self, model_index, bone_index, &bone_name, mode, interpolation_duration);
|
||||
|
||||
if(!mdl) return;
|
||||
|
||||
self->modelData->modelBoneOverrides[model_index][bone_index].translation.Set(FVector3(rot_x, rot_y, rot_z), self->Level->totaltime + ticFrac, interpolation_duration, mode);
|
||||
}
|
||||
|
||||
static void SetNamedBoneTranslationNative(AActor * self, int boneName_i, double rot_x, double rot_y, double rot_z, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
SetModelNamedBoneTranslationNative(self, 0, boneName_i, rot_x, rot_y, rot_z, mode, interpolation_duration, ticFrac);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetBoneTranslation, SetBoneTranslationNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(boneindex);
|
||||
PARAM_FLOAT(rot_x);
|
||||
PARAM_FLOAT(rot_y);
|
||||
PARAM_FLOAT(rot_z);
|
||||
PARAM_INT(mode);
|
||||
PARAM_FLOAT(interplen);
|
||||
|
||||
SetBoneTranslationNative(self, boneindex, rot_x, rot_y, rot_z, mode, interplen, 1.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetNamedBoneTranslation, SetNamedBoneTranslationNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_NAME(bonename);
|
||||
PARAM_FLOAT(rot_x);
|
||||
PARAM_FLOAT(rot_y);
|
||||
PARAM_FLOAT(rot_z);
|
||||
PARAM_INT(mode);
|
||||
PARAM_FLOAT(interplen);
|
||||
|
||||
SetNamedBoneTranslationNative(self, bonename.GetIndex(), rot_x, rot_y, rot_z, mode, interplen, 1.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//================================================
|
||||
//
|
||||
// SetBoneScaling
|
||||
//
|
||||
//================================================
|
||||
|
||||
static void SetModelBoneScalingNative(AActor * self, int model_index, int bone_index, double rot_x, double rot_y, double rot_z, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
FModel * mdl = SetBoneOffsetShared(self, model_index, bone_index, nullptr, mode, interpolation_duration);
|
||||
|
||||
if(!mdl) return;
|
||||
|
||||
self->modelData->modelBoneOverrides[model_index][bone_index].scaling.Set(FVector3(rot_x, rot_y, rot_z), self->Level->totaltime + ticFrac, interpolation_duration, mode);
|
||||
}
|
||||
|
||||
static void SetBoneScalingNative(AActor * self, int bone_index, double rot_x, double rot_y, double rot_z, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
SetModelBoneScalingNative(self, 0, bone_index, rot_x, rot_y, rot_z, mode, interpolation_duration, ticFrac);
|
||||
}
|
||||
|
||||
static void SetModelNamedBoneScalingNative(AActor * self, int model_index, int boneName_i, double rot_x, double rot_y, double rot_z, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
FName bone_name {ENamedName(boneName_i)};
|
||||
|
||||
int bone_index;
|
||||
|
||||
FModel * mdl = SetBoneOffsetShared(self, model_index, bone_index, &bone_name, mode, interpolation_duration);
|
||||
|
||||
if(!mdl) return;
|
||||
|
||||
self->modelData->modelBoneOverrides[model_index][bone_index].scaling.Set(FVector3(rot_x, rot_y, rot_z), self->Level->totaltime + ticFrac, interpolation_duration, mode);
|
||||
}
|
||||
|
||||
static void SetNamedBoneScalingNative(AActor * self, int boneName_i, double rot_x, double rot_y, double rot_z, int mode, double interpolation_duration, double ticFrac)
|
||||
{
|
||||
SetModelNamedBoneScalingNative(self, 0, boneName_i, rot_x, rot_y, rot_z, mode, interpolation_duration, ticFrac);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetBoneScaling, SetBoneScalingNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(boneindex);
|
||||
PARAM_FLOAT(rot_x);
|
||||
PARAM_FLOAT(rot_y);
|
||||
PARAM_FLOAT(rot_z);
|
||||
PARAM_INT(mode);
|
||||
PARAM_FLOAT(interplen);
|
||||
|
||||
SetBoneScalingNative(self, boneindex, rot_x, rot_y, rot_z, mode, interplen, 1.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, SetNamedBoneScaling, SetNamedBoneScalingNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_NAME(bonename);
|
||||
PARAM_FLOAT(rot_x);
|
||||
PARAM_FLOAT(rot_y);
|
||||
PARAM_FLOAT(rot_z);
|
||||
PARAM_INT(mode);
|
||||
PARAM_FLOAT(interplen);
|
||||
|
||||
SetNamedBoneScalingNative(self, bonename.GetIndex(), rot_x, rot_y, rot_z, mode, interplen, 1.0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//================================================
|
||||
//
|
||||
// ClearBoneOffsets
|
||||
//
|
||||
//================================================
|
||||
|
||||
static void ClearBoneOffsetsNative(AActor * self)
|
||||
{
|
||||
if(self->modelData) self->modelData->modelBoneOverrides[0].Clear();
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, ClearBoneOffsets, ClearBoneOffsetsNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
ClearBoneOffsetsNative(self);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//================================================
|
||||
//
|
||||
// GetBoneOffset
|
||||
//
|
||||
//================================================
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, GetBoneOffset)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(bone_index);
|
||||
|
||||
FModel * mdl = GetBoneOffsetShared(self, 0, bone_index, nullptr);
|
||||
|
||||
DVector3 translation(0,0,0);
|
||||
DVector4 rotation(0,0,0,1);
|
||||
DVector3 scaling(0,0,0);
|
||||
|
||||
if(mdl)
|
||||
{
|
||||
auto &mod = self->modelData->modelBoneOverrides[0][bone_index];
|
||||
|
||||
translation = DVector3(mod.translation.Get(FVector3(0,0,0), self->Level->totaltime + 1.0));
|
||||
rotation = DVector4(mod.rotation.Get(FQuaternion(0,0,0,1), self->Level->totaltime + 1.0));
|
||||
scaling = DVector3(mod.scaling.Get(FVector3(0,0,0), self->Level->totaltime + 1.0));
|
||||
}
|
||||
|
||||
if(numret > 2)
|
||||
{
|
||||
ret[2].SetVector(scaling);
|
||||
numret = 3;
|
||||
}
|
||||
|
||||
if(numret > 1)
|
||||
{
|
||||
ret[1].SetVector(translation);
|
||||
}
|
||||
|
||||
if(numret > 0)
|
||||
{
|
||||
ret[0].SetVector4(rotation);
|
||||
}
|
||||
|
||||
return numret;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, GetNamedBoneOffset)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_NAME(bone_name);
|
||||
|
||||
int bone_index;
|
||||
|
||||
FModel * mdl = GetBoneOffsetShared(self, 0, bone_index, &bone_name);
|
||||
|
||||
DVector3 translation(0,0,0);
|
||||
DVector4 rotation(0,0,0,1);
|
||||
DVector3 scaling(0,0,0);
|
||||
|
||||
if(mdl)
|
||||
{
|
||||
auto &mod = self->modelData->modelBoneOverrides[0][bone_index];
|
||||
|
||||
translation = DVector3(mod.translation.Get(FVector3(0,0,0), self->Level->totaltime + 1.0));
|
||||
rotation = DVector4(mod.rotation.Get(FQuaternion(0,0,0,1), self->Level->totaltime + 1.0));
|
||||
scaling = DVector3(mod.scaling.Get(FVector3(0,0,0), self->Level->totaltime + 1.0));
|
||||
}
|
||||
|
||||
if(numret > 2)
|
||||
{
|
||||
ret[2].SetVector(scaling);
|
||||
numret = 3;
|
||||
}
|
||||
|
||||
if(numret > 1)
|
||||
{
|
||||
ret[1].SetVector(translation);
|
||||
}
|
||||
|
||||
if(numret > 0)
|
||||
{
|
||||
ret[0].SetVector4(rotation);
|
||||
}
|
||||
|
||||
return numret;
|
||||
}
|
||||
|
||||
|
||||
//================================================
|
||||
//
|
||||
// Bone Info Getters
|
||||
//
|
||||
//================================================
|
||||
|
||||
static void GetRootBonesNative(AActor * self, TArray<int> *out)
|
||||
{
|
||||
if(out)
|
||||
{
|
||||
FModel * mdl = SetGetBoneShared<false, false>(self, 0);
|
||||
|
||||
mdl->GetRootJoints(*out);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, GetRootBones, GetRootBonesNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_POINTER(out, TArray<int>);
|
||||
|
||||
GetRootBonesNative(self, out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GetBoneNameNative(AActor * self, int bone_index)
|
||||
{
|
||||
FModel * mdl = GetBoneShared(self, 0, bone_index, nullptr);
|
||||
|
||||
return mdl->GetJointName(bone_index).GetIndex();
|
||||
}
|
||||
|
||||
static int GetBoneIndexNative(AActor * self, int boneName_i)
|
||||
{
|
||||
FName bone_name {ENamedName(boneName_i)};
|
||||
|
||||
int bone_index;
|
||||
|
||||
FModel * mdl = GetBoneShared(self, 0, bone_index, &bone_name);
|
||||
|
||||
return bone_index;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, GetBoneName, GetBoneNameNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(boneindex);
|
||||
|
||||
ACTION_RETURN_INT(GetBoneNameNative(self, boneindex));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, GetBoneIndex, GetBoneIndexNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_NAME(bonename);
|
||||
|
||||
ACTION_RETURN_INT(GetBoneIndexNative(self, bonename.GetIndex()));
|
||||
}
|
||||
|
||||
static int GetBoneParentNative(AActor * self, int bone_index)
|
||||
{
|
||||
FModel * mdl = GetBoneShared(self, 0, bone_index, nullptr);
|
||||
|
||||
return mdl->GetJointParent(bone_index);
|
||||
}
|
||||
|
||||
static int GetNamedBoneParentNative(AActor * self, int boneName_i)
|
||||
{
|
||||
FName bone_name {ENamedName(boneName_i)};
|
||||
|
||||
int bone_index;
|
||||
|
||||
FModel * mdl = GetBoneShared(self, 0, bone_index, &bone_name);
|
||||
|
||||
return mdl->GetJointParent(bone_index);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, GetBoneParent, GetBoneParentNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(boneindex);
|
||||
|
||||
ACTION_RETURN_INT(GetBoneParentNative(self, boneindex));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, GetNamedBoneParent, GetNamedBoneParentNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_NAME(bonename);
|
||||
|
||||
ACTION_RETURN_INT(GetNamedBoneParentNative(self, bonename.GetIndex()));
|
||||
}
|
||||
|
||||
static void GetBoneChildrenNative(AActor * self, int bone_index, TArray<int> *out)
|
||||
{
|
||||
if(out)
|
||||
{
|
||||
FModel * mdl = GetBoneShared(self, 0, bone_index, nullptr);
|
||||
|
||||
mdl->GetJointChildren(bone_index, *out);
|
||||
}
|
||||
}
|
||||
|
||||
static void GetNamedBoneChildrenNative(AActor * self, int boneName_i, TArray<int> *out)
|
||||
{
|
||||
if(out)
|
||||
{
|
||||
FName bone_name {ENamedName(boneName_i)};
|
||||
|
||||
int bone_index;
|
||||
|
||||
FModel * mdl = GetBoneShared(self, 0, bone_index, &bone_name);
|
||||
|
||||
mdl->GetJointChildren(bone_index, *out);
|
||||
}
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, GetBoneChildren, GetBoneChildrenNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(boneindex);
|
||||
PARAM_POINTER(out, TArray<int>);
|
||||
|
||||
GetBoneChildrenNative(self, boneindex, out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, GetNamedBoneChildren, GetNamedBoneChildrenNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_NAME(bonename);
|
||||
PARAM_POINTER(out, TArray<int>);
|
||||
|
||||
GetNamedBoneChildrenNative(self, bonename.GetIndex(), out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static double GetBoneLengthNative(AActor * self, int bone_index)
|
||||
{
|
||||
FModel * mdl = GetBoneShared(self, 0, bone_index, nullptr);
|
||||
|
||||
return mdl->GetJointLength(bone_index);
|
||||
}
|
||||
|
||||
static double GetNamedBoneLengthNative(AActor * self, int boneName_i)
|
||||
{
|
||||
FName bone_name {ENamedName(boneName_i)};
|
||||
|
||||
int bone_index;
|
||||
|
||||
FModel * mdl = GetBoneShared(self, 0, bone_index, &bone_name);
|
||||
|
||||
return mdl->GetJointLength(bone_index);
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, GetBoneLength, GetBoneLengthNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(boneindex);
|
||||
|
||||
ACTION_RETURN_FLOAT(GetBoneLengthNative(self, boneindex));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, GetNamedBoneLength, GetNamedBoneLengthNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_NAME(bonename);
|
||||
|
||||
ACTION_RETURN_FLOAT(GetNamedBoneLengthNative(self, bonename.GetIndex()));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, GetBoneDir)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_INT(bone_index);
|
||||
|
||||
FModel * mdl = GetBoneShared(self, 0, bone_index, nullptr);
|
||||
|
||||
ACTION_RETURN_VEC3(DVector3(mdl->GetJointDir(bone_index)));
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION(AActor, GetNamedBoneDir)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
PARAM_NAME(bone_name);
|
||||
|
||||
int bone_index;
|
||||
|
||||
FModel * mdl = GetBoneShared(self, 0, bone_index, &bone_name);
|
||||
|
||||
ACTION_RETURN_VEC3(DVector3(mdl->GetJointDir(bone_index)));
|
||||
}
|
||||
|
||||
static int GetBoneCountNative(AActor * self)
|
||||
{
|
||||
FModel * mdl = SetGetBoneShared<false, false>(self, 0);
|
||||
|
||||
return mdl->NumJoints();
|
||||
}
|
||||
|
||||
DEFINE_ACTION_FUNCTION_NATIVE(AActor, GetBoneCount, GetBoneCountNative)
|
||||
{
|
||||
PARAM_SELF_PROLOGUE(AActor);
|
||||
|
||||
ACTION_RETURN_INT(GetBoneCountNative(self));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//================================================
|
||||
// SetAnimation
|
||||
//================================================
|
||||
|
||||
enum ESetAnimationFlags
|
||||
{
|
||||
SAF_INSTANT = 1 << 0,
|
||||
|
|
@ -5449,6 +6070,7 @@ void ChangeModelNative(
|
|||
}
|
||||
surfaceSkins.Push(skindata);
|
||||
mobj->modelData->models.Push({queryModel, std::move(surfaceSkins)});
|
||||
|
||||
mobj->modelData->modelFrameGenerators.Push(generatorindex);
|
||||
}
|
||||
else
|
||||
|
|
@ -5456,6 +6078,11 @@ void ChangeModelNative(
|
|||
mobj->modelData->models.Push({queryModel, {}});
|
||||
mobj->modelData->modelFrameGenerators.Push(generatorindex);
|
||||
}
|
||||
|
||||
if(queryModel != -1 && mobj->modelData->modelBoneOverrides.Size() > modelindex)
|
||||
{
|
||||
mobj->modelData->modelBoneOverrides[modelindex].Clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -5475,7 +6102,15 @@ void ChangeModelNative(
|
|||
mobj->modelData->models[modelindex].surfaceSkinIDs[skinindex] = skindata;
|
||||
}
|
||||
}
|
||||
if(queryModel != -1) mobj->modelData->models[modelindex].modelID = queryModel;
|
||||
if(queryModel != -1)
|
||||
{
|
||||
mobj->modelData->models[modelindex].modelID = queryModel;
|
||||
|
||||
if(mobj->modelData->modelBoneOverrides.Size() > modelindex)
|
||||
{
|
||||
mobj->modelData->modelBoneOverrides[modelindex].Clear();
|
||||
}
|
||||
}
|
||||
if(generatorindex != -1) mobj->modelData->modelFrameGenerators[modelindex] = generatorindex;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1764,6 +1764,30 @@ void SerializeModelID(FSerializer &arc, const char *key, int &id)
|
|||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static FSerializer &SerializeBoneOverrideComponent(FSerializer &arc, const char *key, T &comp)
|
||||
{
|
||||
arc.BeginObject(key);
|
||||
arc("mode", comp.mode);
|
||||
arc("prev_mode", comp.prev_mode);
|
||||
arc("switchtic", comp.switchtic);
|
||||
arc("interplen", comp.interplen);
|
||||
arc("prev", comp.prev);
|
||||
arc("cur", comp.cur);
|
||||
arc.EndObject();
|
||||
return arc;
|
||||
}
|
||||
|
||||
FSerializer &Serialize(FSerializer &arc, const char *key, BoneOverride &mod, BoneOverride *def)
|
||||
{
|
||||
arc.BeginObject(key);
|
||||
SerializeBoneOverrideComponent(arc, "translation", mod.translation);
|
||||
SerializeBoneOverrideComponent(arc, "rotation", mod.rotation);
|
||||
SerializeBoneOverrideComponent(arc, "scaling", mod.scaling);
|
||||
arc.EndObject();
|
||||
return arc;
|
||||
}
|
||||
|
||||
FSerializer &Serialize(FSerializer &arc, const char *key, ModelOverride &mo, ModelOverride *def)
|
||||
{
|
||||
arc.BeginObject(key);
|
||||
|
|
@ -1869,6 +1893,7 @@ void DActorModelData::Serialize(FSerializer& arc)
|
|||
("skinIDs", skinIDs)
|
||||
("animationIDs", animationIDs)
|
||||
("modelFrameGenerators", modelFrameGenerators)
|
||||
("modelBoneOverrides", modelBoneOverrides)
|
||||
("flags", flags)
|
||||
("overrideFlagsSet", overrideFlagsSet)
|
||||
("overrideFlagsClear", overrideFlagsClear)
|
||||
|
|
|
|||
|
|
@ -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,236 @@ 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, BoneInfo *out)
|
||||
{
|
||||
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,
|
||||
modelData->modelBoneOverrides.Size() > i
|
||||
? &modelData->modelBoneOverrides[i]
|
||||
: nullptr,
|
||||
out,
|
||||
tic);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boneData = animation->CalculateBones(
|
||||
nullptr,
|
||||
{
|
||||
nextFrame ? frameinfo.inter : -1.0f,
|
||||
drawinfo.modelframe,
|
||||
drawinfo.modelframenext
|
||||
},
|
||||
-1.0f,
|
||||
animationData,
|
||||
(modelData && modelData->modelBoneOverrides.Size() > i)
|
||||
? &modelData->modelBoneOverrides[i]
|
||||
: nullptr,
|
||||
out,
|
||||
tic);
|
||||
}
|
||||
|
||||
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, nullptr);
|
||||
|
||||
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 +1125,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 +1156,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
|
||||
|
|
|
|||
|
|
@ -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, BoneInfo *out);
|
||||
|
||||
EXTERN_CVAR(Float, cl_scaleweaponfov)
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -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++)
|
||||
|
|
|
|||
|
|
@ -1359,6 +1359,108 @@ class Actor : Thinker native
|
|||
native bool A_AttachLight(Name lightid, int type, Color lightcolor, int radius1, int radius2, int flags = 0, Vector3 ofs = (0,0,0), double param = 0, double spoti = 10, double spoto = 25, double spotp = 0);
|
||||
native bool A_RemoveLight(Name lightid);
|
||||
|
||||
//================================================
|
||||
//
|
||||
// Bone Offset Setters
|
||||
//
|
||||
//================================================
|
||||
|
||||
native version("4.15.1") void SetBoneRotation(int boneIndex, Quat rotation, int mode = SB_ADD, double interpolation_duration = 1.0);
|
||||
native version("4.15.1") void SetNamedBoneRotation(Name boneName, Quat rotation, int mode = SB_ADD, double interpolation_duration = 1.0);
|
||||
|
||||
version("4.15.1") void SetBoneRotationAngles(int boneIndex, double yaw, double pitch, double roll, int mode = SB_ADD, double interpolation_duration = 1.0)
|
||||
{
|
||||
SetBoneRotation(boneIndex, Quat.FromAngles(yaw, pitch, roll), mode, interpolation_duration);
|
||||
}
|
||||
|
||||
version("4.15.1") void SetNamedBoneRotationAngles(Name boneName, double yaw, double pitch, double roll, int mode = SB_ADD, double interpolation_duration = 1.0)
|
||||
{
|
||||
SetNamedBoneRotation(boneName, Quat.FromAngles(yaw, pitch, roll), mode, interpolation_duration);
|
||||
}
|
||||
|
||||
version("4.15.1") void ClearBoneRotation(int boneIndex, double interpolation_duration = 1.0)
|
||||
{
|
||||
SetBoneRotation(boneIndex, Quat(0, 0, 0, 1), 0, interpolation_duration);
|
||||
}
|
||||
|
||||
version("4.15.1") void ClearNamedBoneRotation(Name boneName, double interpolation_duration = 1.0)
|
||||
{
|
||||
SetNamedBoneRotation(boneName, Quat(0, 0, 0, 1), 0, interpolation_duration);
|
||||
}
|
||||
|
||||
native version("4.15.1") void SetBoneTranslation(int boneIndex, Vector3 translation, int mode = SB_ADD, double interpolation_duration = 1.0);
|
||||
native version("4.15.1") void SetNamedBoneTranslation(Name boneName, Vector3 translation, int mode = SB_ADD, double interpolation_duration = 1.0);
|
||||
|
||||
version("4.15.1") void ClearBoneTranslation(int boneIndex, double interpolation_duration = 1.0)
|
||||
{
|
||||
SetBoneTranslation(boneIndex, (0, 0, 0), 0, interpolation_duration);
|
||||
}
|
||||
|
||||
version("4.15.1") void ClearNamedBoneTranslation(Name boneName, double interpolation_duration = 1.0)
|
||||
{
|
||||
SetNamedBoneTranslation(boneName, (0, 0, 0), 0, interpolation_duration);
|
||||
}
|
||||
|
||||
native version("4.15.1") void SetBoneScaling(int boneIndex, Vector3 scaling, int mode = SB_ADD, double interpolation_duration = 1.0);
|
||||
native version("4.15.1") void SetNamedBoneScaling(Name boneName, Vector3 scaling, int mode = SB_ADD, double interpolation_duration = 1.0);
|
||||
|
||||
version("4.15.1") void ClearBoneScaling(int boneIndex, double interpolation_duration = 1.0)
|
||||
{
|
||||
SetBoneScaling(boneIndex, (0, 0, 0), 0, interpolation_duration);
|
||||
}
|
||||
|
||||
version("4.15.1") void ClearNamedBoneScaling(Name boneName, double interpolation_duration = 1.0)
|
||||
{
|
||||
SetNamedBoneScaling(boneName, (0, 0, 0), 0, interpolation_duration);
|
||||
}
|
||||
|
||||
native version("4.15.1") void ClearBoneOffsets();
|
||||
|
||||
//================================================
|
||||
//
|
||||
// Bone Offset Getters
|
||||
//
|
||||
//================================================
|
||||
|
||||
/* rotation, translation, scaling */
|
||||
native version("4.15.1") Quat, Vector3, Vector3 GetBoneOffset(int boneIndex);
|
||||
native version("4.15.1") Quat, Vector3, Vector3 GetNamedBoneOffset(Name boneName);
|
||||
|
||||
//================================================
|
||||
//
|
||||
// Bone Info Getters
|
||||
//
|
||||
//================================================
|
||||
|
||||
native version("4.15.1") void GetRootBones(out Array<int> rootBones);
|
||||
|
||||
native version("4.15.1") Name GetBoneName(int boneIndex);
|
||||
native version("4.15.1") int GetBoneIndex(Name boneName);
|
||||
|
||||
native version("4.15.1") int GetBoneParent(int boneIndex);
|
||||
native version("4.15.1") int GetNamedBoneParent(Name boneName); // return value lower than 0 means it's a root bone, and as such has no parent
|
||||
|
||||
native version("4.15.1") void GetBoneChildren(int boneIndex, out Array<int> children);
|
||||
native version("4.15.1") void GetNamedBoneChildren(Name boneName, out Array<int> children);
|
||||
|
||||
// this is the length in model units, not in world units, and does not take model scale in MODELDEF, world, etc, or current animation or offset into account at all
|
||||
native version("4.15.1") double GetBoneLength(int boneIndex);
|
||||
native version("4.15.1") double GetNamedBoneLength(Name boneName);
|
||||
|
||||
// this is the direction of the bone in the armature, does not take the current animation or offset into account at all
|
||||
native version("4.15.1") Vector3 GetBoneDir(int boneIndex);
|
||||
native version("4.15.1") Vector3 GetNamedBoneDir(Name boneName);
|
||||
|
||||
native version("4.15.1") int GetBoneCount();
|
||||
|
||||
|
||||
//================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//================================================
|
||||
|
||||
|
||||
native version("4.12") void SetAnimation(Name animName, double framerate = -1, int startFrame = -1, int loopFrame = -1, int endFrame = -1, int interpolateTics = -1, int flags = 0);
|
||||
native version("4.12") ui void SetAnimationUI(Name animName, double framerate = -1, int startFrame = -1, int loopFrame = -1, int endFrame = -1, int interpolateTics = -1, int flags = 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -1547,3 +1547,10 @@ enum EParticleStyle
|
|||
PT_ROUND = 1,
|
||||
PT_SMOOTH = 2,
|
||||
};
|
||||
|
||||
enum ESetBoneMode
|
||||
{
|
||||
SB_CLEAR = 0,
|
||||
SB_ADD = 1,
|
||||
SB_REPLACE = 2,
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue