|
@@ -10,15 +10,15 @@ namespace Max2Babylon
|
|
{
|
|
{
|
|
partial class BabylonExporter
|
|
partial class BabylonExporter
|
|
{
|
|
{
|
|
- private GLTFMesh ExportMesh(BabylonMesh babylonMesh, GLTF gltf, GLTFNode gltfParentNode)
|
|
|
|
|
|
+ private GLTFMesh ExportMesh(BabylonMesh babylonMesh, GLTF gltf, GLTFNode gltfParentNode, BabylonScene babylonScene)
|
|
{
|
|
{
|
|
- RaiseMessage("GLTFExporter.Mesh | ExportMesh babylonMesh.name=" + babylonMesh.name, 1);
|
|
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | Export mesh named: " + babylonMesh.name, 1);
|
|
|
|
|
|
// --------------------------
|
|
// --------------------------
|
|
// ---------- Node ----------
|
|
// ---------- Node ----------
|
|
// --------------------------
|
|
// --------------------------
|
|
|
|
|
|
- RaiseMessage("GLTFExporter.Mesh | Node", 1);
|
|
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | Node", 2);
|
|
// Node
|
|
// Node
|
|
var gltfNode = new GLTFNode();
|
|
var gltfNode = new GLTFNode();
|
|
gltfNode.name = babylonMesh.name;
|
|
gltfNode.name = babylonMesh.name;
|
|
@@ -28,14 +28,14 @@ namespace Max2Babylon
|
|
// Hierarchy
|
|
// Hierarchy
|
|
if (gltfParentNode != null)
|
|
if (gltfParentNode != null)
|
|
{
|
|
{
|
|
- RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as child to " + gltfParentNode.name, 2);
|
|
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as child to " + gltfParentNode.name, 3);
|
|
gltfParentNode.ChildrenList.Add(gltfNode.index);
|
|
gltfParentNode.ChildrenList.Add(gltfNode.index);
|
|
}
|
|
}
|
|
else
|
|
else
|
|
{
|
|
{
|
|
// It's a root node
|
|
// It's a root node
|
|
// Only root nodes are listed in a gltf scene
|
|
// Only root nodes are listed in a gltf scene
|
|
- RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as root node to scene", 2);
|
|
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as root node to scene", 3);
|
|
gltf.scenes[0].NodesList.Add(gltfNode.index);
|
|
gltf.scenes[0].NodesList.Add(gltfNode.index);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -48,17 +48,13 @@ namespace Max2Babylon
|
|
else
|
|
else
|
|
{
|
|
{
|
|
// Convert rotation vector to quaternion
|
|
// Convert rotation vector to quaternion
|
|
- // TODO - Fix it
|
|
|
|
BabylonVector3 rotationVector3 = new BabylonVector3
|
|
BabylonVector3 rotationVector3 = new BabylonVector3
|
|
{
|
|
{
|
|
X = babylonMesh.rotation[0],
|
|
X = babylonMesh.rotation[0],
|
|
Y = babylonMesh.rotation[1],
|
|
Y = babylonMesh.rotation[1],
|
|
Z = babylonMesh.rotation[2]
|
|
Z = babylonMesh.rotation[2]
|
|
};
|
|
};
|
|
- gltfNode.rotation = rotationVector3.toQuaternion().ToArray();
|
|
|
|
-
|
|
|
|
- RaiseMessage("GLTFExporter.Mesh | rotationVector3=[" + rotationVector3.X + "; " + rotationVector3.Y + "; " + rotationVector3.Z + "]", 2);
|
|
|
|
- RaiseMessage("GLTFExporter.Mesh | gltfNode.rotation=[" + gltfNode.rotation[0] + "; " + gltfNode.rotation[1] + "; " + gltfNode.rotation[2] + "; " + gltfNode.rotation[3] + "]", 2);
|
|
|
|
|
|
+ gltfNode.rotation = rotationVector3.toQuaternionGltf().ToArray();
|
|
}
|
|
}
|
|
gltfNode.scale = babylonMesh.scaling;
|
|
gltfNode.scale = babylonMesh.scaling;
|
|
|
|
|
|
@@ -67,56 +63,59 @@ namespace Max2Babylon
|
|
// --- Mesh from babylon ----
|
|
// --- Mesh from babylon ----
|
|
// --------------------------
|
|
// --------------------------
|
|
|
|
|
|
- RaiseMessage("GLTFExporter.Mesh | Mesh from babylon", 1);
|
|
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | Mesh from babylon", 2);
|
|
// Retreive general data from babylon mesh
|
|
// Retreive general data from babylon mesh
|
|
int nbVertices = babylonMesh.positions.Length / 3;
|
|
int nbVertices = babylonMesh.positions.Length / 3;
|
|
|
|
+ bool isMultimaterial = babylonMesh.subMeshes.Length > 1;
|
|
bool hasUV = babylonMesh.uvs != null && babylonMesh.uvs.Length > 0;
|
|
bool hasUV = babylonMesh.uvs != null && babylonMesh.uvs.Length > 0;
|
|
bool hasUV2 = babylonMesh.uvs2 != null && babylonMesh.uvs2.Length > 0;
|
|
bool hasUV2 = babylonMesh.uvs2 != null && babylonMesh.uvs2.Length > 0;
|
|
bool hasColor = babylonMesh.colors != null && babylonMesh.colors.Length > 0;
|
|
bool hasColor = babylonMesh.colors != null && babylonMesh.colors.Length > 0;
|
|
|
|
|
|
- RaiseMessage("GLTFExporter.Mesh | nbVertices=" + nbVertices, 2);
|
|
|
|
- RaiseMessage("GLTFExporter.Mesh | hasUV=" + hasUV, 2);
|
|
|
|
- RaiseMessage("GLTFExporter.Mesh | hasUV2=" + hasUV2, 2);
|
|
|
|
- RaiseMessage("GLTFExporter.Mesh | hasColor=" + hasColor, 2);
|
|
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | nbVertices=" + nbVertices, 3);
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | isMultimaterial=" + isMultimaterial, 3);
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | hasUV=" + hasUV, 3);
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | hasUV2=" + hasUV2, 3);
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | hasColor=" + hasColor, 3);
|
|
|
|
|
|
// Retreive vertices data from babylon mesh
|
|
// Retreive vertices data from babylon mesh
|
|
List<GLTFGlobalVertex> globalVertices = new List<GLTFGlobalVertex>();
|
|
List<GLTFGlobalVertex> globalVertices = new List<GLTFGlobalVertex>();
|
|
- for (int i = 0; i < nbVertices; i++)
|
|
|
|
|
|
+ for (int indexVertex = 0; indexVertex < nbVertices; indexVertex++)
|
|
{
|
|
{
|
|
GLTFGlobalVertex globalVertex = new GLTFGlobalVertex();
|
|
GLTFGlobalVertex globalVertex = new GLTFGlobalVertex();
|
|
- globalVertex.Position = createIPoint3(babylonMesh.positions, i);
|
|
|
|
- globalVertex.Normal = createIPoint3(babylonMesh.normals, i);
|
|
|
|
|
|
+ globalVertex.Position = createIPoint3(babylonMesh.positions, indexVertex);
|
|
|
|
+ globalVertex.Normal = createIPoint3(babylonMesh.normals, indexVertex);
|
|
if (hasUV)
|
|
if (hasUV)
|
|
{
|
|
{
|
|
- globalVertex.UV = createIPoint2(babylonMesh.uvs, i);
|
|
|
|
|
|
+ globalVertex.UV = createIPoint2(babylonMesh.uvs, indexVertex);
|
|
// For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
|
|
// For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
|
|
// While for Babylon, it corresponds to the lower left corner of a texture image
|
|
// While for Babylon, it corresponds to the lower left corner of a texture image
|
|
globalVertex.UV.Y = 1 - globalVertex.UV.Y;
|
|
globalVertex.UV.Y = 1 - globalVertex.UV.Y;
|
|
}
|
|
}
|
|
if (hasUV2)
|
|
if (hasUV2)
|
|
{
|
|
{
|
|
- globalVertex.UV2 = createIPoint2(babylonMesh.uvs2, i);
|
|
|
|
|
|
+ globalVertex.UV2 = createIPoint2(babylonMesh.uvs2, indexVertex);
|
|
// For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
|
|
// For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image
|
|
// While for Babylon, it corresponds to the lower left corner of a texture image
|
|
// While for Babylon, it corresponds to the lower left corner of a texture image
|
|
globalVertex.UV2.Y = 1 - globalVertex.UV2.Y;
|
|
globalVertex.UV2.Y = 1 - globalVertex.UV2.Y;
|
|
}
|
|
}
|
|
if (hasColor)
|
|
if (hasColor)
|
|
{
|
|
{
|
|
- globalVertex.Color = createIPoint4(babylonMesh.colors, i).ToArray();
|
|
|
|
|
|
+ globalVertex.Color = createIPoint4(babylonMesh.colors, indexVertex).ToArray();
|
|
}
|
|
}
|
|
|
|
|
|
globalVertices.Add(globalVertex);
|
|
globalVertices.Add(globalVertex);
|
|
}
|
|
}
|
|
|
|
|
|
// Retreive indices from babylon mesh
|
|
// Retreive indices from babylon mesh
|
|
- List<ushort> indices = new List<ushort>();
|
|
|
|
- indices = babylonMesh.indices.ToList().ConvertAll(new Converter<int, ushort>(n => (ushort)n));
|
|
|
|
|
|
+ List<ushort> babylonIndices = new List<ushort>();
|
|
|
|
+ babylonIndices = babylonMesh.indices.ToList().ConvertAll(new Converter<int, ushort>(n => (ushort)n));
|
|
|
|
+ // For triangle primitives in gltf, the front face has a counter-clockwise (CCW) winding order
|
|
// Swap face side
|
|
// Swap face side
|
|
- for (int i = 0; i < indices.Count; i += 3)
|
|
|
|
- {
|
|
|
|
- var tmp = indices[i];
|
|
|
|
- indices[i] = indices[i + 2];
|
|
|
|
- indices[i+2] = tmp;
|
|
|
|
|
|
+ for (int i = 0; i < babylonIndices.Count; i += 3)
|
|
|
|
+ {
|
|
|
|
+ var tmp = babylonIndices[i];
|
|
|
|
+ babylonIndices[i] = babylonIndices[i + 2];
|
|
|
|
+ babylonIndices[i + 2] = tmp;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -124,7 +123,7 @@ namespace Max2Babylon
|
|
// ------- Init glTF --------
|
|
// ------- Init glTF --------
|
|
// --------------------------
|
|
// --------------------------
|
|
|
|
|
|
- RaiseMessage("GLTFExporter.Mesh | Init glTF", 1);
|
|
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | Init glTF", 2);
|
|
// Mesh
|
|
// Mesh
|
|
var gltfMesh = new GLTFMesh { name = babylonMesh.name };
|
|
var gltfMesh = new GLTFMesh { name = babylonMesh.name };
|
|
gltfMesh.index = gltf.MeshesList.Count;
|
|
gltfMesh.index = gltf.MeshesList.Count;
|
|
@@ -132,15 +131,6 @@ namespace Max2Babylon
|
|
gltfNode.mesh = gltfMesh.index;
|
|
gltfNode.mesh = gltfMesh.index;
|
|
gltfMesh.gltfNode = gltfNode;
|
|
gltfMesh.gltfNode = gltfNode;
|
|
|
|
|
|
- // MeshPrimitive
|
|
|
|
- var meshPrimitives = new List<GLTFMeshPrimitive>();
|
|
|
|
- var meshPrimitive = new GLTFMeshPrimitive
|
|
|
|
- {
|
|
|
|
- attributes = new Dictionary<string, int>(),
|
|
|
|
- mode = GLTFMeshPrimitive.FillMode.TRIANGLES // TODO reteive info from babylon material
|
|
|
|
- };
|
|
|
|
- meshPrimitives.Add(meshPrimitive);
|
|
|
|
-
|
|
|
|
// Buffer
|
|
// Buffer
|
|
var buffer = new GLTFBuffer
|
|
var buffer = new GLTFBuffer
|
|
{
|
|
{
|
|
@@ -171,51 +161,8 @@ namespace Max2Babylon
|
|
bufferViewFloatVec3.index = gltf.BufferViewsList.Count;
|
|
bufferViewFloatVec3.index = gltf.BufferViewsList.Count;
|
|
gltf.BufferViewsList.Add(bufferViewFloatVec3);
|
|
gltf.BufferViewsList.Add(bufferViewFloatVec3);
|
|
|
|
|
|
- // Accessor - Indices
|
|
|
|
- var accessorIndices = new GLTFAccessor
|
|
|
|
- {
|
|
|
|
- name = "accessorIndices",
|
|
|
|
- bufferView = bufferViewScalar.index,
|
|
|
|
- BufferView = bufferViewScalar,
|
|
|
|
- componentType = GLTFAccessor.ComponentType.UNSIGNED_SHORT,
|
|
|
|
- type = GLTFAccessor.TypeEnum.SCALAR.ToString()
|
|
|
|
- };
|
|
|
|
- accessorIndices.index = gltf.AccessorsList.Count;
|
|
|
|
- gltf.AccessorsList.Add(accessorIndices);
|
|
|
|
- meshPrimitive.indices = accessorIndices.index;
|
|
|
|
-
|
|
|
|
- // Accessor - Positions
|
|
|
|
- var accessorPositions = new GLTFAccessor
|
|
|
|
- {
|
|
|
|
- name = "accessorPositions",
|
|
|
|
- bufferView = bufferViewFloatVec3.index,
|
|
|
|
- BufferView = bufferViewFloatVec3,
|
|
|
|
- componentType = GLTFAccessor.ComponentType.FLOAT,
|
|
|
|
- type = GLTFAccessor.TypeEnum.VEC3.ToString(),
|
|
|
|
- min = new float[] { float.MaxValue, float.MaxValue, float.MaxValue },
|
|
|
|
- max = new float[] { float.MinValue, float.MinValue, float.MinValue }
|
|
|
|
- };
|
|
|
|
- accessorPositions.index = gltf.AccessorsList.Count;
|
|
|
|
- gltf.AccessorsList.Add(accessorPositions);
|
|
|
|
- meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.POSITION.ToString(), accessorPositions.index);
|
|
|
|
-
|
|
|
|
- // Accessor - Normals
|
|
|
|
- var accessorNormals = new GLTFAccessor
|
|
|
|
- {
|
|
|
|
- name = "accessorNormals",
|
|
|
|
- bufferView = bufferViewFloatVec3.index,
|
|
|
|
- BufferView = bufferViewFloatVec3,
|
|
|
|
- componentType = GLTFAccessor.ComponentType.FLOAT,
|
|
|
|
- type = GLTFAccessor.TypeEnum.VEC3.ToString()
|
|
|
|
- };
|
|
|
|
- accessorNormals.index = gltf.AccessorsList.Count;
|
|
|
|
- gltf.AccessorsList.Add(accessorNormals);
|
|
|
|
- meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.NORMAL.ToString(), accessorNormals.index);
|
|
|
|
-
|
|
|
|
// BufferView - Vector4
|
|
// BufferView - Vector4
|
|
GLTFBufferView bufferViewFloatVec4 = null;
|
|
GLTFBufferView bufferViewFloatVec4 = null;
|
|
- // Accessor - Colors
|
|
|
|
- GLTFAccessor accessorColors = null;
|
|
|
|
if (hasColor)
|
|
if (hasColor)
|
|
{
|
|
{
|
|
bufferViewFloatVec4 = new GLTFBufferView
|
|
bufferViewFloatVec4 = new GLTFBufferView
|
|
@@ -228,23 +175,11 @@ namespace Max2Babylon
|
|
};
|
|
};
|
|
bufferViewFloatVec4.index = gltf.BufferViewsList.Count;
|
|
bufferViewFloatVec4.index = gltf.BufferViewsList.Count;
|
|
gltf.BufferViewsList.Add(bufferViewFloatVec4);
|
|
gltf.BufferViewsList.Add(bufferViewFloatVec4);
|
|
-
|
|
|
|
- accessorColors = new GLTFAccessor
|
|
|
|
- {
|
|
|
|
- name = "accessorColors",
|
|
|
|
- bufferView = bufferViewFloatVec4.index,
|
|
|
|
- BufferView = bufferViewFloatVec4,
|
|
|
|
- componentType = GLTFAccessor.ComponentType.FLOAT,
|
|
|
|
- type = GLTFAccessor.TypeEnum.VEC4.ToString()
|
|
|
|
- };
|
|
|
|
- accessorColors.index = gltf.AccessorsList.Count;
|
|
|
|
- gltf.AccessorsList.Add(accessorColors);
|
|
|
|
- meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.COLOR_0.ToString(), accessorColors.index);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// BufferView - Vector2
|
|
// BufferView - Vector2
|
|
GLTFBufferView bufferViewFloatVec2 = null;
|
|
GLTFBufferView bufferViewFloatVec2 = null;
|
|
- if (hasUV ||hasUV2)
|
|
|
|
|
|
+ if (hasUV || hasUV2)
|
|
{
|
|
{
|
|
bufferViewFloatVec2 = new GLTFBufferView
|
|
bufferViewFloatVec2 = new GLTFBufferView
|
|
{
|
|
{
|
|
@@ -257,141 +192,280 @@ namespace Max2Babylon
|
|
gltf.BufferViewsList.Add(bufferViewFloatVec2);
|
|
gltf.BufferViewsList.Add(bufferViewFloatVec2);
|
|
}
|
|
}
|
|
|
|
|
|
- // Accessor - UV
|
|
|
|
- GLTFAccessor accessorUVs = null;
|
|
|
|
- if (hasUV)
|
|
|
|
|
|
+ // --------------------------
|
|
|
|
+ // ---- glTF primitives -----
|
|
|
|
+ // --------------------------
|
|
|
|
+
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | glTF primitives", 2);
|
|
|
|
+ var meshPrimitives = new List<GLTFMeshPrimitive>();
|
|
|
|
+
|
|
|
|
+ // Global vertices are sorted per submesh
|
|
|
|
+ var globalVerticesSubMeshes = new List<List<GLTFGlobalVertex>>();
|
|
|
|
+
|
|
|
|
+ // In gltf, indices of each mesh primitive are 0-based (ie: min value is 0)
|
|
|
|
+ // Thus, the gltf indices list is a concatenation of sub lists all 0-based
|
|
|
|
+ // Example for 2 triangles, each being a submesh:
|
|
|
|
+ // babylonIndices = {0,1,2, 3,4,5} gives as result gltfIndicies = {0,1,2, 0,1,2}
|
|
|
|
+ var gltfIndices = new List<ushort>();
|
|
|
|
+
|
|
|
|
+ foreach (BabylonSubMesh babylonSubMesh in babylonMesh.subMeshes)
|
|
{
|
|
{
|
|
- accessorUVs = new GLTFAccessor
|
|
|
|
|
|
+ // --------------------------
|
|
|
|
+ // ------ SubMesh data ------
|
|
|
|
+ // --------------------------
|
|
|
|
+
|
|
|
|
+ List<GLTFGlobalVertex> globalVerticesSubMesh = globalVertices.GetRange(babylonSubMesh.verticesStart, babylonSubMesh.verticesCount);
|
|
|
|
+ globalVerticesSubMeshes.Add(globalVerticesSubMesh);
|
|
|
|
+
|
|
|
|
+ List<ushort> _indices = babylonIndices.GetRange(babylonSubMesh.indexStart, babylonSubMesh.indexCount);
|
|
|
|
+ // Indices of this submesh / primitive are updated to be 0-based
|
|
|
|
+ var minIndiceValue = _indices.Min(); // Should be equal to babylonSubMesh.indexStart
|
|
|
|
+ for (int indexIndice = 0; indexIndice < _indices.Count; indexIndice++)
|
|
|
|
+ {
|
|
|
|
+ _indices[indexIndice] -= minIndiceValue;
|
|
|
|
+ }
|
|
|
|
+ gltfIndices.AddRange(_indices);
|
|
|
|
+
|
|
|
|
+ // --------------------------
|
|
|
|
+ // -- Init glTF primitive ---
|
|
|
|
+ // --------------------------
|
|
|
|
+
|
|
|
|
+ // MeshPrimitive
|
|
|
|
+ var meshPrimitive = new GLTFMeshPrimitive
|
|
|
|
+ {
|
|
|
|
+ attributes = new Dictionary<string, int>()
|
|
|
|
+ };
|
|
|
|
+ meshPrimitives.Add(meshPrimitive);
|
|
|
|
+
|
|
|
|
+ // Accessor - Indices
|
|
|
|
+ var accessorIndices = new GLTFAccessor
|
|
|
|
+ {
|
|
|
|
+ name = "accessorIndices",
|
|
|
|
+ bufferView = bufferViewScalar.index,
|
|
|
|
+ BufferView = bufferViewScalar,
|
|
|
|
+ componentType = GLTFAccessor.ComponentType.UNSIGNED_SHORT,
|
|
|
|
+ type = GLTFAccessor.TypeEnum.SCALAR.ToString()
|
|
|
|
+ };
|
|
|
|
+ accessorIndices.index = gltf.AccessorsList.Count;
|
|
|
|
+ gltf.AccessorsList.Add(accessorIndices);
|
|
|
|
+ meshPrimitive.indices = accessorIndices.index;
|
|
|
|
+
|
|
|
|
+ // Accessor - Positions
|
|
|
|
+ var accessorPositions = new GLTFAccessor
|
|
{
|
|
{
|
|
- name = "accessorUVs",
|
|
|
|
- bufferView = bufferViewFloatVec2.index,
|
|
|
|
- BufferView = bufferViewFloatVec2,
|
|
|
|
|
|
+ name = "accessorPositions",
|
|
|
|
+ bufferView = bufferViewFloatVec3.index,
|
|
|
|
+ BufferView = bufferViewFloatVec3,
|
|
componentType = GLTFAccessor.ComponentType.FLOAT,
|
|
componentType = GLTFAccessor.ComponentType.FLOAT,
|
|
- type = GLTFAccessor.TypeEnum.VEC2.ToString()
|
|
|
|
|
|
+ type = GLTFAccessor.TypeEnum.VEC3.ToString(),
|
|
|
|
+ min = new float[] { float.MaxValue, float.MaxValue, float.MaxValue },
|
|
|
|
+ max = new float[] { float.MinValue, float.MinValue, float.MinValue }
|
|
};
|
|
};
|
|
- accessorUVs.index = gltf.AccessorsList.Count;
|
|
|
|
- gltf.AccessorsList.Add(accessorUVs);
|
|
|
|
- meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_0.ToString(), accessorUVs.index);
|
|
|
|
- }
|
|
|
|
|
|
+ accessorPositions.index = gltf.AccessorsList.Count;
|
|
|
|
+ gltf.AccessorsList.Add(accessorPositions);
|
|
|
|
+ meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.POSITION.ToString(), accessorPositions.index);
|
|
|
|
|
|
- // Accessor - UV2
|
|
|
|
- GLTFAccessor accessorUV2s = null;
|
|
|
|
- if (hasUV2)
|
|
|
|
- {
|
|
|
|
- accessorUV2s = new GLTFAccessor
|
|
|
|
|
|
+ // Accessor - Normals
|
|
|
|
+ var accessorNormals = new GLTFAccessor
|
|
{
|
|
{
|
|
- name = "accessorUV2s",
|
|
|
|
- bufferView = bufferViewFloatVec2.index,
|
|
|
|
- BufferView = bufferViewFloatVec2,
|
|
|
|
|
|
+ name = "accessorNormals",
|
|
|
|
+ bufferView = bufferViewFloatVec3.index,
|
|
|
|
+ BufferView = bufferViewFloatVec3,
|
|
componentType = GLTFAccessor.ComponentType.FLOAT,
|
|
componentType = GLTFAccessor.ComponentType.FLOAT,
|
|
- type = GLTFAccessor.TypeEnum.VEC2.ToString()
|
|
|
|
|
|
+ type = GLTFAccessor.TypeEnum.VEC3.ToString()
|
|
};
|
|
};
|
|
- accessorUV2s.index = gltf.AccessorsList.Count;
|
|
|
|
- gltf.AccessorsList.Add(accessorUV2s);
|
|
|
|
- meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_1.ToString(), accessorUV2s.index);
|
|
|
|
- }
|
|
|
|
|
|
+ accessorNormals.index = gltf.AccessorsList.Count;
|
|
|
|
+ gltf.AccessorsList.Add(accessorNormals);
|
|
|
|
+ meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.NORMAL.ToString(), accessorNormals.index);
|
|
|
|
|
|
|
|
+ // Accessor - Colors
|
|
|
|
+ GLTFAccessor accessorColors = null;
|
|
|
|
+ if (hasColor)
|
|
|
|
+ {
|
|
|
|
+ accessorColors = new GLTFAccessor
|
|
|
|
+ {
|
|
|
|
+ name = "accessorColors",
|
|
|
|
+ bufferView = bufferViewFloatVec4.index,
|
|
|
|
+ BufferView = bufferViewFloatVec4,
|
|
|
|
+ componentType = GLTFAccessor.ComponentType.FLOAT,
|
|
|
|
+ type = GLTFAccessor.TypeEnum.VEC4.ToString()
|
|
|
|
+ };
|
|
|
|
+ accessorColors.index = gltf.AccessorsList.Count;
|
|
|
|
+ gltf.AccessorsList.Add(accessorColors);
|
|
|
|
+ meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.COLOR_0.ToString(), accessorColors.index);
|
|
|
|
+ }
|
|
|
|
|
|
- // --------------------------
|
|
|
|
- // ------ Mesh as glTF ------
|
|
|
|
- // --------------------------
|
|
|
|
|
|
+ // Accessor - UV
|
|
|
|
+ GLTFAccessor accessorUVs = null;
|
|
|
|
+ if (hasUV)
|
|
|
|
+ {
|
|
|
|
+ accessorUVs = new GLTFAccessor
|
|
|
|
+ {
|
|
|
|
+ name = "accessorUVs",
|
|
|
|
+ bufferView = bufferViewFloatVec2.index,
|
|
|
|
+ BufferView = bufferViewFloatVec2,
|
|
|
|
+ componentType = GLTFAccessor.ComponentType.FLOAT,
|
|
|
|
+ type = GLTFAccessor.TypeEnum.VEC2.ToString()
|
|
|
|
+ };
|
|
|
|
+ accessorUVs.index = gltf.AccessorsList.Count;
|
|
|
|
+ gltf.AccessorsList.Add(accessorUVs);
|
|
|
|
+ meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_0.ToString(), accessorUVs.index);
|
|
|
|
+ }
|
|
|
|
|
|
- RaiseMessage("GLTFExporter.Mesh | Mesh as glTF", 1);
|
|
|
|
- // Material
|
|
|
|
- //TODO - Handle multimaterials
|
|
|
|
- GLTFMaterial gltfMaterial = gltf.MaterialsList.Find(material => material.id == babylonMesh.materialId);
|
|
|
|
- if (gltfMaterial != null)
|
|
|
|
- {
|
|
|
|
- meshPrimitive.material = gltfMaterial.index;
|
|
|
|
- }
|
|
|
|
|
|
+ // Accessor - UV2
|
|
|
|
+ GLTFAccessor accessorUV2s = null;
|
|
|
|
+ if (hasUV2)
|
|
|
|
+ {
|
|
|
|
+ accessorUV2s = new GLTFAccessor
|
|
|
|
+ {
|
|
|
|
+ name = "accessorUV2s",
|
|
|
|
+ bufferView = bufferViewFloatVec2.index,
|
|
|
|
+ BufferView = bufferViewFloatVec2,
|
|
|
|
+ componentType = GLTFAccessor.ComponentType.FLOAT,
|
|
|
|
+ type = GLTFAccessor.TypeEnum.VEC2.ToString()
|
|
|
|
+ };
|
|
|
|
+ accessorUV2s.index = gltf.AccessorsList.Count;
|
|
|
|
+ gltf.AccessorsList.Add(accessorUV2s);
|
|
|
|
+ meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_1.ToString(), accessorUV2s.index);
|
|
|
|
+ }
|
|
|
|
|
|
- // Update min and max vertex position for each component (X, Y, Z)
|
|
|
|
- globalVertices.ForEach((globalVertex) =>
|
|
|
|
- {
|
|
|
|
- var positionArray = new float[] { globalVertex.Position.X, globalVertex.Position.Y, globalVertex.Position.Z };
|
|
|
|
- for (int indexComponent = 0; indexComponent < positionArray.Length; indexComponent++)
|
|
|
|
|
|
+
|
|
|
|
+ // --------------------------
|
|
|
|
+ // - Update glTF primitive --
|
|
|
|
+ // --------------------------
|
|
|
|
+
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | Mesh as glTF", 3);
|
|
|
|
+
|
|
|
|
+ // Material
|
|
|
|
+ if (babylonMesh.materialId != null)
|
|
{
|
|
{
|
|
- if (positionArray[indexComponent] < accessorPositions.min[indexComponent])
|
|
|
|
|
|
+ // Retreive the babylon material
|
|
|
|
+ var babylonMaterialId = babylonMesh.materialId;
|
|
|
|
+ if (isMultimaterial)
|
|
{
|
|
{
|
|
- accessorPositions.min[indexComponent] = positionArray[indexComponent];
|
|
|
|
|
|
+ var babylonMultiMaterials = new List<BabylonMultiMaterial>(babylonScene.multiMaterials);
|
|
|
|
+ var babylonMultiMaterial = babylonMultiMaterials.Find(_babylonMultiMaterial => _babylonMultiMaterial.id == babylonMesh.materialId);
|
|
|
|
+ babylonMaterialId = babylonMultiMaterial.materials[babylonSubMesh.materialIndex];
|
|
}
|
|
}
|
|
- if (positionArray[indexComponent] > accessorPositions.max[indexComponent])
|
|
|
|
|
|
+ var babylonMaterials = new List<BabylonMaterial>(babylonScene.materials);
|
|
|
|
+ var babylonMaterial = babylonMaterials.Find(_babylonMaterial => _babylonMaterial.id == babylonMaterialId);
|
|
|
|
+
|
|
|
|
+ // Update primitive material index
|
|
|
|
+ var indexMaterial = babylonMaterialsToExport.FindIndex(_babylonMaterial => _babylonMaterial == babylonMaterial);
|
|
|
|
+ if (indexMaterial == -1)
|
|
{
|
|
{
|
|
- accessorPositions.max[indexComponent] = positionArray[indexComponent];
|
|
|
|
|
|
+ // Store material for exportation
|
|
|
|
+ indexMaterial = babylonMaterialsToExport.Count;
|
|
|
|
+ babylonMaterialsToExport.Add(babylonMaterial);
|
|
}
|
|
}
|
|
- }
|
|
|
|
- });
|
|
|
|
|
|
+ meshPrimitive.material = indexMaterial;
|
|
|
|
|
|
- // Update byte length and count of accessors, bufferViews and buffers
|
|
|
|
- // Scalar
|
|
|
|
- AddElementsToAccessor(accessorIndices, indices.Count);
|
|
|
|
- // Vector3
|
|
|
|
- bufferViewFloatVec3.byteOffset = buffer.byteLength;
|
|
|
|
- AddElementsToAccessor(accessorPositions, globalVertices.Count);
|
|
|
|
- AddElementsToAccessor(accessorNormals, globalVertices.Count);
|
|
|
|
- // Vector4
|
|
|
|
- if (hasColor)
|
|
|
|
- {
|
|
|
|
- bufferViewFloatVec4.byteOffset = buffer.byteLength;
|
|
|
|
- AddElementsToAccessor(accessorColors, globalVertices.Count);
|
|
|
|
- }
|
|
|
|
- // Vector2
|
|
|
|
- if (hasUV || hasUV2)
|
|
|
|
- {
|
|
|
|
- bufferViewFloatVec2.byteOffset = buffer.byteLength;
|
|
|
|
|
|
+ // TODO - Add and retreive info from babylon material
|
|
|
|
+ meshPrimitive.mode = GLTFMeshPrimitive.FillMode.TRIANGLES;
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ // Update min and max vertex position for each component (X, Y, Z)
|
|
|
|
+ globalVerticesSubMesh.ForEach((globalVertex) =>
|
|
|
|
+ {
|
|
|
|
+ var positionArray = new float[] { globalVertex.Position.X, globalVertex.Position.Y, globalVertex.Position.Z };
|
|
|
|
+ for (int indexComponent = 0; indexComponent < positionArray.Length; indexComponent++)
|
|
|
|
+ {
|
|
|
|
+ if (positionArray[indexComponent] < accessorPositions.min[indexComponent])
|
|
|
|
+ {
|
|
|
|
+ accessorPositions.min[indexComponent] = positionArray[indexComponent];
|
|
|
|
+ }
|
|
|
|
+ if (positionArray[indexComponent] > accessorPositions.max[indexComponent])
|
|
|
|
+ {
|
|
|
|
+ accessorPositions.max[indexComponent] = positionArray[indexComponent];
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // Update byte length and count of accessors, bufferViews and buffers
|
|
|
|
+ // Scalar
|
|
|
|
+ AddElementsToAccessor(accessorIndices, _indices.Count);
|
|
|
|
+ // Vector3
|
|
|
|
+ AddElementsToAccessor(accessorPositions, globalVerticesSubMesh.Count);
|
|
|
|
+ AddElementsToAccessor(accessorNormals, globalVerticesSubMesh.Count);
|
|
|
|
+ // Vector4
|
|
|
|
+ if (hasColor)
|
|
|
|
+ {
|
|
|
|
+ AddElementsToAccessor(accessorColors, globalVerticesSubMesh.Count);
|
|
|
|
+ }
|
|
|
|
+ // Vector2
|
|
if (hasUV)
|
|
if (hasUV)
|
|
{
|
|
{
|
|
- AddElementsToAccessor(accessorUVs, globalVertices.Count);
|
|
|
|
|
|
+ AddElementsToAccessor(accessorUVs, globalVerticesSubMesh.Count);
|
|
}
|
|
}
|
|
if (hasUV2)
|
|
if (hasUV2)
|
|
{
|
|
{
|
|
- AddElementsToAccessor(accessorUV2s, globalVertices.Count);
|
|
|
|
|
|
+ AddElementsToAccessor(accessorUV2s, globalVerticesSubMesh.Count);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ gltfMesh.primitives = meshPrimitives.ToArray();
|
|
|
|
+
|
|
|
|
+ // Update byte offset of bufferViews
|
|
|
|
+ GLTFBufferView lastBufferView = null;
|
|
|
|
+ gltf.BufferViewsList.FindAll(bufferView => bufferView.buffer == buffer.index).ForEach(bufferView =>
|
|
|
|
+ {
|
|
|
|
+ if (lastBufferView != null)
|
|
|
|
+ {
|
|
|
|
+ bufferView.byteOffset = lastBufferView.byteOffset + lastBufferView.byteLength;
|
|
|
|
+ }
|
|
|
|
+ lastBufferView = bufferView;
|
|
|
|
+ });
|
|
|
|
|
|
|
|
|
|
// --------------------------
|
|
// --------------------------
|
|
// --------- Saving ---------
|
|
// --------- Saving ---------
|
|
// --------------------------
|
|
// --------------------------
|
|
|
|
|
|
- string outputBinaryFile = Path.Combine(gltf.OutputPath, gltfMesh.name + ".bin");
|
|
|
|
- RaiseMessage("GLTFExporter.Mesh | Saving " + outputBinaryFile, 1);
|
|
|
|
|
|
+ string outputBinaryFile = Path.Combine(gltf.OutputPath, gltfMesh.name + ".bin");
|
|
|
|
+ RaiseMessage("GLTFExporter.Mesh | Saving " + outputBinaryFile, 2);
|
|
|
|
|
|
|
|
+ // Write data to binary file
|
|
using (BinaryWriter writer = new BinaryWriter(File.Open(outputBinaryFile, FileMode.Create)))
|
|
using (BinaryWriter writer = new BinaryWriter(File.Open(outputBinaryFile, FileMode.Create)))
|
|
{
|
|
{
|
|
- // Binary arrays
|
|
|
|
- List<float> vertices = globalVertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToList();
|
|
|
|
- List<float> normals = globalVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToList();
|
|
|
|
|
|
+ // BufferView - Scalar
|
|
|
|
+ gltfIndices.ForEach(n => writer.Write(n));
|
|
|
|
|
|
- List<float> colors = new List<float>();
|
|
|
|
- if (hasColor)
|
|
|
|
|
|
+ // BufferView - Vector3
|
|
|
|
+ globalVerticesSubMeshes.ForEach(globalVerticesSubMesh =>
|
|
{
|
|
{
|
|
- colors = globalVertices.SelectMany(v => new[] { v.Color[0], v.Color[1], v.Color[2], v.Color[3] }).ToList();
|
|
|
|
- }
|
|
|
|
|
|
+ List<float> vertices = globalVerticesSubMesh.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToList();
|
|
|
|
+ vertices.ForEach(n => writer.Write(n));
|
|
|
|
|
|
- List<float> uvs = new List<float>();
|
|
|
|
- if (hasUV)
|
|
|
|
- {
|
|
|
|
- uvs = globalVertices.SelectMany(v => new[] { v.UV.X, v.UV.Y }).ToList(); // No symetry required to perform 3dsMax => gltf conversion
|
|
|
|
- }
|
|
|
|
|
|
+ List<float> normals = globalVerticesSubMesh.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToList();
|
|
|
|
+ normals.ForEach(n => writer.Write(n));
|
|
|
|
+ });
|
|
|
|
|
|
- List<float> uvs2 = new List<float>();
|
|
|
|
- if (hasUV2)
|
|
|
|
|
|
+ // BufferView - Vector4
|
|
|
|
+ globalVerticesSubMeshes.ForEach(globalVerticesSubMesh =>
|
|
{
|
|
{
|
|
- uvs2 = globalVertices.SelectMany(v => new[] { v.UV2.X, v.UV2.Y }).ToList(); // No symetry required to perform 3dsMax => gltf conversion
|
|
|
|
- }
|
|
|
|
|
|
+ if (hasColor)
|
|
|
|
+ {
|
|
|
|
+ List<float> colors = globalVerticesSubMesh.SelectMany(v => new[] { v.Color[0], v.Color[1], v.Color[2], v.Color[3] }).ToList();
|
|
|
|
+ colors.ForEach(n => writer.Write(n));
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
|
|
- // Write data to binary file
|
|
|
|
- indices.ForEach(n => writer.Write(n));
|
|
|
|
- vertices.ForEach(n => writer.Write(n));
|
|
|
|
- normals.ForEach(n => writer.Write(n));
|
|
|
|
- colors.ForEach(n => writer.Write(n));
|
|
|
|
- uvs.ForEach(n => writer.Write(n));
|
|
|
|
|
|
+ // BufferView - Vector2
|
|
|
|
+ globalVerticesSubMeshes.ForEach(globalVerticesSubMesh =>
|
|
|
|
+ {
|
|
|
|
+ if (hasUV)
|
|
|
|
+ {
|
|
|
|
+ List<float> uvs = globalVerticesSubMesh.SelectMany(v => new[] { v.UV.X, v.UV.Y }).ToList();
|
|
|
|
+ uvs.ForEach(n => writer.Write(n));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (hasUV2)
|
|
|
|
+ {
|
|
|
|
+ List<float> uvs2 = globalVerticesSubMesh.SelectMany(v => new[] { v.UV2.X, v.UV2.Y }).ToList();
|
|
|
|
+ uvs2.ForEach(n => writer.Write(n));
|
|
|
|
+ }
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
- gltfMesh.primitives = meshPrimitives.ToArray();
|
|
|
|
-
|
|
|
|
return gltfMesh;
|
|
return gltfMesh;
|
|
}
|
|
}
|
|
|
|
|