/// <summary> /// /// </summary> /// <param name="jobj"></param> /// <param name="figatree"></param> public AnimationPlayer(HSD_JOBJ jobj, float frameCount) { FrameCount = (int)frameCount; foreach (var j in jobj.BreathFirstList) { TrackNode t = new TrackNode(j); jobjToTrack.Add(j, t); } LoadParents(jobj); }
/// <summary> /// /// </summary> /// <param name="jobj"></param> /// <param name="source"></param> /// <param name="source_jobj"></param> public bool PortBoneTo(HSD_JOBJ jobj, AnimationPlayer source, HSD_JOBJ source_jobj) { if (jobjToParent[jobj] == null) { return(false); } var targetWorld = GetWorldTransform(jobj); var sourceWorld = source.GetWorldTransform(source_jobj); Matrix4x4.Invert(sourceWorld, out Matrix4x4 invSourceWorld); var targetRotation = TrackNode.FromEulerAngles(jobj.RZ, jobj.RY, jobj.RX); var sourceRotation = TrackNode.FromEulerAngles(source_jobj.RZ, source_jobj.RY, source_jobj.RX); sourceRotation = Quaternion.Inverse(sourceRotation); // if bones have same orientation we can copy keys directly? if (ApproximatelySameOrientation(targetWorld, sourceWorld)) { jobjToTrack[jobj] = source.jobjToTrack[source_jobj]; return(false); } // otherwise bake List <Matrix4x4> transforms = new List <Matrix4x4>(); for (int i = 0; i <= FrameCount; i++) { var inTargetParentWorld = GetAnimatedWorldTransform(jobjToParent[jobj], i); Matrix4x4.Invert(inTargetParentWorld, out inTargetParentWorld); var sourceAnimatedWorld = source.GetAnimatedWorldTransform(source_jobj, i); var rel = targetWorld * invSourceWorld * sourceAnimatedWorld; var newT = rel * inTargetParentWorld; var relTranslate = (new Vector3(source_jobj.TX, source_jobj.TY, source_jobj.TZ) - source.GetAnimatedTransform(source_jobj, i).Translation); relTranslate = Vector3.Transform(relTranslate, sourceRotation); relTranslate = Vector3.Transform(relTranslate, targetRotation); //newT.Translation = new Vector3(jobj.TX, jobj.TY, jobj.TZ);// + relTranslate; transforms.Add(newT); } // then optimize jobjToTrack[jobj] = new TrackNode(transforms, source.jobjToTrack[source_jobj], source_jobj, jobj); return(true); }
/// <summary> /// /// </summary> /// <param name="jobj"></param> /// <param name="figatree"></param> public AnimationPlayer(HSD_JOBJ jobj, HSD_FigaTree figatree) { FrameCount = (int)figatree.FrameCount; int jobjIndex = 0; foreach (var j in jobj.BreathFirstList) { TrackNode t = new TrackNode(j); var node = figatree.Nodes[jobjIndex]; foreach (var track in node.Tracks) { var fobj = track.ToFOBJ(); switch (fobj.JointTrackType) { case JointTrackType.HSD_A_J_TRAX: t.X.Keys = fobj.GetDecodedKeys(); break; case JointTrackType.HSD_A_J_TRAY: t.Y.Keys = fobj.GetDecodedKeys(); break; case JointTrackType.HSD_A_J_TRAZ: t.Z.Keys = fobj.GetDecodedKeys(); break; case JointTrackType.HSD_A_J_ROTX: t.RX.Keys = fobj.GetDecodedKeys(); break; case JointTrackType.HSD_A_J_ROTY: t.RY.Keys = fobj.GetDecodedKeys(); break; case JointTrackType.HSD_A_J_ROTZ: t.RZ.Keys = fobj.GetDecodedKeys(); break; case JointTrackType.HSD_A_J_SCAX: t.SX.Keys = fobj.GetDecodedKeys(); break; case JointTrackType.HSD_A_J_SCAY: t.SY.Keys = fobj.GetDecodedKeys(); break; case JointTrackType.HSD_A_J_SCAZ: t.SZ.Keys = fobj.GetDecodedKeys(); break; } } jobjToTrack.Add(j, t); jobjIndex++; } LoadParents(jobj); }
/// <summary> /// /// </summary> /// <param name="mat"></param> public TrackNode(List <Matrix4x4> transforms, TrackNode sourceTrack, HSD_JOBJ source, HSD_JOBJ dest) { List <float> x = new List <float>(); List <float> y = new List <float>(); List <float> z = new List <float>(); List <float> rx = new List <float>(); List <float> ry = new List <float>(); List <float> rz = new List <float>(); List <float> sx = new List <float>(); List <float> sy = new List <float>(); List <float> sz = new List <float>(); foreach (var t in transforms) { Vector3 sca, tra; Quaternion quat; Matrix4x4.Decompose(t, out sca, out quat, out tra); Vector3 rot = ToEulerAngles(quat); tra = t.Translation; x.Add(tra.X); y.Add(tra.Y); z.Add(tra.Z); rx.Add(rot.X); ry.Add(rot.Y); rz.Add(rot.Z); sx.Add(sca.X); sy.Add(sca.Y); sz.Add(sca.Z); } // check if any existing tracks match up and use those tracks when possible var xtrack = sourceTrack.GetExistingTrack(x, source, dest); var ytrack = sourceTrack.GetExistingTrack(y, source, dest); var ztrack = sourceTrack.GetExistingTrack(z, source, dest); var rxtrack = sourceTrack.GetExistingTrack(rx, source, dest); var rytrack = sourceTrack.GetExistingTrack(ry, source, dest); var rztrack = sourceTrack.GetExistingTrack(rz, source, dest); var sxtrack = sourceTrack.GetExistingTrack(sx, source, dest); var sytrack = sourceTrack.GetExistingTrack(sy, source, dest); var sztrack = sourceTrack.GetExistingTrack(sz, source, dest); /*if (dest.ClassName != null && dest.ClassName == "RLegJA") * { * System.Diagnostics.Debug.WriteLine(dest.ClassName); * * System.Diagnostics.Debug.WriteLine($"{source.RX} {source.RY} {source.RZ}"); * System.Diagnostics.Debug.WriteLine($"{dest.RX} {dest.RY} {dest.RZ}"); * * Matrix4x4.Decompose(transforms[0], out Vector3 relScale, out Quaternion rot, out Vector3 trans); * * System.Diagnostics.Debug.WriteLine($"{TrackNode.ToEulerAngles(rot).ToString()}"); * System.Diagnostics.Debug.WriteLine($"{sourceTrack.RX.Keys[0].Value} {sourceTrack.RY.Keys[0].Value} {sourceTrack.RZ.Keys[0].Value}"); * * System.Diagnostics.Debug.WriteLine($"{rxtrack != null} {rytrack != null} {rztrack != null}"); * }*/ if (xtrack != null || ytrack != null || ztrack != null || rxtrack != null || rytrack != null || rztrack != null || sxtrack != null || sytrack != null || sztrack != null) { //System.Diagnostics.Debug.WriteLine("Found Existing Track"); } // otherwise we have to approximate var trans_error = 0.05f; var rot_error = 0.01f; var scale_error = 0.1f; X.Keys = xtrack != null ? xtrack : LineSimplification.Simplify(x, trans_error); Y.Keys = ytrack != null ? ytrack : LineSimplification.Simplify(y, trans_error); Z.Keys = ztrack != null ? ztrack : LineSimplification.Simplify(z, trans_error); RX.Keys = rxtrack != null ? rxtrack : LineSimplification.Simplify(rx, rot_error); RY.Keys = rytrack != null ? rytrack : LineSimplification.Simplify(ry, rot_error); RZ.Keys = rztrack != null ? rztrack : LineSimplification.Simplify(rz, rot_error); SX.Keys = sxtrack != null ? sxtrack : LineSimplification.Simplify(sx, scale_error, true); SY.Keys = sytrack != null ? sytrack : LineSimplification.Simplify(sy, scale_error, true); SZ.Keys = sztrack != null ? sztrack : LineSimplification.Simplify(sz, scale_error, true); /* * X.Keys = xtrack != null && xtrack.Count < transforms.Count * 0.85f ? xtrack : LineSimplification.Simplify(x, trans_error); * Y.Keys = ytrack != null && ytrack.Count < transforms.Count * 0.85f ? ytrack : LineSimplification.Simplify(y, trans_error); * Z.Keys = ztrack != null && ztrack.Count < transforms.Count * 0.85f ? ztrack : LineSimplification.Simplify(z, trans_error); * RX.Keys = rxtrack != null && rxtrack.Count < transforms.Count * 0.85f ? rxtrack : LineSimplification.Simplify(rx, rot_error); * RY.Keys = rytrack != null && rytrack.Count < transforms.Count * 0.85f ? rytrack : LineSimplification.Simplify(ry, rot_error); * RZ.Keys = rztrack != null && rztrack.Count < transforms.Count * 0.85f ? rztrack : LineSimplification.Simplify(rz, rot_error); * SX.Keys = sxtrack != null && sxtrack.Count < transforms.Count * 0.85f ? sxtrack : LineSimplification.Simplify(sx, scale_error, true); * SY.Keys = sytrack != null && sytrack.Count < transforms.Count * 0.85f ? sytrack : LineSimplification.Simplify(sy, scale_error, true); * SZ.Keys = sztrack != null && sztrack.Count < transforms.Count * 0.85f ? sztrack : LineSimplification.Simplify(sz, scale_error, true); */ }
/// <summary> /// /// </summary> /// <returns></returns> public Matrix4x4 GetWorldTransform(HSD_JOBJ jobj) { return(TrackNode.CalculateMatrix(jobj) * (jobjToParent[jobj] != null ? GetWorldTransform(jobjToParent[jobj]) : Matrix4x4.Identity)); }