/// <summary> /// Extract ordered indices on a triangle basis /// Extract position and normal of each vertex per face /// </summary> /// <param name="mFnMesh"></param> /// <param name="vertices"></param> /// <param name="indices"></param> /// <param name="subMeshes"></param> /// <param name="optimizeVertices"></param> private void ExtractGeometry(MFnMesh mFnMesh, List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, bool optimizeVertices) { // TODO - Multimaterials: create a BabylonSubMesh per submaterial // TODO - optimizeVertices MIntArray triangleCounts = new MIntArray(); MIntArray trianglesVertices = new MIntArray(); mFnMesh.getTriangles(triangleCounts, trianglesVertices); // For each polygon of this mesh for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++) { MIntArray verticesId = new MIntArray(); int nbTriangles = triangleCounts[polygonId]; // For each triangle of this polygon for (int triangleIndex = 0; triangleIndex < triangleCounts[polygonId]; triangleIndex++) { int[] triangleVertices = new int[3]; mFnMesh.getPolygonTriangleVertices(polygonId, triangleIndex, triangleVertices); // Inverse winding order var tmp = triangleVertices[1]; triangleVertices[1] = triangleVertices[2]; triangleVertices[2] = tmp; // For each vertex of this triangle (3 vertices per triangle) foreach (int vertexId in triangleVertices) { MPoint point = new MPoint(); mFnMesh.getPoint(vertexId, point); MVector normal = new MVector(); mFnMesh.getFaceVertexNormal(polygonId, vertexId, normal); var vertex = new GlobalVertex { BaseIndex = vertexId, Position = point.toArray(), Normal = normal.toArray() }; indices.Add(vertices.Count); vertices.Add(vertex); } } } // BabylonSubMesh var subMesh = new BabylonSubMesh { indexStart = 0, materialIndex = 0 }; subMeshes.Add(subMesh); subMesh.indexCount = indices.Count; subMesh.verticesStart = 0; subMesh.verticesCount = vertices.Count; }
private void SetBabylonMaterial(BabylonMesh babylonMesh, BabylonSubMesh babylonSubMesh, GLTFMeshPrimitive meshPrimitive) { // Retreive the babylon material BabylonMaterial babylonMaterial; var babylonMaterialId = babylonMesh.materialId; // From multi materials first, if any // Loop recursively even though it shouldn't be a real use case var babylonMultiMaterials = new List <BabylonMultiMaterial>(babylonScene.multiMaterials); BabylonMultiMaterial babylonMultiMaterial; do { babylonMultiMaterial = babylonMultiMaterials.Find(_babylonMultiMaterial => _babylonMultiMaterial.id == babylonMaterialId); if (babylonMultiMaterial != null) { babylonMaterialId = babylonMultiMaterial.materials[babylonSubMesh.materialIndex]; } }while (babylonMultiMaterial != null); // Then from materials var babylonMaterials = new List <BabylonMaterial>(babylonScene.materials); babylonMaterial = babylonMaterials.Find(_babylonMaterial => _babylonMaterial.id == babylonMaterialId); meshPrimitive.mode = GLTFMeshPrimitive.FillMode.TRIANGLES; // If babylon material was exported successfully if (babylonMaterial != null) { // Update primitive material index var indexMaterial = babylonMaterialsToExport.FindIndex(_babylonMaterial => _babylonMaterial == babylonMaterial); if (indexMaterial == -1) { // Store material for exportation indexMaterial = babylonMaterialsToExport.Count; babylonMaterialsToExport.Add(babylonMaterial); } meshPrimitive.material = indexMaterial; // TODO - Add and retreive info from babylon material if (babylonMaterial.wireframe) { meshPrimitive.mode = GLTFMeshPrimitive.FillMode.LINE_STRIP; } } }
/// <summary> /// Extract ordered indices on a triangle basis /// Extract position and normal of each vertex per face /// </summary> /// <param name="mFnMesh"></param> /// <param name="vertices"></param> /// <param name="indices"></param> /// <param name="subMeshes"></param> /// <param name="uvSetNames"></param> /// <param name="isUVExportSuccess"></param> /// <param name="optimizeVertices"></param> private void ExtractGeometry(MFnMesh mFnMesh, List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, MStringArray uvSetNames, ref bool[] isUVExportSuccess, bool optimizeVertices) { // TODO - optimizeVertices MIntArray triangleCounts = new MIntArray(); MIntArray trianglesVertices = new MIntArray(); mFnMesh.getTriangles(triangleCounts, trianglesVertices); MObjectArray shaders = new MObjectArray(); MIntArray faceMatIndices = new MIntArray(); // given a face index => get a shader index mFnMesh.getConnectedShaders(0, shaders, faceMatIndices); // Export geometry even if an error occured with shaders // This is a fix for Maya test files // TODO - Find the reason why shaders.count = 0 int nbShaders = Math.Max(1, shaders.Count); bool checkShader = nbShaders == shaders.Count; RaiseVerbose("shaders.Count=" + shaders.Count, 2); // For each material of this mesh for (int indexShader = 0; indexShader < nbShaders; indexShader++) { var nbIndicesSubMesh = 0; var minVertexIndexSubMesh = int.MaxValue; var maxVertexIndexSubMesh = int.MinValue; var subMesh = new BabylonSubMesh { indexStart = indices.Count, materialIndex = indexShader }; // For each polygon of this mesh for (int polygonId = 0; polygonId < faceMatIndices.Count; polygonId++) { if (checkShader && faceMatIndices[polygonId] != indexShader) { continue; } // The object-relative (mesh-relative/global) vertex indices for this face MIntArray polygonVertices = new MIntArray(); mFnMesh.getPolygonVertices(polygonId, polygonVertices); // For each triangle of this polygon for (int triangleId = 0; triangleId < triangleCounts[polygonId]; triangleId++) { int[] polygonTriangleVertices = new int[3]; mFnMesh.getPolygonTriangleVertices(polygonId, triangleId, polygonTriangleVertices); /* * Switch coordinate system at global level * * Piece of code kept just in case * See BabylonExporter for more information */ //// Inverse winding order to flip faces //var tmp = triangleVertices[1]; //triangleVertices[1] = triangleVertices[2]; //triangleVertices[2] = tmp; // For each vertex of this triangle (3 vertices per triangle) foreach (int vertexIndexGlobal in polygonTriangleVertices) { // Get the face-relative (local) vertex id int vertexIndexLocal = 0; for (vertexIndexLocal = 0; vertexIndexLocal < polygonVertices.Count; vertexIndexLocal++) { if (polygonVertices[vertexIndexLocal] == vertexIndexGlobal) { break; } } GlobalVertex vertex = ExtractVertex(mFnMesh, polygonId, vertexIndexGlobal, vertexIndexLocal, uvSetNames, ref isUVExportSuccess); vertex.CurrentIndex = vertices.Count; indices.Add(vertex.CurrentIndex); vertices.Add(vertex); minVertexIndexSubMesh = Math.Min(minVertexIndexSubMesh, vertex.CurrentIndex); maxVertexIndexSubMesh = Math.Max(maxVertexIndexSubMesh, vertex.CurrentIndex); nbIndicesSubMesh++; } } } if (nbIndicesSubMesh != 0) { subMesh.indexCount = nbIndicesSubMesh; subMesh.verticesStart = minVertexIndexSubMesh; subMesh.verticesCount = maxVertexIndexSubMesh - minVertexIndexSubMesh + 1; subMeshes.Add(subMesh); } } }
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); } } }
private void _exportMorphTargets(BabylonMesh babylonMesh, BabylonSubMesh babylonSubMesh, BabylonMorphTargetManager babylonMorphTargetManager, GLTF gltf, GLTFBuffer buffer, GLTFMeshPrimitive meshPrimitive) { var gltfMorphTargets = new List <GLTFMorphTarget>(); foreach (var babylonMorphTarget in babylonMorphTargetManager.targets) { var gltfMorphTarget = new GLTFMorphTarget(); // Positions if (babylonMorphTarget.positions != null) { var accessorTargetPositions = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatVec3(gltf, buffer), "accessorTargetPositions", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC3 ); gltfMorphTarget.Add(GLTFMorphTarget.Attribute.POSITION.ToString(), accessorTargetPositions.index); // Populate accessor int nbComponents = 3; // Vector3 int startIndex = babylonSubMesh.verticesStart * nbComponents; int endIndex = startIndex + babylonSubMesh.verticesCount * nbComponents; accessorTargetPositions.min = new float[] { float.MaxValue, float.MaxValue, float.MaxValue }; accessorTargetPositions.max = new float[] { float.MinValue, float.MinValue, float.MinValue }; for (int indexPosition = startIndex; indexPosition < endIndex; indexPosition += 3) { var positionTarget = Tools.SubArray(babylonMorphTarget.positions, indexPosition, 3); // Babylon stores morph target information as final data while glTF expects deltas from mesh primitive var positionMesh = Tools.SubArray(babylonMesh.positions, indexPosition, 3); for (int indexCoordinate = 0; indexCoordinate < positionTarget.Length; indexCoordinate++) { positionTarget[indexCoordinate] = positionTarget[indexCoordinate] - positionMesh[indexCoordinate]; } positionTarget[2] *= -1; // Store values as bytes foreach (var coordinate in positionTarget) { accessorTargetPositions.bytesList.AddRange(BitConverter.GetBytes(coordinate)); } // Update min and max values GLTFBufferService.UpdateMinMaxAccessor(accessorTargetPositions, positionTarget); } accessorTargetPositions.count = babylonSubMesh.verticesCount; } // Normals if ((babylonMorphTarget.normals != null) && _exportMorphNormal) { var accessorTargetNormals = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatVec3(gltf, buffer), "accessorTargetNormals", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC3 ); gltfMorphTarget.Add(GLTFMorphTarget.Attribute.NORMAL.ToString(), accessorTargetNormals.index); // Populate accessor int nbComponents = 3; // Vector3 int startIndex = babylonSubMesh.verticesStart * nbComponents; int endIndex = startIndex + babylonSubMesh.verticesCount * nbComponents; for (int indexNormal = startIndex; indexNormal < endIndex; indexNormal += 3) { var normalTarget = Tools.SubArray(babylonMorphTarget.normals, indexNormal, 3); // Babylon stores morph target information as final data while glTF expects deltas from mesh primitive var normalMesh = Tools.SubArray(babylonMesh.normals, indexNormal, 3); for (int indexCoordinate = 0; indexCoordinate < normalTarget.Length; indexCoordinate++) { normalTarget[indexCoordinate] = normalTarget[indexCoordinate] - normalMesh[indexCoordinate]; } normalTarget[2] *= -1; // Store values as bytes foreach (var coordinate in normalTarget) { accessorTargetNormals.bytesList.AddRange(BitConverter.GetBytes(coordinate)); } } accessorTargetNormals.count = babylonSubMesh.verticesCount; } // Tangents if ((babylonMorphTarget.tangents != null) && _exportMorphTangent) { var accessorTargetTangents = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatVec3(gltf, buffer), "accessorTargetTangents", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC3 ); gltfMorphTarget.Add(GLTFMeshPrimitive.Attribute.TANGENT.ToString(), accessorTargetTangents.index); // Populate accessor // Note that the w component for handedness is omitted when targeting TANGENT data since handedness cannot be displaced. int nbComponents = 4; // Vector4 int startIndex = babylonSubMesh.verticesStart * nbComponents; int endIndex = startIndex + babylonSubMesh.verticesCount * nbComponents; for (int indexTangent = startIndex; indexTangent < endIndex; indexTangent += 4) { var tangentTarget = Tools.SubArray(babylonMorphTarget.tangents, indexTangent, 3); // Babylon stores morph target information as final data while glTF expects deltas from mesh primitive var tangentMesh = Tools.SubArray(babylonMesh.tangents, indexTangent, 3); for (int indexCoordinate = 0; indexCoordinate < tangentTarget.Length; indexCoordinate++) { tangentTarget[indexCoordinate] = tangentTarget[indexCoordinate] - tangentMesh[indexCoordinate]; } tangentTarget[2] *= -1; // Store values as bytes foreach (var coordinate in tangentTarget) { accessorTargetTangents.bytesList.AddRange(BitConverter.GetBytes(coordinate)); } } accessorTargetTangents.count = babylonSubMesh.verticesCount; } gltfMorphTargets.Add(gltfMorphTarget); } if (gltfMorphTargets.Count > 0) { meshPrimitive.targets = gltfMorphTargets.ToArray(); } }
private void ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene) { if (meshNode.MaxNode.IsInstance()) { return; } if (meshNode.MaxNode.GetBoolProperty("babylonjs_noexport")) { return; } if (!ExportHiddenObjects && meshNode.MaxNode.IsHidden(NodeHideFlags.None, false)) { return; } var gameMesh = meshNode.IGameObject.AsGameMesh(); bool initialized = gameMesh.InitializeData; //needed, the property is in fact a method initializing the exporter that has wrongly been auto // translated into a property because it has no parameters var babylonMesh = new BabylonMesh { name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString() }; if (meshNode.NodeParent != null) { babylonMesh.parentId = GetParentID(meshNode.NodeParent, babylonScene, scene); } // Sounds var soundName = meshNode.MaxNode.GetStringProperty("babylonjs_sound_filename", ""); if (!string.IsNullOrEmpty(soundName)) { var filename = Path.GetFileName(soundName); var meshSound = new BabylonSound { name = filename, autoplay = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_autoplay", 1), loop = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_loop", 1), volume = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_volume", 1.0f), playbackRate = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_playbackrate", 1.0f), connectedMeshId = babylonMesh.id, isDirectional = false, spatialSound = false, distanceModel = meshNode.MaxNode.GetStringProperty("babylonjs_sound_distancemodel", "linear"), maxDistance = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_maxdistance", 100f), rolloffFactor = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_rolloff", 1.0f), refDistance = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_refdistance", 1.0f), }; var isDirectional = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_directional"); if (isDirectional) { meshSound.isDirectional = true; meshSound.coneInnerAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneinnerangle", 360f); meshSound.coneOuterAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneouterangle", 360f); meshSound.coneOuterGain = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneoutergain", 1.0f); } babylonScene.SoundsList.Add(meshSound); try { File.Copy(soundName, Path.Combine(babylonScene.OutputPath, filename), true); } catch { } } // Misc. #if MAX2017 babylonMesh.isVisible = meshNode.MaxNode.Renderable; babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows; babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics; #else babylonMesh.isVisible = meshNode.MaxNode.Renderable == 1; babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1; babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1; #endif babylonMesh.pickable = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable"); babylonMesh.showBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showboundingbox"); babylonMesh.showSubMeshesBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox"); babylonMesh.alphaIndex = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_alphaindex", 1000); // Actions babylonMesh.actions = ExportNodeAction(meshNode); // Collisions babylonMesh.checkCollisions = meshNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions"); var isSkinned = gameMesh.IsObjectSkinned; var skin = gameMesh.IGameSkin; var unskinnedMesh = gameMesh; IGMatrix skinInitPoseMatrix = Loader.Global.GMatrix.Create(Loader.Global.Matrix3.Create(true)); List <int> boneIds = null; int maxNbBones = 0; if (isSkinned) { bonesCount = skin.TotalSkinBoneCount; skins.Add(skin); skinnedNodes.Add(meshNode); babylonMesh.skeletonId = skins.IndexOf(skin); skin.GetInitSkinTM(skinInitPoseMatrix); boneIds = SortBones(skin); skinSortedBones[skin] = boneIds; } // Position / rotation / scaling var localTM = meshNode.GetObjectTM(0); if (meshNode.NodeParent != null) { var parentWorld = meshNode.NodeParent.GetObjectTM(0); localTM.MultiplyBy(parentWorld.Inverse); } var meshTrans = localTM.Translation; var meshRotation = localTM.Rotation; var meshScale = localTM.Scaling; var exportQuaternions = Loader.Core.RootNode.GetBoolProperty("babylonjs_exportquaternions"); babylonMesh.position = new[] { meshTrans.X, meshTrans.Y, meshTrans.Z }; if (exportQuaternions) { babylonMesh.rotationQuaternion = new[] { meshRotation.X, meshRotation.Y, meshRotation.Z, -meshRotation.W }; } else { RotationToEulerAngles(babylonMesh, meshRotation); } babylonMesh.scaling = new[] { meshScale.X, meshScale.Y, meshScale.Z }; // Mesh RaiseMessage(meshNode.Name, 1); if (unskinnedMesh.IGameType == Autodesk.Max.IGameObject.ObjectTypes.Mesh && unskinnedMesh.MaxMesh != null) { if (unskinnedMesh.NumberOfFaces < 1) { RaiseError($"Mesh {babylonMesh.name} has no face", 2); } if (unskinnedMesh.NumberOfVerts < 3) { RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2); } if (unskinnedMesh.NumberOfVerts >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has tmore than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", 2); } if (skin != null) { for (var vertexIndex = 0; vertexIndex < unskinnedMesh.NumberOfVerts; vertexIndex++) { maxNbBones = Math.Max(maxNbBones, skin.GetNumberOfBones(vertexIndex)); } } // Physics var impostorText = meshNode.MaxNode.GetStringProperty("babylonjs_impostor", "None"); if (impostorText != "None") { switch (impostorText) { case "Sphere": babylonMesh.physicsImpostor = 1; break; case "Box": babylonMesh.physicsImpostor = 2; break; case "Plane": babylonMesh.physicsImpostor = 3; break; default: babylonMesh.physicsImpostor = 0; break; } babylonMesh.physicsMass = meshNode.MaxNode.GetFloatProperty("babylonjs_mass"); babylonMesh.physicsFriction = meshNode.MaxNode.GetFloatProperty("babylonjs_friction", 0.2f); babylonMesh.physicsRestitution = meshNode.MaxNode.GetFloatProperty("babylonjs_restitution", 0.2f); } // Material var mtl = meshNode.NodeMaterial; var multiMatsCount = 1; if (mtl != null) { babylonMesh.materialId = mtl.MaxMaterial.GetGuid().ToString(); if (!referencedMaterials.Contains(mtl)) { referencedMaterials.Add(mtl); } multiMatsCount = Math.Max(mtl.SubMaterialCount, 1); } babylonMesh.visibility = meshNode.MaxNode.GetVisibility(0, Tools.Forever); var vertices = new List <GlobalVertex>(); var indices = new List <int>(); var mappingChannels = unskinnedMesh.ActiveMapChannelNum; bool hasUV = false; bool hasUV2 = false; for (int i = 0; i < mappingChannels.Count; ++i) { #if MAX2017 var indexer = i; #else var indexer = new IntPtr(i); #endif var channelNum = mappingChannels[indexer]; if (channelNum == 1) { hasUV = true; } else if (channelNum == 2) { hasUV2 = true; } } var hasColor = unskinnedMesh.NumberOfColorVerts > 0; var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0; var optimizeVertices = meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices"); // Compute normals List <GlobalVertex>[] verticesAlreadyExported = null; if (optimizeVertices) { verticesAlreadyExported = new List <GlobalVertex> [unskinnedMesh.NumberOfVerts]; } var subMeshes = new List <BabylonSubMesh>(); 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); } } if (vertices.Count >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has {vertices.Count} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 2); if (!optimizeVertices) { RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2); } } RaiseMessage($"{vertices.Count} vertices, {indices.Count/3} faces", 2); // Buffers babylonMesh.positions = vertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray(); babylonMesh.normals = vertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray(); if (hasUV) { babylonMesh.uvs = vertices.SelectMany(v => new[] { v.UV.X, 1 - v.UV.Y }).ToArray(); } if (hasUV2) { babylonMesh.uvs2 = vertices.SelectMany(v => new[] { v.UV2.X, 1 - v.UV2.Y }).ToArray(); } if (skin != null) { babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray(); babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray(); babylonMesh.numBoneInfluencers = maxNbBones; if (maxNbBones > 4) { babylonMesh.matricesWeightsExtra = vertices.SelectMany(v => v.WeightsExtra != null ? v.WeightsExtra.ToArray() : new[] { 0.0f, 0.0f, 0.0f, 0.0f }).ToArray(); babylonMesh.matricesIndicesExtra = vertices.Select(v => v.BonesIndicesExtra).ToArray(); } } if (hasColor) { babylonMesh.colors = vertices.SelectMany(v => v.Color.ToArray()).ToArray(); babylonMesh.hasVertexAlpha = hasAlpha; } babylonMesh.subMeshes = subMeshes.ToArray(); // Buffers - Indices babylonMesh.indices = indices.ToArray(); } // Instances var tabs = Loader.Global.NodeTab.Create(); Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode.MaxNode, tabs); var instances = new List <BabylonAbstractMesh>(); for (var index = 0; index < tabs.Count; index++) { #if MAX2017 var indexer = index; #else var indexer = new IntPtr(index); #endif var tab = tabs[indexer]; #if !MAX2017 Marshal.FreeHGlobal(indexer); #endif if (meshNode.MaxNode.GetGuid() == tab.GetGuid()) { continue; } var instanceGameNode = scene.GetIGameNode(tab); if (instanceGameNode == null) { continue; } tab.MarkAsInstance(); var instance = new BabylonAbstractMesh { name = tab.Name }; { var instanceLocalTM = instanceGameNode.GetObjectTM(0); var instanceTrans = instanceLocalTM.Translation; var instanceRotation = instanceLocalTM.Rotation; var instanceScale = instanceLocalTM.Scaling; instance.position = new[] { instanceTrans.X, instanceTrans.Y, instanceTrans.Z }; if (exportQuaternions) { instance.rotationQuaternion = new[] { instanceRotation.X, instanceRotation.Y, instanceRotation.Z, -instanceRotation.W }; } else { RotationToEulerAngles(instance, instanceRotation); } instance.scaling = new[] { instanceScale.X, instanceScale.Y, instanceScale.Z }; } var instanceAnimations = new List <BabylonAnimation>(); GenerateCoordinatesAnimations(meshNode, instanceAnimations); instance.animations = instanceAnimations.ToArray(); instances.Add(instance); } babylonMesh.instances = instances.ToArray(); // Animations var animations = new List <BabylonAnimation>(); GenerateCoordinatesAnimations(meshNode, animations); if (!ExportFloatController(meshNode.MaxNode.VisController, "visibility", animations)) { ExportFloatAnimation("visibility", animations, key => new[] { meshNode.MaxNode.GetVisibility(key, Tools.Forever) }); } babylonMesh.animations = animations.ToArray(); if (meshNode.MaxNode.GetBoolProperty("babylonjs_autoanimate", 1)) { babylonMesh.autoAnimate = true; babylonMesh.autoAnimateFrom = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_from"); babylonMesh.autoAnimateTo = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_to", 100); babylonMesh.autoAnimateLoop = meshNode.MaxNode.GetBoolProperty("babylonjs_autoanimateloop", 1); } babylonScene.MeshesList.Add(babylonMesh); }
void DumpObject(NovaObject novaObject, BabylonScene babylonScene, NovaScene scene, int startIndex, int endIndex, string nameIndex = "") { var babylonMesh = new BabylonMesh(); babylonScene.MeshesList.Add(babylonMesh); babylonMesh.name = novaObject.Name + nameIndex; babylonMesh.id = novaObject.ID.ToString(); babylonMesh.materialId = novaObject.Material == null ? "" : novaObject.Material.ID.ToString(); babylonMesh.parentId = novaObject.ParentEntity == null ? "" : novaObject.ParentEntity.ID.ToString(); babylonMesh.isEnabled = novaObject.Enabled; babylonMesh.isVisible = novaObject.Renderable; babylonMesh.visibility = novaObject.Visibility; babylonMesh.checkCollisions = novaObject.CheckCollisions; babylonMesh.receiveShadows = novaObject.ReceiveShadows; babylonMesh.infiniteDistance = novaObject.InfiniteDistance; if (novaObject.Billboard) { babylonMesh.billboardMode |= (novaObject.BillboardX ? 1 : 0); babylonMesh.billboardMode |= (novaObject.BillboardY ? 2 : 0); babylonMesh.billboardMode |= (novaObject.BillboardZ ? 4 : 0); } if (novaObject.ParticleSystem != null) { particleSystemsToExport.Add(novaObject.ParticleSystem); } // Mirror if (novaObject.IsMirror && novaObject.Material != null) { mirrorsMaterials.Add(novaObject.Material, novaObject); } // World babylonMesh.position = novaObject.Position.ToArray(); babylonMesh.rotation = novaObject.Rotation.ToArray(); babylonMesh.localMatrix = (Matrix.Scaling(novaObject.Scaling) * novaObject.LocalMatrix).ToArray(); // Animations var animations = new List<BabylonAnimation>(); DumpInterpolator("Visibility animation", "visibility", novaObject.VisibilityInterpolator, scene, animations); // Position if (!DumpInterpolator("Position animation", "position", novaObject.PositionInterpolator, scene, animations)) { DumpInterpolator("PositionX animation", "position.x", novaObject.PositionXInterpolator, scene, animations); DumpInterpolator("PositionY animation", "position.y", novaObject.PositionYInterpolator, scene, animations); DumpInterpolator("PositionZ animation", "position.z", novaObject.PositionZInterpolator, scene, animations); } // Rotation if (!DumpInterpolator("Rotation animation", "rotationQuaternion", novaObject.RotationInterpolator, scene, animations)) { DumpInterpolator("RotationX animation", "rotation.x", novaObject.RotationXInterpolator, scene, animations, -novaObject.Determinant); DumpInterpolator("RotationY animation", "rotation.y", novaObject.RotationYInterpolator, scene, animations, -novaObject.Determinant); DumpInterpolator("RotationZ animation", "rotation.z", novaObject.RotationZInterpolator, scene, animations, -novaObject.Determinant); } else { babylonMesh.localMatrix = Matrix.Identity.ToArray(); babylonMesh.scaling = novaObject.Scaling.ToArray(); } // Scaling if (!DumpInterpolator("Scaling animation", "scaling", novaObject.ScalingInterpolator, scene, animations)) { DumpInterpolator("ScalingX animation", "scaling.x", novaObject.ScalingXInterpolator, scene, animations); DumpInterpolator("ScalingY animation", "scaling.y", novaObject.ScalingYInterpolator, scene, animations); DumpInterpolator("ScalingZ animation", "scaling.z", novaObject.ScalingZInterpolator, scene, animations); } else { babylonMesh.localMatrix = novaObject.LocalMatrix.ToArray(); babylonMesh.scaling = novaObject.Scaling.ToArray(); } babylonMesh.animations = animations.ToArray(); babylonMesh.autoAnimate = novaObject.AutoAnimate; if (novaObject.AutoAnimate) { babylonMesh.autoAnimateFrom = novaObject.AnimationStartKey; if (novaObject.AnimationEndKey == -1) { babylonMesh.autoAnimateTo = scene.AnimationKeyMax / scene.AnimationKeyStep; babylonMesh.autoAnimateLoop = true; } else { babylonMesh.autoAnimateTo = novaObject.AnimationEndKey; } } // Vertices & faces var exportedVerticesCount = DumpObjectGeometry(novaObject, babylonMesh, startIndex, endIndex); // Subobjects var subMeshes = new List<BabylonSubMesh>(); if (novaObject.Is32bits) { var subMesh = new BabylonSubMesh(); subMesh.materialIndex = 0; subMesh.verticesStart = 0; subMesh.verticesCount = exportedVerticesCount; subMesh.indexStart = 0; subMesh.indexCount = babylonMesh.indices.Length; subMeshes.Add(subMesh); } else { foreach (NovaSubObject subObject in novaObject.SubObjects) { var subMesh = new BabylonSubMesh(); subMesh.materialIndex = subObject.AttributeRange.AttributeId; subMesh.verticesStart = subObject.AttributeRange.VertexStart; subMesh.verticesCount = subObject.AttributeRange.VertexCount; subMesh.indexStart = subObject.AttributeRange.FaceStart * 3; subMesh.indexCount = subObject.AttributeRange.FaceCount * 3; subMeshes.Add(subMesh); } } babylonMesh.subMeshes = subMeshes.ToArray(); if (novaObject.Material != null) { if (!materialsToExport.Contains(novaObject.Material)) { materialsToExport.Add(novaObject.Material); var multiMat = novaObject.Material as NovaMultiMaterial; if (multiMat != null) { foreach (var mat in multiMat.Materials) { if (!materialsToExport.Contains(mat)) { materialsToExport.Add(mat); } } } } } }
void DumpObject(NovaObject novaObject, BabylonScene babylonScene, NovaScene scene, int startIndex, int endIndex, string nameIndex = "") { var babylonMesh = new BabylonMesh(); babylonScene.MeshesList.Add(babylonMesh); babylonMesh.name = novaObject.Name + nameIndex; babylonMesh.id = novaObject.ID.ToString(); babylonMesh.materialId = novaObject.Material == null ? "" : novaObject.Material.ID.ToString(); babylonMesh.parentId = novaObject.ParentEntity == null ? "" : novaObject.ParentEntity.ID.ToString(); babylonMesh.isEnabled = novaObject.Enabled; babylonMesh.isVisible = novaObject.Renderable; babylonMesh.visibility = novaObject.Visibility; babylonMesh.checkCollisions = novaObject.CheckCollisions; babylonMesh.receiveShadows = novaObject.ReceiveShadows; if (novaObject.Billboard) { babylonMesh.billboardMode |= (novaObject.BillboardX ? 1 : 0); babylonMesh.billboardMode |= (novaObject.BillboardY ? 2 : 0); babylonMesh.billboardMode |= (novaObject.BillboardZ ? 4 : 0); } if (novaObject.ParticleSystem != null) { particleSystemsToExport.Add(novaObject.ParticleSystem); } // Mirror if (novaObject.IsMirror && novaObject.Material != null) { mirrorsMaterials.Add(novaObject.Material, novaObject); } // World babylonMesh.position = novaObject.Position.ToArray(); babylonMesh.rotation = novaObject.Rotation.ToArray(); babylonMesh.localMatrix = (Matrix.Scaling(novaObject.Scaling) * novaObject.LocalMatrix).ToArray(); // Animations var animations = new List <BabylonAnimation>(); DumpInterpolator("Visibility animation", "visibility", novaObject.VisibilityInterpolator, scene, animations); // Position if (!DumpInterpolator("Position animation", "position", novaObject.PositionInterpolator, scene, animations)) { DumpInterpolator("PositionX animation", "position.x", novaObject.PositionXInterpolator, scene, animations); DumpInterpolator("PositionY animation", "position.y", novaObject.PositionYInterpolator, scene, animations); DumpInterpolator("PositionZ animation", "position.z", novaObject.PositionZInterpolator, scene, animations); } // Rotation if (!DumpInterpolator("Rotation animation", "rotationQuaternion", novaObject.RotationInterpolator, scene, animations)) { DumpInterpolator("RotationX animation", "rotation.x", novaObject.RotationXInterpolator, scene, animations, -novaObject.Determinant); DumpInterpolator("RotationY animation", "rotation.y", novaObject.RotationYInterpolator, scene, animations, -novaObject.Determinant); DumpInterpolator("RotationZ animation", "rotation.z", novaObject.RotationZInterpolator, scene, animations, -novaObject.Determinant); } else { babylonMesh.localMatrix = Matrix.Identity.ToArray(); babylonMesh.scaling = novaObject.Scaling.ToArray(); } // Scaling if (!DumpInterpolator("Scaling animation", "scaling", novaObject.ScalingInterpolator, scene, animations)) { DumpInterpolator("ScalingX animation", "scaling.x", novaObject.ScalingXInterpolator, scene, animations); DumpInterpolator("ScalingY animation", "scaling.y", novaObject.ScalingYInterpolator, scene, animations); DumpInterpolator("ScalingZ animation", "scaling.z", novaObject.ScalingZInterpolator, scene, animations); } else { babylonMesh.localMatrix = novaObject.LocalMatrix.ToArray(); babylonMesh.scaling = novaObject.Scaling.ToArray(); } babylonMesh.animations = animations.ToArray(); babylonMesh.autoAnimate = novaObject.AutoAnimate; if (novaObject.AutoAnimate) { babylonMesh.autoAnimateFrom = novaObject.AnimationStartKey; if (novaObject.AnimationEndKey == -1) { babylonMesh.autoAnimateTo = scene.AnimationKeyMax / scene.AnimationKeyStep; babylonMesh.autoAnimateLoop = true; } else { babylonMesh.autoAnimateTo = novaObject.AnimationEndKey; } } // Vertices & faces var exportedVerticesCount = DumpObjectGeometry(novaObject, babylonMesh, startIndex, endIndex); // Subobjects var subMeshes = new List <BabylonSubMesh>(); if (novaObject.Is32bits) { var subMesh = new BabylonSubMesh(); subMesh.materialIndex = 0; subMesh.verticesStart = 0; subMesh.verticesCount = exportedVerticesCount; subMesh.indexStart = 0; subMesh.indexCount = babylonMesh.indices.Length; subMeshes.Add(subMesh); } else { foreach (NovaSubObject subObject in novaObject.SubObjects) { var subMesh = new BabylonSubMesh(); subMesh.materialIndex = subObject.AttributeRange.AttributeId; subMesh.verticesStart = subObject.AttributeRange.VertexStart; subMesh.verticesCount = subObject.AttributeRange.VertexCount; subMesh.indexStart = subObject.AttributeRange.FaceStart * 3; subMesh.indexCount = subObject.AttributeRange.FaceCount * 3; subMeshes.Add(subMesh); } } babylonMesh.subMeshes = subMeshes.ToArray(); if (novaObject.Material != null) { if (!materialsToExport.Contains(novaObject.Material)) { materialsToExport.Add(novaObject.Material); var multiMat = novaObject.Material as NovaMultiMaterial; if (multiMat != null) { foreach (var mat in multiMat.Materials) { if (!materialsToExport.Contains(mat)) { materialsToExport.Add(mat); } } } } } }
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 BabylonMesh ExportMesh(Node meshNode, BabylonScene babylonScene) { var babylonMesh = new BabylonMesh(); int vx1, vx2, vx3; RaiseMessage(meshNode.Name, true); babylonMesh.name = meshNode.Name; babylonMesh.id = meshNode.GetGuid().ToString(); if (meshNode.HasParent()) { babylonMesh.parentId = meshNode.Parent.GetGuid().ToString(); } // Misc. babylonMesh.isVisible = meshNode._Node.Renderable == 1; babylonMesh.pickable = meshNode._Node.GetBoolProperty("babylonjs_checkpickable"); babylonMesh.receiveShadows = meshNode._Node.RcvShadows == 1; // Collisions babylonMesh.checkCollisions = meshNode._Node.GetBoolProperty("babylonjs_checkcollisions"); // Position / rotation / scaling var wm = meshNode.GetWorldMatrix(0, meshNode.HasParent()); babylonMesh.position = wm.Trans.ToArraySwitched(); var parts = Loader.Global.AffineParts.Create(); Loader.Global.DecompAffine(wm, parts); //var rotate = new float[3]; //IntPtr xPtr = Marshal.AllocHGlobal(sizeof(float)); //IntPtr yPtr = Marshal.AllocHGlobal(sizeof(float)); //IntPtr zPtr = Marshal.AllocHGlobal(sizeof(float)); //parts.Q.GetEuler(xPtr, yPtr, zPtr); //Marshal.Copy(xPtr, rotate, 0, 1); //Marshal.Copy(yPtr, rotate, 1, 1); //Marshal.Copy(zPtr, rotate, 2, 1); //var temp = -rotate[1]; //rotate[0] = rotate[0] * parts.F; //rotate[1] = -rotate[2] * parts.F; //rotate[2] = temp * parts.F; //babylonMesh.rotation = rotate; babylonMesh.rotationQuaternion = parts.Q.ToArray(); babylonMesh.scaling = parts.K.ToArraySwitched(); if (wm.Parity) { vx1 = 2; vx2 = 1; vx3 = 0; } else { vx1 = 0; vx2 = 1; vx3 = 2; } // Pivot //var pivotMatrix = Matrix3.Identity._IMatrix3; //pivotMatrix.PreTranslate(meshNode._Node.ObjOffsetPos); //Loader.Global.PreRotateMatrix(pivotMatrix, meshNode._Node.ObjOffsetRot); //Loader.Global.ApplyScaling(pivotMatrix, meshNode._Node.ObjOffsetScale); //babylonMesh.localMatrix = pivotMatrix.ToArray(); // Mesh var objectState = meshNode._Node.EvalWorldState(0, false); var mesh = objectState.Obj.GetMesh(); var computedMesh = meshNode.GetMesh(); if (mesh != null) { mesh.BuildNormals(); if (mesh.NumFaces < 1) { RaiseError(string.Format("Mesh {0} has no face", babylonMesh.name)); } if (mesh.NumVerts < 3) { RaiseError(string.Format("Mesh {0} has not enough vertices", babylonMesh.name)); } if (mesh.NumVerts >= 65536) { RaiseError(string.Format("Mesh {0} has too many vertices (more than 65535)", babylonMesh.name)); } // Material var mtl = meshNode.Material; var multiMatsCount = 1; if (mtl != null) { babylonMesh.materialId = mtl.GetGuid().ToString(); if (!referencedMaterials.Contains(mtl)) { referencedMaterials.Add(mtl); } multiMatsCount = Math.Max(mtl.NumSubMaterials, 1); } babylonMesh.visibility = meshNode._Node.GetVisibility(0, Interval.Forever._IInterval); var vertices = new List <GlobalVertex>(); var indices = new List <int>(); var matIDs = new List <int>(); var hasUV = mesh.NumTVerts > 0; var hasUV2 = mesh.GetNumMapVerts(2) > 0; for (var face = 0; face < mesh.NumFaces; face++) { indices.Add(CreateGlobalVertex(mesh, computedMesh, face, vx1, vertices, hasUV, hasUV2)); indices.Add(CreateGlobalVertex(mesh, computedMesh, face, vx2, vertices, hasUV, hasUV2)); indices.Add(CreateGlobalVertex(mesh, computedMesh, face, vx3, vertices, hasUV, hasUV2)); matIDs.Add(mesh.Faces[face].MatID % multiMatsCount); } // Buffers babylonMesh.positions = vertices.SelectMany(v => v.Position.ToArraySwitched()).ToArray(); babylonMesh.normals = vertices.SelectMany(v => v.Normal.ToArraySwitched()).ToArray(); if (hasUV) { babylonMesh.uvs = vertices.SelectMany(v => v.UV.ToArray()).ToArray(); } if (hasUV2) { babylonMesh.uvs2 = vertices.SelectMany(v => v.UV2.ToArray()).ToArray(); } // Submeshes var sortedIndices = new List <int>(); var subMeshes = new List <BabylonSubMesh>(); var indexStart = 0; for (var index = 0; index < multiMatsCount; index++) { var subMesh = new BabylonSubMesh(); var indexCount = 0; var minVertexIndex = int.MaxValue; var maxVertexIndex = int.MinValue; subMesh.indexStart = indexStart; subMesh.materialIndex = index; for (var face = 0; face < matIDs.Count; face++) { if (matIDs[face] == index) { var a = indices[3 * face]; var b = indices[3 * face + 1]; var c = indices[3 * face + 2]; sortedIndices.Add(a); sortedIndices.Add(b); sortedIndices.Add(c); indexCount += 3; 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; } } } subMesh.indexCount = indexCount; subMesh.verticesStart = minVertexIndex; subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1; indexStart += indexCount; subMeshes.Add(subMesh); } babylonMesh.subMeshes = subMeshes.ToArray(); // Buffers - Indices babylonMesh.indices = sortedIndices.ToArray(); } babylonScene.MeshesList.Add(babylonMesh); return(babylonMesh); }
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); } } }
private void ExportMesh(IINode meshNode, BabylonScene babylonScene) { if (meshNode.IsInstance()) { return; } if (meshNode.GetBoolProperty("babylonjs_noexport")) { return; } if (!ExportHiddenObjects && meshNode.IsHidden(NodeHideFlags.None, false)) { return; } var babylonMesh = new BabylonMesh(); int vx1, vx2, vx3; babylonMesh.name = meshNode.Name; babylonMesh.id = meshNode.GetGuid().ToString(); if (meshNode.HasParent()) { babylonMesh.parentId = meshNode.ParentNode.GetGuid().ToString(); } // Misc. babylonMesh.isVisible = meshNode.Renderable == 1; babylonMesh.pickable = meshNode.GetBoolProperty("babylonjs_checkpickable"); babylonMesh.receiveShadows = meshNode.RcvShadows == 1; babylonMesh.showBoundingBox = meshNode.GetBoolProperty("babylonjs_showboundingbox"); babylonMesh.showSubMeshesBoundingBox = meshNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox"); // Collisions babylonMesh.checkCollisions = meshNode.GetBoolProperty("babylonjs_checkcollisions"); // Skin var skin = GetSkinModifier(meshNode); if (skin != null) { babylonMesh.skeletonId = skins.IndexOf(skin); bonesCount = skin.NumBones; } // Position / rotation / scaling var wm = Tools.ExtractCoordinates(meshNode, babylonMesh, exportQuaternionsInsteadOfEulers); if (wm.Parity) { vx1 = 2; vx2 = 1; vx3 = 0; } else { vx1 = 0; vx2 = 1; vx3 = 2; } // Pivot var pivotMatrix = Tools.Identity; pivotMatrix.PreTranslate(meshNode.ObjOffsetPos); Loader.Global.PreRotateMatrix(pivotMatrix, meshNode.ObjOffsetRot); Loader.Global.ApplyScaling(pivotMatrix, meshNode.ObjOffsetScale); babylonMesh.pivotMatrix = pivotMatrix.ToArray(); // Mesh var objectState = meshNode.EvalWorldState(0, false); var triObject = objectState.Obj.GetMesh(); var mesh = triObject != null ? triObject.Mesh : null; RaiseMessage(meshNode.Name, 1); if (mesh != null) { mesh.BuildNormals(); if (mesh.NumFaces < 1) { RaiseError(string.Format("Mesh {0} has no face", babylonMesh.name), 2); } if (mesh.NumVerts < 3) { RaiseError(string.Format("Mesh {0} has not enough vertices", babylonMesh.name), 2); } if (mesh.NumVerts >= 65536) { RaiseError(string.Format("Mesh {0} has too many vertices (more than 65535)", babylonMesh.name), 2); } // Material var mtl = meshNode.Mtl; var multiMatsCount = 1; if (mtl != null) { babylonMesh.materialId = mtl.GetGuid().ToString(); if (!referencedMaterials.Contains(mtl)) { referencedMaterials.Add(mtl); } multiMatsCount = Math.Max(mtl.NumSubMtls, 1); } babylonMesh.visibility = meshNode.GetVisibility(0, Tools.Forever); var vertices = new List <GlobalVertex>(); var indices = new List <int>(); var matIDs = new List <int>(); var hasUV = mesh.NumTVerts > 0; var hasUV2 = mesh.GetNumMapVerts(2) > 0; var optimizeVertices = meshNode.GetBoolProperty("babylonjs_optimizevertices"); // Skin IISkinContextData skinContext = null; if (skin != null) { skinContext = skin.GetContextInterface(meshNode); } // Compute normals VNormal[] vnorms = Tools.ComputeNormals(mesh, optimizeVertices); List <GlobalVertex>[] verticesAlreadyExported = null; if (optimizeVertices) { verticesAlreadyExported = new List <GlobalVertex> [mesh.NumVerts]; } for (var face = 0; face < mesh.NumFaces; face++) { indices.Add(CreateGlobalVertex(mesh, face, vx1, vertices, hasUV, hasUV2, vnorms, verticesAlreadyExported, skinContext)); indices.Add(CreateGlobalVertex(mesh, face, vx2, vertices, hasUV, hasUV2, vnorms, verticesAlreadyExported, skinContext)); indices.Add(CreateGlobalVertex(mesh, face, vx3, vertices, hasUV, hasUV2, vnorms, verticesAlreadyExported, skinContext)); matIDs.Add(mesh.Faces[face].MatID % multiMatsCount); CheckCancelled(); } if (vertices.Count >= 65536) { RaiseError(string.Format("Mesh {0} has too many vertices: {1} (limit is 65535)", babylonMesh.name, vertices.Count), 2); if (!optimizeVertices) { RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2); } } RaiseMessage(string.Format("{0} vertices, {1} faces", vertices.Count, indices.Count / 3), 2); // Buffers babylonMesh.positions = vertices.SelectMany(v => v.Position.ToArraySwitched()).ToArray(); babylonMesh.normals = vertices.SelectMany(v => v.Normal.ToArraySwitched()).ToArray(); if (hasUV) { babylonMesh.uvs = vertices.SelectMany(v => v.UV.ToArray()).ToArray(); } if (hasUV2) { babylonMesh.uvs2 = vertices.SelectMany(v => v.UV2.ToArray()).ToArray(); } if (skin != null) { babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray(); babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray(); } // Submeshes var sortedIndices = new List <int>(); var subMeshes = new List <BabylonSubMesh>(); var indexStart = 0; for (var index = 0; index < multiMatsCount; index++) { var subMesh = new BabylonSubMesh(); var indexCount = 0; var minVertexIndex = int.MaxValue; var maxVertexIndex = int.MinValue; subMesh.indexStart = indexStart; subMesh.materialIndex = index; for (var face = 0; face < matIDs.Count; face++) { if (matIDs[face] == index) { var a = indices[3 * face]; var b = indices[3 * face + 1]; var c = indices[3 * face + 2]; sortedIndices.Add(a); sortedIndices.Add(b); sortedIndices.Add(c); indexCount += 3; 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; } } } if (indexCount != 0) { subMesh.indexCount = indexCount; subMesh.verticesStart = minVertexIndex; subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1; indexStart += indexCount; subMeshes.Add(subMesh); } CheckCancelled(); } babylonMesh.subMeshes = subMeshes.ToArray(); // Buffers - Indices babylonMesh.indices = sortedIndices.ToArray(); triObject.Dispose(); } // Instances var tabs = Loader.Global.NodeTab.Create(); Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode, tabs); var instances = new List <BabylonAbstractMesh>(); for (var index = 0; index < tabs.Count; index++) { var indexer = new IntPtr(index); var tab = tabs[indexer]; Marshal.FreeHGlobal(indexer); if (meshNode.GetGuid() == tab.GetGuid()) { continue; } tab.MarkAsInstance(); var instance = new BabylonAbstractMesh { name = tab.Name }; Tools.ExtractCoordinates(tab, instance, exportQuaternionsInsteadOfEulers); var instanceAnimations = new List <BabylonAnimation>(); GenerateCoordinatesAnimations(tab, instanceAnimations); instance.animations = instanceAnimations.ToArray(); instances.Add(instance); } babylonMesh.instances = instances.ToArray(); // Animations var animations = new List <BabylonAnimation>(); GenerateCoordinatesAnimations(meshNode, animations); if (!ExportFloatController(meshNode.VisController, "visibility", animations)) { ExportFloatAnimation("visibility", animations, key => new[] { meshNode.GetVisibility(key, Tools.Forever) }); } babylonMesh.animations = animations.ToArray(); if (meshNode.GetBoolProperty("babylonjs_autoanimate", 1)) { babylonMesh.autoAnimate = true; babylonMesh.autoAnimateFrom = (int)meshNode.GetFloatProperty("babylonjs_autoanimate_from"); babylonMesh.autoAnimateTo = (int)meshNode.GetFloatProperty("babylonjs_autoanimate_to", 100); babylonMesh.autoAnimateLoop = meshNode.GetBoolProperty("babylonjs_autoanimateloop", 1); } babylonScene.MeshesList.Add(babylonMesh); }