Skip to content

Commit

Permalink
Update root bone animation matrix (#1027)
Browse files Browse the repository at this point in the history
Update root bone animation matrix according the on set into the bone
  • Loading branch information
pandaGaume committed Feb 5, 2022
1 parent fb062fa commit 1bf7a27
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 19 deletions.
1 change: 1 addition & 0 deletions 3ds Max/Max2Babylon/Exporter/BabylonExporter.Animation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Utilities;

namespace Max2Babylon
{
Expand Down
49 changes: 31 additions & 18 deletions 3ds Max/Max2Babylon/Exporter/BabylonExporter.Skeleton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,24 @@
using System.Collections.Generic;
using Autodesk.Max;
using BabylonExport.Entities;
using Utilities;


namespace Max2Babylon
{
using BoneSearchResult = Tuple<List<IIGameNode>, RootBoneTransformationType>;

internal enum RootBoneTransformationType
{
unknown, local,world
}



partial class BabylonExporter
{
internal static readonly BoneSearchResult EmptyBoneSearchResult = new BoneSearchResult(new List<IIGameNode>(), RootBoneTransformationType.unknown);

readonly List<IIGameSkin> skins = new List<IIGameSkin>();

private bool IsSkinEqualTo(IIGameSkin skin1, IIGameSkin skin2)
Expand Down Expand Up @@ -50,13 +63,13 @@ private List<IIGameNode> GetBones(IIGameSkin skin)
/// <returns>
/// All nodes needed for the skeleton hierarchy
/// </returns>
private Dictionary<IIGameSkin, Tuple<List<IIGameNode>, float[]>> relevantNodesBySkin = new Dictionary<IIGameSkin, Tuple<List<IIGameNode>, float[]>>();
private Tuple<List<IIGameNode>, float[]> GetSkinnedBones(IIGameSkin skin)
private Dictionary<IIGameSkin, BoneSearchResult> relevantNodesBySkin = new Dictionary<IIGameSkin, BoneSearchResult>();
private Tuple<List<IIGameNode>, RootBoneTransformationType> GetSkinnedBones(IIGameSkin skin)
{

if (skin == null)
{
return new Tuple<List<IIGameNode>, float[]>(new List<IIGameNode>(),null);
return EmptyBoneSearchResult;
}

int logRank = 2;
Expand All @@ -72,14 +85,14 @@ private List<IIGameNode> GetBones(IIGameSkin skin)
if (bones.Count == 0)
{
RaiseWarning("Skin has no bones.", logRank);
return new Tuple<List<IIGameNode>, float[]>(new List<IIGameNode>(), null);
return EmptyBoneSearchResult;
}

if (bones.Contains(null))
{
RaiseError("Skin has bones that are outside of the exported hierarchy.", logRank);
RaiseError("The skin cannot be exported", logRank);
return new Tuple<List<IIGameNode>, float[]>(new List<IIGameNode>(), null);
return EmptyBoneSearchResult;
}

List<IIGameNode> allHierarchyNodes = null;
Expand All @@ -90,12 +103,12 @@ private List<IIGameNode> GetBones(IIGameSkin skin)
RaiseError($"More than one root node for the skin. The skeleton bones need to be part of the same hierarchy.", logRank);
RaiseError($"The skin cannot be exported", logRank);

return new Tuple<List<IIGameNode>, float[]>(new List<IIGameNode>(), null);
return EmptyBoneSearchResult;
}

allHierarchyNodes.Add(lowestCommonAncestor);

float[] rootTransformation = null;
RootBoneTransformationType tt = RootBoneTransformationType.world;

// Babylon format assumes skeleton root is at origin, add any additional node parents from the lowest common ancestor to the scene root to the skeleton hierarchy.
if (lowestCommonAncestor.NodeParent != null)
Expand All @@ -106,16 +119,14 @@ private List<IIGameNode> GetBones(IIGameSkin skin)
// in this case, we stop to stack commonAncestor and we set the root transformation Matrix as Local.
if(HasSkinAsChild(lowestCommonAncestor.NodeParent, skin))
{
rootTransformation = lowestCommonAncestor.GetLocalTM(0).ToArray();
tt = RootBoneTransformationType.local;
break;
}
lowestCommonAncestor = lowestCommonAncestor.NodeParent;
allHierarchyNodes.Add(lowestCommonAncestor);
} while (lowestCommonAncestor.NodeParent != null);
}

rootTransformation = rootTransformation ?? lowestCommonAncestor.GetWorldTM(0).ToArray();

// starting from the root, sort the nodes by depth first (add the children before the siblings)
List<IIGameNode> sorted = new List<IIGameNode>();
Stack<IIGameNode> siblings = new Stack<IIGameNode>(); // keep the siblings in a LIFO list to add them after the children
Expand Down Expand Up @@ -146,7 +157,7 @@ private List<IIGameNode> GetBones(IIGameSkin skin)
}
}
}
var result = new Tuple<List<IIGameNode>, float[]>(sorted, rootTransformation);
var result = new BoneSearchResult(sorted, tt);

relevantNodesBySkin.Add(skin, result); // Stock the result for optimization

Expand Down Expand Up @@ -356,7 +367,7 @@ private BabylonBone[] ExportBones(IIGameSkin skin)
{
List<BabylonBone> bones = new List<BabylonBone>();
List<int> nodeIndices = GetNodeIndices(skin);
Tuple<List<IIGameNode>,float[]> revelantNodes = GetSkinnedBones(skin);
BoneSearchResult revelantNodes = GetSkinnedBones(skin);

var rootMatrix = revelantNodes.Item2;

Expand All @@ -365,15 +376,19 @@ private BabylonBone[] ExportBones(IIGameSkin skin)
int parentIndex = (node.NodeParent == null) ? -1 : nodeIndices.IndexOf(node.NodeParent.NodeID);

string boneId = node.MaxNode.GetGuid().ToString();

// this function is select the correct matrix depending hierarchy case
Func<int, float[]> getMatrix = (i) => parentIndex == -1 ? (revelantNodes.Item2 == RootBoneTransformationType.world ? node.GetWorldTM(i).ToArray() : node.GetLocalTM(i).ToArray()) : node.GetLocalTM(i).ToArray();

// create the bone
BabylonBone bone = new BabylonBone()
{
id = (isGltfExported)?boneId:boneId + "-bone",// the suffix "-bone" is added in babylon export format to assure the uniqueness of IDs
parentNodeId = (parentIndex!=-1)?node.NodeParent.MaxNode.GetGuid().ToString():null,
id = (isGltfExported) ? boneId : boneId + "-bone",// the suffix "-bone" is added in babylon export format to assure the uniqueness of IDs
parentNodeId = (parentIndex != -1) ? node.NodeParent.MaxNode.GetGuid().ToString() : null,
name = node.Name,
index = nodeIndices.IndexOf(node.NodeID),
parentBoneIndex = parentIndex,
matrix = (parentIndex == -1) ? rootMatrix : node.GetLocalTM(0).ToArray()
matrix = MathUtilities.CleanEpsilon(getMatrix(0))
};

// Apply unit conversion factor to meter
Expand All @@ -387,9 +402,7 @@ private BabylonBone[] ExportBones(IIGameSkin skin)
// export its animation
var babylonAnimation = ExportMatrixAnimation("_matrix", key =>
{
IGMatrix mat = node.GetLocalTM(key);
float[] matrix = mat.ToArray();
float[] matrix = MathUtilities.CleanEpsilon(getMatrix(key));
// Apply unit conversion factor to meter
// Affect translation only
Expand Down
12 changes: 11 additions & 1 deletion SharedProjects/BabylonExport.Entities/BabylonAnimationKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ namespace BabylonExport.Entities
public class BabylonAnimationKey : IComparable<BabylonAnimationKey>, ICloneable
{
private float _f;
private float[] _values;

[DataMember]
// guard for negative value.
public float frame { get => _f; set => _f = value < 0 ? 0 : value; }

[DataMember]
public float[] values { get; set; }
public float[] values {
get
{
return _values;
}
set
{
_values = MathUtilities.CleanEpsilon(value);
}
}

public object Clone()
{
Expand Down
2 changes: 2 additions & 0 deletions SharedProjects/BabylonExport.Entities/BabylonMatrix.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Utilities;

namespace BabylonExport.Entities
{
Expand Down Expand Up @@ -358,5 +359,6 @@ public BabylonVector3 TransformCoordinatesFromFloatsToRef(float x, float y, floa
result.Z = rz;
return result;
}

}
}
11 changes: 11 additions & 0 deletions SharedProjects/Utilities/MathUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,5 +91,16 @@ public static BabylonMatrix ComputeTextureTransformMatrix(BabylonVector3 pivotCe
.multiply(BabylonMatrix.Translation(pivotCenter));
return transformMatrix;
}


public static float[] CleanEpsilon(float[] data)
{
for (int i = 0; i != data.Length; i++)
{
data[i] = Math.Abs(data[i]) <= MathUtilities.Epsilon ? 0 : data[i];
data[i] = Math.Abs(1.0 - data[i]) <= MathUtilities.Epsilon ? 1 : data[i];
}
return data;
}
}
}

0 comments on commit 1bf7a27

Please sign in to comment.