private void ConvertUnityEmptyObjectToBabylon(GameObject gameObject) { BabylonMesh babylonMesh = new BabylonMesh { name = gameObject.name, id = GetID(gameObject) }; var transform = gameObject.transform; babylonMesh.parentId = GetParentID(transform); babylonMesh.position = transform.localPosition.ToFloat(); babylonMesh.rotation = new float[3]; babylonMesh.rotation[0] = transform.localRotation.eulerAngles.x * (float)Math.PI / 180; babylonMesh.rotation[1] = transform.localRotation.eulerAngles.y * (float)Math.PI / 180; babylonMesh.rotation[2] = transform.localRotation.eulerAngles.z * (float)Math.PI / 180; babylonMesh.scaling = transform.localScale.ToFloat(); babylonScene.MeshesList.Add(babylonMesh); // Animations ExportAnimations(transform, babylonMesh); if (IsRotationQuaternionAnimated(babylonMesh)) { babylonMesh.rotationQuaternion = transform.localRotation.ToFloat(); } }
private void ExportAnimatedNode(GLTF gltf, BabylonNode node, GLTFNode gltfNode, GLTFAnimation gltfAnimation, int startFrame, int endFrame, BabylonAnimationGroup animGroup = null) { bool nodeHasAnimations = node.animations != null && node.animations.Length > 0 && node.animations[0] != null; bool nodeHasExtraAnimations = node.extraAnimations != null && node.extraAnimations.Count > 0 && node.extraAnimations[0] != null; BabylonMesh meshNode = node as BabylonMesh; BabylonMorphTargetManager morphTargetManager = null; bool nodeHasAnimatedMorphTargets = false; if (meshNode != null && meshNode.morphTargetManagerId != null) { morphTargetManager = GetBabylonMorphTargetManager(babylonScene, meshNode); if (morphTargetManager != null) { nodeHasAnimatedMorphTargets = morphTargetManager.targets.Any(target => target.animations != null && target.animations.Length > 0 && target.animations[0] != null); } } if (!nodeHasAnimations && !nodeHasExtraAnimations && !nodeHasAnimatedMorphTargets) { return; } if (nodeHasAnimations && node.animations[0].property == "_matrix") { ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, node, gltfNode, animGroup); } else { ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, node, gltfNode, babylonScene, animGroup); } if (nodeHasAnimatedMorphTargets) { ExportMorphTargetWeightAnimation(morphTargetManager, gltf, gltfNode, gltfAnimation.ChannelList, gltfAnimation.SamplerList, startFrame, endFrame, babylonScene); } }
private BabylonMesh ConvertUnityEmptyObjectToBabylon(GameObject gameObject, ref UnityMetaData metaData, ref List <BabylonExport.Entities.BabylonParticleSystem> particleSystems, ref List <UnityFlareSystem> lensFlares, ref string componentTags, BabylonMesh collisionMesh = null, Collider collider = null) { BabylonMesh babylonMesh = new BabylonMesh { name = gameObject.name, id = GetID(gameObject) }; metaData.type = "Game"; if (!String.IsNullOrEmpty(componentTags)) { babylonMesh.tags = componentTags; } var transform = gameObject.transform; babylonMesh.parentId = GetParentID(transform); babylonMesh.position = transform.localPosition.ToFloat(); babylonMesh.rotation = new float[3]; babylonMesh.rotation[0] = transform.localRotation.eulerAngles.x * (float)Math.PI / 180; babylonMesh.rotation[1] = transform.localRotation.eulerAngles.y * (float)Math.PI / 180; babylonMesh.rotation[2] = transform.localRotation.eulerAngles.z * (float)Math.PI / 180; babylonMesh.scaling = transform.localScale.ToFloat(); babylonMesh.isVisible = false; babylonMesh.visibility = 0; babylonMesh.checkCollisions = false; // Collision mesh (No detail mesh fallback) string collisionMeshId = null; if (collider != null && collisionMesh != null) { collisionMeshId = collisionMesh.id; collisionMesh.parentId = babylonMesh.id; collisionMesh.visibility = collider.isTrigger ? 0.25f : 0.5f; collisionMesh.checkCollisions = (exportationOptions.ExportCollisions && collider.isTrigger == false); } metaData.properties["collisionMeshId"] = collisionMeshId; babylonMesh.metadata = metaData; babylonScene.MeshesList.Add(babylonMesh); // Animations ExportAnimations(transform, babylonMesh); if (IsRotationQuaternionAnimated(babylonMesh)) { babylonMesh.rotationQuaternion = transform.localRotation.ToFloat(); } // Lens Flares ParseLensFlares(gameObject, babylonMesh.id, ref lensFlares); // Particles Systems ParseParticleSystems(gameObject, babylonMesh.id, ref particleSystems); return(babylonMesh); }
private void ConvertUnitySkyboxToBabylon(Camera camera, float progress) { // Skybox if ((camera.clearFlags & CameraClearFlags.Skybox) == CameraClearFlags.Skybox) { if (RenderSettings.skybox != null) { if (RenderSettings.skybox.shader.name == "Skybox/Cubemap") { var cubeMap = RenderSettings.skybox.GetTexture("_Tex") as Cubemap; if (cubeMap != null) { var skytex = new BabylonTexture(); CopyTextureCube("sceneSkybox.hdr", cubeMap, skytex); skytex.coordinatesMode = 5; skytex.level = RenderSettings.reflectionIntensity; BabylonMesh skybox = new BabylonMesh(); skybox.indices = new[] { 0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14, 15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23 }; skybox.positions = new[] { 50.0f, -50.0f, 50.0f, -50.0f, -50.0f, 50.0f, -50.0f, 50.0f, 50.0f, 50.0f, 50.0f, 50.0f, 50.0f, 50.0f, -50.0f, -50.0f, 50.0f, -50.0f, -50.0f, -50.0f, -50.0f, 50.0f, -50.0f, -50.0f, 50.0f, 50.0f, -50.0f, 50.0f, -50.0f, -50.0f, 50.0f, -50.0f, 50.0f, 50.0f, 50.0f, 50.0f, -50.0f, 50.0f, 50.0f, -50.0f, -50.0f, 50.0f, -50.0f, -50.0f, -50.0f, -50.0f, 50.0f, -50.0f, -50.0f, 50.0f, 50.0f, -50.0f, 50.0f, -50.0f, 50.0f, 50.0f, -50.0f, 50.0f, 50.0f, 50.0f, 50.0f, -50.0f, 50.0f, 50.0f, -50.0f, -50.0f, -50.0f, -50.0f, -50.0f, -50.0f, -50.0f, 50.0f }; skybox.uvs = new[] { 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f }; skybox.normals = new[] { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f, -1.0f, 0, 0f }; var skyboxMaterial = new BabylonPBRMaterial() { name = "sceneSkyboxMaterial", id = Guid.NewGuid().ToString(), albedo = new[] { 1.0f, 1.0f, 1.0f, 1.0f }, reflectivity = new[] { 0.0f, 0.0f, 0.0f }, microSurface = 1.0f, directIntensity = 0.0f, specularIntensity = 0.0f, environmentIntensity = 1.0f }; skyboxMaterial.backFaceCulling = false; skybox.materialId = skyboxMaterial.id; skybox.infiniteDistance = true; skyboxMaterial.reflectionTexture = skytex; babylonScene.MeshesList.Add(skybox); babylonScene.MaterialsList.Add(skyboxMaterial); babylonScene.AddTextureCube("sceneSkyboxMaterial"); } } } } }
private BabylonNode ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene) { if (IsMeshExportable(meshNode) == false) { return(null); } logger?.RaiseMessage(meshNode.Name, 1); if (!exportParameters.keepInstances) { return(ExportMasterMesh(scene, meshNode, babylonScene)); } else { // Instances #if MAX2020 || MAX2021 || MAX2022 var tabs = Loader.Global.INodeTab.Create(); #else var tabs = Loader.Global.NodeTab.Create(); #endif Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode.MaxNode, tabs); if (tabs.Count > 1) { IINode Master = TabToList <IINode>(tabs)[tabs.Count - 1]; List <IINode> Instances = TabToList <IINode>(tabs).FindAll(x => x.Handle != Master.Handle); foreach (IINode instanceNode in tabs.ToIEnumerable()) { //this make sure every instance node is indexed in guid dictionary Tools.GetGuid(instanceNode); } BabylonMesh babylonMasterMesh = babylonScene.MeshesList.Find(mesh => mesh.id == Master.GetGuid().ToString()); if (babylonMasterMesh == null) { return(ExportMasterMesh(scene, meshNode, babylonScene)); } else { return(ExportInstanceMesh(scene, meshNode, babylonScene, babylonMasterMesh)); } } return(ExportMasterMesh(scene, meshNode, babylonScene)); } }
private BabylonNode ExportSubModelExtraNode(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene) { logger?.RaiseMessage(meshNode.Name, 1); var babylonMesh = new BabylonMesh { name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString() }; babylonMesh.isDummy = true; // Position / rotation / scaling / hierarchy exportNode(babylonMesh, meshNode, scene, babylonScene); babylonScene.MeshesList.Add(babylonMesh); return(babylonMesh); }
private BabylonMesh GetMasterMesh(MFnMesh mFnMesh, BabylonMesh babylonMesh) { BabylonMesh babylonMasterMesh = null; int index = exportedMFnMesh.FindIndex(mesh => mesh.fullPathName.Equals(mFnMesh.fullPathName)); if (index == -1) { exportedMFnMesh.Add(mFnMesh); exportedMasterBabylonMesh.Add(babylonMesh); } else { babylonMasterMesh = exportedMasterBabylonMesh[index]; } return(babylonMasterMesh); }
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; } } }
private void ConvertUnityEmptyObjectToBabylon(GameObject gameObject, BabylonAbstractMesh[] instances = null) { BabylonMesh babylonMesh = new BabylonMesh { name = gameObject.name, id = GetID(gameObject) }; var transform = gameObject.transform; babylonMesh.parentId = GetParentID(transform); ConvertTransform(babylonMesh, transform, gameObject, instances); babylonScene.MeshesList.Add(babylonMesh); // Animations ExportAnimations(transform, babylonMesh); if (IsRotationQuaternionAnimated(babylonMesh)) { babylonMesh.rotationQuaternion = transform.localRotation.ToFloat(); } }
private BabylonNode ExportDummy(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene) { RaiseMessage(meshNode.Name, 1); 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() }; babylonMesh.isDummy = true; // Position / rotation / scaling / hierarchy exportNode(babylonMesh, meshNode, scene, babylonScene); babylonScene.MeshesList.Add(babylonMesh); return(babylonMesh); }
/// <summary> /// /// </summary> /// <param name="mDagPath">DAG path to the transform</param> /// <param name="babylonScene"></param> /// <returns></returns> private BabylonNode ExportDummy(MDagPath mDagPath, BabylonScene babylonScene) { RaiseMessage(mDagPath.partialPathName, 1); MFnTransform mFnTransform = new MFnTransform(mDagPath); var babylonMesh = new BabylonMesh { name = mFnTransform.name, id = mFnTransform.uuid().asString() }; babylonMesh.isDummy = true; // Position / rotation / scaling / hierarchy ExportNode(babylonMesh, mFnTransform, babylonScene); // TODO - Animations //exportAnimation(babylonMesh, meshNode); babylonScene.MeshesList.Add(babylonMesh); return(babylonMesh); }
private static void ExportSkeletonAnimation(SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, BabylonSkeleton skeleton) { var animator = skinnedMesh.rootBone.gameObject.GetComponent <Animator>(); if (animator != null) { ExportSkeletonAnimationClips(animator, true, skeleton, skinnedMesh.bones, babylonMesh); } else { var parent = skinnedMesh.rootBone.parent; while (parent != null) { animator = parent.gameObject.GetComponent <Animator>(); if (animator != null) { ExportSkeletonAnimationClips(animator, true, skeleton, skinnedMesh.bones, babylonMesh); break; } parent = parent.parent; } } }
private BabylonMorphTargetManager GetBabylonMorphTargetManager(BabylonScene babylonScene, BabylonMesh babylonMesh) { if (babylonMesh.morphTargetManagerId.HasValue) { if (babylonScene.morphTargetManagers == null) { RaiseWarning("GLTFExporter.Mesh | morphTargetManagers is not defined", 3); } else { var babylonMorphTargetManager = babylonScene.morphTargetManagers.ElementAtOrDefault(babylonMesh.morphTargetManagerId.Value); if (babylonMorphTargetManager == null) { RaiseWarning($"GLTFExporter.Mesh | morphTargetManager with index {babylonMesh.morphTargetManagerId.Value} not found", 3); } return(babylonMorphTargetManager); } } return(null); }
private GLTFMesh ExportMesh(BabylonMesh babylonMesh, GLTF gltf, BabylonScene babylonScene) { RaiseMessage("GLTFExporter.Mesh | Export mesh named: " + babylonMesh.name, 1); // -------------------------- // --- Mesh from babylon ---- // -------------------------- if (babylonMesh.positions == null || babylonMesh.positions.Length == 0) { RaiseMessage("GLTFExporter.Mesh | Mesh is a dummy", 2); return(null); } RaiseMessage("GLTFExporter.Mesh | Mesh from babylon", 2); // Retreive general data from babylon mesh int nbVertices = babylonMesh.positions.Length / 3; bool hasTangents = babylonMesh.tangents != null && babylonMesh.tangents.Length > 0; bool hasUV = babylonMesh.uvs != null && babylonMesh.uvs.Length > 0; bool hasUV2 = babylonMesh.uvs2 != null && babylonMesh.uvs2.Length > 0; bool hasColor = babylonMesh.colors != null && babylonMesh.colors.Length > 0; bool hasBones = babylonMesh.matricesIndices != null && babylonMesh.matricesIndices.Length > 0; bool hasBonesExtra = babylonMesh.matricesIndicesExtra != null && babylonMesh.matricesIndicesExtra.Length > 0; RaiseMessage("GLTFExporter.Mesh | nbVertices=" + nbVertices, 3); RaiseMessage("GLTFExporter.Mesh | hasUV=" + hasUV, 3); RaiseMessage("GLTFExporter.Mesh | hasUV2=" + hasUV2, 3); RaiseMessage("GLTFExporter.Mesh | hasColor=" + hasColor, 3); RaiseMessage("GLTFExporter.Mesh | hasBones=" + hasBones, 3); RaiseMessage("GLTFExporter.Mesh | hasBonesExtra=" + hasBonesExtra, 3); // Retreive vertices data from babylon mesh List <GLTFGlobalVertex> globalVertices = new List <GLTFGlobalVertex>(); for (int indexVertex = 0; indexVertex < nbVertices; indexVertex++) { GLTFGlobalVertex globalVertex = new GLTFGlobalVertex(); globalVertex.Position = BabylonVector3.FromArray(babylonMesh.positions, indexVertex); globalVertex.Normal = BabylonVector3.FromArray(babylonMesh.normals, indexVertex); if (hasTangents) { globalVertex.Tangent = BabylonQuaternion.FromArray(babylonMesh.tangents, indexVertex); // Switch coordinate system at object level globalVertex.Tangent.Z *= -1; // Invert W to switch to right handed system globalVertex.Tangent.W *= -1; } // Switch coordinate system at object level globalVertex.Position.Z *= -1; globalVertex.Normal.Z *= -1; if (hasUV) { globalVertex.UV = BabylonVector2.FromArray(babylonMesh.uvs, indexVertex); // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image // While for Babylon, it corresponds to the lower left corner of a texture image globalVertex.UV.Y = 1 - globalVertex.UV.Y; } if (hasUV2) { globalVertex.UV2 = BabylonVector2.FromArray(babylonMesh.uvs2, indexVertex); // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image // While for Babylon, it corresponds to the lower left corner of a texture image globalVertex.UV2.Y = 1 - globalVertex.UV2.Y; } if (hasColor) { globalVertex.Color = Tools.SubArrayFromEntity(babylonMesh.colors, indexVertex, 4); } if (hasBones) { // In babylon, the 4 bones indices are stored in a single int // Each bone index is 8-bit offset from the next int bonesIndicesMerged = babylonMesh.matricesIndices[indexVertex]; int bone3 = bonesIndicesMerged >> 24; bonesIndicesMerged -= bone3 << 24; int bone2 = bonesIndicesMerged >> 16; bonesIndicesMerged -= bone2 << 16; int bone1 = bonesIndicesMerged >> 8; bonesIndicesMerged -= bone1 << 8; int bone0 = bonesIndicesMerged >> 0; bonesIndicesMerged -= bone0 << 0; var bonesIndicesArray = new ushort[] { (ushort)bone0, (ushort)bone1, (ushort)bone2, (ushort)bone3 }; globalVertex.BonesIndices = bonesIndicesArray; globalVertex.BonesWeights = Tools.SubArrayFromEntity(babylonMesh.matricesWeights, indexVertex, 4); } globalVertices.Add(globalVertex); } var babylonMorphTargetManager = GetBabylonMorphTargetManager(babylonScene, babylonMesh); // Retrieve indices from babylon mesh List <int> babylonIndices = babylonMesh.indices.ToList(); // -------------------------- // ------- Init glTF -------- // -------------------------- RaiseMessage("GLTFExporter.Mesh | Init glTF", 2); // Mesh var gltfMesh = new GLTFMesh { name = babylonMesh.name }; gltfMesh.index = gltf.MeshesList.Count; gltf.MeshesList.Add(gltfMesh); gltfMesh.idGroupInstance = babylonMesh.idGroupInstance; if (hasBones) { gltfMesh.idBabylonSkeleton = babylonMesh.skeletonId; } // -------------------------- // ---- glTF primitives ----- // -------------------------- RaiseMessage("GLTFExporter.Mesh | glTF primitives", 2); var meshPrimitives = new List <GLTFMeshPrimitive>(); foreach (BabylonSubMesh babylonSubMesh in babylonMesh.subMeshes) { // -------------------------- // ------ SubMesh data ------ // -------------------------- List <GLTFGlobalVertex> globalVerticesSubMesh = globalVertices.GetRange(babylonSubMesh.verticesStart, babylonSubMesh.verticesCount); var gltfIndices = babylonIndices.GetRange(babylonSubMesh.indexStart, babylonSubMesh.indexCount); // In gltf, indices of each mesh primitive are 0-based (ie: min value is 0) // Thus, the gltf indices list is a concatenation of sub lists all 0-based // Example for 2 triangles, each being a submesh: // babylonIndices = {0,1,2, 3,4,5} gives as result gltfIndicies = {0,1,2, 0,1,2} var minIndiceValue = gltfIndices.Min(); // Should be equal to babylonSubMesh.indexStart for (int indexIndice = 0; indexIndice < gltfIndices.Count; indexIndice++) { gltfIndices[indexIndice] -= minIndiceValue; } // -------------------------- // ----- Mesh primitive ----- // -------------------------- // MeshPrimitive var meshPrimitive = new GLTFMeshPrimitive { attributes = new Dictionary <string, int>() }; meshPrimitives.Add(meshPrimitive); // Material if (babylonMesh.materialId != null) { RaiseMessage("GLTFExporter.Mesh | Material", 3); // 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); // 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 export indexMaterial = babylonMaterialsToExport.Count; babylonMaterialsToExport.Add(babylonMaterial); } meshPrimitive.material = indexMaterial; } // TODO - Add and retreive info from babylon material meshPrimitive.mode = GLTFMeshPrimitive.FillMode.TRIANGLES; } // -------------------------- // ------- Accessors -------- // -------------------------- RaiseMessage("GLTFExporter.Mesh | Geometry", 3); // Buffer var buffer = GLTFBufferService.Instance.GetBuffer(gltf); // --- Indices --- var componentType = GLTFAccessor.ComponentType.UNSIGNED_SHORT; if (nbVertices >= 65536) { componentType = GLTFAccessor.ComponentType.UNSIGNED_INT; } var accessorIndices = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewScalar(gltf, buffer), "accessorIndices", componentType, GLTFAccessor.TypeEnum.SCALAR ); meshPrimitive.indices = accessorIndices.index; // Populate accessor if (componentType == GLTFAccessor.ComponentType.UNSIGNED_INT) { gltfIndices.ForEach(n => accessorIndices.bytesList.AddRange(BitConverter.GetBytes(n))); } else { var gltfIndicesShort = gltfIndices.ConvertAll(new Converter <int, ushort>(n => (ushort)n)); gltfIndicesShort.ForEach(n => accessorIndices.bytesList.AddRange(BitConverter.GetBytes(n))); } accessorIndices.count = gltfIndices.Count; // --- Positions --- var accessorPositions = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatVec3(gltf, buffer), "accessorPositions", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC3 ); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.POSITION.ToString(), accessorPositions.index); // Populate accessor accessorPositions.min = new float[] { float.MaxValue, float.MaxValue, float.MaxValue }; accessorPositions.max = new float[] { float.MinValue, float.MinValue, float.MinValue }; globalVerticesSubMesh.ForEach((globalVertex) => { var positions = globalVertex.Position.ToArray(); // Store values as bytes foreach (var position in positions) { accessorPositions.bytesList.AddRange(BitConverter.GetBytes(position)); } // Update min and max values GLTFBufferService.UpdateMinMaxAccessor(accessorPositions, positions); }); accessorPositions.count = globalVerticesSubMesh.Count; // --- Normals --- var accessorNormals = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatVec3(gltf, buffer), "accessorNormals", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC3 ); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.NORMAL.ToString(), accessorNormals.index); // Populate accessor List <float> normals = globalVerticesSubMesh.SelectMany(v => v.Normal.ToArray()).ToList(); normals.ForEach(n => accessorNormals.bytesList.AddRange(BitConverter.GetBytes(n))); accessorNormals.count = globalVerticesSubMesh.Count; // --- Tangents --- if (hasTangents) { var accessorTangents = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatVec4(gltf, buffer), "accessorTangents", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC4 ); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TANGENT.ToString(), accessorTangents.index); // Populate accessor List <float> tangents = globalVerticesSubMesh.SelectMany(v => v.Tangent.ToArray()).ToList(); tangents.ForEach(n => accessorTangents.bytesList.AddRange(BitConverter.GetBytes(n))); accessorTangents.count = globalVerticesSubMesh.Count; } // --- Colors --- if (hasColor) { var accessorColors = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatVec4(gltf, buffer), "accessorColors", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC4 ); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.COLOR_0.ToString(), accessorColors.index); // Populate accessor List <float> colors = globalVerticesSubMesh.SelectMany(v => new[] { v.Color[0], v.Color[1], v.Color[2], v.Color[3] }).ToList(); colors.ForEach(n => accessorColors.bytesList.AddRange(BitConverter.GetBytes(n))); accessorColors.count = globalVerticesSubMesh.Count; } // --- UV --- if (hasUV) { var accessorUVs = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatVec2(gltf, buffer), "accessorUVs", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC2 ); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_0.ToString(), accessorUVs.index); // Populate accessor List <float> uvs = globalVerticesSubMesh.SelectMany(v => v.UV.ToArray()).ToList(); uvs.ForEach(n => accessorUVs.bytesList.AddRange(BitConverter.GetBytes(n))); accessorUVs.count = globalVerticesSubMesh.Count; } // --- UV2 --- if (hasUV2) { var accessorUV2s = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatVec2(gltf, buffer), "accessorUV2s", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC2 ); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_1.ToString(), accessorUV2s.index); // Populate accessor List <float> uvs2 = globalVerticesSubMesh.SelectMany(v => v.UV2.ToArray()).ToList(); uvs2.ForEach(n => accessorUV2s.bytesList.AddRange(BitConverter.GetBytes(n))); accessorUV2s.count = globalVerticesSubMesh.Count; } // --- Bones --- if (hasBones) { RaiseMessage("GLTFExporter.Mesh | Bones", 3); // --- Joints --- var accessorJoints = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewUnsignedShortVec4(gltf, buffer), "accessorJoints", GLTFAccessor.ComponentType.UNSIGNED_SHORT, GLTFAccessor.TypeEnum.VEC4 ); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.JOINTS_0.ToString(), accessorJoints.index); // Populate accessor List <ushort> joints = globalVerticesSubMesh.SelectMany(v => new[] { v.BonesIndices[0], v.BonesIndices[1], v.BonesIndices[2], v.BonesIndices[3] }).ToList(); joints.ForEach(n => accessorJoints.bytesList.AddRange(BitConverter.GetBytes(n))); accessorJoints.count = globalVerticesSubMesh.Count; // --- Weights --- var accessorWeights = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatVec4(gltf, buffer), "accessorWeights", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.VEC4 ); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.WEIGHTS_0.ToString(), accessorWeights.index); // Populate accessor List <float> weightBones = globalVerticesSubMesh.SelectMany(v => new[] { v.BonesWeights[0], v.BonesWeights[1], v.BonesWeights[2], v.BonesWeights[3] }).ToList(); weightBones.ForEach(n => accessorWeights.bytesList.AddRange(BitConverter.GetBytes(n))); accessorWeights.count = globalVerticesSubMesh.Count; } if (hasBonesExtra) { RaiseWarning("Too many bones influences per vertex. glTF only support up to 4 bones influences per vertex. The result may not be as expected.", 3); } // Morph targets positions and normals if (babylonMorphTargetManager != null) { RaiseMessage("GLTFExporter.Mesh | Morph targets", 3); _exportMorphTargets(babylonMesh, babylonSubMesh, babylonMorphTargetManager, gltf, buffer, meshPrimitive); } } gltfMesh.primitives = meshPrimitives.ToArray(); // Morph targets weights if (babylonMorphTargetManager != null) { var weights = new List <float>(); foreach (BabylonMorphTarget babylonMorphTarget in babylonMorphTargetManager.targets) { weights.Add(babylonMorphTarget.influence); } gltfMesh.weights = weights.ToArray(); } return(gltfMesh); }
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); }
private int DumpObjectGeometry(NovaObject novaObject, BabylonMesh babylonMesh, int startIndex, int endIndex) { var verticesIndices = new int[novaObject.VerticesCount]; for (var index = 0; index < verticesIndices.Length; index++) { verticesIndices[index] = -1; } // Vertices var transformMatrix = Matrix.Identity;//.Scaling(novaObject.Scaling) * novaObject.LocalMatrix; var indicesList = new List<int>(); NovaCustomVertexFormat.GlobalVector3[] vertices = novaObject.InternalMesh.LockVertexBuffer<NovaCustomVertexFormat.GlobalVector3>(NovaLock.ReadOnly, novaObject.VerticesCount); // Faces INovaDataStream data = novaObject.InternalMesh.LockIndexBuffer(NovaLock.ReadOnly); int[] indices; if (novaObject.Is32bits) { indices = data.Read<int>(novaObject.FacesCount * 3 * 4); } else { indices = (data.Read<ushort>(novaObject.FacesCount * 3 * 4)).Select(i => (int)i).ToArray(); } var positions = new List<float>(); var normals = new List<float>(); var uvs = new List<float>(); var uvs2 = new List<float>(); var colors = new List<float>(); int exportedVerticesCount = 0; for (var index = 0; index < novaObject.FacesCount; index++) { var v0 = indices[index * 3]; var v1 = indices[index * 3 + 1]; var v2 = indices[index * 3 + 2]; if (v0 < startIndex || v1 < startIndex || v2 < startIndex) { continue; } if (v0 >= startIndex && v0 < endIndex || v1 >= startIndex && v1 < endIndex || v2 >= startIndex && v2 < endIndex) { if (verticesIndices[v0] == -1) { verticesIndices[v0] = exportedVerticesCount++; DumpVertex(vertices[v0], positions, normals, uvs, uvs2, colors, transformMatrix, novaObject); } if (verticesIndices[v1] == -1) { verticesIndices[v1] = exportedVerticesCount++; DumpVertex(vertices[v1], positions, normals, uvs, uvs2, colors, transformMatrix, novaObject); } if (verticesIndices[v2] == -1) { verticesIndices[v2] = exportedVerticesCount++; DumpVertex(vertices[v2], positions, normals, uvs, uvs2, colors, transformMatrix, novaObject); } indicesList.Add(verticesIndices[v0]); indicesList.Add(verticesIndices[v1]); indicesList.Add(verticesIndices[v2]); } } if (positions.Count > 0) { babylonMesh.positions = positions.ToArray(); } if (normals.Count > 0) { babylonMesh.normals = normals.ToArray(); } if (uvs.Count > 0) { babylonMesh.uvs = uvs.ToArray(); } if (uvs2.Count > 0) { babylonMesh.uvs2 = uvs2.ToArray(); } if (colors.Count > 0) { babylonMesh.colors = colors.ToArray(); } babylonMesh.indices = indicesList.ToArray(); // Invert normal order for (var index = 0; index < babylonMesh.indices.Length; index += 3) { var temp = babylonMesh.indices[index]; babylonMesh.indices[index] = babylonMesh.indices[index + 2]; babylonMesh.indices[index + 2] = temp; } novaObject.InternalMesh.UnlockIndexBuffer(); novaObject.InternalMesh.UnlockVertexBuffer(); return exportedVerticesCount; }
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); } } } } } }
public void Export(string outputDirectory, string outputFileName, string outputFormat, bool generateManifest, bool onlySelected, bool autoSaveMayaFile, bool exportHiddenObjects, bool copyTexturesToOutput) { RaiseMessage("Exportation started", Color.Blue); var progression = 0.0f; ReportProgressChanged(progression); // Store export options _onlySelected = onlySelected; _exportHiddenObjects = exportHiddenObjects; isBabylonExported = outputFormat == "babylon" || outputFormat == "binary babylon"; // Check directory exists if (!Directory.Exists(outputDirectory)) { RaiseError("Exportation stopped: Output folder does not exist"); ReportProgressChanged(100); return; } var watch = new Stopwatch(); watch.Start(); var outputBabylonDirectory = outputDirectory; var babylonScene = new BabylonScene(outputBabylonDirectory); // Save scene if (autoSaveMayaFile) { RaiseMessage("Saving Maya file"); // TODO //MFileIO.save(); } // Force output file extension to be babylon outputFileName = Path.ChangeExtension(outputFileName, "babylon"); // Producer babylonScene.producer = new BabylonProducer { name = "Maya", version = "2018", exporter_version = "1.0", file = outputFileName }; // Global babylonScene.autoClear = true; // TODO - Retreive colors from Maya //babylonScene.clearColor = Loader.Core.GetBackGround(0, Tools.Forever).ToArray(); //babylonScene.ambientColor = Loader.Core.GetAmbient(0, Tools.Forever).ToArray(); // TODO - Add custom properties _exportQuaternionsInsteadOfEulers = true; // -------------------- // ------ Meshes ------ // -------------------- RaiseMessage("Exporting meshes"); // Get all meshes var dagIterator = new MItDag(MItDag.TraversalType.kDepthFirst, MFn.Type.kMesh); List <MObject> mObjects = new List <MObject>(); while (!dagIterator.isDone) { var mObject = dagIterator.currentItem(); mObjects.Add(mObject); dagIterator.next(); } // Export all meshes var progressionStep = 100.0f / mObjects.Count; foreach (MObject mObject in mObjects) { ExportMesh(mObject, babylonScene); // Update progress bar progression += progressionStep; ReportProgressChanged(progression); } RaiseMessage(string.Format("Total meshes: {0}", babylonScene.MeshesList.Count), Color.Gray, 1); dagIterator = new MItDag(MItDag.TraversalType.kDepthFirst, MFn.Type.kMesh); // Switch from right to left handed coordinate system MUuid mUuid = new MUuid(); mUuid.generate(); var rootNode = new BabylonMesh { name = "root", id = mUuid.asString(), scaling = new float[] { 1, 1, -1 } }; foreach (var babylonMesh in babylonScene.MeshesList) { // Add root meshes as child to root node if (babylonMesh.parentId == null) { babylonMesh.parentId = rootNode.id; } } babylonScene.MeshesList.Add(rootNode); // -------------------- // ----- Materials ---- // -------------------- // TODO - Materials // Output babylonScene.Prepare(false, false); if (isBabylonExported) { Write(babylonScene, outputBabylonDirectory, outputFileName, outputFormat, generateManifest); } // TODO - Export glTF watch.Stop(); RaiseMessage(string.Format("Exportation done in {0:0.00}s", watch.ElapsedMilliseconds / 1000.0), Color.Blue); }
private BabylonNode ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene) { if (IsMeshExportable(meshNode) == false) { return(null); } RaiseMessage(meshNode.Name, 1); // Instances var tabs = Loader.Global.NodeTab.Create(); Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode.MaxNode, tabs); if (tabs.Count > 1) { // For a mesh with instances, we distinguish between master and instance meshes: // - a master mesh stores all the info of the mesh (transform, hierarchy, animations + vertices, indices, materials, bones...) // - an instance mesh only stores the info of the node (transform, hierarchy, animations) // Check if this mesh has already been exported BabylonMesh babylonMasterMesh = null; var index = 0; while (babylonMasterMesh == null && index < tabs.Count) { #if MAX2017 var indexer = index; #else var indexer = new IntPtr(index); #endif var tab = tabs[indexer]; babylonMasterMesh = babylonScene.MeshesList.Find(_babylonMesh => { // Same id return(_babylonMesh.id == tab.GetGuid().ToString() && // Mesh is not a dummy _babylonMesh.isDummy == false); }); index++; } if (babylonMasterMesh != null) { // Mesh already exported // Export this node as instance meshNode.MaxNode.MarkAsInstance(); var babylonInstanceMesh = new BabylonAbstractMesh { name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString() }; // Add instance to master mesh List <BabylonAbstractMesh> list = babylonMasterMesh.instances != null?babylonMasterMesh.instances.ToList() : new List <BabylonAbstractMesh>(); list.Add(babylonInstanceMesh); babylonMasterMesh.instances = list.ToArray(); // Export transform / hierarchy / animations exportNode(babylonInstanceMesh, meshNode, scene, babylonScene); // Animations exportAnimation(babylonInstanceMesh, meshNode); return(babylonInstanceMesh); } } 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() }; // Position / rotation / scaling / hierarchy exportNode(babylonMesh, meshNode, scene, babylonScene); // 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); if (isBabylonExported) { 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; var skinAlreadyStored = skins.Find(_skin => IsSkinEqualTo(_skin, skin)); if (skinAlreadyStored == null) { skins.Add(skin); } else { skin = skinAlreadyStored; } skinnedNodes.Add(meshNode); babylonMesh.skeletonId = skins.IndexOf(skin); skin.GetInitSkinTM(skinInitPoseMatrix); boneIds = SortBones(skin); skinSortedBones[skin] = boneIds; } // Mesh 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 var subMeshes = new List <BabylonSubMesh>(); ExtractGeometry(vertices, indices, subMeshes, boneIds, skin, unskinnedMesh, hasUV, hasUV2, hasColor, hasAlpha, optimizeVertices, multiMatsCount, meshNode); 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(); // ------------------------ // ---- Morph targets ----- // ------------------------ // Retreive modifiers with morpher flag List <IIGameModifier> modifiers = new List <IIGameModifier>(); for (int i = 0; i < meshNode.IGameObject.NumModifiers; i++) { var modifier = meshNode.IGameObject.GetIGameModifier(i); if (modifier.ModifierType == Autodesk.Max.IGameModifier.ModType.Morpher) { modifiers.Add(modifier); } } // Cast modifiers to morphers List <IIGameMorpher> morphers = modifiers.ConvertAll(new Converter <IIGameModifier, IIGameMorpher>(modifier => modifier.AsGameMorpher())); var hasMorphTarget = false; morphers.ForEach(morpher => { if (morpher.NumberOfMorphTargets > 0) { hasMorphTarget = true; } }); if (hasMorphTarget) { RaiseMessage("Export morph targets", 2); // Morph Target Manager var babylonMorphTargetManager = new BabylonMorphTargetManager(); babylonScene.MorphTargetManagersList.Add(babylonMorphTargetManager); babylonMesh.morphTargetManagerId = babylonMorphTargetManager.id; // Morph Targets var babylonMorphTargets = new List <BabylonMorphTarget>(); // All morphers are considered identical // Their targets are concatenated morphers.ForEach(morpher => { for (int i = 0; i < morpher.NumberOfMorphTargets; i++) { // Morph target var maxMorphTarget = morpher.GetMorphTarget(i); // Ensure target still exists (green color legend) if (maxMorphTarget != null) { var babylonMorphTarget = new BabylonMorphTarget { name = maxMorphTarget.Name }; babylonMorphTargets.Add(babylonMorphTarget); // TODO - Influence babylonMorphTarget.influence = 0f; // Target geometry var targetVertices = ExtractVertices(maxMorphTarget, optimizeVertices); babylonMorphTarget.positions = targetVertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray(); babylonMorphTarget.normals = targetVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray(); // Animations var animations = new List <BabylonAnimation>(); var morphWeight = morpher.GetMorphWeight(i); ExportFloatGameController(morphWeight, "influence", animations); if (animations.Count > 0) { babylonMorphTarget.animations = animations.ToArray(); } } } }); babylonMorphTargetManager.targets = babylonMorphTargets.ToArray(); } } // Animations // Done last to avoid '0 vertex found' error (unkown cause) exportAnimation(babylonMesh, meshNode); babylonScene.MeshesList.Add(babylonMesh); return(babylonMesh); }
public void ExportGltf(BabylonScene babylonScene, string outputDirectory, string outputFileName, bool generateBinary) { RaiseMessage("GLTFExporter | Exportation started", Color.Blue); // Force output file extension to be gltf outputFileName = Path.ChangeExtension(outputFileName, "gltf"); // Update path of output .gltf file to include subdirectory var outputFile = Path.Combine(outputDirectory, outputFileName); float progressionStep; var progression = 0.0f; ReportProgressChanged((int)progression); // Initialization initBabylonNodes(babylonScene); babylonMaterialsToExport = new List <BabylonMaterial>(); var gltf = new GLTF(outputFile); // Asset gltf.asset = new GLTFAsset { version = "2.0", generator = "Babylon2Gltf2017", copyright = "2017 (c) BabylonJS" // no minVersion }; // Scene gltf.scene = 0; // Scenes GLTFScene scene = new GLTFScene(); GLTFScene[] scenes = { scene }; gltf.scenes = scenes; // Meshes RaiseMessage("GLTFExporter | Exporting meshes"); progression = 10.0f; ReportProgressChanged((int)progression); progressionStep = 40.0f / babylonScene.meshes.Length; foreach (var babylonMesh in babylonScene.meshes) { ExportMesh(babylonMesh, gltf, babylonScene); progression += progressionStep; ReportProgressChanged((int)progression); CheckCancelled(); } // Root nodes RaiseMessage("GLTFExporter | Exporting nodes"); List <BabylonNode> babylonRootNodes = babylonNodes.FindAll(node => node.parentId == null); progressionStep = 40.0f / babylonRootNodes.Count; alreadyExportedSkeletons = new Dictionary <BabylonSkeleton, BabylonSkeletonExportData>(); babylonRootNodes.ForEach(babylonNode => { exportNodeRec(babylonNode, gltf, babylonScene); progression += progressionStep; ReportProgressChanged((int)progression); CheckCancelled(); }); // Switch from left to right handed coordinate system var tmpNodesList = new List <int>(scene.NodesList); var rootNode = new BabylonMesh { name = "root", rotation = new float[] { 0, (float)Math.PI, 0 }, scaling = new float[] { 1, 1, -1 }, idGroupInstance = -1 }; scene.NodesList.Clear(); // Only root node is listed in node list GLTFNode gltfRootNode = ExportAbstractMesh(rootNode, gltf, null, null); gltfRootNode.ChildrenList.AddRange(tmpNodesList); // Materials RaiseMessage("GLTFExporter | Exporting materials"); foreach (var babylonMaterial in babylonMaterialsToExport) { ExportMaterial(babylonMaterial, gltf); CheckCancelled(); } ; RaiseMessage(string.Format("GLTFExporter | Nb materials exported: {0}", gltf.MaterialsList.Count), Color.Gray, 1); // Prepare buffers gltf.BuffersList.ForEach(buffer => { buffer.BufferViews.ForEach(bufferView => { bufferView.Accessors.ForEach(accessor => { // Chunk must be padded with trailing zeros (0x00) to satisfy alignment requirements accessor.bytesList = new List <byte>(padChunk(accessor.bytesList.ToArray(), 4, 0x00)); // Update byte properties accessor.byteOffset = bufferView.byteLength; bufferView.byteLength += accessor.bytesList.Count; // Merge bytes bufferView.bytesList.AddRange(accessor.bytesList); }); // Update byte properties bufferView.byteOffset = buffer.byteLength; buffer.byteLength += bufferView.bytesList.Count; // Merge bytes buffer.bytesList.AddRange(bufferView.bytesList); }); }); // Cast lists to arrays gltf.Prepare(); // Output RaiseMessage("GLTFExporter | Saving to output file"); if (!generateBinary) { // Write .gltf file string outputGltfFile = Path.ChangeExtension(outputFile, "gltf"); File.WriteAllText(outputGltfFile, gltfToJson(gltf)); // Write .bin file string outputBinaryFile = Path.ChangeExtension(outputFile, "bin"); using (BinaryWriter writer = new BinaryWriter(File.Open(outputBinaryFile, FileMode.Create))) { gltf.BuffersList.ForEach(buffer => { buffer.bytesList.ForEach(b => writer.Write(b)); }); } } else { // Export glTF data to binary format .glb // Header UInt32 magic = 0x46546C67; // ASCII code for glTF UInt32 version = 2; UInt32 length = 12; // Header length // --- JSON chunk --- UInt32 chunkTypeJson = 0x4E4F534A; // ASCII code for JSON // Remove buffers uri foreach (GLTFBuffer gltfBuffer in gltf.BuffersList) { gltfBuffer.uri = null; } // Switch images to binary var imageBufferViews = SwitchImagesFromUriToBinary(gltf); imageBufferViews.ForEach(imageBufferView => { imageBufferView.Buffer.bytesList.AddRange(imageBufferView.bytesList); }); gltf.Prepare(); // Serialize gltf data to JSON string then convert it to bytes byte[] chunkDataJson = Encoding.ASCII.GetBytes(gltfToJson(gltf)); // JSON chunk must be padded with trailing Space chars (0x20) to satisfy alignment requirements chunkDataJson = padChunk(chunkDataJson, 4, 0x20); UInt32 chunkLengthJson = (UInt32)chunkDataJson.Length; length += chunkLengthJson + 8; // 8 = JSON chunk header length // bin chunk UInt32 chunkTypeBin = 0x004E4942; // ASCII code for BIN UInt32 chunkLengthBin = 0; if (gltf.BuffersList.Count > 0) { foreach (GLTFBuffer gltfBuffer in gltf.BuffersList) { chunkLengthBin += (uint)gltfBuffer.byteLength; } length += chunkLengthBin + 8; // 8 = bin chunk header length } // Write binary file string outputGlbFile = Path.ChangeExtension(outputFile, "glb"); using (BinaryWriter writer = new BinaryWriter(File.Open(outputGlbFile, FileMode.Create))) { // Header writer.Write(magic); writer.Write(version); writer.Write(length); // JSON chunk writer.Write(chunkLengthJson); writer.Write(chunkTypeJson); writer.Write(chunkDataJson); // bin chunk if (gltf.BuffersList.Count > 0) { writer.Write(chunkLengthBin); writer.Write(chunkTypeBin); gltf.BuffersList[0].bytesList.ForEach(b => writer.Write(b)); } }; } ReportProgressChanged(100); }
private void ConvertUnityMeshToBabylon(Mesh mesh, Transform transform, GameObject gameObject, float progress) { BabylonMesh babylonMesh = new BabylonMesh(); var renderer = gameObject.GetComponent <Renderer>(); ExporterWindow.ReportProgress(progress, "Exporting mesh: " + gameObject.name); babylonMesh.name = gameObject.name; babylonMesh.id = GetID(transform.gameObject); if (renderer != null) { babylonMesh.receiveShadows = renderer.receiveShadows; } babylonMesh.parentId = GetParentID(transform); babylonMesh.position = transform.localPosition.ToFloat(); babylonMesh.rotation = new float[3]; babylonMesh.rotation[0] = transform.localRotation.eulerAngles.x * (float)Math.PI / 180; babylonMesh.rotation[1] = transform.localRotation.eulerAngles.y * (float)Math.PI / 180; babylonMesh.rotation[2] = transform.localRotation.eulerAngles.z * (float)Math.PI / 180; babylonMesh.scaling = transform.localScale.ToFloat(); if (mesh != null) { babylonMesh.positions = new float[mesh.vertexCount * 3]; for (int i = 0; i < mesh.vertices.Length; i++) { babylonMesh.positions[i * 3] = mesh.vertices[i].x; babylonMesh.positions[(i * 3) + 1] = mesh.vertices[i].y; babylonMesh.positions[(i * 3) + 2] = mesh.vertices[i].z; // Computing world extends var worldPosition = transform.TransformPoint(mesh.vertices[i]); if (worldPosition.x > babylonScene.MaxVector.X) { babylonScene.MaxVector.X = worldPosition.x; } if (worldPosition.y > babylonScene.MaxVector.Y) { babylonScene.MaxVector.Y = worldPosition.y; } if (worldPosition.z > babylonScene.MaxVector.Z) { babylonScene.MaxVector.Z = worldPosition.z; } if (worldPosition.x < babylonScene.MinVector.X) { babylonScene.MinVector.X = worldPosition.x; } if (worldPosition.y < babylonScene.MinVector.Y) { babylonScene.MinVector.Y = worldPosition.y; } if (worldPosition.z < babylonScene.MinVector.Z) { babylonScene.MinVector.Z = worldPosition.z; } } babylonMesh.normals = new float[mesh.vertexCount * 3]; for (int i = 0; i < mesh.normals.Length; i++) { babylonMesh.normals[i * 3] = mesh.normals[i].x; babylonMesh.normals[(i * 3) + 1] = mesh.normals[i].y; babylonMesh.normals[(i * 3) + 2] = mesh.normals[i].z; } babylonMesh.uvs = new float[mesh.vertexCount * 2]; for (int i = 0; i < mesh.uv.Length; i++) { babylonMesh.uvs[i * 2] = mesh.uv[i].x; babylonMesh.uvs[(i * 2) + 1] = mesh.uv[i].y; } babylonMesh.uvs2 = new float[mesh.vertexCount * 2]; if (mesh.uv2 != null && mesh.uv2.Length > 0) { for (int i = 0; i < mesh.uv2.Length; i++) { babylonMesh.uvs2[i * 2] = mesh.uv2[i].x; babylonMesh.uvs2[(i * 2) + 1] = mesh.uv2[i].y; } } else { for (int i = 0; i < mesh.uv.Length; i++) { babylonMesh.uvs2[i * 2] = mesh.uv[i].x; babylonMesh.uvs2[(i * 2) + 1] = mesh.uv[i].y; } } babylonMesh.indices = new int[mesh.triangles.Length]; for (int i = 0; i < mesh.triangles.Length; i += 3) { babylonMesh.indices[i] = mesh.triangles[i + 2]; babylonMesh.indices[i + 1] = mesh.triangles[i + 1]; babylonMesh.indices[i + 2] = mesh.triangles[i]; } if (renderer != null && renderer.sharedMaterial != null) { if (mesh.subMeshCount > 1) // Multimaterials { BabylonMultiMaterial bMultiMat; if (!multiMatDictionary.ContainsKey(renderer.sharedMaterial.name)) { bMultiMat = new BabylonMultiMaterial { materials = new string[mesh.subMeshCount], id = Guid.NewGuid().ToString(), name = renderer.sharedMaterial.name }; for (int i = 0; i < renderer.sharedMaterials.Length; i++) { var sharedMaterial = renderer.sharedMaterials[i]; BabylonMaterial babylonMaterial; babylonMaterial = DumpMaterial(sharedMaterial, renderer); bMultiMat.materials[i] = babylonMaterial.id; } if (mesh.subMeshCount > 1) { multiMatDictionary.Add(bMultiMat.name, bMultiMat); } } else { bMultiMat = multiMatDictionary[renderer.sharedMaterial.name]; } babylonMesh.materialId = bMultiMat.id; babylonMesh.subMeshes = new BabylonSubMesh[mesh.subMeshCount]; var offset = 0; for (int materialIndex = 0; materialIndex < mesh.subMeshCount; materialIndex++) { var unityTriangles = mesh.GetTriangles(materialIndex); babylonMesh.subMeshes[materialIndex] = new BabylonSubMesh { verticesStart = 0, verticesCount = mesh.vertexCount, materialIndex = materialIndex, indexStart = offset, indexCount = unityTriangles.Length }; offset += unityTriangles.Length; } } else { babylonMesh.materialId = DumpMaterial(renderer.sharedMaterial, renderer).id; } } babylonScene.MeshesList.Add(babylonMesh); // Animations ExportAnimations(transform, babylonMesh); if (IsRotationQuaternionAnimated(babylonMesh)) { babylonMesh.rotationQuaternion = transform.localRotation.ToFloat(); } // Collisions if (exportationOptions.ExportCollisions) { var collider = gameObject.GetComponent <Collider>(); if (collider != null) { babylonMesh.checkCollisions = true; } } } }
private BabylonNode ExportMesh(MObject mObject, BabylonScene babylonScene) { MFnMesh mFnMesh = new MFnMesh(mObject); MFnDagNode mFnDagNode = new MFnDagNode(mObject); var mObjectParent = mFnMesh.parent(0); MFnDagNode mFnDagNodeParent = new MFnDagNode(mObjectParent); // --- prints --- #region prints RaiseVerbose("BabylonExporter.Mesh | mFnMesh data", 2); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.name=" + mFnMesh.name, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.absoluteName=" + mFnMesh.absoluteName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.fullPathName=" + mFnMesh.fullPathName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.partialPathName=" + mFnMesh.partialPathName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numVertices=" + mFnMesh.numVertices, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numEdges=" + mFnMesh.numEdges, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numPolygons=" + mFnMesh.numPolygons, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numFaceVertices=" + mFnMesh.numFaceVertices, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numNormals=" + mFnMesh.numNormals, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numUVSets=" + mFnMesh.numUVSets, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numUVsProperty=" + mFnMesh.numUVsProperty, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.activeColor=" + mFnMesh.activeColor.toString(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.attributeCount=" + mFnMesh.attributeCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.childCount=" + mFnMesh.childCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.displayColors=" + mFnMesh.displayColors, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.dormantColor=" + mFnMesh.dormantColor, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.hasUniqueName=" + mFnMesh.hasUniqueName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.inUnderWorld=" + mFnMesh.inUnderWorld, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isDefaultNode=" + mFnMesh.isDefaultNode, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isInstanceable=" + mFnMesh.isInstanceable, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isInstanced(true)=" + mFnMesh.isInstanced(true), 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isInstanced(false)=" + mFnMesh.isInstanced(false), 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isInstanced()=" + mFnMesh.isInstanced(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isIntermediateObject=" + mFnMesh.isIntermediateObject, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.isShared=" + mFnMesh.isShared, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numColorSets=" + mFnMesh.numColorSets, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numColorsProperty=" + mFnMesh.numColorsProperty, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.objectColor=" + mFnMesh.objectColor, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.parentCount=" + mFnMesh.parentCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.parentNamespace=" + mFnMesh.parentNamespace, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.uuid().asString()=" + mFnMesh.uuid().asString(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.dagRoot().apiType=" + mFnMesh.dagRoot().apiType, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.model.equalEqual(mFnMesh.objectProperty)=" + mFnMesh.model.equalEqual(mFnMesh.objectProperty), 3); RaiseVerbose("BabylonExporter.Mesh | ToString(mFnMesh.transformationMatrix)=" + mFnMesh.transformationMatrix.toString(), 3); var transformationMatrix = new MTransformationMatrix(mFnMesh.transformationMatrix); RaiseVerbose("BabylonExporter.Mesh | transformationMatrix.getTranslation().toString()=" + transformationMatrix.getTranslation().toString(), 3); var transformationMatrixParent = new MTransformationMatrix(mFnDagNodeParent.transformationMatrix); RaiseVerbose("BabylonExporter.Mesh | transformationMatrixParent.getTranslation().toString()=" + transformationMatrixParent.getTranslation().toString(), 3); // Geometry MIntArray triangleCounts = new MIntArray(); MIntArray trianglesVertices = new MIntArray(); mFnMesh.getTriangles(triangleCounts, trianglesVertices); RaiseVerbose("BabylonExporter.Mesh | triangleCounts.ToArray()=" + triangleCounts.ToArray().toString(), 3); RaiseVerbose("BabylonExporter.Mesh | trianglesVertices.ToArray()=" + trianglesVertices.ToArray().toString(), 3); int[] polygonsVertexCount = new int[mFnMesh.numPolygons]; for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++) { polygonsVertexCount[polygonId] = mFnMesh.polygonVertexCount(polygonId); } RaiseVerbose("BabylonExporter.Mesh | polygonsVertexCount=" + polygonsVertexCount.toString(), 3); //MFloatPointArray points = new MFloatPointArray(); //mFnMesh.getPoints(points); //RaiseVerbose("BabylonExporter.Mesh | points.ToArray()=" + points.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3); //MFloatVectorArray normals = new MFloatVectorArray(); //mFnMesh.getNormals(normals); //RaiseVerbose("BabylonExporter.Mesh | normals.ToArray()=" + normals.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3); for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++) { MIntArray verticesId = new MIntArray(); RaiseVerbose("BabylonExporter.Mesh | polygonId=" + polygonId, 3); int nbTriangles = triangleCounts[polygonId]; RaiseVerbose("BabylonExporter.Mesh | nbTriangles=" + nbTriangles, 3); for (int triangleIndex = 0; triangleIndex < triangleCounts[polygonId]; triangleIndex++) { RaiseVerbose("BabylonExporter.Mesh | triangleIndex=" + triangleIndex, 3); int[] triangleVertices = new int[3]; mFnMesh.getPolygonTriangleVertices(polygonId, triangleIndex, triangleVertices); RaiseVerbose("BabylonExporter.Mesh | triangleVertices=" + triangleVertices.toString(), 3); foreach (int vertexId in triangleVertices) { RaiseVerbose("BabylonExporter.Mesh | vertexId=" + vertexId, 3); MPoint point = new MPoint(); mFnMesh.getPoint(vertexId, point); RaiseVerbose("BabylonExporter.Mesh | point=" + point.toString(), 3); MVector normal = new MVector(); mFnMesh.getFaceVertexNormal(polygonId, vertexId, normal); RaiseVerbose("BabylonExporter.Mesh | normal=" + normal.toString(), 3); } } } #endregion if (IsMeshExportable(mFnMesh) == false) { return(null); } RaiseMessage(mFnMesh.name, 1); var babylonMesh = new BabylonMesh { name = mFnMesh.name, id = mFnMesh.uuid().asString() }; // Position / rotation / scaling / hierarchy ExportNode(babylonMesh, mFnDagNode, babylonScene); // Misc. // TODO - Retreive from Maya // TODO - What is the difference between isVisible and visibility? // TODO - Fix fatal error: Attempting to save in C:/Users/Fabrice/AppData/Local/Temp/Fabrice.20171205.1613.ma //babylonMesh.isVisible = mDagPath.isVisible; //babylonMesh.visibility = meshNode.MaxNode.GetVisibility(0, Tools.Forever); //babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1; //babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1; if (mFnMesh.numPolygons < 1) { RaiseError($"Mesh {babylonMesh.name} has no face", 2); } if (mFnMesh.numVertices < 3) { RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2); } if (mFnMesh.numVertices >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has more 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); } // TODO - Material var vertices = new List <GlobalVertex>(); var indices = new List <int>(); // TODO - UV, color, alpha //var mappingChannels = unskinnedMesh.ActiveMapChannelNum; //bool hasUV = false; //bool hasUV2 = false; //for (int i = 0; i < mappingChannels.Count; ++i) //{ // var indexer = i; // 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; // TODO - Add custom properties var optimizeVertices = false; // meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices"); // Compute normals var subMeshes = new List <BabylonSubMesh>(); ExtractGeometry(mFnMesh, vertices, indices, subMeshes, optimizeVertices); 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 => v.Position).ToArray(); babylonMesh.normals = vertices.SelectMany(v => v.Normal).ToArray(); babylonMesh.subMeshes = subMeshes.ToArray(); // Buffers - Indices babylonMesh.indices = indices.ToArray(); babylonScene.MeshesList.Add(babylonMesh); RaiseMessage("BabylonExporter.Mesh | done", 3); return(babylonMesh); }
/// <summary> /// /// </summary> /// <param name="mDagPath">DAG path to the transform above mesh</param> /// <param name="babylonScene"></param> /// <returns></returns> private BabylonNode ExportMesh(MDagPath mDagPath, BabylonScene babylonScene) { RaiseMessage(mDagPath.partialPathName, 1); // Transform above mesh MFnTransform mFnTransform = new MFnTransform(mDagPath); // Mesh direct child of the transform MFnMesh mFnMesh = null; for (uint i = 0; i < mFnTransform.childCount; i++) { MObject childObject = mFnTransform.child(i); if (childObject.apiType == MFn.Type.kMesh) { var _mFnMesh = new MFnMesh(childObject); if (!_mFnMesh.isIntermediateObject) { mFnMesh = _mFnMesh; } } } if (mFnMesh == null) { RaiseError("No mesh found has child of " + mDagPath.fullPathName); return(null); } RaiseMessage("mFnMesh.fullPathName=" + mFnMesh.fullPathName, 2); // --- prints --- #region prints Action <MFnDagNode> printMFnDagNode = (MFnDagNode mFnDagNode) => { RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.name=" + mFnDagNode.name, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.absoluteName=" + mFnDagNode.absoluteName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.fullPathName=" + mFnDagNode.fullPathName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.partialPathName=" + mFnDagNode.partialPathName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.activeColor=" + mFnDagNode.activeColor.toString(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.attributeCount=" + mFnDagNode.attributeCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.childCount=" + mFnDagNode.childCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.dormantColor=" + mFnDagNode.dormantColor, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.hasUniqueName=" + mFnDagNode.hasUniqueName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.inUnderWorld=" + mFnDagNode.inUnderWorld, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isDefaultNode=" + mFnDagNode.isDefaultNode, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanceable=" + mFnDagNode.isInstanceable, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced(true)=" + mFnDagNode.isInstanced(true), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced(false)=" + mFnDagNode.isInstanced(false), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced()=" + mFnDagNode.isInstanced(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.instanceCount(true)=" + mFnDagNode.instanceCount(true), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.instanceCount(false)=" + mFnDagNode.instanceCount(false), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isIntermediateObject=" + mFnDagNode.isIntermediateObject, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isShared=" + mFnDagNode.isShared, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.objectColor=" + mFnDagNode.objectColor, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.parentCount=" + mFnDagNode.parentCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.parentNamespace=" + mFnDagNode.parentNamespace, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.uuid().asString()=" + mFnDagNode.uuid().asString(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.dagRoot().apiType=" + mFnDagNode.dagRoot().apiType, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.model.equalEqual(mFnDagNode.objectProperty)=" + mFnDagNode.model.equalEqual(mFnDagNode.objectProperty), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.transformationMatrix.toString()=" + mFnDagNode.transformationMatrix.toString(), 3); }; Action <MFnMesh> printMFnMesh = (MFnMesh _mFnMesh) => { printMFnDagNode(mFnMesh); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numVertices=" + _mFnMesh.numVertices, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numEdges=" + _mFnMesh.numEdges, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numPolygons=" + _mFnMesh.numPolygons, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numFaceVertices=" + _mFnMesh.numFaceVertices, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numNormals=" + _mFnMesh.numNormals, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numUVSets=" + _mFnMesh.numUVSets, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numUVsProperty=" + _mFnMesh.numUVsProperty, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.displayColors=" + _mFnMesh.displayColors, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numColorSets=" + _mFnMesh.numColorSets, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numColorsProperty=" + _mFnMesh.numColorsProperty, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.currentUVSetName()=" + _mFnMesh.currentUVSetName(), 3); var _uvSetNames = new MStringArray(); mFnMesh.getUVSetNames(_uvSetNames); foreach (var uvSetName in _uvSetNames) { RaiseVerbose("BabylonExporter.Mesh | uvSetName=" + uvSetName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numUVs(uvSetName)=" + mFnMesh.numUVs(uvSetName), 4); MFloatArray us = new MFloatArray(); MFloatArray vs = new MFloatArray(); mFnMesh.getUVs(us, vs, uvSetName); RaiseVerbose("BabylonExporter.Mesh | us.Count=" + us.Count, 4); } }; Action <MFnTransform> printMFnTransform = (MFnTransform _mFnMesh) => { printMFnDagNode(mFnMesh); }; RaiseVerbose("BabylonExporter.Mesh | mFnMesh data", 2); printMFnMesh(mFnMesh); RaiseVerbose("BabylonExporter.Mesh | mFnTransform data", 2); printMFnTransform(mFnTransform); Print(mFnTransform, 2, "Print ExportMesh mFnTransform"); Print(mFnMesh, 2, "Print ExportMesh mFnMesh"); //// Geometry //MIntArray triangleCounts = new MIntArray(); //MIntArray trianglesVertices = new MIntArray(); //mFnMesh.getTriangles(triangleCounts, trianglesVertices); //RaiseVerbose("BabylonExporter.Mesh | triangleCounts.ToArray()=" + triangleCounts.ToArray().toString(), 3); //RaiseVerbose("BabylonExporter.Mesh | trianglesVertices.ToArray()=" + trianglesVertices.ToArray().toString(), 3); //int[] polygonsVertexCount = new int[mFnMesh.numPolygons]; //for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++) //{ // polygonsVertexCount[polygonId] = mFnMesh.polygonVertexCount(polygonId); //} //RaiseVerbose("BabylonExporter.Mesh | polygonsVertexCount=" + polygonsVertexCount.toString(), 3); ////MFloatPointArray points = new MFloatPointArray(); ////mFnMesh.getPoints(points); ////RaiseVerbose("BabylonExporter.Mesh | points.ToArray()=" + points.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3); ////MFloatVectorArray normals = new MFloatVectorArray(); ////mFnMesh.getNormals(normals); ////RaiseVerbose("BabylonExporter.Mesh | normals.ToArray()=" + normals.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3); //for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++) //{ // MIntArray verticesId = new MIntArray(); // RaiseVerbose("BabylonExporter.Mesh | polygonId=" + polygonId, 3); // int nbTriangles = triangleCounts[polygonId]; // RaiseVerbose("BabylonExporter.Mesh | nbTriangles=" + nbTriangles, 3); // for (int triangleIndex = 0; triangleIndex < triangleCounts[polygonId]; triangleIndex++) // { // RaiseVerbose("BabylonExporter.Mesh | triangleIndex=" + triangleIndex, 3); // int[] triangleVertices = new int[3]; // mFnMesh.getPolygonTriangleVertices(polygonId, triangleIndex, triangleVertices); // RaiseVerbose("BabylonExporter.Mesh | triangleVertices=" + triangleVertices.toString(), 3); // foreach (int vertexId in triangleVertices) // { // RaiseVerbose("BabylonExporter.Mesh | vertexId=" + vertexId, 3); // MPoint point = new MPoint(); // mFnMesh.getPoint(vertexId, point); // RaiseVerbose("BabylonExporter.Mesh | point=" + point.toString(), 3); // MVector normal = new MVector(); // mFnMesh.getFaceVertexNormal(polygonId, vertexId, normal); // RaiseVerbose("BabylonExporter.Mesh | normal=" + normal.toString(), 3); // } // } //} #endregion if (IsMeshExportable(mFnMesh, mDagPath) == false) { return(null); } var babylonMesh = new BabylonMesh { name = mFnTransform.name, id = mFnTransform.uuid().asString() }; // Position / rotation / scaling / hierarchy ExportNode(babylonMesh, mFnTransform, babylonScene); // Misc. // TODO - Retreive from Maya // TODO - What is the difference between isVisible and visibility? // TODO - Fix fatal error: Attempting to save in C:/Users/Fabrice/AppData/Local/Temp/Fabrice.20171205.1613.ma //babylonMesh.isVisible = mDagPath.isVisible; //babylonMesh.visibility = meshNode.MaxNode.GetVisibility(0, Tools.Forever); //babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1; //babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1; if (mFnMesh.numPolygons < 1) { RaiseError($"Mesh {babylonMesh.name} has no face", 2); } if (mFnMesh.numVertices < 3) { RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2); } if (mFnMesh.numVertices >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has more 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); } // Material MObjectArray shaders = new MObjectArray(); mFnMesh.getConnectedShaders(0, shaders, new MIntArray()); if (shaders.Count > 0) { List <MFnDependencyNode> materials = new List <MFnDependencyNode>(); foreach (MObject shader in shaders) { // Retreive material MFnDependencyNode shadingEngine = new MFnDependencyNode(shader); MPlug mPlugSurfaceShader = shadingEngine.findPlug("surfaceShader"); MObject materialObject = mPlugSurfaceShader.source.node; MFnDependencyNode material = new MFnDependencyNode(materialObject); materials.Add(material); } if (shaders.Count == 1) { MFnDependencyNode material = materials[0]; // Material is referenced by id babylonMesh.materialId = material.uuid().asString(); // Register material for export if not already done if (!referencedMaterials.Contains(material, new MFnDependencyNodeEqualityComparer())) { referencedMaterials.Add(material); } } else { // Create a new id for the group of sub materials string uuidMultiMaterial = GetMultimaterialUUID(materials); // Multi material is referenced by id babylonMesh.materialId = uuidMultiMaterial; // Register multi material for export if not already done if (!multiMaterials.ContainsKey(uuidMultiMaterial)) { multiMaterials.Add(uuidMultiMaterial, materials); } } } var vertices = new List <GlobalVertex>(); var indices = new List <int>(); var uvSetNames = new MStringArray(); mFnMesh.getUVSetNames(uvSetNames); bool[] isUVExportSuccess = new bool[Math.Min(uvSetNames.Count, 2)]; for (int indexUVSet = 0; indexUVSet < isUVExportSuccess.Length; indexUVSet++) { isUVExportSuccess[indexUVSet] = true; } // TODO - color, alpha //var hasColor = unskinnedMesh.NumberOfColorVerts > 0; //var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0; // TODO - Add custom properties var optimizeVertices = false; // meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices"); // Compute normals var subMeshes = new List <BabylonSubMesh>(); ExtractGeometry(mFnMesh, vertices, indices, subMeshes, uvSetNames, ref isUVExportSuccess, optimizeVertices); 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); } } for (int indexUVSet = 0; indexUVSet < isUVExportSuccess.Length; indexUVSet++) { string uvSetName = uvSetNames[indexUVSet]; // If at least one vertex is mapped to an UV coordinate but some have failed to be exported if (isUVExportSuccess[indexUVSet] == false && mFnMesh.numUVs(uvSetName) > 0) { RaiseWarning($"Failed to export UV set named {uvSetName}. Ensure all vertices are mapped to a UV coordinate.", 2); } } RaiseMessage($"{vertices.Count} vertices, {indices.Count / 3} faces", 2); // Buffers babylonMesh.positions = vertices.SelectMany(v => v.Position).ToArray(); babylonMesh.normals = vertices.SelectMany(v => v.Normal).ToArray(); // TODO - Export colors ? //babylonMesh.colors = vertices.SelectMany(v => v.Color).ToArray(); if (uvSetNames.Count > 0 && isUVExportSuccess[0]) { babylonMesh.uvs = vertices.SelectMany(v => v.UV).ToArray(); } if (uvSetNames.Count > 1 && isUVExportSuccess[1]) { babylonMesh.uvs2 = vertices.SelectMany(v => v.UV2).ToArray(); } babylonMesh.subMeshes = subMeshes.ToArray(); // Buffers - Indices babylonMesh.indices = indices.ToArray(); babylonScene.MeshesList.Add(babylonMesh); RaiseMessage("BabylonExporter.Mesh | done", 2); return(babylonMesh); }
private void ExportAnimationGroups(GLTF gltf, BabylonScene babylonScene) { // Retreive and parse animation group data var animationGroupList = babylonScene.animationGroups; var animationGroupCount = animationGroupList == null ? 0 : animationGroupList.Count; gltf.AnimationsList.Clear(); gltf.AnimationsList.Capacity = Math.Max(gltf.AnimationsList.Capacity, animationGroupCount); if (animationGroupCount <= 0) { logger.RaiseMessage("GLTFExporter.Animation | No AnimationGroups: exporting all animations together.", 1); GLTFAnimation gltfAnimation = new GLTFAnimation(); gltfAnimation.name = "All Animations"; int startFrame = babylonScene.TimelineStartFrame; int endFrame = babylonScene.TimelineEndFrame; foreach (var pair in nodeToGltfNodeMap) { BabylonNode node = pair.Key; GLTFNode gltfNode = pair.Value; bool nodeHasAnimations = node.animations != null && node.animations.Length > 0 && node.animations[0] != null; bool nodeHasExtraAnimations = node.extraAnimations != null && node.extraAnimations.Count > 0 && node.extraAnimations[0] != null; BabylonMesh meshNode = node as BabylonMesh; BabylonMorphTargetManager morphTargetManager = null; bool nodeHasAnimatedMorphTargets = false; if (meshNode != null && meshNode.morphTargetManagerId != null) { morphTargetManager = GetBabylonMorphTargetManager(babylonScene, meshNode); if (morphTargetManager != null) { nodeHasAnimatedMorphTargets = morphTargetManager.targets.Any(target => target.animations != null && target.animations.Length > 0 && target.animations[0] != null); } } if (!nodeHasAnimations && !nodeHasExtraAnimations && !nodeHasAnimatedMorphTargets) { continue; } if (nodeHasAnimations && node.animations[0].property == "_matrix") { ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, node, pair.Value); } else { ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, node, gltfNode, babylonScene); } if (nodeHasAnimatedMorphTargets) { ExportMorphTargetWeightAnimation(morphTargetManager, gltf, gltfNode, gltfAnimation.ChannelList, gltfAnimation.SamplerList, startFrame, endFrame, babylonScene); } } if (gltfAnimation.ChannelList.Count > 0) { gltf.AnimationsList.Add(gltfAnimation); } else { logger.RaiseMessage("GLTFExporter.Animation | No animation data for this animation, it is ignored.", 2); } } else { foreach (BabylonAnimationGroup animGroup in animationGroupList) { logger.RaiseMessage("GLTFExporter.Animation | " + animGroup.name, 1); GLTFAnimation gltfAnimation = new GLTFAnimation(); gltfAnimation.name = animGroup.name; int startFrame = MathUtilities.RoundToInt(animGroup.from); int endFrame = MathUtilities.RoundToInt(animGroup.to); var uniqueNodeIds = animGroup.targetedAnimations.Select(targetAnim => targetAnim.targetId).Distinct(); foreach (var id in uniqueNodeIds) { BabylonNode babylonNode = babylonNodes.Find(node => node.id.Equals(id)); GLTFNode gltfNode = null; // search the babylon scene id map for the babylon node that matches this id if (babylonNode != null) { BabylonMorphTargetManager morphTargetManager = null; // search our babylon->gltf node mapping to see if this node is included in the exported gltf scene if (!nodeToGltfNodeMap.TryGetValue(babylonNode, out gltfNode)) { continue; } bool nodeHasAnimations = babylonNode.animations != null && babylonNode.animations.Length > 0 && babylonNode.animations[0] != null; bool nodeHasExtraAnimations = babylonNode.extraAnimations != null && babylonNode.extraAnimations.Count > 0 && babylonNode.extraAnimations[0] != null; if (!nodeHasAnimations && !nodeHasExtraAnimations) { continue; } if (nodeHasAnimations && babylonNode.animations[0].property == "_matrix") //TODO: Is this check accurate for deciphering between bones and nodes? { ExportBoneAnimation(gltfAnimation, startFrame, endFrame, gltf, babylonNode, gltfNode, animGroup); } else { ExportNodeAnimation(gltfAnimation, startFrame, endFrame, gltf, babylonNode, gltfNode, babylonScene, animGroup); } } else { // if the node isn't found in the scene id map, check if it is the id for a morph target BabylonMorphTargetManager morphTargetManager = babylonScene.morphTargetManagers.FirstOrDefault(mtm => mtm.targets.Any(target => target.animations != null && target.animations.Length > 0 && target.animations[0] != null)); if (morphTargetManager != null) { BabylonMesh mesh = morphTargetManager.sourceMesh; if (mesh != null && nodeToGltfNodeMap.TryGetValue(mesh, out gltfNode)) { ExportMorphTargetWeightAnimation(morphTargetManager, gltf, gltfNode, gltfAnimation.ChannelList, gltfAnimation.SamplerList, startFrame, endFrame, babylonScene); } } } } if (gltfAnimation.ChannelList.Count > 0) { gltf.AnimationsList.Add(gltfAnimation); } else { logger.RaiseMessage("No data exported for this animation, it is ignored.", 2); } // clear the exported morph target cache, since we are exporting a new animation group. //TODO: we should probably do this more elegantly. exportedMorphTargets.Clear(); } } }
public void Export(ExportParameters exportParameters) { // Check input text is valid var scaleFactorFloat = 1.0f; string scaleFactor = exportParameters.scaleFactor; try { scaleFactor = scaleFactor.Replace(".", System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator); scaleFactor = scaleFactor.Replace(",", System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator); scaleFactorFloat = float.Parse(scaleFactor); } catch { RaiseError("Scale factor is not a valid number."); return; } long quality = 0L; string txtQuality = exportParameters.txtQuality; try { quality = long.Parse(txtQuality); if (quality < 0 || quality > 100) { throw new Exception(); } } catch { RaiseError("Quality is not a valid number. It should be an integer between 0 and 100."); RaiseError("This parameter set the quality of jpg compression."); return; } this.exportParameters = exportParameters; var gameConversionManger = Loader.Global.ConversionManager; gameConversionManger.CoordSystem = Autodesk.Max.IGameConversionManager.CoordSystem.D3d; var gameScene = Loader.Global.IGameInterface; if (exportParameters.exportNode == null) { gameScene.InitialiseIGame(false); } else { gameScene.InitialiseIGame(exportParameters.exportNode, true); } gameScene.SetStaticFrame(0); MaxSceneFileName = gameScene.SceneFileName; IsCancelled = false; string fileExportString = exportParameters.exportNode != null ? $"{exportParameters.exportNode.NodeName} | {exportParameters.outputPath}" : exportParameters.outputPath; RaiseMessage($"Exportation started: {fileExportString}", Color.Blue); ReportProgressChanged(0); string tempOutputDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); string outputDirectory = Path.GetDirectoryName(exportParameters.outputPath); string outputFileName = Path.GetFileName(exportParameters.outputPath); // Check directory exists if (!Directory.Exists(outputDirectory)) { RaiseError("Exportation stopped: Output folder does not exist"); ReportProgressChanged(100); return; } Directory.CreateDirectory(tempOutputDirectory); var outputBabylonDirectory = tempOutputDirectory; // Force output file extension to be babylon outputFileName = Path.ChangeExtension(outputFileName, "babylon"); var babylonScene = new BabylonScene(outputBabylonDirectory); var rawScene = Loader.Core.RootNode; var watch = new Stopwatch(); watch.Start(); string outputFormat = exportParameters.outputFormat; isBabylonExported = outputFormat == "babylon" || outputFormat == "binary babylon"; isGltfExported = outputFormat == "gltf" || outputFormat == "glb"; // Get scene parameters optimizeAnimations = !Loader.Core.RootNode.GetBoolProperty("babylonjs_donotoptimizeanimations"); exportNonAnimated = Loader.Core.RootNode.GetBoolProperty("babylonjs_animgroup_exportnonanimated"); // Save scene if (exportParameters.autoSave3dsMaxFile) { RaiseMessage("Saving 3ds max file"); var forceSave = Loader.Core.FileSave; callerForm?.BringToFront(); } // Producer babylonScene.producer = new BabylonProducer { name = "3dsmax", #if MAX2019 version = "2019", #elif MAX2018 version = "2018", #elif MAX2017 version = "2017", #else version = Loader.Core.ProductVersion.ToString(), #endif exporter_version = exporterVersion, file = outputFileName }; // Global babylonScene.autoClear = true; babylonScene.clearColor = Loader.Core.GetBackGround(0, Tools.Forever).ToArray(); babylonScene.ambientColor = Loader.Core.GetAmbient(0, Tools.Forever).ToArray(); babylonScene.gravity = rawScene.GetVector3Property("babylonjs_gravity"); ExportQuaternionsInsteadOfEulers = rawScene.GetBoolProperty("babylonjs_exportquaternions", 1); if (Loader.Core.UseEnvironmentMap && Loader.Core.EnvironmentMap != null) { // Environment texture var environmentMap = Loader.Core.EnvironmentMap; // Copy image file to output if necessary var babylonTexture = ExportEnvironmnentTexture(environmentMap, babylonScene); if (babylonTexture != null) { babylonScene.environmentTexture = babylonTexture.name; // Skybox babylonScene.createDefaultSkybox = rawScene.GetBoolProperty("babylonjs_createDefaultSkybox"); babylonScene.skyboxBlurLevel = rawScene.GetFloatProperty("babylonjs_skyboxBlurLevel"); } } // Sounds var soundName = rawScene.GetStringProperty("babylonjs_sound_filename", ""); if (!string.IsNullOrEmpty(soundName)) { var filename = Path.GetFileName(soundName); var globalSound = new BabylonSound { autoplay = rawScene.GetBoolProperty("babylonjs_sound_autoplay", 1), loop = rawScene.GetBoolProperty("babylonjs_sound_loop", 1), name = filename }; babylonScene.SoundsList.Add(globalSound); if (isBabylonExported) { try { File.Copy(soundName, Path.Combine(babylonScene.OutputPath, filename), true); } catch { } } } // Root nodes RaiseMessage("Exporting nodes"); HashSet <IIGameNode> maxRootNodes = getRootNodes(gameScene); var progressionStep = 80.0f / maxRootNodes.Count; var progression = 10.0f; ReportProgressChanged((int)progression); referencedMaterials.Clear(); Tools.guids.Clear(); // Reseting is optionnal. It makes each morph target manager export starts from id = 0. BabylonMorphTargetManager.Reset(); foreach (var maxRootNode in maxRootNodes) { BabylonNode node = exportNodeRec(maxRootNode, babylonScene, gameScene); // if we're exporting from a specific node, reset the pivot to {0,0,0} if (node != null && exportParameters.exportNode != null) { SetNodePosition(ref node, ref babylonScene, new float[] { 0, 0, 0 }); } progression += progressionStep; ReportProgressChanged((int)progression); CheckCancelled(); } ; RaiseMessage(string.Format("Total meshes: {0}", babylonScene.MeshesList.Count), Color.Gray, 1); // In 3DS Max the default camera look down (in the -z direction for the 3DS Max reference (+y for babylon)) // In Babylon the default camera look to the horizon (in the +z direction for the babylon reference) // In glTF the default camera look to the horizon (in the +Z direction for glTF reference) RaiseMessage("Update camera rotation and position", 1); for (int index = 0; index < babylonScene.CamerasList.Count; index++) { BabylonCamera camera = babylonScene.CamerasList[index]; FixCamera(ref camera, ref babylonScene); } // Light for glTF if (isGltfExported) { RaiseMessage("Update light rotation for glTF export", 1); for (int index = 0; index < babylonScene.LightsList.Count; index++) { BabylonNode light = babylonScene.LightsList[index]; FixNodeRotation(ref light, ref babylonScene, -Math.PI / 2); } } // Main camera BabylonCamera babylonMainCamera = null; ICameraObject maxMainCameraObject = null; if (babylonMainCamera == null && babylonScene.CamerasList.Count > 0) { // Set first camera as main one babylonMainCamera = babylonScene.CamerasList[0]; babylonScene.activeCameraID = babylonMainCamera.id; RaiseMessage("Active camera set to " + babylonMainCamera.name, Color.Green, 1, true); // Retreive camera node with same GUID var maxCameraNodesAsTab = gameScene.GetIGameNodeByType(Autodesk.Max.IGameObject.ObjectTypes.Camera); var maxCameraNodes = TabToList(maxCameraNodesAsTab); var maxMainCameraNode = maxCameraNodes.Find(_camera => _camera.MaxNode.GetGuid().ToString() == babylonMainCamera.id); maxMainCameraObject = (maxMainCameraNode.MaxNode.ObjectRef as ICameraObject); } if (babylonMainCamera == null) { RaiseWarning("No camera defined", 1); } else { RaiseMessage(string.Format("Total cameras: {0}", babylonScene.CamerasList.Count), Color.Gray, 1); } // Default light bool addDefaultLight = rawScene.GetBoolProperty("babylonjs_addDefaultLight", 1); if (addDefaultLight && babylonScene.LightsList.Count == 0) { RaiseWarning("No light defined", 1); RaiseWarning("A default hemispheric light was added for your convenience", 1); ExportDefaultLight(babylonScene); } else { RaiseMessage(string.Format("Total lights: {0}", babylonScene.LightsList.Count), Color.Gray, 1); } if (scaleFactorFloat != 1.0f) { RaiseMessage("A root node is added for scaling", 1); // Create root node for scaling BabylonMesh rootNode = new BabylonMesh { name = "root", id = Guid.NewGuid().ToString() }; rootNode.isDummy = true; float rootNodeScale = scaleFactorFloat; rootNode.scaling = new float[3] { rootNodeScale, rootNodeScale, rootNodeScale }; if (ExportQuaternionsInsteadOfEulers) { rootNode.rotationQuaternion = new float[] { 0, 0, 0, 1 }; } else { rootNode.rotation = new float[] { 0, 0, 0 }; } // Update all top nodes var babylonNodes = new List <BabylonNode>(); babylonNodes.AddRange(babylonScene.MeshesList); babylonNodes.AddRange(babylonScene.CamerasList); babylonNodes.AddRange(babylonScene.LightsList); foreach (BabylonNode babylonNode in babylonNodes) { if (babylonNode.parentId == null) { babylonNode.parentId = rootNode.id; } } // Store root node babylonScene.MeshesList.Add(rootNode); } // Materials if (exportParameters.exportMaterials) { RaiseMessage("Exporting materials"); var matsToExport = referencedMaterials.ToArray(); // Snapshot because multimaterials can export new materials foreach (var mat in matsToExport) { ExportMaterial(mat, babylonScene); CheckCancelled(); } RaiseMessage(string.Format("Total: {0}", babylonScene.MaterialsList.Count + babylonScene.MultiMaterialsList.Count), Color.Gray, 1); } else { RaiseMessage("Skipping material export."); } // Fog for (var index = 0; index < Loader.Core.NumAtmospheric; index++) { var atmospheric = Loader.Core.GetAtmospheric(index); if (atmospheric.Active(0) && atmospheric.ClassName == "Fog") { var fog = atmospheric as IStdFog; RaiseMessage("Exporting fog"); if (fog != null) { babylonScene.fogColor = fog.GetColor(0).ToArray(); babylonScene.fogMode = 3; } if (babylonMainCamera != null) { babylonScene.fogStart = maxMainCameraObject.GetEnvRange(0, 0, Tools.Forever); babylonScene.fogEnd = maxMainCameraObject.GetEnvRange(0, 1, Tools.Forever); } } } // Skeletons if (skins.Count > 0) { RaiseMessage("Exporting skeletons"); foreach (var skin in skins) { ExportSkin(skin, babylonScene); } } // Animation group if (isBabylonExported) { RaiseMessage("Export animation groups"); // add animation groups to the scene babylonScene.animationGroups = ExportAnimationGroups(babylonScene); // if there is animationGroup, then remove animations from nodes if (babylonScene.animationGroups.Count > 0) { foreach (BabylonNode node in babylonScene.MeshesList) { node.animations = null; } foreach (BabylonNode node in babylonScene.LightsList) { node.animations = null; } foreach (BabylonNode node in babylonScene.CamerasList) { node.animations = null; } foreach (BabylonSkeleton skel in babylonScene.SkeletonsList) { foreach (BabylonBone bone in skel.bones) { bone.animation = null; } } } } // Output babylonScene.Prepare(false, false); if (isBabylonExported) { RaiseMessage("Saving to output file"); var outputFile = Path.Combine(outputBabylonDirectory, outputFileName); var jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings()); var sb = new StringBuilder(); var sw = new StringWriter(sb, CultureInfo.InvariantCulture); using (var jsonWriter = new JsonTextWriterOptimized(sw)) { jsonWriter.Formatting = Formatting.None; jsonSerializer.Serialize(jsonWriter, babylonScene); } File.WriteAllText(outputFile, sb.ToString()); if (exportParameters.generateManifest) { File.WriteAllText(outputFile + ".manifest", "{\r\n\"version\" : 1,\r\n\"enableSceneOffline\" : true,\r\n\"enableTexturesOffline\" : true\r\n}"); } // Binary if (outputFormat == "binary babylon") { RaiseMessage("Generating binary files"); BabylonFileConverter.BinaryConverter.Convert(outputFile, outputBabylonDirectory + "\\Binary", message => RaiseMessage(message, 1), error => RaiseError(error, 1)); } } ReportProgressChanged(100); // Export glTF if (isGltfExported) { bool generateBinary = outputFormat == "glb"; ExportGltf(babylonScene, tempOutputDirectory, outputFileName, generateBinary); } // Move files to output directory var filePaths = Directory.GetFiles(tempOutputDirectory); if (outputFormat == "binary babylon") { var tempBinaryOutputDirectory = Path.Combine(tempOutputDirectory, "Binary"); var binaryFilePaths = Directory.GetFiles(tempBinaryOutputDirectory); foreach (var filePath in binaryFilePaths) { if (filePath.EndsWith(".binary.babylon")) { var file = Path.GetFileName(filePath); var tempFilePath = Path.Combine(tempBinaryOutputDirectory, file); var outputFile = Path.Combine(outputDirectory, file); moveFileToOutputDirectory(tempFilePath, outputFile, exportParameters); } else if (filePath.EndsWith(".babylonbinarymeshdata")) { var file = Path.GetFileName(filePath); var tempFilePath = Path.Combine(tempBinaryOutputDirectory, file); var outputFile = Path.Combine(outputDirectory, file); moveFileToOutputDirectory(tempFilePath, outputFile, exportParameters); } } } if (outputFormat == "glb") { foreach (var file_path in filePaths) { if (Path.GetExtension(file_path) == ".glb") { var file = Path.GetFileName(file_path); var tempFilePath = Path.Combine(tempOutputDirectory, file); var outputFile = Path.Combine(outputDirectory, file); moveFileToOutputDirectory(tempFilePath, outputFile, exportParameters); break; } } } else { foreach (var filePath in filePaths) { var file = Path.GetFileName(filePath); var outputPath = Path.Combine(outputDirectory, file); var tempFilePath = Path.Combine(tempOutputDirectory, file); moveFileToOutputDirectory(tempFilePath, outputPath, exportParameters); } } Directory.Delete(tempOutputDirectory, true); watch.Stop(); RaiseMessage(string.Format("Exportation done in {0:0.00}s: {1}", watch.ElapsedMilliseconds / 1000.0, fileExportString), Color.Blue); }
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(); } }
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 int DumpObjectGeometry(NovaObject novaObject, BabylonMesh babylonMesh, int startIndex, int endIndex) { var verticesIndices = new int[novaObject.VerticesCount]; for (var index = 0; index < verticesIndices.Length; index++) { verticesIndices[index] = -1; } // Vertices var transformMatrix = Matrix.Identity;//.Scaling(novaObject.Scaling) * novaObject.LocalMatrix; var indicesList = new List <int>(); NovaCustomVertexFormat.GlobalVector3[] vertices = novaObject.InternalMesh.LockVertexBuffer <NovaCustomVertexFormat.GlobalVector3>(NovaLock.ReadOnly, novaObject.VerticesCount); // Faces INovaDataStream data = novaObject.InternalMesh.LockIndexBuffer(NovaLock.ReadOnly); int[] indices; if (novaObject.Is32bits) { indices = data.Read <int>(novaObject.FacesCount * 3 * 4); } else { indices = (data.Read <ushort>(novaObject.FacesCount * 3 * 4)).Select(i => (int)i).ToArray(); } var positions = new List <float>(); var normals = new List <float>(); var uvs = new List <float>(); var uvs2 = new List <float>(); var colors = new List <float>(); int exportedVerticesCount = 0; for (var index = 0; index < novaObject.FacesCount; index++) { var v0 = indices[index * 3]; var v1 = indices[index * 3 + 1]; var v2 = indices[index * 3 + 2]; if (v0 < startIndex || v1 < startIndex || v2 < startIndex) { continue; } if (v0 >= startIndex && v0 < endIndex || v1 >= startIndex && v1 < endIndex || v2 >= startIndex && v2 < endIndex) { if (verticesIndices[v0] == -1) { verticesIndices[v0] = exportedVerticesCount++; DumpVertex(vertices[v0], positions, normals, uvs, uvs2, colors, transformMatrix, novaObject); } if (verticesIndices[v1] == -1) { verticesIndices[v1] = exportedVerticesCount++; DumpVertex(vertices[v1], positions, normals, uvs, uvs2, colors, transformMatrix, novaObject); } if (verticesIndices[v2] == -1) { verticesIndices[v2] = exportedVerticesCount++; DumpVertex(vertices[v2], positions, normals, uvs, uvs2, colors, transformMatrix, novaObject); } indicesList.Add(verticesIndices[v0]); indicesList.Add(verticesIndices[v1]); indicesList.Add(verticesIndices[v2]); } } if (positions.Count > 0) { babylonMesh.positions = positions.ToArray(); } if (normals.Count > 0) { babylonMesh.normals = normals.ToArray(); } if (uvs.Count > 0) { babylonMesh.uvs = uvs.ToArray(); } if (uvs2.Count > 0) { babylonMesh.uvs2 = uvs2.ToArray(); } if (colors.Count > 0) { babylonMesh.colors = colors.ToArray(); } babylonMesh.indices = indicesList.ToArray(); // Invert normal order for (var index = 0; index < babylonMesh.indices.Length; index += 3) { var temp = babylonMesh.indices[index]; babylonMesh.indices[index] = babylonMesh.indices[index + 2]; babylonMesh.indices[index + 2] = temp; } novaObject.InternalMesh.UnlockIndexBuffer(); novaObject.InternalMesh.UnlockVertexBuffer(); return(exportedVerticesCount); }
private BabylonMesh ConvertUnityMeshToBabylon(Mesh mesh, Transform transform, GameObject gameObject, float progress, ref UnityMetaData metaData, ref List <BabylonExport.Entities.BabylonParticleSystem> particleSystems, ref List <UnityFlareSystem> lensFlares, ref string componentTags, BabylonMesh collisionMesh = null, Collider collider = null) { BabylonMesh babylonMesh = new BabylonMesh(); metaData.type = "Mesh"; if (!String.IsNullOrEmpty(componentTags)) { babylonMesh.tags = componentTags; } ExporterWindow.ReportProgress(progress, "Exporting mesh: " + gameObject.name); babylonMesh.name = gameObject.name; babylonMesh.id = GetID(transform.gameObject); var renderer = gameObject.GetComponent <Renderer>(); if (renderer != null) { babylonMesh.receiveShadows = renderer.receiveShadows; } babylonMesh.parentId = GetParentID(transform); babylonMesh.position = transform.localPosition.ToFloat(); babylonMesh.rotation = new float[3]; babylonMesh.rotation[0] = transform.localRotation.eulerAngles.x * (float)Math.PI / 180; babylonMesh.rotation[1] = transform.localRotation.eulerAngles.y * (float)Math.PI / 180; babylonMesh.rotation[2] = transform.localRotation.eulerAngles.z * (float)Math.PI / 180; babylonMesh.scaling = transform.localScale.ToFloat(); babylonMesh.checkCollisions = false; // Collision mesh (With detail mesh fallback) string collisionMeshId = null; if (collider != null) { if (collisionMesh != null) { collisionMeshId = collisionMesh.id; collisionMesh.parentId = babylonMesh.id; collisionMesh.visibility = collider.isTrigger ? 0.25f : 0.5f; collisionMesh.checkCollisions = (exportationOptions.ExportCollisions && collider.isTrigger == false); } else { babylonMesh.checkCollisions = exportationOptions.ExportCollisions; } } metaData.properties["collisionMeshId"] = collisionMeshId; if (mesh != null) { Tools.GenerateBabylonMeshData(mesh, babylonMesh, babylonScene, transform); int index = 0; if (mesh.boneWeights.Length == mesh.vertexCount) { babylonMesh.matricesIndices = new int[mesh.vertexCount]; babylonMesh.matricesWeights = new float[mesh.vertexCount * 4]; index = 0; foreach (BoneWeight bw in mesh.boneWeights) { babylonMesh.matricesIndices[index] = (bw.boneIndex3 << 24) | (bw.boneIndex2 << 16) | (bw.boneIndex1 << 8) | bw.boneIndex0; babylonMesh.matricesWeights[index * 4 + 0] = bw.weight0; babylonMesh.matricesWeights[index * 4 + 1] = bw.weight1; babylonMesh.matricesWeights[index * 4 + 2] = bw.weight2; babylonMesh.matricesWeights[index * 4 + 3] = bw.weight3; var totalWeight = bw.weight0 + bw.weight1 + bw.weight2 + bw.weight3; if (Mathf.Abs(totalWeight - 1.0f) > 0.01f) { throw new Exception("Total bone weights is not normalized for: " + mesh); } index++; } } index = 0; if (renderer != null && renderer.sharedMaterial != null) { // Validate Multi Materials if (mesh.subMeshCount > 1) { BabylonMultiMaterial bMultiMat; string multiMatName = ""; for (int i = 0; i < renderer.sharedMaterials.Length; i++) { multiMatName += renderer.sharedMaterials[i].name; } if (!multiMatDictionary.ContainsKey(multiMatName)) { bMultiMat = new BabylonMultiMaterial { materials = new string[mesh.subMeshCount], id = Guid.NewGuid().ToString(), name = multiMatName }; for (int i = 0; i < renderer.sharedMaterials.Length; i++) { var sharedMaterial = renderer.sharedMaterials[i]; BabylonMaterial babylonMaterial; babylonMaterial = DumpMaterial(sharedMaterial, renderer.lightmapIndex, renderer.lightmapScaleOffset); bMultiMat.materials[i] = babylonMaterial.id; } if (mesh.subMeshCount > 1) { multiMatDictionary.Add(bMultiMat.name, bMultiMat); } } else { bMultiMat = multiMatDictionary[multiMatName]; } babylonMesh.materialId = bMultiMat.id; babylonMesh.subMeshes = new BabylonSubMesh[mesh.subMeshCount]; var offset = 0; for (int materialIndex = 0; materialIndex < mesh.subMeshCount; materialIndex++) { var unityTriangles = mesh.GetTriangles(materialIndex); babylonMesh.subMeshes[materialIndex] = new BabylonSubMesh { verticesStart = 0, verticesCount = mesh.vertexCount, materialIndex = materialIndex, indexStart = offset, indexCount = unityTriangles.Length }; offset += unityTriangles.Length; } } else { babylonMesh.materialId = DumpMaterial(renderer.sharedMaterial, renderer.lightmapIndex, renderer.lightmapScaleOffset).id; } } babylonMesh.metadata = metaData; babylonScene.MeshesList.Add(babylonMesh); // Animations ExportAnimations(transform, babylonMesh); if (IsRotationQuaternionAnimated(babylonMesh)) { babylonMesh.rotationQuaternion = transform.localRotation.ToFloat(); } // Lens Flares ParseLensFlares(gameObject, babylonMesh.id, ref lensFlares); // Particles Systems ParseParticleSystems(gameObject, babylonMesh.id, ref particleSystems); // Babylon Physics if (exportationOptions.ExportPhysics) { var physics = gameObject.GetComponent <BabylonPhysicsState>(); if (physics != null) { babylonMesh.physicsMass = physics.mass; babylonMesh.physicsFriction = physics.friction; babylonMesh.physicsRestitution = physics.restitution; babylonMesh.physicsImpostor = (int)physics.imposter; } } } return(babylonMesh); }
/// <summary> /// Export to file /// </summary> /// <param name="outputDirectory">The directory to store the generated files</param> /// <param name="outputFileName">The filename to use for the generated files</param> /// <param name="outputFormat">The format to use for the generated files</param> /// <param name="generateManifest">Specifies if a manifest file should be generated</param> /// <param name="onlySelected">Specifies if only the selected objects should be exported</param> /// <param name="autoSaveMayaFile">Specifies if the Maya scene should be auto-saved</param> /// <param name="exportHiddenObjects">Specifies if hidden objects should be exported</param> /// <param name="copyTexturesToOutput">Specifies if textures should be copied to the output directory</param> /// <param name="optimizeVertices">Specifies if vertices should be optimized on export</param> /// <param name="exportTangents">Specifies if tangents should be exported</param> /// <param name="scaleFactor">Scales the scene by this factor</param> /// <param name="exportSkin">Specifies if skins should be exported</param> /// <param name="quality">The texture quality</param> /// <param name="dracoCompression">Specifies if draco compression should be used</param> /// <param name="exportMorphNormal">Specifies if normals should be exported for morph targets</param> /// <param name="exportMorphTangent">Specifies if tangents should be exported for morph targets</param> /// <param name="exportKHRLightsPunctual">Specifies if the KHR_lights_punctual extension should be enabled</param> /// <param name="exportKHRTextureTransform">Specifies if the KHR_texture_transform extension should be enabled</param> /// <param name="bakeAnimationFrames">Specifies if animations should be exporting keyframes directly or should manually bake out animations frame by frame</param> public void Export(ExportParameters exportParameters) { this.exportParameters = exportParameters; // Check if the animation is running MGlobal.executeCommand("play -q - state", out int isPlayed); if (isPlayed == 1) { RaiseError("Stop the animation before exporting."); return; } RaiseMessage("Export started", Color.Blue); var progression = 0.0f; ReportProgressChanged(progression); // Store export options this.isBabylonExported = exportParameters.outputFormat == "babylon" || exportParameters.outputFormat == "binary babylon"; var outputBabylonDirectory = Path.GetDirectoryName(exportParameters.outputPath); // Check directory exists if (!Directory.Exists(outputBabylonDirectory)) { RaiseError("Export stopped: Output folder does not exist"); ReportProgressChanged(100); return; } var watch = new Stopwatch(); watch.Start(); var babylonScene = new BabylonScene(outputBabylonDirectory); // Save scene if (exportParameters.autoSaveSceneFile) { RaiseMessage("Saving Maya file"); // Query expand file name string fileName = MGlobal.executeCommandStringResult($@"file -q -exn;"); // If scene has already been saved previously if (fileName.EndsWith(".ma") || fileName.EndsWith(".mb")) { // Name is already specified and this line will not fail MFileIO.save(); } else { // Open SaveAs dialog window MGlobal.executeCommand($@"fileDialog2;"); } } // Force output file extension to be babylon var outputFileName = Path.ChangeExtension(Path.GetFileName(exportParameters.outputPath), "babylon"); // Store selected nodes MSelectionList selectedNodes = new MSelectionList(); MGlobal.getActiveSelectionList(selectedNodes); selectedNodeFullPaths = new List <string>(); MItSelectionList mItSelectionList = new MItSelectionList(selectedNodes); while (!mItSelectionList.isDone) { MDagPath mDagPath = new MDagPath(); try { mItSelectionList.getDagPath(mDagPath); selectedNodeFullPaths.Add(mDagPath.fullPathName); } catch { // selected object is not a DAG object // fail silently } mItSelectionList.next(); } if (selectedNodeFullPaths.Count > 0) { RaiseMessage("Selected nodes full path"); foreach (string selectedNodeFullPath in selectedNodeFullPaths) { RaiseMessage(selectedNodeFullPath, 1); } } // Producer babylonScene.producer = new BabylonProducer { name = "Maya", version = "2018", exporter_version = exporterVersion, file = outputFileName }; // Global babylonScene.autoClear = true; // TODO - Retreive colors from Maya //babylonScene.clearColor = Loader.Core.GetBackGround(0, Tools.Forever).ToArray(); //babylonScene.ambientColor = Loader.Core.GetAmbient(0, Tools.Forever).ToArray(); babylonScene.TimelineStartFrame = Loader.GetMinTime(); babylonScene.TimelineEndFrame = Loader.GetMaxTime(); babylonScene.TimelineFramesPerSecond = Loader.GetFPS(); // TODO - Add custom properties _exportQuaternionsInsteadOfEulers = true; PrintDAG(true); PrintDAG(false); // Store the current frame. It can be change to find a proper one for the node/bone export double currentTime = Loader.GetCurrentTime(); // -------------------- // ------ Nodes ------- // -------------------- RaiseMessage("Exporting nodes"); // It makes each morph target manager export starts from id = 0. BabylonMorphTargetManager.Reset(); // Clear materials referencedMaterials.Clear(); multiMaterials.Clear(); // Get all nodes var dagIterator = new MItDag(MItDag.TraversalType.kDepthFirst, MFn.Type.kTransform); List <MDagPath> nodes = new List <MDagPath>(); while (!dagIterator.isDone) { MDagPath mDagPath = new MDagPath(); dagIterator.getPath(mDagPath); // Check if one of its descendant (direct or not) is a mesh/camera/light/locator if (isNodeRelevantToExportRec(mDagPath) // Ensure it's not one of the default cameras used as viewports in Maya && defaultCameraNames.Contains(mDagPath.partialPathName) == false) { nodes.Add(mDagPath); } else { // Skip descendants dagIterator.prune(); } dagIterator.next(); } // Export all nodes var progressionStep = 100.0f / nodes.Count; foreach (MDagPath mDagPath in nodes) { BabylonNode babylonNode = null; try { switch (getApiTypeOfDirectDescendants(mDagPath)) { case MFn.Type.kMesh: babylonNode = ExportMesh(mDagPath, babylonScene); break; case MFn.Type.kCamera: babylonNode = ExportCamera(mDagPath, babylonScene); break; case MFn.Type.kLight: // Lights api type are actually kPointLight, kSpotLight... babylonNode = ExportLight(mDagPath, babylonScene); break; case MFn.Type.kLocator: // Camera target babylonNode = ExportDummy(mDagPath, babylonScene); break; } } catch (Exception e) { this.RaiseWarning(String.Format("Exception raised during export. Node will be exported as dummy node. \r\nMessage: \r\n{0} \r\n{1}", e.Message, e.InnerException), 2); } // If node is not exported successfully if (babylonNode == null) { // Create a dummy (empty mesh) babylonNode = ExportDummy(mDagPath, babylonScene); } ; // Update progress bar progression += progressionStep; ReportProgressChanged(progression); CheckCancelled(); } RaiseMessage(string.Format("Total meshes: {0}", babylonScene.MeshesList.Count), Color.Gray, 1); // if nothing is enlightened, exclude all meshes foreach (BabylonLight light in babylonScene.LightsList) { if (light.includedOnlyMeshesIds.Length == 0) { light.excludedMeshesIds = babylonScene.MeshesList.Select(m => m.id).ToArray(); } } /* * Switch coordinate system at global level * * Add a root node with negative scaling * Pros - It's safer to use a root node * Cons - It's cleaner to switch at object level (as it is done now) * Use root node method when you want to be 100% sure of the output * Don't forget to also inverse winding order of mesh indices */ //// Switch from right to left handed coordinate system //MUuid mUuid = new MUuid(); //mUuid.generate(); //var rootNode = new BabylonMesh //{ // name = "root", // id = mUuid.asString(), // scaling = new float[] { 1, 1, -1 } //}; //foreach(var babylonMesh in babylonScene.MeshesList) //{ // // Add root meshes as child to root node // if (babylonMesh.parentId == null) // { // babylonMesh.parentId = rootNode.id; // } //} //babylonScene.MeshesList.Add(rootNode); // Main camera BabylonCamera babylonMainCamera = null; if (babylonScene.CamerasList.Count > 0) { // Set first camera as main one babylonMainCamera = babylonScene.CamerasList[0]; babylonScene.activeCameraID = babylonMainCamera.id; RaiseMessage("Active camera set to " + babylonMainCamera.name, Color.Green, 1, true); } if (babylonMainCamera == null) { RaiseWarning("No camera defined", 1); } else { RaiseMessage(string.Format("Total cameras: {0}", babylonScene.CamerasList.Count), Color.Gray, 1); } // Default light if (!exportParameters.pbrNoLight && babylonScene.LightsList.Count == 0) { RaiseWarning("No light defined", 1); RaiseWarning("A default ambient light was added for your convenience", 1); ExportDefaultLight(babylonScene); } else { RaiseMessage(string.Format("Total lights: {0}", babylonScene.LightsList.Count), Color.Gray, 1); } var sceneScaleFactor = exportParameters.scaleFactor; if (exportParameters.scaleFactor != 1.0f) { RaiseMessage(String.Format("A root node is added to globally scale the scene by {0}", sceneScaleFactor), 1); // Create root node for scaling BabylonMesh rootNode = new BabylonMesh { name = "root", id = Tools.GenerateUUID() }; rootNode.isDummy = true; float rootNodeScale = sceneScaleFactor; rootNode.scaling = new float[3] { rootNodeScale, rootNodeScale, rootNodeScale }; if (ExportQuaternionsInsteadOfEulers) { rootNode.rotationQuaternion = new float[] { 0, 0, 0, 1 }; } else { rootNode.rotation = new float[] { 0, 0, 0 }; } // Update all top nodes var babylonNodes = new List <BabylonNode>(); babylonNodes.AddRange(babylonScene.MeshesList); babylonNodes.AddRange(babylonScene.CamerasList); babylonNodes.AddRange(babylonScene.LightsList); foreach (BabylonNode babylonNode in babylonNodes) { if (babylonNode.parentId == null) { babylonNode.parentId = rootNode.id; } } // Store root node babylonScene.MeshesList.Add(rootNode); } // -------------------- // ----- Materials ---- // -------------------- RaiseMessage("Exporting materials"); GenerateMaterialDuplicationDatas(babylonScene); foreach (var mat in referencedMaterials) { ExportMaterial(mat, babylonScene, exportParameters.pbrFull); CheckCancelled(); } foreach (var mat in multiMaterials) { ExportMultiMaterial(mat.Key, mat.Value, babylonScene, exportParameters.pbrFull); CheckCancelled(); } UpdateMeshesMaterialId(babylonScene); RaiseMessage(string.Format("Total: {0}", babylonScene.MaterialsList.Count + babylonScene.MultiMaterialsList.Count), Color.Gray, 1); // Export skeletons if (exportParameters.exportSkins && skins.Count > 0) { progressSkin = 0; progressSkinStep = 100 / skins.Count; ReportProgressChanged(progressSkin); RaiseMessage("Exporting skeletons"); foreach (var skin in skins) { ExportSkin(skin, babylonScene); } } // set back the frame Loader.SetCurrentTime(currentTime); // ---------------------------- // ----- Animation groups ----- // ---------------------------- RaiseMessage("Export animation groups"); // add animation groups to the scene babylonScene.animationGroups = ExportAnimationGroups(babylonScene); if (isBabylonExported) { // if we are exporting to .Babylon then remove then remove animations from nodes if there are animation groups. if (babylonScene.animationGroups.Count > 0) { // add animations of each nodes in the animGroup List <BabylonNode> babylonNodes = new List <BabylonNode>(); babylonNodes.AddRange(babylonScene.MeshesList); babylonNodes.AddRange(babylonScene.CamerasList); babylonNodes.AddRange(babylonScene.LightsList); foreach (BabylonNode node in babylonNodes) { node.animations = null; } foreach (BabylonSkeleton skel in babylonScene.SkeletonsList) { foreach (BabylonBone bone in skel.bones) { bone.animation = null; } } } // setup a default skybox for the scene for .Babylon export. var sourcePath = exportParameters.pbrEnvironment; if (!string.IsNullOrEmpty(sourcePath)) { babylonScene.createDefaultSkybox = exportParameters.createDefaultSkybox; var fileName = Path.GetFileName(sourcePath); // Allow only dds file format if (!fileName.EndsWith(".dds")) { RaiseWarning("Failed to export defauenvironment texture: only .dds format is supported."); } else { RaiseMessage($"texture id = Max_Babylon_Default_Environment"); babylonScene.environmentTexture = fileName; if (exportParameters.writeTextures) { try { var destPath = Path.Combine(babylonScene.OutputPath, fileName); if (File.Exists(sourcePath) && sourcePath != destPath) { File.Copy(sourcePath, destPath, true); } } catch { // silently fails RaiseMessage($"Fail to export the default env texture", 3); } } } } } // Output babylonScene.Prepare(false, false); if (isBabylonExported) { Write(babylonScene, outputBabylonDirectory, outputFileName, exportParameters.outputFormat, exportParameters.generateManifest); } ReportProgressChanged(100); // Export glTF if (exportParameters.outputFormat == "gltf" || exportParameters.outputFormat == "glb") { bool generateBinary = exportParameters.outputFormat == "glb"; GLTFExporter gltfExporter = new GLTFExporter(); gltfExporter.ExportGltf(this.exportParameters, babylonScene, outputBabylonDirectory, outputFileName, generateBinary, this); } watch.Stop(); RaiseMessage(string.Format("Export done in {0:0.00}s", watch.ElapsedMilliseconds / 1000.0), Color.Blue); }
public void Export(string outputDirectory, string outputFileName, string outputFormat, bool generateManifest, bool onlySelected, bool autoSaveMayaFile, bool exportHiddenObjects, bool copyTexturesToOutput, bool optimizeVertices, bool exportTangents, string scaleFactor, bool exportSkin) { // Chekc if the animation is running MGlobal.executeCommand("play -q - state", out int isPlayed); if (isPlayed == 1) { RaiseError("Stop the animation before exporting."); return; } // Check input text is valid var scaleFactorFloat = 1.0f; try { scaleFactor = scaleFactor.Replace(".", System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator); scaleFactor = scaleFactor.Replace(",", System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator); scaleFactorFloat = float.Parse(scaleFactor); } catch { RaiseError("Scale factor is not a valid number."); return; } RaiseMessage("Exportation started", Color.Blue); var progression = 0.0f; ReportProgressChanged(progression); // Store export options _onlySelected = onlySelected; _exportHiddenObjects = exportHiddenObjects; _optimizeVertices = optimizeVertices; _exportTangents = exportTangents; CopyTexturesToOutput = copyTexturesToOutput; isBabylonExported = outputFormat == "babylon" || outputFormat == "binary babylon"; _exportSkin = exportSkin; // Check directory exists if (!Directory.Exists(outputDirectory)) { RaiseError("Exportation stopped: Output folder does not exist"); ReportProgressChanged(100); return; } var watch = new Stopwatch(); watch.Start(); var outputBabylonDirectory = outputDirectory; var babylonScene = new BabylonScene(outputBabylonDirectory); // Save scene if (autoSaveMayaFile) { RaiseMessage("Saving Maya file"); // Query expand file name string fileName = MGlobal.executeCommandStringResult($@"file -q -exn;"); // If scene has already been saved previously if (fileName.EndsWith(".ma") || fileName.EndsWith(".mb")) { // Name is already specified and this line will not fail MFileIO.save(); } else { // Open SaveAs dialog window MGlobal.executeCommand($@"fileDialog2;"); } } // Force output file extension to be babylon outputFileName = Path.ChangeExtension(outputFileName, "babylon"); // Store selected nodes MSelectionList selectedNodes = new MSelectionList(); MGlobal.getActiveSelectionList(selectedNodes); selectedNodeFullPaths = new List <string>(); MItSelectionList mItSelectionList = new MItSelectionList(selectedNodes); while (!mItSelectionList.isDone) { MDagPath mDagPath = new MDagPath(); try { mItSelectionList.getDagPath(mDagPath); selectedNodeFullPaths.Add(mDagPath.fullPathName); } catch { // selected object is not a DAG object // fail silently } mItSelectionList.next(); } if (selectedNodeFullPaths.Count > 0) { RaiseMessage("Selected nodes full path"); foreach (string selectedNodeFullPath in selectedNodeFullPaths) { RaiseMessage(selectedNodeFullPath, 1); } } // Producer babylonScene.producer = new BabylonProducer { name = "Maya", version = "2018", exporter_version = exporterVersion, file = outputFileName }; // Global babylonScene.autoClear = true; // TODO - Retreive colors from Maya //babylonScene.clearColor = Loader.Core.GetBackGround(0, Tools.Forever).ToArray(); //babylonScene.ambientColor = Loader.Core.GetAmbient(0, Tools.Forever).ToArray(); // TODO - Add custom properties _exportQuaternionsInsteadOfEulers = true; PrintDAG(true); PrintDAG(false); // -------------------- // ------ Nodes ------- // -------------------- RaiseMessage("Exporting nodes"); // Clear materials referencedMaterials.Clear(); multiMaterials.Clear(); // Get all nodes var dagIterator = new MItDag(MItDag.TraversalType.kDepthFirst, MFn.Type.kTransform); List <MDagPath> nodes = new List <MDagPath>(); while (!dagIterator.isDone) { MDagPath mDagPath = new MDagPath(); dagIterator.getPath(mDagPath); // Check if one of its descendant (direct or not) is a mesh/camera/light/locator if (isNodeRelevantToExportRec(mDagPath) // Ensure it's not one of the default cameras used as viewports in Maya && defaultCameraNames.Contains(mDagPath.partialPathName) == false) { nodes.Add(mDagPath); } else { // Skip descendants dagIterator.prune(); } dagIterator.next(); } // Export all nodes var progressionStep = 100.0f / nodes.Count; foreach (MDagPath mDagPath in nodes) { BabylonNode babylonNode = null; switch (getApiTypeOfDirectDescendants(mDagPath)) { case MFn.Type.kMesh: babylonNode = ExportMesh(mDagPath, babylonScene); break; case MFn.Type.kCamera: babylonNode = ExportCamera(mDagPath, babylonScene); break; case MFn.Type.kLight: // Lights api type are actually kPointLight, kSpotLight... babylonNode = ExportLight(mDagPath, babylonScene); break; case MFn.Type.kLocator: // Camera target babylonNode = ExportDummy(mDagPath, babylonScene); break; } // If node is not exported successfully if (babylonNode == null) { // Create a dummy (empty mesh) babylonNode = ExportDummy(mDagPath, babylonScene); } ; // Update progress bar progression += progressionStep; ReportProgressChanged(progression); CheckCancelled(); } RaiseMessage(string.Format("Total meshes: {0}", babylonScene.MeshesList.Count), Color.Gray, 1); // if nothing is enlightened, exclude all meshes foreach (BabylonLight light in babylonScene.LightsList) { if (light.includedOnlyMeshesIds.Length == 0) { light.excludedMeshesIds = babylonScene.MeshesList.Select(m => m.id).ToArray(); } } /* * Switch coordinate system at global level * * Add a root node with negative scaling * Pros - It's safer to use a root node * Cons - It's cleaner to switch at object level (as it is done now) * Use root node method when you want to be 100% sure of the output * Don't forget to also inverse winding order of mesh indices */ //// Switch from right to left handed coordinate system //MUuid mUuid = new MUuid(); //mUuid.generate(); //var rootNode = new BabylonMesh //{ // name = "root", // id = mUuid.asString(), // scaling = new float[] { 1, 1, -1 } //}; //foreach(var babylonMesh in babylonScene.MeshesList) //{ // // Add root meshes as child to root node // if (babylonMesh.parentId == null) // { // babylonMesh.parentId = rootNode.id; // } //} //babylonScene.MeshesList.Add(rootNode); // Main camera BabylonCamera babylonMainCamera = null; if (babylonScene.CamerasList.Count > 0) { // Set first camera as main one babylonMainCamera = babylonScene.CamerasList[0]; babylonScene.activeCameraID = babylonMainCamera.id; RaiseMessage("Active camera set to " + babylonMainCamera.name, Color.Green, 1, true); } if (babylonMainCamera == null) { RaiseWarning("No camera defined", 1); } else { RaiseMessage(string.Format("Total cameras: {0}", babylonScene.CamerasList.Count), Color.Gray, 1); } // Default light if (babylonScene.LightsList.Count == 0) { RaiseWarning("No light defined", 1); RaiseWarning("A default ambient light was added for your convenience", 1); ExportDefaultLight(babylonScene); } else { RaiseMessage(string.Format("Total lights: {0}", babylonScene.LightsList.Count), Color.Gray, 1); } if (scaleFactorFloat != 1.0f) { RaiseMessage("A root node is added for scaling", 1); // Create root node for scaling BabylonMesh rootNode = new BabylonMesh { name = "root", id = Tools.GenerateUUID() }; rootNode.isDummy = true; float rootNodeScale = 1.0f / scaleFactorFloat; rootNode.scaling = new float[3] { rootNodeScale, rootNodeScale, rootNodeScale }; // Update all top nodes var babylonNodes = new List <BabylonNode>(); babylonNodes.AddRange(babylonScene.MeshesList); babylonNodes.AddRange(babylonScene.CamerasList); babylonNodes.AddRange(babylonScene.LightsList); foreach (BabylonNode babylonNode in babylonNodes) { if (babylonNode.parentId == null) { babylonNode.parentId = rootNode.id; } } // Store root node babylonScene.MeshesList.Add(rootNode); } // -------------------- // ----- Materials ---- // -------------------- RaiseMessage("Exporting materials"); GenerateMaterialDuplicationDatas(babylonScene); foreach (var mat in referencedMaterials) { ExportMaterial(mat, babylonScene); CheckCancelled(); } foreach (var mat in multiMaterials) { ExportMultiMaterial(mat.Key, mat.Value, babylonScene); CheckCancelled(); } UpdateMeshesMaterialId(babylonScene); RaiseMessage(string.Format("Total: {0}", babylonScene.MaterialsList.Count + babylonScene.MultiMaterialsList.Count), Color.Gray, 1); // Export skeletons if (_exportSkin && skins.Count > 0) { progressSkin = 0; progressSkinStep = 100 / skins.Count; ReportProgressChanged(progressSkin); RaiseMessage("Exporting skeletons"); foreach (var skin in skins) { ExportSkin(skin, babylonScene); } } // Output babylonScene.Prepare(false, false); if (isBabylonExported) { Write(babylonScene, outputBabylonDirectory, outputFileName, outputFormat, generateManifest); } ReportProgressChanged(100); // Export glTF if (outputFormat == "gltf" || outputFormat == "glb") { bool generateBinary = outputFormat == "glb"; ExportGltf(babylonScene, outputDirectory, outputFileName, generateBinary); } watch.Stop(); RaiseMessage(string.Format("Exportation done in {0:0.00}s", watch.ElapsedMilliseconds / 1000.0), Color.Blue); }
private BabylonMesh ConvertUnityTerrainToBabylon(Terrain terrain, GameObject gameObject, float progress, ref UnityMetaData metaData, ref List <BabylonExport.Entities.BabylonParticleSystem> particleSystems, ref List <UnityFlareSystem> lensFlares, ref string componentTags) { ExporterWindow.ReportProgress(progress, "Exporting terrain: " + gameObject.name); var transform = gameObject.transform; float[] position = transform.localPosition.ToFloat(); float[] rotation = new float[3]; rotation[0] = transform.localRotation.eulerAngles.x * (float)Math.PI / 180; rotation[1] = transform.localRotation.eulerAngles.y * (float)Math.PI / 180; rotation[2] = transform.localRotation.eulerAngles.z * (float)Math.PI / 180; float[] scaling = transform.localScale.ToFloat(); BabylonMesh babylonMesh = new BabylonMesh { name = gameObject.name, id = GetID(gameObject) }; metaData.type = "Terrain"; if (!String.IsNullOrEmpty(componentTags)) { babylonMesh.tags = componentTags; } babylonMesh.tags += " [TERRAIN]"; if (!String.IsNullOrEmpty(babylonMesh.tags)) { babylonMesh.tags = babylonMesh.tags.Trim(); } babylonMesh.parentId = GetParentID(transform); babylonMesh.position = Vector3.zero.ToFloat(); babylonMesh.rotation = rotation; babylonMesh.scaling = scaling; babylonMesh.isVisible = true; babylonMesh.visibility = 1; babylonMesh.checkCollisions = false; metaData.properties["collisionMeshId"] = null; var generator = gameObject.GetComponent <BabylonTerrainGenerator>(); if (generator != null && terrain != null) { // TODO: Terrain tree information object treeInstances = null; object treePrototypes = null; // Terrain metadata infomation Vector3 terrainSize = terrain.terrainData.size; metaData.properties.Add("width", terrainSize.x); metaData.properties.Add("length", terrainSize.z); metaData.properties.Add("height", terrainSize.y); metaData.properties.Add("position", position); metaData.properties.Add("rotation", rotation); metaData.properties.Add("scaling", scaling); metaData.properties.Add("thickness", terrain.terrainData.thickness); metaData.properties.Add("detailWidth", terrain.terrainData.detailWidth); metaData.properties.Add("detailHeight", terrain.terrainData.detailHeight); metaData.properties.Add("heightmapWidth", terrain.terrainData.heightmapWidth); metaData.properties.Add("heightmapHeight", terrain.terrainData.heightmapHeight); metaData.properties.Add("wavingGrassAmount", terrain.terrainData.wavingGrassAmount); metaData.properties.Add("wavingGrassSpeed", terrain.terrainData.wavingGrassSpeed); metaData.properties.Add("wavingGrassStrength", terrain.terrainData.wavingGrassStrength); metaData.properties.Add("wavingGrassTint", terrain.terrainData.wavingGrassTint.ToFloat()); metaData.properties.Add("treeInstanceCount", terrain.terrainData.treeInstanceCount); metaData.properties.Add("treeInstances", treeInstances); metaData.properties.Add("treePrototypes", treePrototypes); metaData.properties.Add("physicsState", generator.physicsActive); metaData.properties.Add("physicsMass", generator.physicsMass); metaData.properties.Add("physicsFriction", generator.physicsFriction); metaData.properties.Add("physicsRestitution", generator.physicsRestitution); metaData.properties.Add("physicsImpostor", (int)generator.physicsImpostor); metaData.properties.Add("groundTessellation", generator.groundTessellation); // Generate detailed mesh ExporterWindow.ReportProgress(progress, "Generating terrain mesh: " + gameObject.name); BabylonTerrainData terrainMeshData = Unity3D2Babylon.Tools.CreateTerrainData(terrain.terrainData, (int)generator.terrainResolution, transform.localPosition, true); Tools.GenerateBabylonMeshTerrainData(terrainMeshData, babylonMesh, false, babylonScene, transform); if (generator.surfaceMaterial != null) { babylonMesh.materialId = DumpMaterial(generator.surfaceMaterial, terrain.lightmapIndex, terrain.lightmapScaleOffset, generator.coordinatesIndex).id; } // Generate collision heightmap var terrainCollider = gameObject.GetComponent <TerrainCollider>(); if (terrainCollider != null && terrainCollider.enabled) { ExporterWindow.ReportProgress(progress, "Generating terrain heightmap: " + gameObject.name); float minheight = float.MaxValue; float maxheight = float.MinValue; int hwidth = terrain.terrainData.heightmapWidth; int hheight = terrain.terrainData.heightmapHeight; float[,] rawHeights = terrain.terrainData.GetHeights(0, 0, hwidth, hheight); Texture2D heightMap = new Texture2D(hwidth, hheight, TextureFormat.ARGB32, false); for (int y = 0; y < hheight; y++) { for (int x = 0; x < hwidth; x++) { float inverted = rawHeights[y, x]; minheight = Mathf.Min(minheight, inverted); maxheight = Mathf.Max(maxheight, inverted); } } List <Color32> pixels = new List <Color32>(); for (int y = 0; y < hheight; y++) { for (int x = 0; x < hwidth; x++) { float inverted = rawHeights[y, x]; if (generator.heightmapStrength > 0) { float threadhold = minheight + generator.floorThreashold; if (inverted > threadhold) { inverted += (generator.heightmapStrength / 10.0f); } } byte[] packed = BitConverter.GetBytes(inverted); if (packed != null && packed.Length >= 4) { pixels.Add(new Color32(packed[0], packed[1], packed[2], packed[3])); } } } heightMap.SetPixels32(pixels.ToArray()); heightMap.Apply(); byte[] heightmapBytes = heightMap.EncodeToPNG(); metaData.properties.Add("heightmapBase64", ("data:image/png;base64," + Convert.ToBase64String(heightmapBytes))); } } else { UnityEngine.Debug.LogWarning("No valid terrain or generator found for: " + gameObject.name); } babylonMesh.metadata = metaData; babylonScene.MeshesList.Add(babylonMesh); SceneBuilder.Metadata.properties["hasTerrainMeshes"] = true; // Animations ExportAnimations(transform, babylonMesh); if (IsRotationQuaternionAnimated(babylonMesh)) { babylonMesh.rotationQuaternion = transform.localRotation.ToFloat(); } // Lens Flares ParseLensFlares(gameObject, babylonMesh.id, ref lensFlares); // Particles Systems ParseParticleSystems(gameObject, babylonMesh.id, ref particleSystems); return(babylonMesh); }