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; }
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)); } }
// 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); }
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;
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++; }
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; }
/// <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); }
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); }
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 {} }
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); }
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); }
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); }