Skip to content

Commit 253701d

Browse files
authored
Merge branch 'master' into drigax/fixEulerToQuaternionConversion
2 parents f9ff6bc + 3364eb0 commit 253701d

16 files changed

+4198
-140
lines changed

3ds Max/Max2Babylon/Exporter/AnimationGroup.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ public class AnimationGroupList : List<AnimationGroup>
256256
{
257257
const string s_AnimationListPropertyName = "babylonjs_AnimationList";
258258

259+
public AnimationGroup GetAnimationGroupByName(string name)
260+
{
261+
return this.First(animationGroup => animationGroup.Name == name);
262+
}
263+
259264
public void LoadFromData()
260265
{
261266
string[] animationPropertyNames = Loader.Core.RootNode.GetStringArrayProperty(s_AnimationListPropertyName);

3ds Max/Max2Babylon/Exporter/BabylonExporter.GLTFExporter.AbstractMesh.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using BabylonExport.Entities;
22
using GLTFExport.Entities;
33
using System.Collections.Generic;
4+
using System.Linq;
45

56
namespace Max2Babylon
67
{
@@ -25,9 +26,9 @@ private GLTFNode ExportAbstractMesh(ref GLTFNode gltfNode, BabylonAbstractMesh b
2526
if (gltfMesh.idBabylonSkeleton.HasValue)
2627
{
2728
var babylonSkeleton = babylonScene.skeletons[gltfMesh.idBabylonSkeleton.Value];
28-
// Export a new skeleton if necessary and a new skin
29-
var gltfSkin = ExportSkin(babylonSkeleton, gltf, gltfNode);
30-
gltfNode.skin = gltfSkin.index;
29+
// Export a new skeleton if necessary and a new skin
30+
var gltfSkin = ExportSkin(babylonSkeleton, gltf, gltfNode, gltfMesh);
31+
gltfNode.skin = gltfSkin.index;
3132
}
3233
}
3334

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

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ namespace Max2Babylon
88
{
99
partial class BabylonExporter
1010
{
11+
private List<BabylonMesh> alreadyExportedSkinnedMeshes = new List<BabylonMesh>();
12+
13+
// Meshes that share skinning information, indexed by the exported mesh with the original skinning information.
14+
private Dictionary<GLTFMesh, List<GLTFMesh>> sharedSkinnedMeshesByOriginal = new Dictionary<GLTFMesh, List<GLTFMesh>>();
1115
private GLTFMesh ExportMesh(BabylonMesh babylonMesh, GLTF gltf, BabylonScene babylonScene)
1216
{
1317
RaiseMessage("GLTFExporter.Mesh | Export mesh named: " + babylonMesh.name, 1);
@@ -346,34 +350,52 @@ private GLTFMesh ExportMesh(BabylonMesh babylonMesh, GLTF gltf, BabylonScene bab
346350
// --- Bones ---
347351
if (hasBones)
348352
{
349-
RaiseMessage("GLTFExporter.Mesh | Bones", 3);
350-
// --- Joints ---
351-
var accessorJoints = GLTFBufferService.Instance.CreateAccessor(
352-
gltf,
353-
GLTFBufferService.Instance.GetBufferViewUnsignedShortVec4(gltf, buffer),
354-
"accessorJoints",
355-
GLTFAccessor.ComponentType.UNSIGNED_SHORT,
356-
GLTFAccessor.TypeEnum.VEC4
357-
);
358-
meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.JOINTS_0.ToString(), accessorJoints.index);
359-
// Populate accessor
360-
List<ushort> joints = globalVerticesSubMesh.SelectMany(v => new[] { v.BonesIndices[0], v.BonesIndices[1], v.BonesIndices[2], v.BonesIndices[3] }).ToList();
361-
joints.ForEach(n => accessorJoints.bytesList.AddRange(BitConverter.GetBytes(n)));
362-
accessorJoints.count = globalVerticesSubMesh.Count;
363-
364-
// --- Weights ---
365-
var accessorWeights = GLTFBufferService.Instance.CreateAccessor(
366-
gltf,
367-
GLTFBufferService.Instance.GetBufferViewFloatVec4(gltf, buffer),
368-
"accessorWeights",
369-
GLTFAccessor.ComponentType.FLOAT,
370-
GLTFAccessor.TypeEnum.VEC4
371-
);
372-
meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.WEIGHTS_0.ToString(), accessorWeights.index);
373-
// Populate accessor
374-
List<float> weightBones = globalVerticesSubMesh.SelectMany(v => new[] { v.BonesWeights[0], v.BonesWeights[1], v.BonesWeights[2], v.BonesWeights[3] }).ToList();
375-
weightBones.ForEach(n => accessorWeights.bytesList.AddRange(BitConverter.GetBytes(n)));
376-
accessorWeights.count = globalVerticesSubMesh.Count;
353+
RaiseMessage("GLTFExporter.Mesh | Bones", 3);
354+
355+
// if we've already exported this mesh's skeleton, check if the skins match,
356+
// if so then export this mesh primitive to share joint and weight accessors.
357+
var matchingSkinnedMesh = alreadyExportedSkinnedMeshes.FirstOrDefault(skinnedMesh => skinnedMesh.skeletonId == babylonMesh.skeletonId);
358+
if (matchingSkinnedMesh != null && BabylonMesh.MeshesShareSkin(matchingSkinnedMesh, babylonMesh))
359+
{
360+
var tmpGltfMesh = gltf.MeshesList.FirstOrDefault(mesh => matchingSkinnedMesh.name == mesh.name);
361+
var tmpGltfMeshPrimitive = tmpGltfMesh.primitives.First();
362+
363+
meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.JOINTS_0.ToString(), tmpGltfMeshPrimitive.attributes[GLTFMeshPrimitive.Attribute.JOINTS_0.ToString()]);
364+
meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.WEIGHTS_0.ToString(), tmpGltfMeshPrimitive.attributes[GLTFMeshPrimitive.Attribute.WEIGHTS_0.ToString()]);
365+
sharedSkinnedMeshesByOriginal[tmpGltfMesh].Add(gltfMesh);
366+
}
367+
else
368+
{
369+
// Create new joint and weight accessors for this mesh's skinning.
370+
// --- Joints ---
371+
sharedSkinnedMeshesByOriginal[gltfMesh] = new List<GLTFMesh>();
372+
var accessorJoints = GLTFBufferService.Instance.CreateAccessor(
373+
gltf,
374+
GLTFBufferService.Instance.GetBufferViewUnsignedShortVec4(gltf, buffer),
375+
"accessorJoints",
376+
GLTFAccessor.ComponentType.UNSIGNED_SHORT,
377+
GLTFAccessor.TypeEnum.VEC4
378+
);
379+
meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.JOINTS_0.ToString(), accessorJoints.index);
380+
// Populate accessor
381+
List<ushort> joints = globalVerticesSubMesh.SelectMany(v => new[] { v.BonesIndices[0], v.BonesIndices[1], v.BonesIndices[2], v.BonesIndices[3] }).ToList();
382+
joints.ForEach(n => accessorJoints.bytesList.AddRange(BitConverter.GetBytes(n)));
383+
accessorJoints.count = globalVerticesSubMesh.Count;
384+
385+
// --- Weights ---
386+
var accessorWeights = GLTFBufferService.Instance.CreateAccessor(
387+
gltf,
388+
GLTFBufferService.Instance.GetBufferViewFloatVec4(gltf, buffer),
389+
"accessorWeights",
390+
GLTFAccessor.ComponentType.FLOAT,
391+
GLTFAccessor.TypeEnum.VEC4
392+
);
393+
meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.WEIGHTS_0.ToString(), accessorWeights.index);
394+
// Populate accessor
395+
List<float> weightBones = globalVerticesSubMesh.SelectMany(v => new[] { v.BonesWeights[0], v.BonesWeights[1], v.BonesWeights[2], v.BonesWeights[3] }).ToList();
396+
weightBones.ForEach(n => accessorWeights.bytesList.AddRange(BitConverter.GetBytes(n)));
397+
accessorWeights.count = globalVerticesSubMesh.Count;
398+
}
377399
}
378400

379401
// Morph targets positions and normals
@@ -394,8 +416,13 @@ private GLTFMesh ExportMesh(BabylonMesh babylonMesh, GLTF gltf, BabylonScene bab
394416
weights.Add(babylonMorphTarget.influence);
395417
}
396418
gltfMesh.weights = weights.ToArray();
397-
}
398-
419+
}
420+
421+
if (hasBones)
422+
{
423+
alreadyExportedSkinnedMeshes.Add(babylonMesh);
424+
}
425+
399426
return gltfMesh;
400427
}
401428

3ds Max/Max2Babylon/Exporter/BabylonExporter.GLTFExporter.Skin.cs

Lines changed: 110 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ partial class BabylonExporter
1313
// This dictionary is reset everytime a scene is exported
1414
private Dictionary<BabylonSkeleton, BabylonSkeletonExportData> alreadyExportedSkeletons = new Dictionary<BabylonSkeleton, BabylonSkeletonExportData>();
1515

16-
private GLTFSkin ExportSkin(BabylonSkeleton babylonSkeleton, GLTF gltf, GLTFNode gltfNode)
16+
private GLTFSkin ExportSkin(BabylonSkeleton babylonSkeleton, GLTF gltf, GLTFNode gltfNode, GLTFMesh gltfMesh)
1717
{
1818
RaiseMessage("GLTFExporter.Skin | Export skin of node '" + gltfNode.name + "' based on skeleton '" + babylonSkeleton.name + "'", 2);
1919

@@ -41,97 +41,110 @@ private GLTFSkin ExportSkin(BabylonSkeleton babylonSkeleton, GLTF gltf, GLTFNode
4141
babylonBone.matrix = boneLocalMatrix.m;
4242
}
4343
}
44-
var babylonSkeletonExportData = alreadyExportedSkeletons[babylonSkeleton];
45-
46-
// Skin
47-
var nameSuffix = babylonSkeletonExportData.nb != 0 ? "_" + babylonSkeletonExportData.nb : "";
48-
GLTFSkin gltfSkin = new GLTFSkin
49-
{
50-
name = babylonSkeleton.name + nameSuffix
51-
};
52-
gltfSkin.index = gltf.SkinsList.Count;
53-
gltf.SkinsList.Add(gltfSkin);
54-
babylonSkeletonExportData.nb++;
55-
56-
var bones = new List<BabylonBone>(babylonSkeleton.bones);
57-
58-
// Compute and store world matrix of each bone
59-
var bonesWorldMatrices = new Dictionary<int, BabylonMatrix>();
60-
foreach (var babylonBone in babylonSkeleton.bones)
61-
{
62-
if (!bonesWorldMatrices.ContainsKey(babylonBone.index))
63-
{
64-
BabylonMatrix boneWorldMatrix = _getBoneWorldMatrix(babylonBone, bones);
65-
bonesWorldMatrices.Add(babylonBone.index, boneWorldMatrix);
66-
}
67-
}
68-
69-
// Buffer
70-
var buffer = GLTFBufferService.Instance.GetBuffer(gltf);
71-
72-
// Accessor - InverseBindMatrices
73-
var accessorInverseBindMatrices = GLTFBufferService.Instance.CreateAccessor(
74-
gltf,
75-
GLTFBufferService.Instance.GetBufferViewFloatMat4(gltf, buffer),
76-
"accessorInverseBindMatrices",
77-
GLTFAccessor.ComponentType.FLOAT,
78-
GLTFAccessor.TypeEnum.MAT4
79-
);
80-
gltfSkin.inverseBindMatrices = accessorInverseBindMatrices.index;
81-
82-
// World matrix of the node
83-
var nodeWorldMatrix = _getNodeWorldMatrix(gltfNode);
84-
85-
var gltfJoints = new List<int>();
86-
87-
foreach (var babylonBone in babylonSkeleton.bones)
88-
{
89-
GLTFNode gltfBoneNode = null;
90-
if (!babylonSkeletonExportData.nodeByBone.ContainsKey(babylonBone))
91-
{
92-
// Export bone as a new node
93-
gltfBoneNode = _exportBone(babylonBone, gltf, babylonSkeleton, bones);
94-
babylonSkeletonExportData.nodeByBone.Add(babylonBone, gltfBoneNode);
95-
}
96-
gltfBoneNode = babylonSkeletonExportData.nodeByBone[babylonBone];
97-
98-
gltfJoints.Add(gltfBoneNode.index);
99-
100-
// Set this bone as skeleton if it is a root
101-
// Meaning of 'skeleton' here is the top root bone
102-
if (babylonBone.parentBoneIndex == -1)
103-
{
104-
gltfSkin.skeleton = gltfBoneNode.index;
105-
}
106-
107-
// Compute inverseBindMatrice for this bone when attached to this node
108-
var boneLocalMatrix = new BabylonMatrix();
109-
boneLocalMatrix.m = babylonBone.matrix;
110-
//printMatrix("boneLocalMatrix[" + babylonBone.name + "]", boneLocalMatrix);
111-
112-
BabylonMatrix boneWorldMatrix = null;
113-
if (babylonBone.parentBoneIndex == -1)
114-
{
115-
boneWorldMatrix = boneLocalMatrix;
116-
}
117-
else
118-
{
119-
var parentWorldMatrix = bonesWorldMatrices[babylonBone.parentBoneIndex];
120-
// Remove scale of parent
121-
// This actually enable to take into account the scale of the bones, except for the root one
122-
parentWorldMatrix = _removeScale(parentWorldMatrix);
123-
124-
boneWorldMatrix = boneLocalMatrix * parentWorldMatrix;
125-
}
126-
//printMatrix("boneWorldMatrix[" + babylonBone.name + "]", boneWorldMatrix);
127-
128-
var inverseBindMatrices = nodeWorldMatrix * BabylonMatrix.Invert(boneWorldMatrix);
129-
130-
// Populate accessor
131-
List<float> matrix = new List<float>(inverseBindMatrices.m);
132-
matrix.ForEach(n => accessorInverseBindMatrices.bytesList.AddRange(BitConverter.GetBytes(n)));
133-
accessorInverseBindMatrices.count++;
134-
}
44+
var babylonSkeletonExportData = alreadyExportedSkeletons[babylonSkeleton];
45+
46+
// Skin
47+
48+
// if this mesh is sharing a skin with another mesh, use the already exported skin
49+
var sharedSkinnedMeshesByOriginalPair = sharedSkinnedMeshesByOriginal.Where(skinSharingMeshPair => skinSharingMeshPair.Value.Contains(gltfMesh)).Select(kvp => (KeyValuePair<GLTFMesh, List<GLTFMesh>>?) kvp).FirstOrDefault();
50+
if (sharedSkinnedMeshesByOriginalPair != null)
51+
{
52+
RaiseMessage("GLTFExporter.Skin | Sharing skinning information from mesh '" + sharedSkinnedMeshesByOriginalPair.Value.Key.name + "'", 3);
53+
var skeletonExportData = alreadyExportedSkeletons[babylonSkeleton];
54+
gltfNode.skin = skeletonExportData.skinIndex;
55+
return gltf.skins[(int)gltfNode.skin];
56+
}
57+
58+
// otherwise create a new GLTFSkin
59+
var nameSuffix = babylonSkeletonExportData.nb != 0 ? "_" + babylonSkeletonExportData.nb : "";
60+
GLTFSkin gltfSkin = new GLTFSkin
61+
{
62+
name = babylonSkeleton.name + nameSuffix
63+
};
64+
gltfSkin.index = gltf.SkinsList.Count;
65+
gltf.SkinsList.Add(gltfSkin);
66+
babylonSkeletonExportData.nb++;
67+
babylonSkeletonExportData.skinIndex = gltfSkin.index;
68+
69+
var bones = new List<BabylonBone>(babylonSkeleton.bones);
70+
71+
// Compute and store world matrix of each bone
72+
var bonesWorldMatrices = new Dictionary<int, BabylonMatrix>();
73+
foreach (var babylonBone in babylonSkeleton.bones)
74+
{
75+
if (!bonesWorldMatrices.ContainsKey(babylonBone.index))
76+
{
77+
BabylonMatrix boneWorldMatrix = _getBoneWorldMatrix(babylonBone, bones);
78+
bonesWorldMatrices.Add(babylonBone.index, boneWorldMatrix);
79+
}
80+
}
81+
82+
// Buffer
83+
var buffer = GLTFBufferService.Instance.GetBuffer(gltf);
84+
85+
// Accessor - InverseBindMatrices
86+
var accessorInverseBindMatrices = GLTFBufferService.Instance.CreateAccessor(
87+
gltf,
88+
GLTFBufferService.Instance.GetBufferViewFloatMat4(gltf, buffer),
89+
"accessorInverseBindMatrices",
90+
GLTFAccessor.ComponentType.FLOAT,
91+
GLTFAccessor.TypeEnum.MAT4
92+
);
93+
gltfSkin.inverseBindMatrices = accessorInverseBindMatrices.index;
94+
95+
// World matrix of the node
96+
var nodeWorldMatrix = _getNodeWorldMatrix(gltfNode);
97+
98+
var gltfJoints = new List<int>();
99+
100+
foreach (var babylonBone in babylonSkeleton.bones)
101+
{
102+
GLTFNode gltfBoneNode = null;
103+
if (!babylonSkeletonExportData.nodeByBone.ContainsKey(babylonBone))
104+
{
105+
// Export bone as a new node
106+
gltfBoneNode = _exportBone(babylonBone, gltf, babylonSkeleton, bones);
107+
babylonSkeletonExportData.nodeByBone.Add(babylonBone, gltfBoneNode);
108+
}
109+
gltfBoneNode = babylonSkeletonExportData.nodeByBone[babylonBone];
110+
111+
gltfJoints.Add(gltfBoneNode.index);
112+
113+
// Set this bone as skeleton if it is a root
114+
// Meaning of 'skeleton' here is the top root bone
115+
if (babylonBone.parentBoneIndex == -1)
116+
{
117+
gltfSkin.skeleton = gltfBoneNode.index;
118+
}
119+
120+
// Compute inverseBindMatrice for this bone when attached to this node
121+
var boneLocalMatrix = new BabylonMatrix();
122+
boneLocalMatrix.m = babylonBone.matrix;
123+
//printMatrix("boneLocalMatrix[" + babylonBone.name + "]", boneLocalMatrix);
124+
125+
BabylonMatrix boneWorldMatrix = null;
126+
if (babylonBone.parentBoneIndex == -1)
127+
{
128+
boneWorldMatrix = boneLocalMatrix;
129+
}
130+
else
131+
{
132+
var parentWorldMatrix = bonesWorldMatrices[babylonBone.parentBoneIndex];
133+
// Remove scale of parent
134+
// This actually enable to take into account the scale of the bones, except for the root one
135+
parentWorldMatrix = _removeScale(parentWorldMatrix);
136+
137+
boneWorldMatrix = boneLocalMatrix * parentWorldMatrix;
138+
}
139+
//printMatrix("boneWorldMatrix[" + babylonBone.name + "]", boneWorldMatrix);
140+
141+
var inverseBindMatrices = nodeWorldMatrix * BabylonMatrix.Invert(boneWorldMatrix);
142+
143+
// Populate accessor
144+
List<float> matrix = new List<float>(inverseBindMatrices.m);
145+
matrix.ForEach(n => accessorInverseBindMatrices.bytesList.AddRange(BitConverter.GetBytes(n)));
146+
accessorInverseBindMatrices.count++;
147+
}
135148
gltfSkin.joints = gltfJoints.ToArray();
136149

137150
return gltfSkin;
@@ -261,6 +274,11 @@ private class BabylonSkeletonExportData
261274
/// </summary>
262275
public int nb = 0;
263276

277+
/// <summary>
278+
/// Which skin index is used for this skeleton
279+
/// </summary>
280+
public int skinIndex = -1;
281+
264282
/// <summary>
265283
/// Each glTF bone is binded to a babylon bone
266284
/// </summary>

0 commit comments

Comments
 (0)