List<int> SortBones(IIGameSkin skin) { var boneIds = new List<int>(); var boneIndex = new Dictionary<int, IIGameNode>(); for (var index = 0; index < skin.TotalSkinBoneCount; index++) { var bone = skin.GetIGameBone(index, false); if (bone == null) { // non bone in skeletton boneIds.Add(-2); } else { boneIds.Add(bone.NodeID); boneIndex[bone.NodeID] = bone; } } while (true) { bool foundMisMatch = false; for (int i = 0; i < boneIds.Count; ++i) { var id = boneIds[i]; if (id == -2) { continue; } var parent = boneIndex[id].NodeParent; if (parent != null) { var parentId = parent.NodeID; if (boneIds.IndexOf(parentId) > i) { boneIds.RemoveAt(i); boneIds.Insert(boneIds.IndexOf(parentId) + 1, id); foundMisMatch = true; break; } } } if (!foundMisMatch) { break; } } return boneIds; }
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; }
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 ExportSkin(IIGameSkin skin, BabylonScene babylonScene) { var babylonSkeleton = new BabylonSkeleton { id = skins.IndexOf(skin) }; babylonSkeleton.name = "skeleton #" + babylonSkeleton.id; RaiseMessage(babylonSkeleton.name, 1); var skinIndex = skins.IndexOf(skin); var bones = new List<BabylonBone>(); var gameBones = new List<IIGameNode>(); var boneIds = new List<int>(); var bindPoseInfos = new List<BonePoseInfo>(); for(int i = 0; i < skin.TotalSkinBoneCount; ++i) { bones.Add(null); gameBones.Add(null); boneIds.Add(-1); bindPoseInfos.Add(null); } for (var index = 0; index < skin.TotalSkinBoneCount; index++) { var gameBone = skin.GetIGameBone(index, false); var sortedIndex = skinSortedBones[skin].IndexOf(gameBone.NodeID); gameBones[sortedIndex] = (gameBone); boneIds[sortedIndex] =(gameBone.NodeID); bones[sortedIndex]=(new BabylonBone { index = sortedIndex, name = gameBone.Name }); var boneInitMatrix = gameBone.GetObjectTM(0); bindPoseInfos[sortedIndex] = (new BonePoseInfo { AbsoluteTransform = boneInitMatrix }); } // fix hierarchy an generate animation keys var exportNonOptimizedAnimations = Loader.Core.RootNode.GetBoolProperty("babylonjs_exportnonoptimizedanimations"); for (var index = 0; index < skin.TotalSkinBoneCount; index++) { var gameBone = gameBones[index]; var parent = gameBone.NodeParent; var babBone = bones[index]; if (parent != null) { babBone.parentBoneIndex = boneIds.IndexOf(parent.NodeID); } if (babBone.parentBoneIndex == -1) { bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform; } else { var parentBindPoseInfos = bindPoseInfos[babBone.parentBoneIndex]; bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform.Multiply(parentBindPoseInfos.AbsoluteTransform.Inverse); } babBone.matrix = bindPoseInfos[index].LocalTransform.ToArray(); var babylonAnimation = new BabylonAnimation { name = gameBone.Name + "Animation", property = "_matrix", dataType = (int)BabylonAnimation.DataType.Matrix, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, framePerSecond = Loader.Global.FrameRate }; var start = Loader.Core.AnimRange.Start; var end = Loader.Core.AnimRange.End; float[] previous = null; var keys = new List<BabylonAnimationKey>(); for (var key = start; key <= end; key += Ticks) { var objectTM = gameBone.GetObjectTM(key); var parentNode = gameBone.NodeParent; IGMatrix mat; if (parentNode == null || babBone.parentBoneIndex == -1) { mat = objectTM; } else { mat = objectTM.Multiply(parentNode.GetObjectTM(key).Inverse); } var current = mat.ToArray(); if (key == start || key == end || exportNonOptimizedAnimations || !(previous.IsEqualTo(current))) { keys.Add(new BabylonAnimationKey { frame = key / Ticks, values = current }); } previous = current; } babylonAnimation.keys = keys.ToArray(); babBone.animation = babylonAnimation; } babylonSkeleton.needInitialSkinMatrix = true; babylonSkeleton.bones = bones.ToArray(); babylonScene.SkeletonsList.Add(babylonSkeleton); }
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 ExportSkin(IIGameSkin skin, BabylonScene babylonScene) { var babylonSkeleton = new BabylonSkeleton { id = skins.IndexOf(skin) }; babylonSkeleton.name = "skeleton #" + babylonSkeleton.id; RaiseMessage(babylonSkeleton.name, 1); var skinIndex = skins.IndexOf(skin); var bones = new List <BabylonBone>(); var gameBones = new List <IIGameNode>(); var boneIds = new List <int>(); var bindPoseInfos = new List <BonePoseInfo>(); for (int i = 0; i < skin.TotalSkinBoneCount; ++i) { bones.Add(null); gameBones.Add(null); boneIds.Add(-1); bindPoseInfos.Add(null); } for (var index = 0; index < skin.TotalSkinBoneCount; index++) { var gameBone = skin.GetIGameBone(index, false); var sortedIndex = skinSortedBones[skin].IndexOf(gameBone.NodeID); gameBones[sortedIndex] = (gameBone); boneIds[sortedIndex] = (gameBone.NodeID); bones[sortedIndex] = (new BabylonBone { index = sortedIndex, name = gameBone.Name }); var boneInitMatrix = gameBone.GetObjectTM(0); bindPoseInfos[sortedIndex] = (new BonePoseInfo { AbsoluteTransform = boneInitMatrix }); } // fix hierarchy an generate animation keys var exportNonOptimizedAnimations = Loader.Core.RootNode.GetBoolProperty("babylonjs_exportnonoptimizedanimations"); for (var index = 0; index < skin.TotalSkinBoneCount; index++) { var gameBone = gameBones[index]; var parent = gameBone.NodeParent; var babBone = bones[index]; if (parent != null) { babBone.parentBoneIndex = boneIds.IndexOf(parent.NodeID); } if (babBone.parentBoneIndex == -1) { bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform; } else { var parentBindPoseInfos = bindPoseInfos[babBone.parentBoneIndex]; bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform.Multiply(parentBindPoseInfos.AbsoluteTransform.Inverse); } babBone.matrix = bindPoseInfos[index].LocalTransform.ToArray(); var babylonAnimation = new BabylonAnimation { name = gameBone.Name + "Animation", property = "_matrix", dataType = (int)BabylonAnimation.DataType.Matrix, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, framePerSecond = Loader.Global.FrameRate }; var start = Loader.Core.AnimRange.Start; var end = Loader.Core.AnimRange.End; float[] previous = null; var keys = new List <BabylonAnimationKey>(); for (var key = start; key <= end; key += Ticks) { var objectTM = gameBone.GetObjectTM(key); var parentNode = gameBone.NodeParent; IGMatrix mat; if (parentNode == null || babBone.parentBoneIndex == -1) { mat = objectTM; } else { mat = objectTM.Multiply(parentNode.GetObjectTM(key).Inverse); } var current = mat.ToArray(); if (key == start || key == end || exportNonOptimizedAnimations || !(previous.IsEqualTo(current))) { keys.Add(new BabylonAnimationKey { frame = key / Ticks, values = current }); } previous = current; } babylonAnimation.keys = keys.ToArray(); babBone.animation = babylonAnimation; } babylonSkeleton.needInitialSkinMatrix = true; babylonSkeleton.bones = bones.ToArray(); babylonScene.SkeletonsList.Add(babylonSkeleton); }
private void ExportSkin(IIGameSkin skin, BabylonScene babylonScene) { var babylonSkeleton = new BabylonSkeleton { id = skins.IndexOf(skin) }; babylonSkeleton.name = "skeleton #" + babylonSkeleton.id; RaiseMessage(babylonSkeleton.name, 1); var skinIndex = skins.IndexOf(skin); var bones = new List <BabylonBone>(); var gameBones = new List <IIGameNode>(); var boneIds = new List <int>(); var bindPoseInfos = new List <BonePoseInfo>(); for (int i = 0; i < skin.TotalSkinBoneCount; ++i) { bones.Add(null); gameBones.Add(null); boneIds.Add(-1); bindPoseInfos.Add(null); } for (var index = 0; index < skin.TotalSkinBoneCount; index++) { var gameBone = skin.GetIGameBone(index, false); var sortedIndex = skinSortedBones[skin].IndexOf(gameBone.NodeID); gameBones[sortedIndex] = (gameBone); boneIds[sortedIndex] = (gameBone.NodeID); bones[sortedIndex] = (new BabylonBone { index = sortedIndex, name = gameBone.Name }); var boneInitMatrix = gameBone.GetObjectTM(0); bindPoseInfos[sortedIndex] = (new BonePoseInfo { AbsoluteTransform = boneInitMatrix }); } // fix hierarchy and generate animation keys for (var index = 0; index < skin.TotalSkinBoneCount; index++) { var gameBone = gameBones[index]; var parent = gameBone.NodeParent; var babBone = bones[index]; if (parent != null) { babBone.parentBoneIndex = boneIds.IndexOf(parent.NodeID); } if (babBone.parentBoneIndex == -1) { bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform; } else { var parentBindPoseInfos = bindPoseInfos[babBone.parentBoneIndex]; bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform.Multiply(parentBindPoseInfos.AbsoluteTransform.Inverse); } babBone.matrix = bindPoseInfos[index].LocalTransform.ToArray(); var babylonAnimation = ExportMatrixAnimation("_matrix", key => { var objectTM = gameBone.GetObjectTM(key); var parentNode = gameBone.NodeParent; IGMatrix mat; if (parentNode == null || babBone.parentBoneIndex == -1) { mat = objectTM; } else { mat = objectTM.Multiply(parentNode.GetObjectTM(key).Inverse); } return(mat.ToArray()); }, false); // Do not remove linear animation keys for bones if (babylonAnimation != null) { babylonAnimation.name = gameBone.Name + "Animation"; // override default animation name babBone.animation = babylonAnimation; } } babylonSkeleton.needInitialSkinMatrix = true; babylonSkeleton.bones = bones.ToArray(); babylonScene.SkeletonsList.Add(babylonSkeleton); }