private void ExtractFace(IIGameSkin skin, IIGameMesh unskinnedMesh, List <GlobalVertex> vertices, List <int> indices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, ref int indexCount, ref int minVertexIndex, ref int maxVertexIndex, IFaceEx face, List <int> boneIds) { var a = CreateGlobalVertex(unskinnedMesh, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); var b = CreateGlobalVertex(unskinnedMesh, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); var c = CreateGlobalVertex(unskinnedMesh, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); indices.Add(a); indices.Add(b); indices.Add(c); if (a < minVertexIndex) { minVertexIndex = a; } if (b < minVertexIndex) { minVertexIndex = b; } if (c < minVertexIndex) { minVertexIndex = c; } if (a > maxVertexIndex) { maxVertexIndex = a; } if (b > maxVertexIndex) { maxVertexIndex = b; } if (c > maxVertexIndex) { maxVertexIndex = c; } indexCount += 3; CheckCancelled(); }
int CreateGlobalVertex(IIGameMesh mesh, IFaceEx face, int facePart, List <GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List <int> boneIds) { var vertexIndex = (int)face.Vert[facePart]; var vertex = new GlobalVertex { BaseIndex = vertexIndex, Position = mesh.GetVertex(vertexIndex, true), Normal = mesh.GetNormal((int)face.Norm[facePart], true) }; if (hasUV) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(1, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(1, indices[facePart]); vertex.UV = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasUV2) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(2, indices[facePart]); vertex.UV2 = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasColor) { var vertexColorIndex = (int)face.Color[facePart]; var vertexColor = mesh.GetColorVertex(vertexColorIndex); float alpha = 1; if (hasAlpha) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(-2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var color = mesh.GetMapVertex(-2, indices[facePart]); alpha = color.X; } vertex.Color = new[] { vertexColor.X, vertexColor.Y, vertexColor.Z, alpha }; } if (skin != null) { float weight0 = 0; float weight1 = 0; float weight2 = 0; float weight3 = 0; int bone0 = bonesCount; int bone1 = bonesCount; int bone2 = bonesCount; int bone3 = bonesCount; var nbBones = skin.GetNumberOfBones(vertexIndex); if (nbBones > 0) { bone0 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 0).NodeID); weight0 = skin.GetWeight(vertexIndex, 0); } if (nbBones > 1) { bone1 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 1).NodeID); weight1 = skin.GetWeight(vertexIndex, 1); } if (nbBones > 2) { bone2 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 2).NodeID); weight2 = skin.GetWeight(vertexIndex, 2); } if (nbBones > 3) { bone3 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 3).NodeID); weight3 = skin.GetWeight(vertexIndex, 3); } if (nbBones == 0) { weight0 = 1.0f; bone0 = bonesCount; } vertex.Weights = Loader.Global.Point4.Create(weight0, weight1, weight2, weight3); vertex.BonesIndices = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0; if (nbBones > 4) { bone0 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 4).NodeID); weight0 = skin.GetWeight(vertexIndex, 4); weight1 = 0; weight2 = 0; weight3 = 0; if (nbBones > 5) { bone1 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 5).NodeID); weight1 = skin.GetWeight(vertexIndex, 5); } if (nbBones > 6) { bone2 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 6).NodeID); weight2 = skin.GetWeight(vertexIndex, 6); } if (nbBones > 7) { bone3 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 7).NodeID); weight3 = skin.GetWeight(vertexIndex, 7); } vertex.WeightsExtra = Loader.Global.Point4.Create(weight0, weight1, weight2, weight3); vertex.BonesIndicesExtra = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0; if (nbBones > 8) { RaiseError("Too many bones influences per vertex: " + nbBones + ". Babylon.js only support 8 bones influences per vertex.", 2); } } } if (verticesAlreadyExported != null) { if (verticesAlreadyExported[vertexIndex] != null) { var index = verticesAlreadyExported[vertexIndex].IndexOf(vertex); if (index > -1) { return(verticesAlreadyExported[vertexIndex][index].CurrentIndex); } } else { verticesAlreadyExported[vertexIndex] = new List <GlobalVertex>(); } vertex.CurrentIndex = vertices.Count; verticesAlreadyExported[vertexIndex].Add(vertex); } vertices.Add(vertex); return(vertices.Count - 1); }
int CreateGlobalVertex(IIGameMesh mesh, IFaceEx face, int facePart, List<GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List<int> boneIds) { var vertexIndex = (int)face.Vert[facePart]; var vertex = new GlobalVertex { BaseIndex = vertexIndex, Position = mesh.GetVertex(vertexIndex, true), Normal = mesh.GetNormal((int)face.Norm[facePart], true) }; if (hasUV) { var indices = new int[3]; unsafe { fixed (int* indicesPtr = indices) { mesh.GetMapFaceIndex(1, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(1, indices[facePart]); vertex.UV = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasUV2) { var indices = new int[3]; unsafe { fixed (int* indicesPtr = indices) { mesh.GetMapFaceIndex(2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(2, indices[facePart]); vertex.UV2 = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasColor) { var vertexColorIndex = (int)face.Color[facePart]; var vertexColor = mesh.GetColorVertex(vertexColorIndex); float alpha = 1; if (hasAlpha) { var indices = new int[3]; unsafe { fixed (int* indicesPtr = indices) { mesh.GetMapFaceIndex(-2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var color = mesh.GetMapVertex(-2, indices[facePart]); alpha = color.X; } vertex.Color = new[] { vertexColor.X, vertexColor.Y, vertexColor.Z, alpha }; } if (skin != null) { float weight0 = 0; float weight1 = 0; float weight2 = 0; int bone0 = bonesCount; int bone1 = bonesCount; int bone2 = bonesCount; int bone3 = bonesCount; int nbBones = skin.GetNumberOfBones(vertexIndex); if (nbBones > 0) { bone0 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 0).NodeID); weight0 = skin.GetWeight(vertexIndex, 0); } if (nbBones > 1) { bone1 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 1).NodeID); weight1 = skin.GetWeight(vertexIndex, 1); } if (nbBones > 2) { bone2 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 2).NodeID); weight2 = skin.GetWeight(vertexIndex, 2); } if (nbBones > 3) { bone3 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 3).NodeID); } if (nbBones == 0) { weight0 = 1.0f; bone0 = bonesCount; } if (nbBones > 4) { RaiseError("Too many bones influences per vertex: " + nbBones + ". Babylon.js only support 4 bones influences per vertex.", 2); } vertex.Weights = Loader.Global.Point4.Create(weight0, weight1, weight2, 1.0 - weight0 - weight1 - weight2); vertex.BonesIndices = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0; } if (verticesAlreadyExported != null) { if (verticesAlreadyExported[vertexIndex] != null) { var index = verticesAlreadyExported[vertexIndex].IndexOf(vertex); if (index > -1) { return verticesAlreadyExported[vertexIndex][index].CurrentIndex; } } else { verticesAlreadyExported[vertexIndex] = new List<GlobalVertex>(); } vertex.CurrentIndex = vertices.Count; verticesAlreadyExported[vertexIndex].Add(vertex); } vertices.Add(vertex); return vertices.Count - 1; }
private void ExtractGeometry(List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, List <int> boneIds, IIGameSkin skin, IIGameMesh unskinnedMesh, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, bool optimizeVertices, int multiMatsCount, IIGameNode meshNode) { List <GlobalVertex>[] verticesAlreadyExported = null; if (optimizeVertices) { verticesAlreadyExported = new List <GlobalVertex> [unskinnedMesh.NumberOfVerts]; } var indexStart = 0; for (int i = 0; i < multiMatsCount; ++i) { int materialId = meshNode.NodeMaterial?.GetMaterialID(i) ?? 0; var indexCount = 0; var minVertexIndex = int.MaxValue; var maxVertexIndex = int.MinValue; var subMesh = new BabylonSubMesh { indexStart = indexStart, materialIndex = i }; if (multiMatsCount == 1) { for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j) { var face = unskinnedMesh.GetFace(j); ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } else { ITab <IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId); for (int j = 0; j < materialFaces.Count; ++j) { #if MAX2017 var faceIndexer = j; #else var faceIndexer = new IntPtr(j); #endif var face = materialFaces[faceIndexer]; #if !MAX2017 Marshal.FreeHGlobal(faceIndexer); #endif ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } if (indexCount != 0) { subMesh.indexCount = indexCount; subMesh.verticesStart = minVertexIndex; subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1; indexStart += indexCount; subMeshes.Add(subMesh); } } }
int CreateGlobalVertex(IIGameNode meshNode, IIGameMesh mesh, BabylonAbstractMesh babylonAbstractMesh, IMatrix3 invertedWorldMatrix, IFaceEx face, int facePart, List <GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List <int> boneIds) { var vertexIndex = (int)face.Vert[facePart]; // Position can by retreived in world space or object space // Unfortunately, this value can't be retreived in local space var vertex = new GlobalVertex { BaseIndex = vertexIndex, Position = mesh.GetVertex(vertexIndex, false), // world space Normal = mesh.GetNormal((int)face.Norm[facePart], false) // world space }; if (exportParameters.exportTangents) { int indexTangentBinormal = mesh.GetFaceVertexTangentBinormal(face.MeshFaceIndex, facePart, 1); IPoint3 normal = vertex.Normal.Normalize; IPoint3 tangent = mesh.GetTangent(indexTangentBinormal, 1).Normalize; IPoint3 bitangent = mesh.GetBinormal(indexTangentBinormal, 1).Normalize; int w = GetW(normal, tangent, bitangent); vertex.Tangent = new float[] { tangent.X, tangent.Y, tangent.Z, w }; } // Convert position and normal to local space vertex.Position = invertedWorldMatrix.PointTransform(vertex.Position); vertex.Normal = invertedWorldMatrix.VectorTransform(vertex.Normal); // 1. scale normals with node scales var nodeScaling = BabylonVector3.FromArray(babylonAbstractMesh.scaling); vertex.Normal = vertex.Normal.Multiply(Loader.Global.Point3.Create(Math.Abs(nodeScaling.X), Math.Abs(nodeScaling.Y), Math.Abs(nodeScaling.Z))); // 2. scale normals with objectOffsetScales (unrotate by objectOffsetRot, then scale, then rotate again) // note: LH coordinate system => flip y and z var objOffsetScale = Loader.Global.Point3.Create(meshNode.MaxNode.ObjOffsetScale.S); var scaleX = Math.Abs(objOffsetScale.X); var scaleY = Math.Abs(objOffsetScale.Y); var scaleZ = Math.Abs(objOffsetScale.Z); var objOffsetScaleFlipYZInv = Loader.Global.Point3.Create(1 / scaleX, 1 / scaleZ, 1 / scaleY); var objOffsetQuat = meshNode.MaxNode.ObjOffsetRot; var qFlippedYZ = objOffsetQuat; var tmpSwap = objOffsetQuat.Y; qFlippedYZ.Y = objOffsetQuat.Z; qFlippedYZ.Z = tmpSwap; var nUnrotated = RotateVectorByQuaternion(vertex.Normal, qFlippedYZ); var nUnrotatedScaled = nUnrotated.Multiply(objOffsetScaleFlipYZInv); nUnrotatedScaled = nUnrotatedScaled.Normalize; var nRerotatedScaled = RotateVectorByQuaternion(nUnrotatedScaled, qFlippedYZ.Conjugate); vertex.Normal = nRerotatedScaled; if (hasUV) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(1, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(1, indices[facePart]); vertex.UV = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasUV2) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(2, indices[facePart]); vertex.UV2 = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasColor) { var vertexColorIndex = (int)face.Color[facePart]; var vertexColor = mesh.GetColorVertex(vertexColorIndex); float alpha = 1; if (hasAlpha) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(-2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var color = mesh.GetMapVertex(-2, indices[facePart]); alpha = color.X; } vertex.Color = new[] { vertexColor.X, vertexColor.Y, vertexColor.Z, alpha }; } if (skin != null) { float weight0 = 0; float weight1 = 0; float weight2 = 0; float weight3 = 0; int bone0 = bonesCount; int bone1 = bonesCount; int bone2 = bonesCount; int bone3 = bonesCount; var nbBones = skin.GetNumberOfBones(vertexIndex); if (nbBones > 0) { bone0 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 0).NodeID); weight0 = skin.GetWeight(vertexIndex, 0); } if (nbBones > 1) { bone1 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 1).NodeID); weight1 = skin.GetWeight(vertexIndex, 1); } if (nbBones > 2) { bone2 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 2).NodeID); weight2 = skin.GetWeight(vertexIndex, 2); } if (nbBones > 3) { bone3 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 3).NodeID); weight3 = skin.GetWeight(vertexIndex, 3); } if (nbBones == 0) { weight0 = 1.0f; bone0 = bonesCount; } vertex.Weights = Loader.Global.Point4.Create(weight0, weight1, weight2, weight3); vertex.BonesIndices = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0; if (nbBones > 4) { bone0 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 4).NodeID); weight0 = skin.GetWeight(vertexIndex, 4); weight1 = 0; weight2 = 0; weight3 = 0; if (nbBones > 5) { bone1 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 5).NodeID); weight1 = skin.GetWeight(vertexIndex, 5); } if (nbBones > 6) { bone2 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 6).NodeID); weight2 = skin.GetWeight(vertexIndex, 6); } if (nbBones > 7) { bone3 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 7).NodeID); weight3 = skin.GetWeight(vertexIndex, 7); } vertex.WeightsExtra = Loader.Global.Point4.Create(weight0, weight1, weight2, weight3); vertex.BonesIndicesExtra = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0; if (nbBones > 8) { RaiseError("Too many bones influences per vertex: " + nbBones + ". Babylon.js only support 8 bones influences per vertex.", 2); } } } if (verticesAlreadyExported != null) { if (verticesAlreadyExported[vertexIndex] != null) { var index = verticesAlreadyExported[vertexIndex].IndexOf(vertex); if (index > -1) { return(verticesAlreadyExported[vertexIndex][index].CurrentIndex); } } else { verticesAlreadyExported[vertexIndex] = new List <GlobalVertex>(); } vertex.CurrentIndex = vertices.Count; verticesAlreadyExported[vertexIndex].Add(vertex); } vertices.Add(vertex); return(vertices.Count - 1); }
private void ExtractFace(IIGameSkin skin, IIGameMesh unskinnedMesh, List<GlobalVertex> vertices, List<int> indices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List<GlobalVertex>[] verticesAlreadyExported, ref int indexCount, ref int minVertexIndex, ref int maxVertexIndex, IFaceEx face, List<int> boneIds) { var a = CreateGlobalVertex(unskinnedMesh, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); var b = CreateGlobalVertex(unskinnedMesh, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); var c = CreateGlobalVertex(unskinnedMesh, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); indices.Add(a); indices.Add(b); indices.Add(c); if (a < minVertexIndex) { minVertexIndex = a; } if (b < minVertexIndex) { minVertexIndex = b; } if (c < minVertexIndex) { minVertexIndex = c; } if (a > maxVertexIndex) { maxVertexIndex = a; } if (b > maxVertexIndex) { maxVertexIndex = b; } if (c > maxVertexIndex) { maxVertexIndex = c; } indexCount += 3; CheckCancelled(); }
private void ExtractFace(IIGameNode meshNode, IIGameSkin skin, IIGameMesh unskinnedMesh, BabylonAbstractMesh babylonAbstractMesh, IMatrix3 invertedWorldMatrix, List <GlobalVertex> vertices, List <int> indices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, ref int indexCount, ref int minVertexIndex, ref int maxVertexIndex, IFaceEx face, List <int> boneIds) { int a, b, c; // parity is TRUE, if determinant negative ( counter-intuitive convention of 3ds max, see docs... :/ ) // fix for cesium: currently, cesium does not expect a reversed winding order for negative scales //if (false) // for threejs and babylonjs (handle negative scales correctly (reversed winding order expected) if (invertedWorldMatrix.Parity) { // flipped case: reverse winding order a = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); b = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); c = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); } else { // normal case a = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); b = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); c = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); } indices.Add(a); indices.Add(b); indices.Add(c); if (a < minVertexIndex) { minVertexIndex = a; } if (b < minVertexIndex) { minVertexIndex = b; } if (c < minVertexIndex) { minVertexIndex = c; } if (a > maxVertexIndex) { maxVertexIndex = a; } if (b > maxVertexIndex) { maxVertexIndex = b; } if (c > maxVertexIndex) { maxVertexIndex = c; } indexCount += 3; CheckCancelled(); }
private void ExtractGeometry(BabylonAbstractMesh babylonAbstractMesh, List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, List <int> boneIds, IIGameSkin skin, IIGameMesh unskinnedMesh, IMatrix3 invertedWorldMatrix, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, bool optimizeVertices, int multiMatsCount, IIGameNode meshNode, ref List <int> faceIndexes) { List <GlobalVertex>[] verticesAlreadyExported = null; if (optimizeVertices) { verticesAlreadyExported = new List <GlobalVertex> [unskinnedMesh.NumberOfVerts]; } var indexStart = 0; // Whether or not to store order in which faces are exported // Storage is used when exporting Morph Targets geometry // To ensure face order is identical, especially with multimaterials involved bool storeFaceIndexes = faceIndexes == null; if (storeFaceIndexes) { faceIndexes = new List <int>(); } int indexInFaceIndexesArray = 0; for (int i = 0; i < multiMatsCount; ++i) { int materialId = meshNode.NodeMaterial?.GetMaterialID(i) ?? 0; var indexCount = 0; var minVertexIndex = int.MaxValue; var maxVertexIndex = int.MinValue; var subMesh = new BabylonSubMesh { indexStart = indexStart, materialIndex = i }; if (multiMatsCount == 1) { for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j) { IFaceEx face = null; if (storeFaceIndexes) { face = unskinnedMesh.GetFace(j); // Store face index (j = face.MeshFaceIndex) faceIndexes.Add(j); } else { face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]); } ExtractFace(meshNode, skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } else { ITab <IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId); for (int j = 0; j < materialFaces.Count; ++j) { IFaceEx face = null; if (storeFaceIndexes) { // Retreive face #if MAX2017 || MAX2018 face = materialFaces[j]; #else face = materialFaces[new IntPtr(j)]; #endif // Store face index faceIndexes.Add(face.MeshFaceIndex); } else { face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]); } ExtractFace(meshNode, skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } if (indexCount != 0) { subMesh.indexCount = indexCount; subMesh.verticesStart = minVertexIndex; subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1; indexStart += indexCount; subMeshes.Add(subMesh); } } }
int CreateGlobalVertex(IIGameMesh mesh, BabylonAbstractMesh babylonAbstractMesh, IMatrix3 invertedWorldMatrix, IMatrix3 offsetTM, IFaceEx face, int facePart, List <GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List <int> boneIds) { var vertexIndex = (int)face.Vert[facePart]; // Position can by retrieved in world space or object space // Unfortunately, this value can't be retrieved in local space var vertex = new GlobalVertex { BaseIndex = vertexIndex, Position = mesh.GetVertex(vertexIndex, false), // world space Normal = mesh.GetNormal((int)face.Norm[facePart], true) // object space (world space was somehow bugged for normal) }; //System.Diagnostics.Debug.WriteLine("vertex normal: " + string.Join(", ", vertex.Normal.ToArray().Select(v => Math.Round(v, 3)))); // position (from world to local/node space) vertex.Position = invertedWorldMatrix.PointTransform(vertex.Position); // normal (from object to local/node space) vertex.Normal = offsetTM.VectorTransform(vertex.Normal).Normalize; // tangent if (exportParameters.exportTangents) { int indexTangentBinormal = mesh.GetFaceVertexTangentBinormal(face.MeshFaceIndex, facePart, 1); IPoint3 normal = vertex.Normal.Normalize; IPoint3 tangent = mesh.GetTangent(indexTangentBinormal, 1).Normalize; IPoint3 bitangent = mesh.GetBinormal(indexTangentBinormal, 1).Normalize; int w = GetW(normal, tangent, bitangent); vertex.Tangent = new float[] { tangent.X, tangent.Y, tangent.Z, w }; } if (hasUV) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(1, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(1, indices[facePart]); vertex.UV = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasUV2) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(2, indices[facePart]); vertex.UV2 = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasColor) { var vertexColorIndex = (int)face.Color[facePart]; var vertexColor = mesh.GetColorVertex(vertexColorIndex); float alpha = 1; if (hasAlpha) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(-2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var color = mesh.GetMapVertex(-2, indices[facePart]); alpha = color.X; } vertex.Color = new[] { vertexColor.X, vertexColor.Y, vertexColor.Z, alpha }; } if (skin != null) { float[] weight = new float[4] { 0, 0, 0, 0 }; int[] bone = new int[4] { 0, 0, 0, 0 }; var nbBones = skin.GetNumberOfBones(vertexIndex); int currentVtxBone = 0; int currentSkinBone = 0; // process skin bones until we have 4 bones for this vertex or we run out of skin bones for (currentSkinBone = 0; currentSkinBone < nbBones && currentVtxBone < 4; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } bone[currentVtxBone] = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, currentSkinBone).NodeID); weight[currentVtxBone] = skin.GetWeight(vertexIndex, currentSkinBone); ++currentVtxBone; } // if we didnt have any bones with a weight > 0 if (currentVtxBone == 0) { weight[0] = 1.0f; bone[0] = 0; } vertex.Weights = Loader.Global.Point4.Create(weight); vertex.BonesIndices = (bone[3] << 24) | (bone[2] << 16) | (bone[1] << 8) | bone[0]; if (currentVtxBone >= 4 && currentSkinBone < nbBones) { weight = new float[4] { 0, 0, 0, 0 }; bone = new int[4] { 0, 0, 0, 0 }; // process remaining skin bones until we have a total of 8 bones for this vertex or we run out of skin bones for (; currentSkinBone < nbBones && currentVtxBone < 8; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } if (isGltfExported) { RaiseError("Too many bone influences per vertex for vertexIndex: " + vertexIndex + ". glTF only supports up to 4 bone influences per vertex.", 2); break; } bone[currentVtxBone - 4] = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, currentSkinBone).NodeID); weight[currentVtxBone - 4] = skin.GetWeight(vertexIndex, currentSkinBone); ++currentVtxBone; } // if we have any extra bone weights if (currentVtxBone > 4) { vertex.WeightsExtra = Loader.Global.Point4.Create(weight); vertex.BonesIndicesExtra = (bone[3] << 24) | (bone[2] << 16) | (bone[1] << 8) | bone[0]; if (currentSkinBone < nbBones) { // if we have more skin bones left, this means we have used up all our bones for this vertex // check if any of the remaining bones has a weight > 0 for (; currentSkinBone < nbBones; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } RaiseError("Too many bone influences per vertex for vertexIndex: " + vertexIndex + ". Babylon.js only supports up to 8 bone influences per vertex.", 2); break; } } } } } if (verticesAlreadyExported != null) { if (verticesAlreadyExported[vertexIndex] != null) { var index = verticesAlreadyExported[vertexIndex].IndexOf(vertex); if (index > -1) { return(verticesAlreadyExported[vertexIndex][index].CurrentIndex); } } else { verticesAlreadyExported[vertexIndex] = new List <GlobalVertex>(); } vertex.CurrentIndex = vertices.Count; verticesAlreadyExported[vertexIndex].Add(vertex); } vertices.Add(vertex); return(vertices.Count - 1); }
int CreateGlobalVertex(IIGameNode meshNode, IIGameMesh mesh, BabylonAbstractMesh babylonAbstractMesh, IMatrix3 invertedWorldMatrix, IFaceEx face, int facePart, List <GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List <int> boneIds) { var vertexIndex = (int)face.Vert[facePart]; // Position can by retreived in world space or object space // Unfortunately, this value can't be retreived in local space var vertex = new GlobalVertex { BaseIndex = vertexIndex, Position = mesh.GetVertex(vertexIndex, false), // world space Normal = mesh.GetNormal((int)face.Norm[facePart], false) // world space }; if (exportParameters.exportTangents) { int indexTangentBinormal = mesh.GetFaceVertexTangentBinormal(face.MeshFaceIndex, facePart, 1); IPoint3 normal = vertex.Normal.Normalize; IPoint3 tangent = mesh.GetTangent(indexTangentBinormal, 1).Normalize; IPoint3 bitangent = mesh.GetBinormal(indexTangentBinormal, 1).Normalize; int w = GetW(normal, tangent, bitangent); vertex.Tangent = new float[] { tangent.X, tangent.Y, tangent.Z, w }; } // Convert position and normal to local space vertex.Position = invertedWorldMatrix.PointTransform(vertex.Position); vertex.Normal = invertedWorldMatrix.VectorTransform(vertex.Normal); // 1. scale normals with node scales var nodeScaling = BabylonVector3.FromArray(babylonAbstractMesh.scaling); vertex.Normal = vertex.Normal.Multiply(Loader.Global.Point3.Create(Math.Abs(nodeScaling.X), Math.Abs(nodeScaling.Y), Math.Abs(nodeScaling.Z))); // 2. scale normals with objectOffsetScales (unrotate by objectOffsetRot, then scale, then rotate again) // note: LH coordinate system => flip y and z var objOffsetScale = Loader.Global.Point3.Create(meshNode.MaxNode.ObjOffsetScale.S); var scaleX = Math.Abs(objOffsetScale.X); var scaleY = Math.Abs(objOffsetScale.Y); var scaleZ = Math.Abs(objOffsetScale.Z); var objOffsetScaleFlipYZInv = Loader.Global.Point3.Create(1 / scaleX, 1 / scaleZ, 1 / scaleY); var objOffsetQuat = meshNode.MaxNode.ObjOffsetRot; var qFlippedYZ = objOffsetQuat; var tmpSwap = objOffsetQuat.Y; qFlippedYZ.Y = objOffsetQuat.Z; qFlippedYZ.Z = tmpSwap; var nUnrotated = RotateVectorByQuaternion(vertex.Normal, qFlippedYZ); var nUnrotatedScaled = nUnrotated.Multiply(objOffsetScaleFlipYZInv); nUnrotatedScaled = nUnrotatedScaled.Normalize; var nRerotatedScaled = RotateVectorByQuaternion(nUnrotatedScaled, qFlippedYZ.Conjugate); vertex.Normal = nRerotatedScaled; if (hasUV) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(1, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(1, indices[facePart]); vertex.UV = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasUV2) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(2, indices[facePart]); vertex.UV2 = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasColor) { var vertexColorIndex = (int)face.Color[facePart]; var vertexColor = mesh.GetColorVertex(vertexColorIndex); float alpha = 1; if (hasAlpha) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(-2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var color = mesh.GetMapVertex(-2, indices[facePart]); alpha = color.X; } vertex.Color = new[] { vertexColor.X, vertexColor.Y, vertexColor.Z, alpha }; } if (skin != null) { float[] weight = new float[4] { 0, 0, 0, 0 }; int[] bone = new int[4] { bonesCount, bonesCount, bonesCount, bonesCount }; var nbBones = skin.GetNumberOfBones(vertexIndex); int currentVtxBone = 0; int currentSkinBone = 0; // process skin bones until we have 4 bones for this vertex or we run out of skin bones for (currentSkinBone = 0; currentSkinBone < nbBones && currentVtxBone < 4; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } bone[currentVtxBone] = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, currentSkinBone).NodeID); weight[currentVtxBone] = skin.GetWeight(vertexIndex, currentSkinBone); ++currentVtxBone; } // if we didnt have any bones with a weight > 0 if (currentVtxBone == 0) { weight[0] = 1.0f; bone[0] = bonesCount; } vertex.Weights = Loader.Global.Point4.Create(weight); vertex.BonesIndices = (bone[3] << 24) | (bone[2] << 16) | (bone[1] << 8) | bone[0]; if (currentVtxBone >= 4 && currentSkinBone < nbBones) { weight = new float[4] { 0, 0, 0, 0 }; bone = new int[4] { bonesCount, bonesCount, bonesCount, bonesCount }; // process remaining skin bones until we have a total of 8 bones for this vertex or we run out of skin bones for (; currentSkinBone < nbBones && currentVtxBone < 8; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } if (isGltfExported) { RaiseError("Too many bone influences per vertex for vertexIndex: " + vertexIndex + ". glTF only supports up to 4 bone influences per vertex.", 2); break; } bone[currentVtxBone - 4] = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, currentSkinBone).NodeID); weight[currentVtxBone - 4] = skin.GetWeight(vertexIndex, currentSkinBone); ++currentVtxBone; } // if we have any extra bone weights if (currentVtxBone > 4) { vertex.WeightsExtra = Loader.Global.Point4.Create(weight); vertex.BonesIndicesExtra = (bone[3] << 24) | (bone[2] << 16) | (bone[1] << 8) | bone[0]; if (currentSkinBone < nbBones) { // if we have more skin bones left, this means we have used up all our bones for this vertex // check if any of the remaining bones has a weight > 0 for (; currentSkinBone < nbBones; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } RaiseError("Too many bone influences per vertex for vertexIndex: " + vertexIndex + ". Babylon.js only supports up to 8 bone influences per vertex.", 2); break; } } } } } if (verticesAlreadyExported != null) { if (verticesAlreadyExported[vertexIndex] != null) { var index = verticesAlreadyExported[vertexIndex].IndexOf(vertex); if (index > -1) { return(verticesAlreadyExported[vertexIndex][index].CurrentIndex); } } else { verticesAlreadyExported[vertexIndex] = new List <GlobalVertex>(); } vertex.CurrentIndex = vertices.Count; verticesAlreadyExported[vertexIndex].Add(vertex); } vertices.Add(vertex); return(vertices.Count - 1); }
private void ExtractGeometry(BabylonAbstractMesh babylonAbstractMesh, List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, List <int> boneIds, IIGameSkin skin, IIGameMesh unskinnedMesh, IMatrix3 invertedWorldMatrix, IMatrix3 offsetTM, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, bool optimizeVertices, int multiMatsCount, IIGameNode meshNode, ref List <int> faceIndexes) { Dictionary <GlobalVertex, List <GlobalVertex> > verticesAlreadyExported = null; if (optimizeVertices) { verticesAlreadyExported = new Dictionary <GlobalVertex, List <GlobalVertex> >(); } var indexStart = 0; // Whether or not to store order in which faces are exported // Storage is used when exporting Morph Targets geometry // To ensure face order is identical, especially with multimaterials involved bool storeFaceIndexes = faceIndexes == null; if (storeFaceIndexes) { faceIndexes = new List <int>(); } int indexInFaceIndexesArray = 0; for (int i = 0; i < multiMatsCount; ++i) { int materialId = i; var indexCount = 0; var minVertexIndex = int.MaxValue; var maxVertexIndex = int.MinValue; var subMesh = new BabylonSubMesh { indexStart = indexStart, materialIndex = i }; if (multiMatsCount == 1) { for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j) { IFaceEx face = null; if (storeFaceIndexes) { face = unskinnedMesh.GetFace(j); // Store face index (j = face.MeshFaceIndex) faceIndexes.Add(j); } else { face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]); } ExtractFace(skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, offsetTM, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } else { if (i == 0 || isMaterialDoubleSided == false) { ITab <IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId); for (int j = 0; j < materialFaces.Count; ++j) { IFaceEx face = null; if (storeFaceIndexes) { // Retreive face #if MAX2017 || MAX2018 || MAX2019 || MAX2020 face = materialFaces[j]; #else face = materialFaces[new IntPtr(j)]; #endif // Store face index faceIndexes.Add(face.MeshFaceIndex); } else { face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]); } ExtractFace(skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, offsetTM, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } else { // It's a double sided material // The back faces are created at runtime // WARNING - Nested multimaterial and double sided material are not supported minVertexIndex = vertices.Count; maxVertexIndex = vertices.Count * 2 - 1; // Vertices int nbVertices = vertices.Count; for (int index = 0; index < nbVertices; index++) { GlobalVertex vertexOrg = vertices[index]; // Duplicate vertex GlobalVertex vertexNew = new GlobalVertex(vertexOrg); // Inverse back vertices normal vertexNew.Normal = vertexNew.Normal.MultiplyBy(-1); vertexNew.Tangent = vertexNew.Tangent.MultiplyBy(-1); vertices.Add(vertexNew); } // Faces int nbIndices = indices.Count; for (int index = 0; index < nbIndices; index += 3) { // Duplicate and flip faces indices.Add(indices[index + 2] + nbIndices); indices.Add(indices[index + 1] + nbIndices); indices.Add(indices[index] + nbIndices); indexCount += 3; } } } if (indexCount != 0) { subMesh.indexCount = indexCount; subMesh.verticesStart = minVertexIndex; subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1; indexStart += indexCount; subMeshes.Add(subMesh); } } }