コード例 #1
0
        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);
        }
コード例 #2
0
 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)
 };
コード例 #3
0
ファイル: RadialPoints.cs プロジェクト: still-scene/Operators
        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;
        }
コード例 #4
0
        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);
        }
コード例 #5
0
        /// <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);
        }
コード例 #6
0
        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);
        }