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 Ai.Mesh ConvertGeometry(Scene scene, Node geometryNode, Geometry geometry) { var aiMesh = new Ai.Mesh(Ai.PrimitiveType.Triangle); if (geometry.Flags.HasFlag(GeometryFlags.HasMaterial)) { aiMesh.MaterialIndex = mAiScene.Materials.FindIndex(x => x.Name == AssimpConverterCommon.EscapeName(geometry.MaterialName)); } if (geometry.Flags.HasFlag(GeometryFlags.HasTriangles)) { foreach (var triangle in geometry.Triangles) { var aiFace = new Ai.Face(); aiFace.Indices.Add(( int )triangle.A); aiFace.Indices.Add(( int )triangle.B); aiFace.Indices.Add(( int )triangle.C); aiMesh.Faces.Add(aiFace); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.Position)) { foreach (var vertex in geometry.Vertices) { aiMesh.Vertices.Add(new Ai.Vector3D(vertex.X, vertex.Y, vertex.Z)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.Normal)) { foreach (var normal in geometry.Normals) { aiMesh.Normals.Add(new Ai.Vector3D(normal.X, normal.Y, normal.Z)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.Tangent)) { foreach (var tangent in geometry.Tangents) { aiMesh.Tangents.Add(new Ai.Vector3D(tangent.X, tangent.Y, tangent.Z)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.Binormal)) { foreach (var binormal in geometry.Binormals) { aiMesh.BiTangents.Add(new Ai.Vector3D(binormal.X, binormal.Y, binormal.Z)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.TexCoord0)) { foreach (var vertex in geometry.TexCoordsChannel0) { aiMesh.TextureCoordinateChannels[0].Add(new Ai.Vector3D(vertex.X, vertex.Y, 0)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.TexCoord1)) { foreach (var vertex in geometry.TexCoordsChannel1) { aiMesh.TextureCoordinateChannels[1].Add(new Ai.Vector3D(vertex.X, vertex.Y, 0)); } } if (geometry.VertexAttributeFlags.HasFlag(VertexAttributeFlags.TexCoord2)) { foreach (var vertex in geometry.TexCoordsChannel2) { aiMesh.TextureCoordinateChannels[2].Add(new Ai.Vector3D(vertex.X, vertex.Y, 0)); } } /* todo: colors * if ( geometry.VertexAttributeFlags.HasFlag( VertexAttributeFlags.Color0 ) ) * { * foreach ( var color in geometry.ColorChannel0 ) * { * aiMesh.VertexColorChannels[0].Add( new Ai.Color4D( color. )) * } * } */ if (geometry.Flags.HasFlag(GeometryFlags.HasVertexWeights)) { var boneMap = new Dictionary <int, Ai.Bone>(); for (int i = 0; i < geometry.VertexWeights.Length; i++) { var vertexWeight = geometry.VertexWeights[i]; for (int j = 0; j < 4; j++) { var boneWeight = vertexWeight.Weights[j]; if (boneWeight == 0f) { continue; } var boneIndex = vertexWeight.Indices[j]; var nodeIndex = scene.BonePalette.BoneToNodeIndices[boneIndex]; if (!boneMap.ContainsKey(nodeIndex)) { var aiBone = new Ai.Bone(); var boneNode = scene.GetNode(nodeIndex); aiBone.Name = AssimpConverterCommon.EscapeName(boneNode.Name); aiBone.VertexWeights.Add(new Ai.VertexWeight(i, boneWeight)); Matrix4x4.Invert(geometryNode.WorldTransform, out Matrix4x4 invGeometryNodeWorldTransform); Matrix4x4.Invert(boneNode.WorldTransform * invGeometryNodeWorldTransform, out Matrix4x4 offsetMatrix); aiBone.OffsetMatrix = new Ai.Matrix4x4(offsetMatrix.M11, offsetMatrix.M21, offsetMatrix.M31, offsetMatrix.M41, offsetMatrix.M12, offsetMatrix.M22, offsetMatrix.M32, offsetMatrix.M42, offsetMatrix.M13, offsetMatrix.M23, offsetMatrix.M33, offsetMatrix.M43, offsetMatrix.M14, offsetMatrix.M24, offsetMatrix.M34, offsetMatrix.M44); boneMap[nodeIndex] = aiBone; } else { boneMap[nodeIndex].VertexWeights.Add(new Ai.VertexWeight(i, boneWeight)); } } } aiMesh.Bones.AddRange(boneMap.Values); } return(aiMesh); }
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); }