public void ApplyExternalTransform(NVector4 transform)
        {
            // Find where loop start is relative to current location
            var deltaCurrentToLoopStart = (LoopStartTransform - CurrentTransform);


            // Rotate loop start such that the current location is pivoted
            var newTranslation = NVector3.Transform(deltaCurrentToLoopStart.XYZ(), NMatrix.CreateRotationY(transform.W));

            deltaCurrentToLoopStart.X = CurrentTransform.X + newTranslation.X + transform.X;
            deltaCurrentToLoopStart.Y = CurrentTransform.Y + newTranslation.Y + transform.Y;
            deltaCurrentToLoopStart.Z = CurrentTransform.Z + newTranslation.Z + transform.Z;
            deltaCurrentToLoopStart.W = LoopStartTransform.W + transform.W;

            LoopStartTransform = deltaCurrentToLoopStart;

            var tr = CurrentTransform;

            tr += transform;
            CurrentTransform = tr;

            tr  = PreviousFrameTransform;
            tr += transform;
            PreviousFrameTransform = tr;

            //tr = LoopStartTransform;
            //tr.W += rotation;
            //LoopStartTransform = tr;
        }
示例#2
0
        public static NQuaternion GetDeltaQuaternionWithDirectionVectors(NQuaternion from, NQuaternion to)
        {
            var a = NVector3.Transform(NVector3.UnitZ, NMatrix.CreateFromQuaternion(from));
            var b = NVector3.Transform(NVector3.UnitZ, NMatrix.CreateFromQuaternion(to));

            var dot = NVector3.Dot(a, b);

            if (dot < -0.999999)
            {
                var cross = NVector3.Cross(a, b);
                if (cross.Length() < 0.000001)
                {
                    cross = NVector3.Cross(NVector3.UnitY, a);
                }
                cross = NVector3.Normalize(cross);
                return(NQuaternion.CreateFromAxisAngle(cross, Pi));
            }
            else if (dot > 0.999999)
            {
                return(new NQuaternion(0, 0, 0, 1));
            }
            else
            {
                var xyz = NVector3.Cross(a, b);
                var w   = (float)(Math.Sqrt(a.Length() * a.Length() + b.Length() * b.Length()) + dot);
                return(new NQuaternion(xyz.X, xyz.Y, xyz.Z, w));
            }
        }
示例#3
0
        // a simple 2d vehicle on the XZ plane
        public static void DrawBasic2dCircularVehicle(IVehicle vehicle, Color color)
        {
            // "aspect ratio" of body (as seen from above)
            const float X = 0.5f;
            float       y = (float)Math.Sqrt(1 - (X * X));

            // radius and position of vehicle
            float   r = vehicle.Radius;
            Vector3 p = vehicle.Position;

            // shape of triangular body
            Vector3 u = new Vector3(0, 1, 0) * r * 0.05f;             // slightly up
            Vector3 f = Vector3.UnitZ * r;
            Vector3 s = Vector3.UnitX * X * r;
            Vector3 b = Vector3.UnitZ * -y * r;

            var matrix = vehicle.ToMatrix();

            // draw double-sided triangle (that is: no (back) face culling)
            BeginDoubleSidedDrawing();
            iDrawTriangle(Vector3.Transform(f + u, matrix),
                          Vector3.Transform(b - s + u, matrix),
                          Vector3.Transform(b + s + u, matrix),
                          color);
            EndDoubleSidedDrawing();

            // draw the circular collision boundary
            DrawXZCircle(r, p + u, Color.White, 20);
        }
示例#4
0
        public void DrawOutlines(ShapeCache cache, Color color, Vector position, Vector size, float angle)
        {
            if ((iVertexBuffer + cache.Vertices.Length) > vertexBuffer.Length || (iIndexBuffer + cache.Triangles.Length) > indexBuffer.Length)
            {
                Flush();
            }

            Matrix matrix =
                Matrix.CreateScale((float)size.X, (float)size.Y, 1f)
                * Matrix.CreateRotationZ(angle)
                * Matrix.CreateTranslation((float)position.X, (float)position.Y, 0);

            uint startIndex = iVertexBuffer;

            for (int i = 0; i < cache.Vertices.Length; i++)
            {
                Vector v = cache.Vertices[i];
                vertexBuffer[iVertexBuffer++] = new VertexPositionColorTexture(Vector3.Transform(new Vector3((float)v.X, (float)v.Y, 0), matrix), color, Vector.Zero);
            }

            for (int i = 0; i < cache.OutlineIndices.Length - 1; i++)
            {
                indexBuffer[iIndexBuffer++] = (uint)cache.OutlineIndices[i] + startIndex;
                indexBuffer[iIndexBuffer++] = (uint)cache.OutlineIndices[i + 1] + startIndex;
            }

            indexBuffer[iIndexBuffer++] = (uint)cache.OutlineIndices[^ 1] + startIndex;
示例#5
0
        public void Draw(Image img, Vector2 position, System.Drawing.Rectangle?sourceRectangle, System.Drawing.Color color, Vector2 scale, float angle, Vector2 origin)
        {
            Debug.Assert(beginHasBeenCalled);

            if (iTexture >= BufferSize)
            {
                Flush();
            }

            texture = img;
            float iw = img.Width;
            float ih = img.Height;

            System.Drawing.Rectangle rect = sourceRectangle.Value;

            Vector transf = new Vector(position.X - origin.X * scale.X + (float)rect.Width / 2 * scale.X, position.Y + origin.Y * scale.Y - (float)rect.Height / 2 * scale.Y);

            Matrix matrix =
                Matrix.CreateScale(scale.X * rect.Width, scale.Y * rect.Height, 1f)
                * Matrix.CreateRotationZ(angle)
                * Matrix.CreateTranslation((float)transf.X, (float)transf.Y, 0);

            Vector3[] transformedPoints = new Vector3[VerticesPerTexture];
            for (int i = 0; i < transformedPoints.Length; i++)
            {
                transformedPoints[i] = Vector3.Transform(Vertices[i], matrix);
            }

            uint startIndex = (iTexture * VerticesPerTexture);

            for (int i = 0; i < VerticesPerTexture; i++)
            {
                uint bi = (uint)((iTexture * VerticesPerTexture) + i);
                vertexBuffer[bi].Position = transformedPoints[i];
            }

            // Triangle 1
            vertexBuffer[startIndex + 0].TexCoords = new Vector2(rect.Left / iw, rect.Top / ih);
            vertexBuffer[startIndex + 0].SetColor(color);
            vertexBuffer[startIndex + 1].TexCoords = new Vector2(rect.Left / iw, rect.Bottom / ih);
            vertexBuffer[startIndex + 1].SetColor(color);
            vertexBuffer[startIndex + 2].TexCoords = new Vector2(rect.Right / iw, rect.Top / ih);
            vertexBuffer[startIndex + 2].SetColor(color);

            // Triangle 2
            vertexBuffer[startIndex + 3].TexCoords = new Vector2(rect.Left / iw, rect.Bottom / ih);
            vertexBuffer[startIndex + 3].SetColor(color);
            vertexBuffer[startIndex + 4].TexCoords = new Vector2(rect.Right / iw, rect.Bottom / ih);
            vertexBuffer[startIndex + 4].SetColor(color);
            vertexBuffer[startIndex + 5].TexCoords = new Vector2(rect.Right / iw, rect.Top / ih);
            vertexBuffer[startIndex + 5].SetColor(color);

            iTexture++;
        }
示例#6
0
        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 void SetTransformToTimeWithinCurrentLoop(float localTime)
        {
            // Get sample at specified time.
            var sampleAtTime = SampleRootMotionData(localTime);

            // Rotate sample translation to be relative to loop start transform rotation.
            var translation = NVector3.Transform(sampleAtTime.XYZ(), NMatrix.CreateRotationY(LoopStartTransform.W));

            sampleAtTime.X = translation.X;
            sampleAtTime.Y = translation.Y;
            sampleAtTime.Z = translation.Z;

            CurrentTransform = LoopStartTransform + sampleAtTime;
        }
示例#8
0
        /// <summary>
        /// Draws a 3-axis diagram of a given rotation
        /// </summary>
        /// <param name="origin">The origin point of these axes (where they intersect)</param>
        /// <param name="length">The length of each axis</param>
        /// <param name="rotation">A quaternion representing the rotation the axes should show</param>
        public void DrawAxes(Vector2 origin, float length, System.Numerics.Quaternion rotation, Action <TexturedVertex2D> vertexAction = null)
        {
            var x = new Vector3(1, 0, 0);
            var y = new Vector3(0, 1, 0);
            var z = new Vector3(0, 0, 1);

            var xRot = Vector3.Transform(x, rotation) * length;
            var yRot = Vector3.Transform(y, rotation) * length;
            var zRot = Vector3.Transform(z, rotation) * length;

            // Y offsets are all inverted due to conflicting coord systems
            DrawLine(Texture.WhitePixel, origin, origin + new Vector2(xRot.X, -xRot.Y), 2, Colour4.Red, vertexAction);
            DrawLine(Texture.WhitePixel, origin, origin + new Vector2(yRot.X, -yRot.Y), 2, Colour4.Green, vertexAction);
            DrawLine(Texture.WhitePixel, origin, origin + new Vector2(zRot.X, -zRot.Y), 2, Colour4.Blue, vertexAction);
        }
示例#9
0
        public void Draw(TextureCoordinates c, Vector position, Vector size, float angle)
        {
            Debug.Assert(beginHasBeenCalled);

            if (iTexture >= BufferSize)
            {
                Flush();
            }

            Matrix matrix =
                Matrix.CreateScale((float)size.X, (float)size.Y, 1f)
                * Matrix.CreateRotationZ(angle)
                * Matrix.CreateTranslation((float)position.X, (float)position.Y, 0);

            Vector3[] transformedPoints = new Vector3[VerticesPerTexture];
            for (int i = 0; i < transformedPoints.Length; i++)
            {
                transformedPoints[i] = Vector3.Transform(Vertices[i], matrix);
            }

            uint startIndex = (iTexture * VerticesPerTexture);

            for (int i = 0; i < VerticesPerTexture; i++)
            {
                uint bi = (uint)((iTexture * VerticesPerTexture) + i);
                vertexBuffer[bi].Position = transformedPoints[i];
            }

            var color = System.Drawing.Color.FromArgb(255, 255, 255, 255);

            // Triangle 1
            vertexBuffer[startIndex + 0].TexCoords = new Vector2((float)c.TopLeft.X, (float)c.TopLeft.Y);
            vertexBuffer[startIndex + 0].SetColor(color);
            vertexBuffer[startIndex + 1].TexCoords = new Vector2((float)c.BottomLeft.X, (float)c.BottomLeft.Y);
            vertexBuffer[startIndex + 1].SetColor(color);
            vertexBuffer[startIndex + 2].TexCoords = new Vector2((float)c.TopRight.X, (float)c.TopRight.Y);
            vertexBuffer[startIndex + 2].SetColor(color);

            // Triangle 2
            vertexBuffer[startIndex + 3].TexCoords = new Vector2((float)c.BottomLeft.X, (float)c.BottomLeft.Y);
            vertexBuffer[startIndex + 3].SetColor(color);
            vertexBuffer[startIndex + 4].TexCoords = new Vector2((float)c.BottomRight.X, (float)c.BottomRight.Y);
            vertexBuffer[startIndex + 4].SetColor(color);
            vertexBuffer[startIndex + 5].TexCoords = new Vector2((float)c.TopRight.X, (float)c.TopRight.Y);
            vertexBuffer[startIndex + 5].SetColor(color);

            iTexture++;
        }
        /// <summary>
        /// TODO: Check if this actually works.
        /// </summary>
        private NVector4 GetLoopStartFromLoopEnd(NVector4 loopEnd)
        {
            var angleAtStart = loopEnd.W + (Data.FirstFrame.W - Data.LastFrame.W);
            // Find the delta from end to start
            var deltaStartToEnd = (Data.LastFrame - Data.FirstFrame);

            // Rotate delta translation by the delta from end to start
            var translation = NVector3.Transform(deltaStartToEnd.XYZ(), NMatrix.CreateRotationY(angleAtStart));

            deltaStartToEnd.X = loopEnd.X - translation.X;
            deltaStartToEnd.Y = loopEnd.Y - translation.Y;
            deltaStartToEnd.Z = loopEnd.Z - translation.Z;
            deltaStartToEnd.W = angleAtStart;

            return(deltaStartToEnd);
        }
示例#11
0
        public static void UpdateBoundingBox(this FLVER.Bone b, List <FLVER.Bone> bones, NVector3 vertexPos)
        {
            var boneAbsoluteMatrix = b.GetAbsoluteNMatrix(bones);

            if (NMatrix.Invert(boneAbsoluteMatrix, out NMatrix invertexBoneMat))
            {
                var posForBBox = NVector3.Transform(vertexPos, invertexBoneMat);

                var minX = Math.Min(b.BoundingBoxMin.X, posForBBox.X);
                var minY = Math.Min(b.BoundingBoxMin.Y, posForBBox.Y);
                var minZ = Math.Min(b.BoundingBoxMin.Z, posForBBox.Z);
                var maxX = Math.Max(b.BoundingBoxMax.X, posForBBox.X);
                var maxY = Math.Max(b.BoundingBoxMax.Y, posForBBox.Y);
                var maxZ = Math.Max(b.BoundingBoxMax.Z, posForBBox.Z);

                b.BoundingBoxMin = new NVector3(minX, minY, minZ);
                b.BoundingBoxMax = new NVector3(maxX, maxY, maxZ);
            }
            //ErrorTODO: when this fails, else {}
        }
示例#12
0
        private void Update(EvaluationContext context)
        {
            var sourcePoints      = SourcePoints.GetValue(context) as StructuredList <Point>;
            var destinationPoints = DestinationsPoints.GetValue(context) as StructuredList <Point>;

            if (sourcePoints == null || destinationPoints == null ||
                sourcePoints.NumElements == 0 || destinationPoints.NumElements == 0)
            {
                _pointList.SetLength(0);
                ResultList.Value = _pointList;
                return;
            }

            var count = sourcePoints.NumElements * destinationPoints.NumElements;

            if (_pointList.NumElements != count)
            {
                _pointList.SetLength(count);
            }

            for (var destinationIndex = 0; destinationIndex < destinationPoints.NumElements; destinationIndex++)
            {
                var destination = destinationPoints.TypedElements[destinationIndex];

                for (var sourceIndex = 0; sourceIndex < sourcePoints.NumElements; sourceIndex++)
                {
                    var source = sourcePoints.TypedElements[sourceIndex];
                    _pointList.TypedElements[destinationIndex * sourcePoints.NumElements + sourceIndex]
                        = new Point()
                        {
                        Position    = destination.Position + Vector3.Transform(source.Position, destination.Orientation),
                        W           = source.W,
                        Orientation = Quaternion.Multiply(destination.Orientation, source.Orientation),
                        };
                }
            }

            ResultList.Value = _pointList;
        }
        private CameraExtrinsics GetExtrinsics(SpatialCoordinateSystem frameCoordinateSystem)
        {
            if (frameCoordinateSystem == null)
            {
                return(null);
            }

            CameraExtrinsics extrinsics = null;

            if (rootCoordinateSystem == null)
            {
                return(null);
            }

            System.Numerics.Matrix4x4?worldMatrix = frameCoordinateSystem.TryGetTransformTo(rootCoordinateSystem);

            if (worldMatrix.HasValue)
            {
                WindowsVector3    position;
                WindowsVector3    scale;
                WindowsQuaternion rotation;
                WindowsMatrix4x4.Decompose(worldMatrix.Value, out scale, out rotation, out position);

                WindowsVector3 forward = WindowsVector3.Transform(-WindowsVector3.UnitZ, rotation);
                WindowsVector3 up      = WindowsVector3.Transform(WindowsVector3.UnitY, rotation);

                Matrix4x4 unityWorldMatrix = Matrix4x4.TRS(WindowsVectorToUnityVector(position), Quaternion.LookRotation(WindowsVectorToUnityVector(forward), WindowsVectorToUnityVector(up)), Vector3.one);

                extrinsics = new CameraExtrinsics()
                {
                    ViewFromWorld = unityWorldMatrix
                };
            }

            return(extrinsics);
        }
示例#14
0
        public ImportedFLVER2Model ImportFromAssimpScene(Scene scene, FLVER2ImportSettings settings)
        {
            LoadMaterialInfoBankForGame(settings.Game);

            var result = new ImportedFLVER2Model();
            var flver  = result.Flver = new FLVER2();

            flver.Header.BigEndian      = settings.FlverHeader.BigEndian;
            flver.Header.BoundingBoxMax = new NVector3(float.MinValue);
            flver.Header.BoundingBoxMin = new NVector3(float.MaxValue);
            flver.Header.Unicode        = settings.FlverHeader.Unicode;
            flver.Header.Unk4A          = settings.FlverHeader.Unk4A;
            flver.Header.Unk4C          = settings.FlverHeader.Unk4C;
            flver.Header.Unk5C          = settings.FlverHeader.Unk5C;
            flver.Header.Unk5D          = settings.FlverHeader.Unk5D;
            flver.Header.Unk68          = settings.FlverHeader.Unk68;
            flver.Header.Version        = settings.FlverHeader.Version;

            var flverSceneMatrix = NMatrix.CreateScale(NVector3.One * settings.SceneScale);



            if (settings.ConvertFromZUp)
            {
                flverSceneMatrix *= SapMath.ZUpToYUpNMatrix;
            }

            //flverSceneMatrix *= NMatrix.CreateRotationY(SapMath.Pi);

            flverSceneMatrix *= settings.SceneCorrectMatrix;



            flverSceneMatrix *= NMatrix.CreateScale(1, 1, -1);


            var coordMat = AssimpUtilities.GetSceneCoordSystemMatrix(scene);

            scene.RootNode.Transform *= coordMat;

            var skeletonRootNode = AssimpUtilities.FindRootNode(scene, settings.RootNodeName, out Matrix4x4 skeletonRootNodeMatrix);


            var metaskeleton = FLVERImportHelpers.GenerateFlverMetaskeletonFromRootNode(
                skeletonRootNode, skeletonRootNodeMatrix, settings.SceneScale);

            flver.Bones   = metaskeleton.Bones;
            flver.Dummies = metaskeleton.DummyPoly;

            foreach (var b in flver.Bones)
            {
                // Mark as dummied-out bone until iterating over them later and seeing which are weighted to meshes.
                if (b.ParentIndex == -1)
                {
                    b.Unk3C = 1;
                }
            }

            var usesIndirectBones = flver.Header.Version <= 0x20010;

            if (settings.SkeletonTransformsOverride != null)
            {
                flver.Bones = settings.SkeletonTransformsOverride;
            }



            //var flverMaterialList = new List<FLVER2.Material>();

            foreach (var material in scene.Materials)
            {
                string[] materialNameSplit = material.Name.Split('|');
                string   mtd = materialNameSplit.Length > 1 ? materialNameSplit[1].Trim() + ".mtd" : null;

                // If MTD doesn't exist, use original
                mtd = MaterialInfoBankPerGame[settings.Game].FallbackToDefaultMtdIfNecessary(mtd, Logger);

                //ErrorTODO: materialNameSplit should be 2 items long.
                var flverMaterial = new FLVER2.Material(materialNameSplit[0].Trim(), mtd, 0);


                void AddTextureSlot(TextureSlot slot, string ingameSlot)
                {
                    flverMaterial.Textures.Add(new FLVER2.Texture(type: ingameSlot,
                                                                  path: slot.FilePath != null ? Path.GetFullPath(slot.FilePath) : "",
                                                                  scale: System.Numerics.Vector2.One,
                                                                  1, true, 0, 0, 0));

                    string texName = Path.GetFileNameWithoutExtension(slot.FilePath);

                    byte[] texData = scene.GetEmbeddedTexture(slot.FilePath)?.CompressedData;

                    if (texData != null)
                    {
                        var ddsFormat = TPFTextureFormatFinder.GetTpfFormatFromDdsBytes(texData);

                        result.Textures.Add(new TPF.Texture(texName, format: ddsFormat, flags1: 0, bytes: texData));
                    }
                }

                var materialDefinition = MaterialInfoBankPerGame[settings.Game].MaterialDefs[mtd.ToLower()];
                var texChanDefs        = materialDefinition.TextureChannels;
                foreach (var kvp in texChanDefs)
                {
                    if (kvp.Key.Index == 0)
                    {
                        if (kvp.Key.Semantic == TextureChannelSemantic.Diffuse)
                        {
                            AddTextureSlot(material.TextureDiffuse, kvp.Value);
                        }
                        else if (kvp.Key.Semantic == TextureChannelSemantic.Specular)
                        {
                            AddTextureSlot(material.TextureSpecular, kvp.Value);
                        }
                        else if (kvp.Key.Semantic == TextureChannelSemantic.Normals)
                        {
                            AddTextureSlot(material.TextureNormal, kvp.Value);
                        }
                        else if (kvp.Key.Semantic == TextureChannelSemantic.Emissive)
                        {
                            AddTextureSlot(material.TextureEmissive, kvp.Value);
                        }
                        else
                        {
                            flverMaterial.Textures.Add(new FLVER2.Texture(type: kvp.Value,
                                                                          path: string.Empty,
                                                                          scale: System.Numerics.Vector2.One,
                                                                          0, false, 0, 0, 0));
                        }
                    }
                }

                if (materialDefinition.GXItems.Count > 0)
                {
                    flverMaterial.GXIndex = flver.GXLists.Count;
                    var gxList = new FLVER2.GXList();

                    for (int i = 0; i < materialDefinition.GXItems.Count; i++)
                    {
                        var    gxid  = materialDefinition.GXItems[i].GXID;
                        var    unk04 = materialDefinition.GXItems[i].Unk04;
                        byte[] data  = MaterialInfoBankPerGame[settings.Game].DefaultGXItemDataExamples[mtd][i];
                        gxList.Add(new FLVER2.GXItem(gxid, unk04, data));
                    }
                    flver.GXLists.Add(gxList);
                }

                flver.Materials.Add(flverMaterial);
                //flverMaterialList.Add(flverMaterial);
            }

            //var properBoneParentRegistry = new Dictionary<Bone, string>();

            //foreach (var mesh in scene.Meshes)
            //{
            //    foreach (var b in mesh.Bones)
            //    {
            //        bool alreadyRegistered = false;
            //        foreach (var bone in properBoneParentRegistry.Keys)
            //        {
            //            if (bone.Name == b.Name)
            //            {
            //                alreadyRegistered = true;
            //                break;
            //            }
            //        }
            //        if (alreadyRegistered)
            //            continue;
            //        mesh.
            //        properBoneParentRegistry.Add(b, b.)
            //    }
            //}

            if (settings.BoneNameRemapper != null)
            {
                foreach (var bn in settings.BoneNameRemapper)
                {
                    var bone = flver.Bones.FindIndex(b => b.Name == bn.Key);
                    if (bone >= 0)
                    {
                        flver.Bones[bone].Name = bn.Value;
                    }
                }
            }

            foreach (var mesh in scene.Meshes)
            {
                var flverMesh = new FLVER2.Mesh();

                flverMesh.BoundingBox = new FLVER2.Mesh.BoundingBoxes();

                //TODO: ACTUALLY READ FROM THINGS
                flverMesh.Dynamic = 1;



                // Register mesh transform bone:
                //flverMesh.DefaultBoneIndex = flver.Bones.Count;
                //int flverLastRootBoneIndex = flver.Bones.FindLastIndex(b => b.ParentIndex == -1);
                //// Register this new bone as a sibling.
                //if (flverLastRootBoneIndex >= 0)
                //    flver.Bones[flverLastRootBoneIndex].NextSiblingIndex = (short)flverMesh.DefaultBoneIndex;
                //flver.Bones.Add(new FLVER.Bone()
                //{
                //    Name = mesh.Name,
                //    Translation = NVector3.Zero,
                //    Rotation = NVector3.Zero,
                //    Scale = NVector3.One,
                //    BoundingBoxMin = NVector3.One * -0.05f,
                //    BoundingBoxMax = NVector3.One * 0.05f,
                //    // Cross-register sibling from above.
                //    PreviousSiblingIndex = (short)flverLastRootBoneIndex,
                //    NextSiblingIndex = -1,
                //    ParentIndex = -1,
                //    ChildIndex = -1,
                //    Unk3C = 1,
                //});



                int meshUVCount = 0;
                for (int i = 0; i < mesh.UVComponentCount.Length; i++)
                {
                    if (mesh.UVComponentCount[i] > 0)
                    {
                        meshUVCount++;
                    }
                }
                if (mesh.PrimitiveType != PrimitiveType.Triangle)
                {
                    Console.WriteLine();
                }

                var flverFaceSet = new FLVER2.FaceSet();

                //flverFaceSet.TriangleStrip = true;

                // Handle vertex buffers / layouts:
                flverMesh.MaterialIndex = mesh.MaterialIndex;
                //var newMat = flverMaterialList[mesh.MaterialIndex];
                //var indexOfNewMat = flver.Materials.IndexOf(newMat);
                //if (indexOfNewMat >= 0)
                //{
                //    flverMesh.MaterialIndex = indexOfNewMat;
                //}
                //else
                //{
                //    flverMesh.MaterialIndex = flver.Materials.Count;
                //    flver.Materials.Add(newMat);
                //}


                var flverMaterial            = flver.Materials[flverMesh.MaterialIndex];
                var matDefinition            = MaterialInfoBankPerGame[settings.Game].MaterialDefs[flverMaterial.MTD.ToLower()];
                var defaultBufferDeclaration = matDefinition.AcceptableVertexBufferDeclarations[0];

                Dictionary <FLVER.LayoutSemantic, int> requiredVertexBufferMembers =
                    new Dictionary <FLVER.LayoutSemantic, int>();

                foreach (var buff in defaultBufferDeclaration.Buffers)
                {
                    foreach (var m in buff)
                    {
                        if (!requiredVertexBufferMembers.ContainsKey(m.Semantic))
                        {
                            requiredVertexBufferMembers.Add(m.Semantic, 0);
                        }
                        requiredVertexBufferMembers[m.Semantic]++;
                    }

                    int nextLayoutIndex = flver.BufferLayouts.Count;
                    flver.BufferLayouts.Add(buff);
                    var vertBuffer = new FLVER2.VertexBuffer(nextLayoutIndex);
                    flverMesh.VertexBuffers.Add(vertBuffer);
                }



                flverMesh.Vertices = new List <FLVER.Vertex>(mesh.VertexCount);

                for (int i = 0; i < mesh.VertexCount; i++)
                {
                    var newVert = new FLVER.Vertex(uvCapacity: meshUVCount,
                                                   //TODO: Figure out what multiple tangents are used for etc and implement all
                                                   //      of that into the XML vert layout system stuff etc etc.
                                                   tangentCapacity: mesh.HasTangentBasis ? 1 : 0,
                                                   colorCapacity: mesh.VertexColorChannelCount);

                    newVert.Position = NVector3.Transform(mesh.Vertices[i].ToNumerics(), flverSceneMatrix);

                    flver.Header.UpdateBoundingBox(newVert.Position);
                    if (flverMesh.BoundingBox != null)
                    {
                        flverMesh.UpdateBoundingBox(newVert.Position);
                    }

                    newVert.Normal = NVector3.Normalize(NVector3.TransformNormal(mesh.Normals[i].ToNumerics(), flverSceneMatrix));

                    //TODO: TEST THIS AGAINST OTHER GAMES ETC
                    //newVert.NormalW = 127;

                    if (mesh.HasTangentBasis)
                    {
                        //ErrorTODO: Throw error if mesh somehow has tangents but not normals.
                        var tan      = mesh.Tangents[i];
                        var bitanXYZ = mesh.BiTangents[i];
                        //TODO: Check Bitangent W calculation
                        var bitanW = Vector3D.Dot(Vector3D.Cross(tan, mesh.Normals[i]), bitanXYZ) >= 0 ? 1 : -1;
                        var bitanXYZTransformed = NVector3.Normalize(NVector3.TransformNormal(bitanXYZ.ToNumerics(), flverSceneMatrix));
                        newVert.Tangents.Add(new System.Numerics.Vector4(bitanXYZTransformed, bitanW));
                        //TODO: CHECK THIS AND SEE WTF IT EVEN IS SUPPOSED TO BE
                        newVert.Bitangent = new System.Numerics.Vector4(
                            NVector3.TransformNormal(tan.ToNumerics(), flverSceneMatrix), 0);
                    }

                    for (int j = 0; j < meshUVCount; j++)
                    {
                        var uv = mesh.TextureCoordinateChannels[j][i];
                        newVert.UVs.Add(new NVector3(uv.X, 1 - uv.Y, uv.Z));
                    }

                    for (int j = 0; j < mesh.VertexColorChannelCount; j++)
                    {
                        newVert.Colors.Add(mesh.VertexColorChannels[j][i].ToFlverVertexColor());
                    }

                    for (int j = 0; j < 4; j++)
                    {
                        newVert.BoneIndices[j] = -1;
                    }

                    newVert.EnsureLayoutMembers(requiredVertexBufferMembers);

                    flverMesh.Vertices.Add(newVert);
                }

                if (usesIndirectBones)
                {
                    var bonesInMesh = mesh.Bones.OrderByDescending(mb => mb.VertexWeightCount).ToList();
                    foreach (var bone in bonesInMesh)
                    {
                        var boneIndex = flver.Bones.FindIndex(b => b.Name == bone.Name);

                        if (!flverMesh.BoneIndices.Contains(boneIndex))
                        {
                            flverMesh.BoneIndices.Add(boneIndex);
                        }
                    }

                    flverMesh.BoneIndices = flverMesh.BoneIndices.OrderBy(idx => idx).ToList();
                }



                foreach (var bone in mesh.Bones)
                {
                    var boneIndex = flver.Bones.FindIndex(b => b.Name == bone.Name);

                    if (boneIndex == -1)
                    {
                        Logger.LogWarning($"No bone with exact name '{bone.Name}' found. Looking for a bone that starts with that name");
                        boneIndex = flver.Bones.FindIndex(b => b.Name.StartsWith(bone.Name));
                    }

                    var boneDoesNotExist = false;

                    // Mark bone as not-dummied-out since there is geometry skinned to it.
                    if (boneIndex >= 0 && boneIndex < flver.Bones.Count)
                    {
                        flver.Bones[boneIndex].Unk3C = 0;
                    }
                    else
                    {
                        Logger.LogWarning($"Vertex skinned to bone '{bone.Name}' which does NOT exist in the skeleton.");
                        boneDoesNotExist = true;
                    }

                    int GetNextAvailableBoneSlotOfVert(int vertIndex)
                    {
                        if (flverMesh.Vertices[vertIndex].BoneIndices[0] < 0)
                        {
                            return(0);
                        }
                        else if (flverMesh.Vertices[vertIndex].BoneIndices[1] < 0)
                        {
                            return(1);
                        }
                        else if (flverMesh.Vertices[vertIndex].BoneIndices[2] < 0)
                        {
                            return(2);
                        }
                        else if (flverMesh.Vertices[vertIndex].BoneIndices[3] < 0)
                        {
                            return(3);
                        }
                        else
                        {
                            return(-1);
                        }
                    }

                    foreach (var weight in bone.VertexWeights)
                    {
                        int boneSlot = GetNextAvailableBoneSlotOfVert(weight.VertexID);
                        if (boneSlot >= 0)
                        {
                            var indexToAssign = usesIndirectBones ? flverMesh.BoneIndices.IndexOf(boneIndex) : boneIndex;
                            if (indexToAssign == -1)
                            {
                                Console.WriteLine("fatcat");
                            }
                            flverMesh.Vertices[weight.VertexID].BoneIndices[boneSlot] = boneDoesNotExist ? 0 : indexToAssign;
                            flverMesh.Vertices[weight.VertexID].BoneWeights[boneSlot] = boneDoesNotExist ? 0 : weight.Weight;
                            if (!boneDoesNotExist)
                            {
                                flver.Bones[boneIndex].UpdateBoundingBox(flver.Bones, flverMesh.Vertices[weight.VertexID].Position);
                            }
                        }
                    }
                }

                for (int i = 0; i < flverMesh.Vertices.Count; i++)
                {
                    float weightMult = 1 / (
                        flverMesh.Vertices[i].BoneWeights[0] +
                        flverMesh.Vertices[i].BoneWeights[1] +
                        flverMesh.Vertices[i].BoneWeights[2] +
                        flverMesh.Vertices[i].BoneWeights[3]);

                    for (int j = 0; j < 4; j++)
                    {
                        //flverMesh.Vertices[i].BoneWeights[j] = flverMesh.Vertices[i].BoneWeights[j] * weightMult;
                        if (flverMesh.Vertices[i].BoneIndices[j] < 0)
                        {
                            flverMesh.Vertices[i].BoneIndices[j] = 0;
                        }
                    }

                    //TODO: TEST THIS AGAINST OTHER GAMES ETC
                    if (!requiredVertexBufferMembers.ContainsKey(FLVER.LayoutSemantic.BoneIndices))
                    {
                        flverMesh.Vertices[i].NormalW = flverMesh.Vertices[i].BoneIndices[0];
                    }
                }

                //foreach (var face in mesh.Faces)
                //{
                //    //TODO: See if resets need to be added inbetween or anything.
                //    flverFaceSet.Indices.AddRange(face.Indices);
                //}

                flverFaceSet.Indices.AddRange(mesh.GetIndices());

                flverMesh.FaceSets.Add(flverFaceSet);
                GenerateLodAndMotionBlurFacesets(flverMesh);

                flver.Meshes.Add(flverMesh);
            }

            // DEBUGGING

            //flver.Bones.RemoveAt(0);
            //foreach (var mm in flver.Meshes)
            //    for (int mbi = 0; mbi < mm.BoneIndices.Count; mbi++)
            //        mm.BoneIndices[mbi] = mm.BoneIndices[mbi] - 1;
            //foreach (var b in flver.Bones)
            //{
            //    if (b.ParentIndex >= 0)
            //        b.ParentIndex--;
            //    if (b.ChildIndex >= 0)
            //        b.ChildIndex--;
            //    if (b.NextSiblingIndex >= 0)
            //        b.NextSiblingIndex--;
            //    if (b.PreviousSiblingIndex >= 0)
            //        b.PreviousSiblingIndex--;
            //}

            ///////////////////

            foreach (var b in flver.Bones)
            {
                if (settings.SkeletonTransformsOverride != null)
                {
                    var match = settings.SkeletonTransformsOverride.FindIndex(bn => bn.Name == b.Name);
                    if (match >= 0)
                    {
                        b.Translation = settings.SkeletonTransformsOverride[match].Translation;
                        b.Rotation    = settings.SkeletonTransformsOverride[match].Rotation;
                        b.Scale       = settings.SkeletonTransformsOverride[match].Scale;
                    }
                }

                if (float.IsInfinity(b.BoundingBoxMin.X) || float.IsInfinity(b.BoundingBoxMin.Y) || float.IsInfinity(b.BoundingBoxMin.Z) ||
                    float.IsInfinity(b.BoundingBoxMax.X) || float.IsInfinity(b.BoundingBoxMax.Y) || float.IsInfinity(b.BoundingBoxMax.Z))
                {
                    b.BoundingBoxMin = NVector3.One * -0.1f;
                    b.BoundingBoxMax = NVector3.One * 0.1f;
                }
            }

            return(result);
        }
        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);
        }
示例#16
0
        public static FLVERMetaskeleton GenerateFlverMetaskeletonFromRootNode(
            Node rootNode, Matrix4x4 rootNodeAbsoluteMatrix, float importScale)
        {
            var bonesAssimp          = new List <Node>();
            var skel                 = new FLVERMetaskeleton();
            var dummyAttachBoneNames = new List <string>();

            NMatrix matrixScale = NMatrix.CreateScale(importScale, importScale, importScale);

            // Returns index of bone in master bone list if boneNode is a bone.
            // Returns -1 if boneNode is a DummyPoly (denoted with a node name starting with "DUMMY_POLY").
            int AddBone(Node boneNode, Node parentBoneNode, Matrix4x4 parentAbsoluteMatrix)
            {
                short parentBoneIndex = (short)(bonesAssimp.IndexOf(parentBoneNode));

                var thisBoneMatrix         = boneNode.Transform;
                var thisNodeAbsoluteMatrix = thisBoneMatrix * parentAbsoluteMatrix;


                var boneTrans = FLVERBoneTransform.FromMatrix4x4(
                    (parentBoneIndex == -1 ? thisNodeAbsoluteMatrix : thisBoneMatrix), true);

                if (boneNode.Name.StartsWith("DUMMY_POLY"))
                {
                    // TODO

                    thisNodeAbsoluteMatrix.Decompose(out Vector3D dummyScale, out Quaternion dummyQuat, out Vector3D dummyTranslation);
                    var dmy = new FLVER.Dummy();
                    dmy.ParentBoneIndex = parentBoneIndex;
                    dmy.Position        = dummyTranslation.ToNumerics();

                    // Format: "DUMMY_POLY|<RefID>|<AttachBoneName>"
                    // Example: "DUMMY_POLY|220|Spine1"
                    string[] dummyNameParts = boneNode.Name.Split('|');

                    //ErrorTODO: TryParse
                    dmy.ReferenceID = short.Parse(dummyNameParts[1].Trim());

                    if (dummyNameParts.Length == 3)
                    {
                        dummyAttachBoneNames.Add(dummyNameParts[2]);
                    }
                    else
                    {
                        dummyAttachBoneNames.Add(null);
                    }

                    //NOTE: Maybe this should be specifiable? I forget what the point of false is here.
                    dmy.UseUpwardVector = true;

                    var sceneRotation = NMatrix.CreateRotationX(boneTrans.Rotation.X) *
                                        NMatrix.CreateRotationZ(boneTrans.Rotation.Z) *
                                        NMatrix.CreateRotationY(boneTrans.Rotation.Y);

                    dmy.Upward = NVector3.Transform(new NVector3(0, 1, 0), sceneRotation);
                    //TODO: Check if forward vector3 should be 1 or -1;
                    dmy.Forward = NVector3.Transform(new NVector3(0, 0, 1), sceneRotation);

                    skel.DummyPoly.Add(dmy);

                    return(-1);
                }
                else
                {
                    bonesAssimp.Add(boneNode);

                    int thisBoneIndex = bonesAssimp.Count - 1;

                    var flverBone = new FLVER.Bone();

                    if (parentBoneNode != null)
                    {
                        flverBone.ParentIndex = parentBoneIndex;
                    }

                    flverBone.Name           = boneNode.Name;
                    flverBone.BoundingBoxMin = new NVector3(float.MaxValue, float.MaxValue, float.MaxValue);
                    flverBone.BoundingBoxMax = new NVector3(float.MinValue, float.MinValue, float.MinValue);
                    flverBone.Translation    = boneTrans.Translation * importScale;
                    flverBone.Rotation       = boneTrans.Rotation;
                    flverBone.Scale          = boneTrans.Scale;

                    skel.Bones.Add(flverBone);

                    List <int> childBoneIndices = new List <int>();

                    foreach (var c in boneNode.Children)
                    {
                        int cIndex = AddBone(c, boneNode, thisNodeAbsoluteMatrix);

                        //cIndex will be -1 if the child node was a DummyPoly instead of a bone.
                        if (cIndex >= 0)
                        {
                            childBoneIndices.Add(cIndex);
                        }
                    }

                    if (childBoneIndices.Count > 0)
                    {
                        flverBone.ChildIndex = (short)childBoneIndices[0];

                        for (int i = 0; i < childBoneIndices.Count; i++)
                        {
                            var thisChildBone = skel.Bones[childBoneIndices[i]];
                            if (i == 0)
                            {
                                thisChildBone.PreviousSiblingIndex = -1;
                            }
                            else
                            {
                                thisChildBone.PreviousSiblingIndex = (short)(childBoneIndices[i - 1]);
                            }

                            if (i == childBoneIndices.Count - 1)
                            {
                                thisChildBone.NextSiblingIndex = -1;
                            }
                            else
                            {
                                thisChildBone.NextSiblingIndex = (short)(childBoneIndices[i + 1]);
                            }
                        }
                    }

                    return(thisBoneIndex);
                }
            }

            //if (rootNode.Children == null)
            //    throw new InvalidDataException("Assimp scene has no heirarchy.");

            var root = rootNode;

            //var master = root.Children[0];
            //foreach (var c in root.Children)
            //{
            //    AddBone(c, null, root.Transform * rootNodeAbsoluteMatrix);
            //}
            AddBone(root, null, rootNodeAbsoluteMatrix);
            // Apply parent bone transforms to DummyPoly
            foreach (var d in skel.DummyPoly)
            {
                if (d.ParentBoneIndex >= 0)
                {
                    var parentMat = skel.Bones[d.ParentBoneIndex].GetAbsoluteNMatrix(skel.Bones);
                    d.Position = NVector3.Transform(d.Position, parentMat);
                    d.Upward   = NVector3.TransformNormal(d.Upward, parentMat);
                    d.Forward  = NVector3.TransformNormal(d.Forward, parentMat);
                }
            }

            return(skel);
        }