private IMatrix3 GetOffsetTM(IIGameNode gameNode, int key) { IPoint3 objOffsetPos = gameNode.MaxNode.ObjOffsetPos; IQuat objOffsetQuat = gameNode.MaxNode.ObjOffsetRot; IPoint3 objOffsetScale = gameNode.MaxNode.ObjOffsetScale.S; // conversion: LH vs RH coordinate system (swap Y and Z) var tmpSwap = objOffsetPos.Y; objOffsetPos.Y = objOffsetPos.Z; objOffsetPos.Z = tmpSwap; tmpSwap = objOffsetQuat.Y; objOffsetQuat.Y = objOffsetQuat.Z; objOffsetQuat.Z = tmpSwap; var objOffsetRotMat = Tools.Identity; objOffsetQuat.MakeMatrix(objOffsetRotMat, true); tmpSwap = objOffsetScale.Y; objOffsetScale.Y = objOffsetScale.Z; objOffsetScale.Z = tmpSwap; // build the offset transform; equivalent in maxscript: // offsetTM = (scaleMatrix $.objectOffsetScale) * ($.objectOffsetRot as matrix3) * (transMatrix $.objectOffsetPos) IMatrix3 offsetTM = Tools.Identity; offsetTM.Scale(objOffsetScale, false); offsetTM.MultiplyBy(objOffsetRotMat); offsetTM.Translate(objOffsetPos); return(offsetTM); }
public static IDMatrix3 ToDMatrix3(this IMatrix3 matrix3) { return(MaxGlobal.DMatrix3.Create( MaxGlobal.DPoint3.Create(matrix3.GetRow(0)), MaxGlobal.DPoint3.Create(matrix3.GetRow(1)), MaxGlobal.DPoint3.Create(matrix3.GetRow(2)), MaxGlobal.DPoint3.Create(matrix3.GetRow(3)) )); }
/// <summary> /// Extracts the transformation matrix from a line /// </summary> /// <param name="line"></param> /// <returns></returns> public IMatrix3 GetTransform(Match values) { // Row 1 float row1x; float.TryParse(values.Result("$5"), out row1x); float row1y; float.TryParse(values.Result("$6"), out row1y); float row1z; float.TryParse(values.Result("$7"), out row1z); // Row 2 float row2x; float.TryParse(values.Result("$8"), out row2x); float row2y; float.TryParse(values.Result("$9"), out row2y); float row2z; float.TryParse(values.Result("$10"), out row2z); // Row 3 float row3x; float.TryParse(values.Result("$11"), out row3x); float row3y; float.TryParse(values.Result("$12"), out row3y); float row3z; float.TryParse(values.Result("$13"), out row3z); // Position float posX; float.TryParse(values.Result("$2"), out posX); float posY; float.TryParse(values.Result("$3"), out posY); float posZ; float.TryParse(values.Result("$4"), out posZ); //posY = -posY; IPoint3 row1 = global.Point3.Create(row1x, row1y, row1z); IPoint3 row2 = global.Point3.Create(row2x, row2y, row2z); IPoint3 row3 = global.Point3.Create(row3x, row3y, row3z); IPoint3 position = global.Point3.Create(posX, posY, posZ); IMatrix3 matrix = global.Matrix3.Create(row1, row2, row3, position); return(matrix); //return null; }
public Matrix3 Invert() { // Lazy, terribly inefficent way to do this. // do this properly (without thunking to unmanaged code) another time. IMatrix3 cppversion = _IMatrix3; cppversion.Invert(); return(new Matrix3(cppversion)); }
public ROD_ExportG() { maxGlobal = Autodesk.Max.GlobalInterface.Instance; maxInterface = maxGlobal.COREInterface14; IPoint3 U = maxGlobal.Point3.Create(1.0, 0.0, 0.0); IPoint3 V = maxGlobal.Point3.Create(0.0, 0.0, 1.0); IPoint3 N = maxGlobal.Point3.Create(0.0, -1.0, 0.0); IPoint3 T = maxGlobal.Point3.Create(0.0, 0.0, 0.0); _leftHanded = maxGlobal.Matrix3.Create(U, V, N, T); _GleftHanded = maxGlobal.GMatrix.Create(_leftHanded); }
public string SetTransform(IMatrix3 matrix) { var row1T = matrix.GetRow(0); var row2T = matrix.GetRow(1); var row3T = matrix.GetRow(2); var row4T = matrix.GetRow(3); string row1 = "[" + row1T.X + ", " + row1T.Y + ", " + row1T.Z + "]"; string row2 = "[" + row2T.X + ", " + row2T.Y + ", " + row2T.Z + "]"; string row3 = "[" + row3T.X + ", " + row3T.Y + ", " + row3T.Z + "]"; string row4 = "[" + (row4T.X * Utility.scaleFactor) + ", " + (row4T.Y * Utility.scaleFactor) + ", " + (row4T.Z * Utility.scaleFactor) + "]"; string transform = "(matrix3 " + row1 + " " + row2 + " " + row3 + " " + row4 + ")"; return(transform); }
public static float[] ToArray(this IMatrix3 value) { var row0 = value.GetRow(0).ToArraySwitched(); var row1 = value.GetRow(1).ToArraySwitched(); var row2 = value.GetRow(2).ToArraySwitched(); var row3 = value.GetRow(3).ToArraySwitched(); return(new[] { row0[0], row0[1], row0[2], 0, row2[0], row2[1], row2[2], 0, row1[0], row1[1], row1[2], 0, row3[0], row3[1], row3[2], 1 }); }
private void ExtractFace(IIGameSkin skin, IIGameMesh unskinnedMesh, 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) { var a = CreateGlobalVertex(unskinnedMesh, invertedWorldMatrix, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); var b = CreateGlobalVertex(unskinnedMesh, invertedWorldMatrix, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); var c = CreateGlobalVertex(unskinnedMesh, 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(); }
public static float[] GetTranslation(IINode node, IINode renderedNode) { float[] res = new float[3]; //IPoint3 translation = node.GetNodeTM(0, Tools.Forever).Trans; //position relative to parent, translation IObject obj = node.ObjectRef; IBox3 bbox = obj.GetWorldBoundBox(0, node, Loader.Core.ActiveViewExp); IPoint3 bboxCenter = bbox.Center; IMatrix3 inverted = renderedNode.GetNodeTM(0, Tools.Forever); inverted.Invert(); IPoint3 bboxCenterInRenderNodeSpace = inverted.PointTransform(bboxCenter); res[0] = bboxCenterInRenderNodeSpace.X; res[1] = bboxCenterInRenderNodeSpace.Z; res[2] = -bboxCenterInRenderNodeSpace.Y; return(res); }
static void Main(string[] args) { Console.WriteLine("Я хочу знайти рiшення системи:\n 1)системи двох лiнiйних рiвнянь з двома невiдомими \n " + "2) системи трьох лiнiйних рiвнянь з трьома невiдомими"); int userChoice = Convert.ToInt32(Console.ReadLine()); if (userChoice == 1) { double x1; double x2; Equation eq = new Equation(); IMatrix2 eq2 = (IMatrix2)eq; eq2.SetValues(ref eq); eq2.Solution(out x1, out x2); Console.WriteLine(); eq2.EquationOutput(); Console.WriteLine(); Console.WriteLine($"x = {x1}, y = {x2}"); } else if (userChoice == 2) { double x1; double x2; double x3; Equation eq = new Equation(); IMatrix3 eq3 = (IMatrix3)eq; eq3.SetValues(ref eq); eq3.Solution(out x1, out x2, out x3); Console.WriteLine(); eq3.EquationOutput(); Console.WriteLine($"x = {x1}, y = {x2}, z = {x3}"); } else { Console.WriteLine("Спробуйте ще раз..."); } }
public static float[] GetRotation(IINode node, IINode renderedNode) { float[] res = new float[4]; IMatrix3 nodeTm = node.GetNodeTM(0, Tools.Forever); IMatrix3 inverted = renderedNode.GetNodeTM(0, Tools.Forever); inverted.Invert(); nodeTm = nodeTm.Multiply(inverted); IPoint3 p = Loader.Global.Point3.Create(0, 0, 0); IQuat q = Loader.Global.IdentQuat; IPoint3 s = Loader.Global.Point3.Create(0, 0, 0); Loader.Global.DecomposeMatrix(nodeTm, p, q, s); q.Normalize(); res[0] = q[0]; res[1] = q[2]; res[2] = -q[1]; res[3] = -q[3]; return(res); }
public ROD_ExportG() { maxGlobal = Autodesk.Max.GlobalInterface.Instance; maxInterface = maxGlobal.COREInterface13; IPoint3 U = maxGlobal.Point3.Create(1.0, 0.0, 0.0); IPoint3 V = maxGlobal.Point3.Create(0.0, 0.0, 1.0); IPoint3 N = maxGlobal.Point3.Create(0.0, -1.0, 0.0); IPoint3 T = maxGlobal.Point3.Create(0.0, 0.0, 0.0); _leftHanded = maxGlobal.Matrix3.Create(U, V, N, T); _GleftHanded = maxGlobal.GMatrix.Create(_leftHanded); }
public static Matrix convertTo(this IMatrix3 _input) { Matrix _output = new Matrix(_input.GetRow(0).X, _input.GetRow(0).Y, _input.GetRow(0).Z, 0, _input.GetRow(1).X, _input.GetRow(1).Y, _input.GetRow(1).Z, 0, _input.GetRow(2).X, _input.GetRow(2).Y, _input.GetRow(2).Z, 0, 0, 0, 0, 1); return(_output); }
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 ExtractGeometry(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) { 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, 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) { #if MAX2017 || MAX2018 var faceIndexer = j; #else var faceIndexer = new IntPtr(j); #endif var face = materialFaces[faceIndexer]; #if !MAX2017 && !MAX2018 Marshal.FreeHGlobal(faceIndexer); #endif ExtractFace(skin, unskinnedMesh, 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); } } }
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); } } }
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(); }
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); }
public Matrix3(IMatrix3 x) : this(new Point3(x.GetRow(0)), new Point3(x.GetRow(1)), new Point3(x.GetRow(2)), new Point3(x.GetRow(3))) { }
/// <summary> /// This is the routine to convert the input node to polygon faces. /// </summary> /// <param name="nodeHandle"> Input the node by handle. </param> /// <param name="convertToTri"> Input whether to convert to a poly object first. </param> /// <param name="addShell"> Input whether to add the shell modifier when finished converting to face. </param> /// <param name="shell"> Input the shell thickness amount. </param> /// <param name="addEditMesh"> Input whether to add the Edit Mesh modifier when finished converting to face. </param> /// <param name="collapseNode"> Input whether to collapse the node afterwards. </param> /// <param name="centerPivot"> Input whether to center the pivot on each new face. </param> /// <returns> Returns 1 if successful or -1 if not. </returns> static public int ConvertToPolygonFaces(uint nodeHandle, bool convertToPoly = true, // C# now supports default parameters bool addShell = true, float shell = 0.1f, bool addEditMesh = true, bool collapseNode = true, bool centerPivot = true) { try { IGlobal global = Autodesk.Max.GlobalInterface.Instance; IInterface14 ip = global.COREInterface14; IINode node = ip.GetINodeByHandle(nodeHandle); if (node == null) { return(-1); } // Get it's current object state. If a modifier has been applied, for example, // it is going to return the OS of the mesh in it's current form in the timeline. IObjectState os = node.ObjectRef.Eval(ip.Time); // Now grab the object itself. IObject objOriginal = os.Obj; IPolyObject polyObject = objOriginal as IPolyObject; IClass_ID cid = global.Class_ID.Create((uint)BuiltInClassIDA.POLYOBJ_CLASS_ID, 0); IPolyObject polyObj = ip.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID) as IPolyObject; if (polyObject == null && convertToPoly) { if (objOriginal.CanConvertToType(global.TriObjectClassID) == 1) { objOriginal = objOriginal.ConvertToType(ip.Time, global.TriObjectClassID); } else { return(-1); } ITriObject triOriginal = objOriginal as ITriObject; polyObj.Mesh.AddTri(triOriginal.Mesh); polyObj.Mesh.FillInMesh(); polyObj.Mesh.EliminateBadVerts(0); polyObj.Mesh.MakePolyMesh(0, true); } else if (polyObject == null) { polyObj = polyObject; } else { return(-1); } IMatrix3 mat = node.GetNodeTM(0, null); IPoint3 ptOffsetPos = node.ObjOffsetPos; IQuat quatOffsetRot = node.ObjOffsetRot; IScaleValue scaleOffsetScale = node.ObjOffsetScale; // We can grab the faces as a List and iterate them in .NET API. int nNumFaces = polyObj.Mesh.FNum; if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressMaxNum = nNumFaces; } ADN_UserBreakCheck checkUserBreak = new ADN_UserBreakCheck(); for (int i = 0; i < nNumFaces; i++) { if (checkUserBreak.Check() == true) { return(-1); } if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressCurrNum = i; } // Create a new poly object for each new face. object objectNewFace = ip.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID); // Create a new node to hold it in the scene. IObject objNewFace = (IObject)objectNewFace; IINode n = global.COREInterface.CreateObjectNode(objNewFace); // Name it and ensure it is unique... string newname = "ADN-Sample-Face"; ip.MakeNameUnique(ref newname); n.Name = newname; // Based on what we created above, we can safely cast it to TriObject IPolyObject polyNewFace = objNewFace as IPolyObject; // Setup the new poly object with 1 face, and the vertex count from the original object's face we are processing polyNewFace.Mesh.SetNumFaces(1); polyNewFace.Mesh.SetMapNum(2); IMNFace f = polyObj.Mesh.F(i); polyNewFace.Mesh.F(0).Assign(f); IMNFace fnew = polyNewFace.Mesh.F(0); IList <int> vtx = f.Vtx; polyNewFace.Mesh.SetNumVerts(vtx.Count); for (int k = 0; k < vtx.Count; k++) { int nvindex = vtx[k]; IMNVert vert = polyObj.Mesh.V(nvindex); Debug.Print("\nVertex = " + k + ", " + nvindex); polyNewFace.Mesh.V(k).Assign(vert); fnew.Vtx[k] = k; } int nedge = nedge = polyNewFace.Mesh.SimpleNewEdge(0, 1); IMNEdge edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); nedge = polyNewFace.Mesh.SimpleNewEdge(1, 2); edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); nedge = polyNewFace.Mesh.SimpleNewEdge(2, 3); edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); nedge = polyNewFace.Mesh.SimpleNewEdge(3, 0); edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); polyNewFace.Mesh.FillInMesh(); // make it update. polyNewFace.Mesh.InvalidateGeomCache(); if (addShell) { AddOsmShell(n.Handle, shell); } if (addEditMesh) { AddOsmEditMesh(n.Handle); } if (collapseNode) { ip.CollapseNode(n, true); } // update transform to match object being exploded. n.SetNodeTM(0, mat); n.ObjOffsetPos = ptOffsetPos; n.ObjOffsetRot = quatOffsetRot; n.ObjOffsetScale = scaleOffsetScale; n.ObjOffsetPos = ptOffsetPos; if (centerPivot) { n.CenterPivot(0, false); } } } catch (Exception ex) { Debug.Print(ex.Message); return(-1); } return(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); } } }
/// <summary> /// This is the routine to convert the input node to triangle faces. /// </summary> /// <param name="nodeHandle"> Input the node by handle. </param> /// <param name="convertToTri"> Input whether to convert to a tri object first. </param> /// <param name="addShell"> Input whether to add the shell modifier when finished converting to face. </param> /// <param name="shell"> Input the shell thickness amount. </param> /// <param name="addEditMesh"> Input whether to add the Edit Mesh modifier when finished converting to face. </param> /// <param name="collapseNode"> Input whether to collapse the node afterwards. </param> /// <param name="centerPivot"> Input whether to center the pivot on each new face. </param> /// <returns> Returns 1 if successful or -1 if not. </returns> static public int ConvertToTriangleFaces(uint nodeHandle, bool convertToTri = true, // C# now supports default parameters bool addShell = true, float shell = 0.1f, bool addEditMesh = true, bool collapseNode = true, bool centerPivot = true) { try { IGlobal global = Autodesk.Max.GlobalInterface.Instance; IInterface14 ip = global.COREInterface14; IINode node = ip.GetINodeByHandle(nodeHandle); // Get it's current object state. If a modifier has been applied, for example, // it is going to return the OS of the mesh in it's current form in the timeline. IObjectState os = node.ObjectRef.Eval(ip.Time); // Now grab the object itself. IObject objOriginal = os.Obj; // Let's make sure it is a TriObject, which is the typical kind of object with a mesh if (!objOriginal.IsSubClassOf(global.TriObjectClassID)) { // If it is NOT, see if we can convert it... if (convertToTri && objOriginal.CanConvertToType(global.TriObjectClassID) == 1) { objOriginal = objOriginal.ConvertToType(ip.Time, global.TriObjectClassID); } else { return(-1); } } // Now we should be safe to know it is a TriObject and we can cast it as such. // An exception will be thrown... ITriObject triOriginal = objOriginal as ITriObject; // Let's first setup a class ID for the type of objects are are creating. // New TriObject in this case to hold each face. IClass_ID cid = global.Class_ID.Create((uint)BuiltInClassIDA.TRIOBJ_CLASS_ID, 0); IMatrix3 mat = node.GetNodeTM(0, null); IPoint3 ptOffsetPos = node.ObjOffsetPos; IQuat quatOffsetRot = node.ObjOffsetRot; IScaleValue scaleOffsetScale = node.ObjOffsetScale; // We can grab the faces as a List and iterate them in .NET API. IMesh mesh = triOriginal.Mesh; IList <IFace> faces = triOriginal.Mesh.Faces; int nNumFaces = faces.Count; if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressMaxNum = nNumFaces; } ADN_UserBreakCheck checkUserBreak = new ADN_UserBreakCheck(); int count = 0; foreach (IFace face in faces) { if (checkUserBreak.Check() == true) { return(-1); } if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressCurrNum = ++count; } // Create a new TriObject for each new face. object objectNewFace = ip.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID); // Create a new node to hold it in the scene. IObject objNewFace = (IObject)objectNewFace; IINode n = global.COREInterface.CreateObjectNode(objNewFace); // Name it and ensure it is unique... string newname = "ADN-Sample-Face"; ip.MakeNameUnique(ref newname); n.Name = newname; // Based on what we created above, we can safely cast it to TriObject ITriObject triNewFace = objNewFace as ITriObject; // Setup the new TriObject with 1 face, and the vertex count from the original object's face we are processing triNewFace.Mesh.SetNumFaces(1, false, false); triNewFace.Mesh.SetNumVerts(face.V.Count(), false, false); // Finish setting up the face (always face '0' because there will only be one per object). triNewFace.Mesh.Faces[0].SetVerts(0, 1, 2); triNewFace.Mesh.Faces[0].SetEdgeVisFlags(EdgeVisibility.Vis, EdgeVisibility.Vis, EdgeVisibility.Vis); triNewFace.Mesh.Faces[0].SmGroup = 2; // Now, for each vertex, get the old face's points and store into new. for (int i = 0; i < face.V.Count(); i++) { //Get the vertex from the original object's face we are processing IPoint3 point = triOriginal.Mesh.GetVert((int)face.GetVert(i)); // Set the vertex point in the new face vertex triNewFace.Mesh.SetVert(i, point); } // make it draw. triNewFace.Mesh.InvalidateGeomCache(); if (addShell) { AddOsmShell(n.Handle, shell); } if (addEditMesh) { AddOsmEditMesh(n.Handle); } if (collapseNode) { ip.CollapseNode(n, true); } // update transform to match object being exploded. n.SetNodeTM(0, mat); n.ObjOffsetPos = ptOffsetPos; n.ObjOffsetRot = quatOffsetRot; n.ObjOffsetScale = scaleOffsetScale; n.ObjOffsetPos = ptOffsetPos; if (centerPivot) { n.CenterPivot(0, false); } } } catch (Exception) { return(-1); } return(1); }
internal static unsafe void ExplodeNode(BaseNode baseNode, IPoint3 moveValue, ushort matID) { //A node is a part of an IIINode, it's a matID part of the real IINode var node = baseNode.INode; var world = node.GetObjTMAfterWSM(0, null); var local = world; if (!node.IsRootNode) { IMatrix3 m3Parent = node.ParentNode.GetObjTMAfterWSM(0, null); local = world.Multiply(m_Global.Inverse(m3Parent)); } //We basically need to keep all the verts - but this could be millions, or we could keep an ID //IINode inode = baseNode.Node; //string sKey = matID.ToString(); //string sMoveValue = string.Format("[{0};{1};{2}]", moveValue.X, moveValue.Y, moveValue.Z); //inode.SetUserPropString(ref sKey, ref sMoveValue); //--------------------------------------------------------------------------------------------- if (baseNode.Mesh.TriMesh != null) //mesh { var mesh = baseNode.Mesh.TriMesh; BitArray facesPerID = baseNode.GetMaterialBitArray(matID); HashSet <int> uniqueVertexIndexList = new HashSet <int>(); //loop through the bitarray to see which faces are set. For the set faces, save the verts for (int i = 0; i < facesPerID.Count; i++) { if (facesPerID[i]) { //This should be a singular face that has the defined ID IFace face = mesh.Faces[i]; IntPtr vertsIndices = face.AllVerts; uint *dwordVertIndices = (uint *)vertsIndices.ToPointer(); for (int j = 0; j < 3; j++) { var vertexIndex = (int)dwordVertIndices[j]; uniqueVertexIndexList.Add(vertexIndex); } } } foreach (var vertexIndex in uniqueVertexIndexList) { var localPos = mesh.GetVert(vertexIndex); var finalPos = CalculateWorldPosVerts(localPos, moveValue, local); mesh.SetVert(vertexIndex, finalPos); } } else //poly { var mesh = baseNode.Mesh.PolyMesh; //Get the bitArray for each ID BitArray facesPerID = baseNode.GetMaterialBitArray(matID); HashSet <int> uniqueVertexIndexList = new HashSet <int>(); //loop through the bitarray to see which faces are set. For the set faces, save the verts for (int i = 0; i < facesPerID.Count; i++) { if (facesPerID[i]) { //This should be a singular face that has the defined ID IMNFace face = mesh.F(i); IList <int> vertIndices = face.Vtx; foreach (var vertexIndex in vertIndices) { uniqueVertexIndexList.Add(vertexIndex); } } } foreach (var vertexIndex in uniqueVertexIndexList) { var localPos = mesh.P(vertexIndex); var finalPos = CalculateWorldPosVerts(localPos, moveValue, local); mesh.V(vertexIndex).P = finalPos; } } }
int CreateGlobalVertex(IIGameMesh mesh, 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], true) // object space }; // Convert position to local space vertex.Position = invertedWorldMatrix.PointTransform(vertex.Position); 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 static IPoint3 CalculateWorldPosVerts(IPoint3 localPos, IPoint3 moveValue, IMatrix3 local) { //Convert to World space var worldPos = local.PointTransform(localPos); //Make changes worldPos.X += moveValue.X; worldPos.Y += moveValue.Y; worldPos.Z += moveValue.Z; //Invert the local matrix var m3LocalInverse = m_Global.Inverse(local); //Convert back to local space return(m3LocalInverse.PointTransform(worldPos)); }
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); }
public static unsafe void BuildBoundingBox(ParentNode parentNode, ushort matID) { var node = parentNode.INode; var world = node.GetObjTMAfterWSM(0, null); var local = world; if (!node.IsRootNode) { IMatrix3 m3Parent = node.ParentNode.GetObjTMAfterWSM(0, null); local = world.Multiply(m_Global.Inverse(m3Parent)); } if (parentNode.Mesh.TriMesh != null) //mesh { var mesh = parentNode.Mesh.TriMesh; BitArray facesPerID = parentNode.GetMaterialBitArray(matID); float?xMaxValue = null, xMinValue = null; float?yMaxValue = null, yMinValue = null; float?zMaxValue = null, zMinValue = null; IPoint3 maxValues, minValues; //loop through the bitarray to see which faces are set. For the set faces, save the verts for (int i = 0; i < facesPerID.Count; i++) { if (facesPerID[i]) { //This should be a singular face that has the defined ID IFace face = mesh.Faces[i]; IntPtr vertsIndices = face.AllVerts; uint *dwordVertIndices = (uint *)vertsIndices.ToPointer(); for (int j = 0; j < 3; j++) { var vertexIndex = (int)dwordVertIndices[j]; //Maybe don't hold giant lists of vertex indices - just do it while we loop over them all //TODO very important check if this is necessary //parentNode.MaterialIDVertexList.Add(vertexIndex); var pos = mesh.GetVert(vertexIndex); pos = local.PointTransform(pos); FindMaxMinValues(ref xMaxValue, ref xMinValue, ref yMaxValue, ref yMinValue, ref zMaxValue, ref zMinValue, pos); } } } maxValues = m_Global.Point3.Create(xMaxValue.Value, yMaxValue.Value, zMaxValue.Value); minValues = m_Global.Point3.Create(xMinValue.Value, yMinValue.Value, zMinValue.Value); var bb = new BoundingBox(maxValues, minValues); parentNode.SetBoundingBox(matID, bb); } else { var mesh = parentNode.Mesh.PolyMesh; //Get the bitArray for each ID BitArray facesPerID = parentNode.GetMaterialBitArray(matID); float?xMaxValue = null, xMinValue = null; float?yMaxValue = null, yMinValue = null; float?zMaxValue = null, zMinValue = null; IPoint3 maxValues, minValues; //loop through the bitarray to see which faces are set. For the set faces, save the verts for (int i = 0; i < facesPerID.Count; i++) { if (facesPerID[i]) { //This should be a singular face that has the defined ID IMNFace face = mesh.F(i); IList <int> vertIndices = face.Vtx; foreach (var vertexIndex in vertIndices) { //parentNode.MaterialIDVertexList.Add(vertexIndex); var pos = mesh.P(vertexIndex); pos = local.PointTransform(pos); FindMaxMinValues(ref xMaxValue, ref xMinValue, ref yMaxValue, ref yMinValue, ref zMaxValue, ref zMinValue, pos); } } } maxValues = m_Global.Point3.Create(xMaxValue.Value, yMaxValue.Value, zMaxValue.Value); minValues = m_Global.Point3.Create(xMinValue.Value, yMinValue.Value, zMinValue.Value); var bb = new BoundingBox(maxValues, minValues); parentNode.SetBoundingBox(matID, bb); } }