/// <summary> /// モーションの基本姿勢を basePose ベースに再計算する /// /// basePose は Humanoid.CopyNodes が必用 ! /// </summary> public Animation RebaseAnimation(Humanoid basePose) { var map = NodeMap.ToDictionary(kv => kv.Key, kv => new List <Quaternion>()); var hipsPositions = new List <Vector3>(); foreach (var(seconds, keyframes) in KeyFramesGroupBySeconds()) { // モーション適用 SetTime(seconds); Root.CalcWorldMatrix(); foreach (var keyframe in keyframes) { if (!keyframe.Node.HumanoidBone.HasValue || keyframe.Node.HumanoidBone.Value == HumanoidBones.unknown) { continue; } // ローカル回転を算出する var t = basePose[keyframe.Node].Rotation; var w = keyframe.Node.Rotation; var w_from_t = w * Quaternion.Inverse(t); // parent var key = keyframe.Node.HumanoidBone.Value; var curve = map[keyframe.Node]; if (key != HumanoidBones.hips) { if (basePose[key].Parent == null) { throw new Exception(); } var parent_t = basePose[key].Parent.Rotation; var parent_w = keyframe.Node.Parent.Rotation; var parent_w_from_t = parent_w * Quaternion.Inverse(parent_t); var r = Quaternion.Inverse(parent_w_from_t) * w_from_t; curve.Add(r); } else { // hips curve.Add(w_from_t); hipsPositions.Add(keyframe.Node.Translation); } } } var dst = new Animation(Name + ".tpose"); foreach (var kv in map) { if (!kv.Value.Any()) { continue; } var bone = kv.Key.HumanoidBone.Value; var inCurve = NodeMap[kv.Key].Curves[AnimationPathType.Rotation].In; if (inCurve.Count != kv.Value.Count) { throw new Exception(); } var nodeAnimation = new NodeAnimation(); nodeAnimation.Curves.Add(AnimationPathType.Rotation, new CurveSampler { In = inCurve, Out = BufferAccessor.Create(kv.Value.ToArray()), }); if (bone == HumanoidBones.hips) { nodeAnimation.Curves.Add(AnimationPathType.Translation, new CurveSampler { In = inCurve, Out = BufferAccessor.Create(hipsPositions.ToArray()), }); } dst.AddCurve(kv.Key, nodeAnimation); } return(dst); }
static Animation LoadAnimation(string name, Bvh.Bvh bvh, Model model, float scalingFactor) { var animation = new Animation(name); Dictionary <string, BvhNodeCurves> pathMap = new Dictionary <string, BvhNodeCurves>(); for (int i = 0; i < bvh.Channels.Length; ++i) { var channel = bvh.Channels[i]; if (!bvh.TryGetPathWithPropertyFromChannel(channel, out Bvh.Bvh.PathWithProperty prop)) { throw new Exception(); } if (!pathMap.TryGetValue(prop.Path, out BvhNodeCurves curves)) { curves = new BvhNodeCurves(); pathMap.Add(prop.Path, curves); } curves.Set(prop.Property, channel); } // setup time var timeBytes = new byte[Marshal.SizeOf(typeof(float)) * bvh.FrameCount]; var timeSpan = SpanLike.Wrap <Single>(new ArraySegment <byte>(timeBytes)); var now = 0.0; for (int i = 0; i < timeSpan.Length; ++i, now += bvh.FrameTime.TotalSeconds) { timeSpan[i] = (float)now; } var times = new BufferAccessor(new ArraySegment <byte>(timeBytes), AccessorValueType.FLOAT, AccessorVectorType.SCALAR, bvh.FrameCount); foreach (var(key, nodeCurve) in pathMap) { var node = Model.GetNode(model.Root, key); var bvhNode = GetNode(bvh.Root, key); var curve = new NodeAnimation(); if (nodeCurve.LocalPositionX != null) { var values = new byte[Marshal.SizeOf(typeof(Vector3)) * nodeCurve.LocalPositionX.Keys.Length]; var span = SpanLike.Wrap <Vector3>(new ArraySegment <byte>(values)); for (int i = 0; i < nodeCurve.LocalPositionX.Keys.Length; ++i) { span[i] = new Vector3 { X = nodeCurve.LocalPositionX.Keys[i] * scalingFactor, Y = nodeCurve.LocalPositionY.Keys[i] * scalingFactor, Z = nodeCurve.LocalPositionZ.Keys[i] * scalingFactor, }; } var sampler = new CurveSampler { In = times, Out = new BufferAccessor(new ArraySegment <byte>(values), AccessorValueType.FLOAT, AccessorVectorType.VEC3, span.Length) }; curve.Curves.Add(AnimationPathType.Translation, sampler); } if (nodeCurve.EulerX != null) { var values = new byte[Marshal.SizeOf(typeof(Quaternion)) * nodeCurve.EulerX.Keys.Length]; var span = SpanLike.Wrap <Quaternion>(new ArraySegment <byte>(values)); Func <Quaternion, BvhNodeCurves, int, Quaternion> getRot = (q, c, i) => q; foreach (var ch in bvhNode.Channels) { var tmp = getRot; switch (ch) { case Channel.Xrotation: getRot = (_, c, i) => { return(tmp(_, c, i) * Quaternion.CreateFromAxisAngle(Vector3.UnitX, ToRad(c.EulerX.Keys[i]))); }; break; case Channel.Yrotation: getRot = (_, c, i) => { return(tmp(_, c, i) * Quaternion.CreateFromAxisAngle(Vector3.UnitY, ToRad(c.EulerY.Keys[i]))); }; break; case Channel.Zrotation: getRot = (_, c, i) => { return(tmp(_, c, i) * Quaternion.CreateFromAxisAngle(Vector3.UnitZ, ToRad(c.EulerZ.Keys[i]))); }; break; default: // throw new NotImplementedException(); break; } } for (int i = 0; i < nodeCurve.EulerX.Keys.Length; ++i) { span[i] = getRot(Quaternion.Identity, nodeCurve, i); } var sampler = new CurveSampler { In = times, Out = new BufferAccessor(new ArraySegment <byte>(values), AccessorValueType.FLOAT, AccessorVectorType.VEC4, span.Length) }; curve.Curves.Add(AnimationPathType.Rotation, sampler); } animation.AddCurve(node, curve); } return(animation); }