public override void Redraw(float currentTime, float elapsedTime) { // selected vehicle (user can mouse click to select another) IVehicle selected = Demo.SelectedVehicle; // vehicle nearest mouse (to be highlighted) IVehicle nearMouse = Demo.VehicleNearestToMouse(); // update camera Demo.UpdateCamera(elapsedTime, selected); // draw "ground plane" centered between base and selected vehicle Vector3 goalOffset = Globals.HomeBaseCenter - Demo.Camera.Position; Vector3 goalDirection = Vector3.Normalize(goalOffset); Vector3 cameraForward = Demo.Camera.xxxls().Forward; float goalDot = Vector3.Dot(cameraForward, goalDirection); float blend = Utilities.RemapIntervalClip(goalDot, 1, 0, 0.5f, 0); Vector3 gridCenter = Vector3.Lerp(selected.Position, Globals.HomeBaseCenter, blend); Demo.GridUtility(gridCenter); // draw the seeker, obstacles and home base CtfSeeker.Draw(); DrawObstacles(); DrawHomeBase(); // draw each enemy foreach (CtfEnemy enemy in CtfEnemies) { enemy.Draw(); } // highlight vehicle nearest mouse Demo.HighlightVehicleUtility(nearMouse); }
public static Vertex FastLerp(Vertex a, Vertex b, float t) => new Vertex { point = Vector4.Lerp(a.point, b.point, t), onePerZ = MathRaster.Lerp(a.onePerZ, b.onePerZ, t), uv = Vector2.Lerp(a.uv, b.uv, t), normal = Vector3.Lerp(a.normal, b.normal, t), color = Color32.Lerp(a.color, b.color, t), distance2Cam = MathRaster.Lerp(a.distance2Cam, b.distance2Cam, t) };
private void Update(EvaluationContext context) { var closeCircle = CloseCircle.GetValue(context); var circleOffset = closeCircle ? 1 : 0; var corners = Count.GetValue(context).Clamp(1, 10000); var pointCount = corners + circleOffset; var listCount = corners + 2 * circleOffset; // Separator if (_pointList.NumElements != listCount) { //_points = new T3.Core.DataTypes.Point[count]; _pointList.SetLength(listCount); } var axis = Axis.GetValue(context); var center = Center.GetValue(context); var offset = Offset.GetValue(context); var radius = Radius.GetValue(context); var radiusOffset = RadiusOffset.GetValue(context); var thickness = W.GetValue(context); var thicknessOffset = WOffset.GetValue(context); var angelInRads = StartAngel.GetValue(context) * MathUtils.ToRad + (float)Math.PI / 2; var deltaAngle = -Cycles.GetValue(context) * MathUtils.Pi2 / (pointCount - circleOffset); for (var index = 0; index < pointCount; index++) { var f = corners == 1 ? 1 : (float)index / pointCount; var length = MathUtils.Lerp(radius, radius + radiusOffset, f); var v = Vector3.UnitX * length; var rot = Quaternion.CreateFromAxisAngle(axis, angelInRads); var vInAxis = Vector3.Transform(v, rot) + Vector3.Lerp(center, center + offset, f); var p = new Point { Position = vInAxis, W = MathUtils.Lerp(thickness, thickness + thicknessOffset, f), Orientation = rot }; _pointList[index] = p; angelInRads += deltaAngle; } if (closeCircle) { _pointList[listCount - 1] = Point.Separator(); } ResultList.Value = _pointList; }
private static IFlowField GenerateFlowField() { var f = new SimpleFlowField(50, 1, 50, new Vector3(25, 0.5f, 25)); //Start random f.Randomize(1); //Swirl around center //Half the field is a swirl (basically just concentric circles) while the other half has a slight bias to spiral inwards towards the center f.Func(pos => Vector3.Lerp(pos / 5, Vector3.Normalize(Vector3.Cross(pos, Vector3.UnitY)), pos.X > 0.5f ? 0.75f : 0.9f), 0.85f); //Keep it flat on the plane f.ClampXZ(); //Clean NaN values f.Clean(); return(f); }
/// <summary> /// 相交点辐射光 /// </summary> /// <param name="point"></param> /// <param name="normal"></param> /// <param name="deep"></param> /// <returns></returns> public virtual Light IntersectLight(Vector3 point, Vector3 dir, Vector3 normal, int deep) { #if RayDebugger SceneDebug Debugger = Scene.debugger; if (Debugger != null) { Debugger.BeginBranch(point); } #endif Light returnlight = default; // 发光体返回发光颜色 if (Material.LightAble) { returnlight = Material.LightColor; goto returnPoint; } // 递归深度极限 if (deep <= 1) { returnlight = Material.BaseColor * 0.3f; goto returnPoint; } dir = Vector3.Normalize(dir); bool IsBackFace = false; if (Vector3.Dot(dir, normal) > 0) // 背面 { IsBackFace = true; normal = -normal; } #region 计算追踪光线总数 int traceRayNum = RenderConfiguration.Configurations.ReflectSmapingLevel - RenderConfiguration.Configurations.RayTraceDeep + deep; { traceRayNum = (int)(traceRayNum * Material.AMetalDegree); if (traceRayNum < 1) { traceRayNum = 1; } traceRayNum = traceRayNum * 3 - 2; } #endregion #region 计算折射光 Light refractl = default; // 折射光 float refractPower = 0.0f; // 折射光强度 if (Material.IsTransparent) { float riindex = Material.RefractiveIndices; if (!IsBackFace) { riindex = 1.0f / riindex; } // 计算折射光线 (float pow, Vector3 rdir) = Tools.Refract(dir, normal, riindex); if (pow < 0) { goto endRefract; } refractPower = pow * Material.TransparentIndex; int raycount = (int)(traceRayNum * refractPower); traceRayNum -= raycount; float randomScale = Material.AMetalDegree * Material.AMetalDegree * 0.5f; //raycount = (int)(traceRayNum * randomScale); //raycount = (int)(traceRayNum * Material.AMetalDegree); if (raycount < 1 && refractPower > 0.00001) { raycount = 1; } if (raycount == 0) { refractl = Material.BaseColor; goto endRefract; } rdir = normal * randomScale + rdir * (1.0f - randomScale); for (int nsmap = 0; nsmap < raycount; nsmap++) { Vector3 raydir = Tools.RandomPointInSphere() * randomScale + rdir; Ray r = new Ray(point, raydir); //Console.WriteLine('\t' + this.Name + " [refract] : " + r); (Light c, float distance) = Scene.Light(r, deep - 1, this); if (IsBackFace) //内部光线,进行吸收计算 { float xsl = Math.Log(distance + 1.0f) + 1.0f; refractl *= Material.BaseColor / xsl; } refractl += c; } refractl /= raycount; } endRefract: #endregion #region 计算反射光 Light reflectl = default; // 反射光 { int raycount = traceRayNum; if (raycount < 1) { if (refractPower < 0.99f) { raycount = 1; } else { reflectl = Material.BaseColor; goto endReflact; } } Vector3 spO; { //Vector3 spRO = Tools.Reflect(dir, normal); Vector3 spRO = Vector3.Reflect(dir, normal); //spO = normal * (1.0f - Material.MetalDegree) + spRO * Material.MetalDegree; spO = Vector3.Lerp(normal, spRO, Material.MetalDegree); } for (int nsmap = 0; nsmap < raycount; nsmap++) { Vector3 tp = Tools.RandomPointInSphere() * Material.AMetalDegree + spO; Vector3 raydir = tp; while (raydir.LengthSquared() < 0.1) { tp = Tools.RandomPointInSphere() + spO; raydir = tp; } Ray r = new Ray(point, raydir); //Console.WriteLine('\t' + this.Name + " [reflact] : " + r); (Light c, float _) = Scene.Light(r, deep - 1, this); //, this); reflectl += c; } reflectl /= raycount; reflectl *= (0.06f * Material.MetalDegree + 0.93f) * Material.BaseColor; } #endregion returnlight = refractl * refractPower + reflectl * (1.0f - refractPower); endReflact: returnPoint: #if RayDebugger if (Debugger != null) { Debugger.EndBranch(); } #endif return(returnlight); }
public static ImportedAnimation ImportFromAssimpScene(Scene scene, AnimationImportSettings settings) { ImportedAnimation result = new ImportedAnimation(); var sceneMatrix = NMatrix.Identity; if (!settings.FlipQuaternionHandedness) { sceneMatrix *= NMatrix.CreateScale(-1, 1, 1); } if (settings.ExistingHavokAnimationTemplate == null) { throw new NotImplementedException("Reading skeleton/binding from assimp scene not supported yet. Please import using existing havok animation as template."); } else { result.hkaSkeleton = settings.ExistingHavokAnimationTemplate.hkaSkeleton; result.HkxBoneIndexToTransformTrackMap = settings.ExistingHavokAnimationTemplate.HkxBoneIndexToTransformTrackMap; result.TransformTrackIndexToHkxBoneMap = settings.ExistingHavokAnimationTemplate.TransformTrackIndexToHkxBoneMap; } if (settings.ConvertFromZUp) { sceneMatrix *= NMatrix.CreateRotationZ((float)(Math.PI)); sceneMatrix *= NMatrix.CreateRotationX((float)(-Math.PI / 2.0)); } var sceneMatrix_ForRootMotion = NMatrix.CreateScale(NVector3.One * settings.SceneScale) * sceneMatrix; if (settings.UseRootMotionScaleOverride) { sceneMatrix_ForRootMotion = NMatrix.CreateScale(NVector3.One * settings.RootMotionScaleOverride) * sceneMatrix; } sceneMatrix = NMatrix.CreateScale(NVector3.One * settings.SceneScale) * sceneMatrix; foreach (var anim in scene.Animations) { if (anim.HasNodeAnimations) { // Setup framerate. double tickScaler = (settings.ResampleToFramerate / anim.TicksPerSecond); result.Duration = anim.DurationInTicks != 0 ? // Don't divide by 0 (float)(anim.DurationInTicks / anim.TicksPerSecond) : 0; result.FrameDuration = (float)(1 / settings.ResampleToFramerate); //result.Duration += result.FrameDuration; int frameCount = (int)Math.Round(result.Duration / result.FrameDuration); double resampleTickMult = settings.ResampleToFramerate / anim.TicksPerSecond; Dictionary <string, int> transformTrackIndexMapping = new Dictionary <string, int>(); List <string> transformTrackNames = new List <string>(); // Populate transform track names. foreach (var nodeChannel in anim.NodeAnimationChannels) { if (nodeChannel.NodeName == settings.RootMotionNodeName && settings.ExcludeRootMotionNodeFromTransformTracks) { continue; } transformTrackNames.Add(nodeChannel.NodeName); } result.TransformTrackToBoneIndices.Clear(); if (settings.ExistingBoneDefaults != null) { var boneNamesInExistingSkel = settings.ExistingBoneDefaults.Keys.ToList(); transformTrackNames = boneNamesInExistingSkel; foreach (var tt in transformTrackNames) { result.TransformTrackToBoneIndices.Add(tt, boneNamesInExistingSkel.IndexOf(tt)); } } else { int i = 0; foreach (var t in transformTrackNames) { result.TransformTrackToBoneIndices.Add(t, i++); } } // Populate transform track names. foreach (var nodeChannel in anim.NodeAnimationChannels) { //if (nodeChannel.NodeName == settings.RootMotionNodeName && settings.ExcludeRootMotionNodeFromTransformTracks) // continue; transformTrackIndexMapping.Add(nodeChannel.NodeName, transformTrackNames.IndexOf(nodeChannel.NodeName)); } result.TransformTrackNames = transformTrackNames; result.Frames = new List <ImportedAnimation.Frame>(); for (int i = 0; i <= frameCount; i++) { var f = new ImportedAnimation.Frame(); for (int j = 0; j < transformTrackNames.Count; j++) { if (settings.ExistingBoneDefaults != null && settings.ExistingBoneDefaults.ContainsKey(transformTrackNames[j]) && settings.InitalizeUnanimatedTracksToTPose) { f.BoneTransforms.Add(settings.ExistingBoneDefaults[transformTrackNames[j]]); } else { f.BoneTransforms.Add(NewBlendableTransform.Identity); } } result.Frames.Add(f); } var rootMotionRotationFrames = new NQuaternion[frameCount + 1]; //DEBUGGING var DEBUG_ALL_NODE_NAMES_SORTED = anim.NodeAnimationChannels.Select(n => n.NodeName).OrderBy(n => n).ToList(); for (int i = 0; i < anim.NodeAnimationChannelCount; i++) { var nodeChannel = anim.NodeAnimationChannels[i]; int lastKeyIndex = -1; bool hasPosition = nodeChannel.HasPositionKeys; bool hasRotation = nodeChannel.HasRotationKeys; bool hasScale = nodeChannel.HasScalingKeys; if (nodeChannel.NodeName.Contains("$AssimpFbx$_Translation")) { hasPosition = true; hasRotation = false; hasScale = false; } else if (nodeChannel.NodeName.Contains("$AssimpFbx$_Rotation")) { hasPosition = false; hasRotation = true; hasScale = false; } else if (nodeChannel.NodeName.Contains("$AssimpFbx$_Scaling")) { hasPosition = false; hasRotation = false; hasScale = true; } bool isRootMotionNode = nodeChannel.NodeName == settings.RootMotionNodeName || (nodeChannel.NodeName.StartsWith(settings.RootMotionNodeName) && nodeChannel.NodeName.Contains("_$AssimpFbx$_")); if (isRootMotionNode) { if (hasPosition) { lastKeyIndex = -1; foreach (var keyPos in nodeChannel.PositionKeys) { int frame = (int)Math.Floor(keyPos.Time * resampleTickMult); result.Frames[frame].RootMotionTranslation = NVector3.Transform(keyPos.Value.ToNumerics(), sceneMatrix_ForRootMotion); //if (settings.FlipQuaternionHandedness) //{ // result.Frames[frame].RootMotionTranslation.X *= -1; //} // Fill in from the last keyframe to this one for (int f = lastKeyIndex + 1; f <= Math.Min(frame - 1, result.Frames.Count - 1); f++) { float lerpS = 1f * (f - lastKeyIndex) / (frame - lastKeyIndex); var blendFrom = result.Frames[lastKeyIndex].RootMotionTranslation; var blendTo = result.Frames[frame].RootMotionTranslation; result.Frames[f].RootMotionTranslation = NVector3.Lerp(blendFrom, blendTo, lerpS); } lastKeyIndex = frame; } // Fill in from last key to end of animation. for (int f = lastKeyIndex + 1; f <= result.Frames.Count - 1; f++) { result.Frames[f].RootMotionTranslation = result.Frames[lastKeyIndex].RootMotionTranslation; } } if (hasRotation && settings.EnableRotationalRootMotion) { lastKeyIndex = -1; foreach (var keyPos in nodeChannel.RotationKeys) { int frame = (int)Math.Floor(keyPos.Time * resampleTickMult); var curFrameRotation = keyPos.Value.ToNumerics(); curFrameRotation.Y *= -1; curFrameRotation.Z *= -1; if (settings.FlipQuaternionHandedness) { curFrameRotation = SapMath.MirrorQuat(curFrameRotation); } if (frame >= 0 && frame < frameCount) { rootMotionRotationFrames[frame] = curFrameRotation; } // Fill in from the last keyframe to this one for (int f = lastKeyIndex + 1; f <= Math.Min(frame - 1, result.Frames.Count - 1); f++) { float lerpS = 1f * (f - lastKeyIndex) / (frame - lastKeyIndex); var blendFrom = rootMotionRotationFrames[lastKeyIndex]; var blendTo = curFrameRotation; var blended = NQuaternion.Slerp(blendFrom, blendTo, lerpS); //blended = NQuaternion.Normalize(blended); rootMotionRotationFrames[f] = blended; } lastKeyIndex = frame; } // Fill in from last key to end of animation. for (int f = lastKeyIndex + 1; f <= result.Frames.Count - 1; f++) { rootMotionRotationFrames[f] = rootMotionRotationFrames[lastKeyIndex]; } } } if (isRootMotionNode) { hasPosition = false; hasRotation = !settings.EnableRotationalRootMotion; } if (!(isRootMotionNode && !settings.ExcludeRootMotionNodeFromTransformTracks)) { string nodeName = nodeChannel.NodeName; int transformIndex = transformTrackIndexMapping[nodeName]; int memeIndex = nodeName.IndexOf("_$AssimpFbx$_"); if (memeIndex >= 0) { nodeName = nodeName.Substring(0, memeIndex); } if (transformIndex >= 0 && transformIndex < transformTrackNames.Count) { // TRANSLATION if (hasPosition) { lastKeyIndex = -1; foreach (var keyPos in nodeChannel.PositionKeys) { int frame = (int)Math.Floor(keyPos.Time * resampleTickMult); var curFrameTransform = result.Frames[frame].BoneTransforms[transformIndex]; curFrameTransform.Translation = NVector3.Transform(keyPos.Value.ToNumerics(), sceneMatrix); result.Frames[frame].BoneTransforms[transformIndex] = curFrameTransform; // Fill in from the last keyframe to this one for (int f = lastKeyIndex + 1; f <= Math.Min(frame - 1, result.Frames.Count - 1); f++) { float lerpS = 1f * (f - lastKeyIndex) / (frame - lastKeyIndex); var blendFrom = result.Frames[lastKeyIndex].BoneTransforms[transformIndex].Translation; var blendTo = curFrameTransform.Translation; var blended = NVector3.Lerp(blendFrom, blendTo, lerpS); var copyOfStruct = result.Frames[f].BoneTransforms[transformIndex]; copyOfStruct.Translation = blended; result.Frames[f].BoneTransforms[transformIndex] = copyOfStruct; } lastKeyIndex = frame; } // Fill in from last key to end of animation. for (int f = lastKeyIndex + 1; f <= result.Frames.Count - 1; f++) { var x = result.Frames[f].BoneTransforms[transformIndex]; x.Translation = result.Frames[lastKeyIndex].BoneTransforms[transformIndex].Translation; result.Frames[f].BoneTransforms[transformIndex] = x; } } // SCALE if (hasScale) { lastKeyIndex = -1; foreach (var keyPos in nodeChannel.ScalingKeys) { int frame = (int)Math.Floor(keyPos.Time * resampleTickMult); var curFrameTransform = result.Frames[frame].BoneTransforms[transformIndex]; curFrameTransform.Scale = keyPos.Value.ToNumerics(); result.Frames[frame].BoneTransforms[transformIndex] = curFrameTransform; // Fill in from the last keyframe to this one for (int f = lastKeyIndex + 1; f <= Math.Min(frame - 1, result.Frames.Count - 1); f++) { float lerpS = 1f * (f - lastKeyIndex) / (frame - lastKeyIndex); var blendFrom = result.Frames[lastKeyIndex].BoneTransforms[transformIndex].Scale; var blendTo = curFrameTransform.Scale; var blended = NVector3.Lerp(blendFrom, blendTo, lerpS); var copyOfStruct = result.Frames[f].BoneTransforms[transformIndex]; copyOfStruct.Scale = blended; result.Frames[f].BoneTransforms[transformIndex] = copyOfStruct; } lastKeyIndex = frame; } // Fill in from last key to end of animation. for (int f = lastKeyIndex + 1; f <= result.Frames.Count - 1; f++) { var x = result.Frames[f].BoneTransforms[transformIndex]; x.Scale = result.Frames[lastKeyIndex].BoneTransforms[transformIndex].Scale; result.Frames[f].BoneTransforms[transformIndex] = x; } } // ROTATION if (hasRotation) { lastKeyIndex = -1; foreach (var keyPos in nodeChannel.RotationKeys) { int frame = (int)Math.Floor(keyPos.Time * resampleTickMult); var curFrameTransform = result.Frames[frame].BoneTransforms[transformIndex]; curFrameTransform.Rotation = keyPos.Value.ToNumerics(); curFrameTransform.Rotation.Y *= -1; curFrameTransform.Rotation.Z *= -1; if (settings.FlipQuaternionHandedness) { curFrameTransform.Rotation = SapMath.MirrorQuat(curFrameTransform.Rotation); } result.Frames[frame].BoneTransforms[transformIndex] = curFrameTransform; // Fill in from the last keyframe to this one for (int f = lastKeyIndex + 1; f <= Math.Min(frame - 1, result.Frames.Count - 1); f++) { float lerpS = 1f * (f - lastKeyIndex) / (frame - lastKeyIndex); var blendFrom = result.Frames[lastKeyIndex].BoneTransforms[transformIndex].Rotation; var blendTo = curFrameTransform.Rotation; var blended = NQuaternion.Slerp(blendFrom, blendTo, lerpS); //blended = NQuaternion.Normalize(blended); var copyOfStruct = result.Frames[f].BoneTransforms[transformIndex]; copyOfStruct.Rotation = blended; result.Frames[f].BoneTransforms[transformIndex] = copyOfStruct; } lastKeyIndex = frame; } // Fill in from last key to end of animation. for (int f = lastKeyIndex + 1; f <= result.Frames.Count - 1; f++) { var x = result.Frames[f].BoneTransforms[transformIndex]; x.Rotation = result.Frames[lastKeyIndex].BoneTransforms[transformIndex].Rotation; result.Frames[f].BoneTransforms[transformIndex] = x; } } } else { Console.WriteLine("unmapped transform track."); } } } if (settings.BonesToFlipBackwardsAboutYAxis.Count > 0) { var trackIndicesToFlip = result.TransformTrackNames .Select((x, i) => i) .Where(x => settings.BonesToFlipBackwardsAboutYAxis.Contains(result.TransformTrackNames[x])) .ToList(); foreach (var f in result.Frames) { foreach (var i in trackIndicesToFlip) { var t = f.BoneTransforms[i]; t.Rotation = NQuaternion.CreateFromRotationMatrix(NMatrix.CreateFromQuaternion(f.BoneTransforms[i].Rotation) * NMatrix.CreateRotationY(SapMath.Pi)); t.Translation = NVector3.Transform(t.Translation, NMatrix.CreateRotationY(SapMath.Pi)); f.BoneTransforms[i] = t; } } } result.FrameCount = frameCount; result.Name = anim.Name ?? settings.ExistingHavokAnimationTemplate?.Name ?? "SAP Custom Animation"; float rootMotionRot = 0; for (int f = 0; f < result.Frames.Count; f++) { if (f > 0 && settings.EnableRotationalRootMotion) { var curMat = NMatrix.CreateFromQuaternion(rootMotionRotationFrames[f]); var oldMat = NMatrix.CreateFromQuaternion(rootMotionRotationFrames[f - 1]); if (NMatrix.Invert(oldMat, out NMatrix inverseOldMat)) { var deltaMat = curMat * inverseOldMat; var deltaVec = NVector3.Transform(NVector3.UnitX, deltaMat); float deltaAngle = (float)Math.Atan2(deltaVec.Z, deltaVec.X); rootMotionRot += deltaAngle; } } result.Frames[f].RootMotionRotation = rootMotionRot; } break; } } result.RootMotion = new RootMotionData( new NVector4(0, 1, 0, 0), new NVector4(0, 0, 1, 0), result.Duration, result.Frames.Select(f => f.RootMotion).ToArray()); // Copy first frame for loop? //for (int i = 0; i < result.TransformTrackNames.Count; i++) //{ // result.Frames[result.Frames.Count - 1].BoneTransforms[i] = result.Frames[0].BoneTransforms[i]; //} var rootMotionStart = result.Frames[0].RootMotion; for (int i = 0; i < result.Frames.Count; i++) { result.Frames[i].RootMotionTranslation.X -= rootMotionStart.X; result.Frames[i].RootMotionTranslation.Y -= rootMotionStart.Y; result.Frames[i].RootMotionTranslation.Z -= rootMotionStart.Z; result.Frames[i].RootMotionRotation -= rootMotionStart.W; var xyz = NVector3.Transform(result.Frames[i].RootMotion.XYZ(), NMatrix.CreateRotationY(-rootMotionStart.W)); result.Frames[i].RootMotionTranslation.X = xyz.X; result.Frames[i].RootMotionTranslation.Y = xyz.Y; result.Frames[i].RootMotionTranslation.Z = xyz.Z; //for (int t = 0; t < result.Frames[i].BoneTransforms.Count; t++) //{ // if (i > 0 && NQuaternion.Dot(result.Frames[i - 1].BoneTransforms[t].Rotation, result.Frames[i].BoneTransforms[t].Rotation) < 0.995) // { // var tf = result.Frames[i].BoneTransforms[t]; // tf.Rotation = NQuaternion.Conjugate(result.Frames[i].BoneTransforms[t].Rotation); // result.Frames[i].BoneTransforms[t] = tf; // } //} } // HOTFIX FOR BAD FBX if (result.Frames.Count >= 3) { result.Frames[result.Frames.Count - 1].RootMotionRotation = result.Frames[result.Frames.Count - 2].RootMotionRotation + (result.Frames[result.Frames.Count - 2].RootMotionRotation - result.Frames[result.Frames.Count - 3].RootMotionRotation); } //var endFrame = new ImportedAnimation.Frame(); //foreach (var t in result.Frames[0].BoneTransforms) //{ // endFrame.BoneTransforms.Add(t); //} //endFrame.RootMotionTranslation = result.Frames[result.Frames.Count - 1].RootMotionTranslation + // (result.Frames[result.Frames.Count - 1].RootMotionTranslation - result.Frames[result.Frames.Count - 2].RootMotionTranslation); //endFrame.RootMotionRotation = result.Frames[result.Frames.Count - 1].RootMotionRotation + // (result.Frames[result.Frames.Count - 1].RootMotionRotation - result.Frames[result.Frames.Count - 2].RootMotionRotation); ////endFrame.RootMotionRotation = result.Frames[result.Frames.Count - 1].RootMotionRotation; //result.Frames.Add(endFrame); return(result); }