From 6f0a7d99f97caae3a6164aa3dbc29b96906dd52c Mon Sep 17 00:00:00 2001 From: dexy Date: Sat, 2 Nov 2019 18:14:36 +1100 Subject: [PATCH] Experimental bone animations playing from YCD. Windmills, fans, radars, ufos etc --- CodeWalker.Core/GameFiles/Resources/Clip.cs | 6 +- .../GameFiles/Resources/Drawable.cs | 25 +++++ Rendering/Renderable.cs | 102 +++++++++++++++++- Rendering/Renderer.cs | 6 +- 4 files changed, 131 insertions(+), 8 deletions(-) diff --git a/CodeWalker.Core/GameFiles/Resources/Clip.cs b/CodeWalker.Core/GameFiles/Resources/Clip.cs index dbb4e9f..0c51857 100644 --- a/CodeWalker.Core/GameFiles/Resources/Clip.cs +++ b/CodeWalker.Core/GameFiles/Resources/Clip.cs @@ -697,7 +697,9 @@ namespace CodeWalker.GameFiles public override float EvaluateFloat(int frame) { - return Values[frame]; + if (frame < Values?.Length) return Values[frame]; + if (Values?.Length > 0) return Values[0]; + return 0.0f; } public override void Read(Sequence blockStream, ref int channelOffset) @@ -769,7 +771,7 @@ namespace CodeWalker.GameFiles for (int n = 0; n < 4; n++) { if ((c + n) >= 4) break; - v[c + n] = sv3c.Value[n]; + v[c + n] = ssqc.Value[n]; } c += 4; } diff --git a/CodeWalker.Core/GameFiles/Resources/Drawable.cs b/CodeWalker.Core/GameFiles/Resources/Drawable.cs index badd816..98f9b15 100644 --- a/CodeWalker.Core/GameFiles/Resources/Drawable.cs +++ b/CodeWalker.Core/GameFiles/Resources/Drawable.cs @@ -550,6 +550,10 @@ namespace CodeWalker.GameFiles private ResourceSystemStructBlock Unknown_40h_DataBlock = null; + public Dictionary BonesMap { get; set; }//for convienience finding bones by tag + + + /// /// Reads the data-block from a stream. /// @@ -611,6 +615,16 @@ namespace CodeWalker.GameFiles } } + BonesMap = new Dictionary(); + if (Bones != null) + { + for (int i = 0; i < Bones.Count; i++) + { + var bone = Bones[i]; + BonesMap[bone.Id] = bone; + } + } + } /// @@ -784,6 +798,13 @@ namespace CodeWalker.GameFiles private string_r NameBlock = null; + + //used by CW for animating skeletons. + public Quaternion AnimRotation; + public Vector3 AnimTranslation; + + + /// /// Reads the data-block from a stream. /// @@ -819,6 +840,10 @@ namespace CodeWalker.GameFiles this.Name = reader.ReadStringAt(//BlockAt( this.NamePointer // offset ); + + + AnimRotation = Rotation; + AnimTranslation = Translation; } /// diff --git a/Rendering/Renderable.cs b/Rendering/Renderable.cs index 69329f1..1febd56 100644 --- a/Rendering/Renderable.cs +++ b/Rendering/Renderable.cs @@ -78,6 +78,7 @@ namespace CodeWalker.Rendering public ClipMapEntry ClipMapEntryUV0; public ClipMapEntry ClipMapEntryUV1; + public Dictionary ModelBoneLinks; public override void Init(DrawableBase drawable) @@ -254,6 +255,17 @@ namespace CodeWalker.Rendering Matrix trans = (boneidx < modeltransforms.Length) ? modeltransforms[boneidx] : Matrix.Identity; Bone bone = (hasbones && (boneidx < bones.Count)) ? bones[boneidx] : null; + if (mi < HDModels.Length) //populate bone links map for hd models + { + if (bone != null) + { + if (ModelBoneLinks == null) ModelBoneLinks = new Dictionary(); + ModelBoneLinks[bone.Id] = model; + } + } + + + if ((fragtransforms != null))// && (fragtransformid < fragtransforms.Length)) { if (fragtransformid < fragtransforms.Length) @@ -364,8 +376,92 @@ namespace CodeWalker.Rendering var anim = clipanim?.Animation; if (anim == null) { return; } + if (anim.BoneIds?.data_items == null) + { return; } + if (anim.Sequences?.data_items == null) + { return; } + + bool interpolate = true; //how to know? eg. cs4_14_hickbar_anim shouldn't + bool ignoreLastFrame = true;//if last frame is equivalent to the first one, eg rollercoaster small light "globes" don't + + var duration = anim.Duration; + var frames = anim.Frames; + var nframes = (ignoreLastFrame) ? (frames - 1) : frames; + + var curPos = ((CurrentAnimTime % duration) / duration) * nframes; + var frame0 = ((ushort)curPos) % frames; + var frame1 = (frame0 + 1) % frames; + var falpha = (float)(curPos - Math.Floor(curPos)); + var ialpha = 1.0f - falpha; + + + var dwbl = this.Key; + var skel = dwbl?.Skeleton; + var bones = skel?.Bones; + if (bones == null) + { return; } + + for (int i = 0; i < anim.BoneIds.data_items.Length; i++) + { + var boneiditem = anim.BoneIds.data_items[i]; + var track = boneiditem.Track; + + Bone bone = null; + skel?.BonesMap?.TryGetValue(boneiditem.BoneId, out bone); + if (bone == null) + { continue; } + + + for (int s = 0; s < anim.Sequences.data_items.Length; s++) + { + var seq = anim.Sequences.data_items[s]; + var aseq = seq.Sequences[i]; + switch (track) + { + case 0: //bone position + var v0 = aseq.EvaluateVector(frame0); + var v1 = aseq.EvaluateVector(frame1); + var v = interpolate ? (v0 * ialpha) + (v1 * falpha) : v0; + bone.AnimTranslation = v.XYZ(); + break; + case 1: //bone orientation + var q0 = new Quaternion(aseq.EvaluateVector(frame0)); + var q1 = new Quaternion(aseq.EvaluateVector(frame1)); + var q = interpolate ? Quaternion.Slerp(q0, q1, falpha) : q0; + bone.AnimRotation = q; + break; + default: + break; + } + } + } + + for (int i = 0; i < bones.Count; i++) + { + var bone = bones[i]; + + RenderableModel bmodel = null; + ModelBoneLinks?.TryGetValue(bone.Id, out bmodel); + if (bmodel == null) + { continue; } + + //update model's transform from animated bone + + var pos = bone.AnimTranslation; + var ori = bone.AnimRotation; + var pbone = bone.Parent; + while (pbone != null) + { + pos = pbone.AnimRotation.Multiply(pos) + pbone.AnimTranslation; + ori = pbone.AnimRotation * ori; + pbone = pbone.Parent; + } + + bmodel.Transform = Matrix.AffineTransformation(1.0f, ori, pos); + + } + - //TODO........ } private void UpdateAnimUV(ClipMapEntry cme) { @@ -384,11 +480,11 @@ namespace CodeWalker.Rendering { return; } bool interpolate = true; //how to know? eg. cs4_14_hickbar_anim shouldn't - bool lastFrameSameAsFirst = true;//if last frame is equivalent to the first one, eg rollercoaster small light "globes" don't + bool ignoreLastFrame = true;//if last frame is equivalent to the first one, eg rollercoaster small light "globes" don't var duration = anim.Duration; var frames = anim.Frames; - var nframes = (lastFrameSameAsFirst) ? (frames - 1) : frames; + var nframes = (ignoreLastFrame) ? (frames - 1) : frames; var curPos = ((CurrentAnimTime % duration) / duration) * nframes; var frame0 = ((ushort)curPos) % frames; diff --git a/Rendering/Renderer.cs b/Rendering/Renderer.cs index 47a565c..02263c1 100644 --- a/Rendering/Renderer.cs +++ b/Rendering/Renderer.cs @@ -1141,11 +1141,11 @@ namespace CodeWalker.Rendering //draw line from bone's position to parent position... Vector3 lbeg = Vector3.Zero; - Vector3 lend = bone.Translation;// bone.Rotation.Multiply(); + Vector3 lend = bone.AnimTranslation;// bone.Rotation.Multiply(); while (pbone != null) { - lbeg = pbone.Rotation.Multiply(lbeg) + pbone.Translation; - lend = pbone.Rotation.Multiply(lend) + pbone.Translation; + lbeg = pbone.AnimRotation.Multiply(lbeg) + pbone.AnimTranslation; + lend = pbone.AnimRotation.Multiply(lend) + pbone.AnimTranslation; pbone = pbone.Parent; }