Skip to content

Commit fb062fa

Browse files
authored
Skin hierarchy (#1026)
* Skin_hierarchy_check Check the skeleton hierarchy to see if any ancestor has the skin as children. The goal is to choose which transformation matrix to apply at the root of the skeleton (World or Local). * Add comment * Change Default Tuple syntax to Tuple to retro-compatibilty
1 parent 3f50c8f commit fb062fa

File tree

2 files changed

+60
-18
lines changed

2 files changed

+60
-18
lines changed

3ds Max/Max2Babylon/Exporter/BabylonExporter.Mesh.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ private BabylonNode ExportMasterMesh(IIGameScene scene, IIGameNode meshNode, Bab
236236
IGMatrix skinInitPoseMatrix = Loader.Global.GMatrix.Create(Loader.Global.Matrix3.Create(true));
237237
List<int> boneIds = null;
238238
int maxNbBones = 0;
239-
List<IIGameNode> skinnedBones = GetSkinnedBones(skin);
239+
List<IIGameNode> skinnedBones = GetSkinnedBones(skin).Item1;
240240
if (isSkinned && skinnedBones.Count > 0) // if the mesh has a skin with at least one bone
241241
{
242242
var skinAlreadyStored = skins.Find(_skin => IsSkinEqualTo(_skin, skin));

3ds Max/Max2Babylon/Exporter/BabylonExporter.Skeleton.cs

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ private List<IIGameNode> GetBones(IIGameSkin skin)
5050
/// <returns>
5151
/// All nodes needed for the skeleton hierarchy
5252
/// </returns>
53-
private Dictionary<IIGameSkin, List<IIGameNode>> relevantNodesBySkin = new Dictionary<IIGameSkin, List<IIGameNode>>();
54-
private List<IIGameNode> GetSkinnedBones(IIGameSkin skin)
53+
private Dictionary<IIGameSkin, Tuple<List<IIGameNode>, float[]>> relevantNodesBySkin = new Dictionary<IIGameSkin, Tuple<List<IIGameNode>, float[]>>();
54+
private Tuple<List<IIGameNode>, float[]> GetSkinnedBones(IIGameSkin skin)
5555
{
5656

5757
if (skin == null)
5858
{
59-
return new List<IIGameNode>();
59+
return new Tuple<List<IIGameNode>, float[]>(new List<IIGameNode>(),null);
6060
}
6161

6262
int logRank = 2;
@@ -72,14 +72,14 @@ private List<IIGameNode> GetSkinnedBones(IIGameSkin skin)
7272
if (bones.Count == 0)
7373
{
7474
RaiseWarning("Skin has no bones.", logRank);
75-
return new List<IIGameNode>();
75+
return new Tuple<List<IIGameNode>, float[]>(new List<IIGameNode>(), null);
7676
}
7777

7878
if (bones.Contains(null))
7979
{
8080
RaiseError("Skin has bones that are outside of the exported hierarchy.", logRank);
8181
RaiseError("The skin cannot be exported", logRank);
82-
return new List<IIGameNode>();
82+
return new Tuple<List<IIGameNode>, float[]>(new List<IIGameNode>(), null);
8383
}
8484

8585
List<IIGameNode> allHierarchyNodes = null;
@@ -90,18 +90,31 @@ private List<IIGameNode> GetSkinnedBones(IIGameSkin skin)
9090
RaiseError($"More than one root node for the skin. The skeleton bones need to be part of the same hierarchy.", logRank);
9191
RaiseError($"The skin cannot be exported", logRank);
9292

93-
return new List<IIGameNode>();
93+
return new Tuple<List<IIGameNode>, float[]>(new List<IIGameNode>(), null);
9494
}
9595

96-
// 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.
9796
allHierarchyNodes.Add(lowestCommonAncestor);
97+
98+
float[] rootTransformation = null;
99+
100+
// 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.
98101
if (lowestCommonAncestor.NodeParent != null)
99102
{
100-
do {
103+
do
104+
{
105+
// we need to check if the ancestor has the current skin as child (anywhere down the hierarchy)
106+
// in this case, we stop to stack commonAncestor and we set the root transformation Matrix as Local.
107+
if(HasSkinAsChild(lowestCommonAncestor.NodeParent, skin))
108+
{
109+
rootTransformation = lowestCommonAncestor.GetLocalTM(0).ToArray();
110+
break;
111+
}
101112
lowestCommonAncestor = lowestCommonAncestor.NodeParent;
102113
allHierarchyNodes.Add(lowestCommonAncestor);
103-
} while (lowestCommonAncestor.NodeParent != null) ;
104-
}
114+
} while (lowestCommonAncestor.NodeParent != null);
115+
}
116+
117+
rootTransformation = rootTransformation ?? lowestCommonAncestor.GetWorldTM(0).ToArray();
105118

106119
// starting from the root, sort the nodes by depth first (add the children before the siblings)
107120
List<IIGameNode> sorted = new List<IIGameNode>();
@@ -133,10 +146,11 @@ private List<IIGameNode> GetSkinnedBones(IIGameSkin skin)
133146
}
134147
}
135148
}
149+
var result = new Tuple<List<IIGameNode>, float[]>(sorted, rootTransformation);
150+
151+
relevantNodesBySkin.Add(skin, result); // Stock the result for optimization
136152

137-
relevantNodesBySkin.Add(skin, sorted); // Stock the result for optimization
138-
139-
return sorted;
153+
return result;
140154
}
141155

142156
private IIGameNode GetLowestCommonAncestor(List<IIGameNode> nodes, ref List<IIGameNode> allHierarchyNodes)
@@ -169,6 +183,32 @@ private IIGameNode GetLowestCommonAncestor(List<IIGameNode> nodes, ref List<IIGa
169183
return commonAncestor;
170184
}
171185

186+
// fetch recursively the childrens to see if any of it, reference the skin.
187+
// the fetch stop at the first node passing the test.
188+
// down search is deep first.
189+
private bool HasSkinAsChild(IIGameNode node, IIGameSkin skin)
190+
{
191+
if( skin == null)
192+
{
193+
return false;
194+
}
195+
196+
if(node.IGameObject.IGameSkin != null && skin.Equals(node.IGameObject.IGameSkin))
197+
{
198+
return true;
199+
}
200+
201+
for( var i=0; i != node.ChildCount; i++)
202+
{
203+
var n = node.GetNodeChild(i);
204+
if (HasSkinAsChild(n, skin)){
205+
return true;
206+
}
207+
}
208+
return false;
209+
}
210+
211+
172212
private IIGameNode GetLowestCommonAncestor(IIGameNode nodeA, IIGameNode nodeB, List<IIGameNode> nodeHierarchyA = null, List<IIGameNode> nodeHierarchyB = null)
173213
{
174214
if (nodeA == nodeB || nodeB == null) return nodeA;
@@ -272,7 +312,7 @@ private List<int> GetNodeIndices(IIGameSkin skin)
272312
}
273313

274314
List<int> nodeIndex = new List<int>();
275-
List<IIGameNode> revelantNodes = GetSkinnedBones(skin);
315+
List<IIGameNode> revelantNodes = GetSkinnedBones(skin).Item1;
276316

277317
for (int index = 0; index < revelantNodes.Count; index++)
278318
{
@@ -316,9 +356,11 @@ private BabylonBone[] ExportBones(IIGameSkin skin)
316356
{
317357
List<BabylonBone> bones = new List<BabylonBone>();
318358
List<int> nodeIndices = GetNodeIndices(skin);
319-
List<IIGameNode> revelantNodes = GetSkinnedBones(skin);
359+
Tuple<List<IIGameNode>,float[]> revelantNodes = GetSkinnedBones(skin);
360+
361+
var rootMatrix = revelantNodes.Item2;
320362

321-
foreach (IIGameNode node in revelantNodes)
363+
foreach (IIGameNode node in revelantNodes.Item1)
322364
{
323365
int parentIndex = (node.NodeParent == null) ? -1 : nodeIndices.IndexOf(node.NodeParent.NodeID);
324366

@@ -331,7 +373,7 @@ private BabylonBone[] ExportBones(IIGameSkin skin)
331373
name = node.Name,
332374
index = nodeIndices.IndexOf(node.NodeID),
333375
parentBoneIndex = parentIndex,
334-
matrix = (parentIndex==-1)?node.GetWorldTM(0).ToArray():node.GetLocalTM(0).ToArray()
376+
matrix = (parentIndex == -1) ? rootMatrix : node.GetLocalTM(0).ToArray()
335377
};
336378

337379
// Apply unit conversion factor to meter

0 commit comments

Comments
 (0)