private void exportTransform(BabylonAbstractMesh babylonAbstractMesh, IIGameNode maxGameNode) { // Position / rotation / scaling var localTM = maxGameNode.GetObjectTM(0); if (maxGameNode.NodeParent != null) { var parentWorld = maxGameNode.NodeParent.GetObjectTM(0); localTM.MultiplyBy(parentWorld.Inverse); } var meshTrans = localTM.Translation; var meshRotation = localTM.Rotation; var meshScale = localTM.Scaling; babylonAbstractMesh.position = new[] { meshTrans.X, meshTrans.Y, meshTrans.Z }; var rotationQuaternion = new BabylonQuaternion { X = meshRotation.X, Y = meshRotation.Y, Z = meshRotation.Z, W = -meshRotation.W }; if (ExportQuaternionsInsteadOfEulers) { babylonAbstractMesh.rotationQuaternion = rotationQuaternion.ToArray(); } else { babylonAbstractMesh.rotation = rotationQuaternion.toEulerAngles().ToArray(); } babylonAbstractMesh.scaling = new[] { meshScale.X, meshScale.Y, meshScale.Z }; }
private void exportTransform(BabylonAbstractMesh babylonAbstractMesh, IIGameNode maxGameNode) { // Position / rotation / scaling var localTM = maxGameNode.GetLocalTM(0); // use babylon decomposition, as 3ds max built-in values are no correct var tm_babylon = new BabylonMatrix(); tm_babylon.m = localTM.ToArray(); var s_babylon = new BabylonVector3(); var q_babylon = new BabylonQuaternion(); var t_babylon = new BabylonVector3(); tm_babylon.decompose(s_babylon, q_babylon, t_babylon); if (ExportQuaternionsInsteadOfEulers) { babylonAbstractMesh.rotationQuaternion = q_babylon.ToArray(); } else { babylonAbstractMesh.rotation = q_babylon.toEulerAngles().ToArray(); } // normalize quaternion var q = q_babylon; float q_length = (float)Math.Sqrt(q.X * q.X + q.Y * q.Y + q.Z * q.Z + q.W * q.W); babylonAbstractMesh.rotationQuaternion = new[] { q_babylon.X / q_length, q_babylon.Y / q_length, q_babylon.Z / q_length, q_babylon.W / q_length }; babylonAbstractMesh.scaling = new[] { s_babylon.X, s_babylon.Y, s_babylon.Z }; babylonAbstractMesh.position = new[] { t_babylon.X, t_babylon.Y, t_babylon.Z }; }
private void exportTransform(BabylonAbstractMesh babylonAbstractMesh, IIGameNode maxGameNode) { // Position / rotation / scaling var localTM = GetLocalTM(maxGameNode, 0); var meshTrans = localTM.Translation; var meshRotation = localTM.Rotation; var meshScale = localTM.Scaling; babylonAbstractMesh.position = new[] { meshTrans.X, meshTrans.Y, meshTrans.Z }; var rotationQuaternion = new BabylonQuaternion { X = meshRotation.X, Y = meshRotation.Y, Z = meshRotation.Z, W = -meshRotation.W }; if (ExportQuaternionsInsteadOfEulers) { babylonAbstractMesh.rotationQuaternion = rotationQuaternion.ToArray(); } else { babylonAbstractMesh.rotation = rotationQuaternion.toEulerAngles().ToArray(); } babylonAbstractMesh.scaling = new[] { meshScale.X, meshScale.Y, meshScale.Z }; }
private BabylonNode ExportInstanceMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene, BabylonMesh babylonMasterMesh) { meshNode.MaxNode.MarkAsInstance(); var babylonInstanceMesh = new BabylonAbstractMesh { id = meshNode.MaxNode.GetGuid().ToString(), name = meshNode.Name, pickable = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable"), checkCollisions = meshNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions"), showBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showboundingbox"), showSubMeshesBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox"), alphaIndex = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_alphaindex", 1000) }; // Physics var impostorText = meshNode.MaxNode.GetStringProperty("babylonjs_impostor", "None"); if (impostorText != "None") { switch (impostorText) { case "Sphere": babylonInstanceMesh.physicsImpostor = 1; break; case "Box": babylonInstanceMesh.physicsImpostor = 2; break; case "Plane": babylonInstanceMesh.physicsImpostor = 3; break; default: babylonInstanceMesh.physicsImpostor = 0; break; } babylonInstanceMesh.physicsMass = meshNode.MaxNode.GetFloatProperty("babylonjs_mass"); babylonInstanceMesh.physicsFriction = meshNode.MaxNode.GetFloatProperty("babylonjs_friction", 0.2f); babylonInstanceMesh.physicsRestitution = meshNode.MaxNode.GetFloatProperty("babylonjs_restitution", 0.2f); } // 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); }
private GLTFNode ExportAbstractMesh(BabylonAbstractMesh babylonAbstractMesh, GLTF gltf, GLTFNode gltfParentNode) { RaiseMessage("GLTFExporter.AbstractMesh | Export abstract mesh named: " + babylonAbstractMesh.name, 1); // Node var gltfNode = new GLTFNode(); gltfNode.name = babylonAbstractMesh.name; gltfNode.index = gltf.NodesList.Count; gltf.NodesList.Add(gltfNode); // Hierarchy if (gltfParentNode != null) { RaiseMessage("GLTFExporter.AbstractMesh | Add " + babylonAbstractMesh.name + " as child to " + gltfParentNode.name, 2); gltfParentNode.ChildrenList.Add(gltfNode.index); } else { // It's a root node // Only root nodes are listed in a gltf scene RaiseMessage("GLTFExporter.AbstractMesh | Add " + babylonAbstractMesh.name + " as root node to scene", 2); gltf.scenes[0].NodesList.Add(gltfNode.index); } // Transform gltfNode.translation = babylonAbstractMesh.position; // TODO - Choose between this method and the extra root node // Switch from left to right handed coordinate system //gltfNode.translation[0] *= -1; if (babylonAbstractMesh.rotationQuaternion != null) { gltfNode.rotation = babylonAbstractMesh.rotationQuaternion; } else { // Convert rotation vector to quaternion BabylonVector3 rotationVector3 = new BabylonVector3 { X = babylonAbstractMesh.rotation[0], Y = babylonAbstractMesh.rotation[1], Z = babylonAbstractMesh.rotation[2] }; gltfNode.rotation = rotationVector3.toQuaternion().ToArray(); } gltfNode.scale = babylonAbstractMesh.scaling; // Mesh var gltfMesh = gltf.MeshesList.Find(_gltfMesh => _gltfMesh.idGroupInstance == babylonAbstractMesh.idGroupInstance); if (gltfMesh != null) { gltfNode.mesh = gltfMesh.index; } return(gltfNode); }
private void RotationToEulerAngles(BabylonAbstractMesh babylonMesh, IQuat rotation) { float rotx = 0, roty = 0, rotz = 0; unsafe { rotation.GetEuler(new IntPtr(&rotx), new IntPtr(&roty), new IntPtr(&rotz)); } babylonMesh.rotation = new[] { rotx, roty, rotz }; }
private void ExportNode(BabylonAbstractMesh babylonAbstractMesh, MFnTransform mFnTransform, BabylonScene babylonScene) { RaiseVerbose("BabylonExporter.Mesh | ExportNode", 2); // Position / rotation / scaling ExportTransform(babylonAbstractMesh, mFnTransform); // Hierarchy ExportHierarchy(babylonAbstractMesh, mFnTransform); }
private void exportNode(BabylonAbstractMesh babylonAbstractMesh, IIGameNode maxGameNode, IIGameScene maxGameScene, BabylonScene babylonScene) { // Position / rotation / scaling exportTransform(babylonAbstractMesh, maxGameNode); // Hierarchy if (maxGameNode.NodeParent != null) { babylonAbstractMesh.parentId = maxGameNode.NodeParent.MaxNode.GetGuid().ToString(); } }
private void ExportNode(BabylonAbstractMesh babylonAbstractMesh, MFnDagNode mFnDagNode, BabylonScene babylonScene) { if (mFnDagNode.parentCount != 0) { MObject parentMObject = mFnDagNode.parent(0); MFnDagNode mFnDagNodeParent = new MFnDagNode(parentMObject); // Position / rotation / scaling ExportTransform(babylonAbstractMesh, mFnDagNodeParent); // TODO - Hierarchy //babylonAbstractMesh.parentId = mFnDagNodeParent.uuid().asString(); } }
private void ExportTransform(BabylonAbstractMesh babylonAbstractMesh, MFnDagNode mFnDagNode) { // Position / rotation / scaling var transformationMatrix = new MTransformationMatrix(mFnDagNode.transformationMatrix); babylonAbstractMesh.position = transformationMatrix.getTranslation(); if (_exportQuaternionsInsteadOfEulers) { babylonAbstractMesh.rotationQuaternion = transformationMatrix.getRotationQuaternion(); } else { babylonAbstractMesh.rotation = transformationMatrix.getRotation(); } babylonAbstractMesh.scaling = transformationMatrix.getScale(); }
public static IMatrix3 ExtractCoordinates(IINode meshNode, BabylonAbstractMesh babylonMesh, bool exportQuaternionsInsteadOfEulers) { var wm = meshNode.GetWorldMatrix(0, meshNode.HasParent()); babylonMesh.position = wm.Trans.ToArraySwitched(); var parts = Loader.Global.AffineParts.Create(); Loader.Global.DecompAffine(wm, parts); if (exportQuaternionsInsteadOfEulers) { babylonMesh.rotationQuaternion = parts.Q.ToArray(); } else { var rotate = new float[3]; IntPtr xPtr = Marshal.AllocHGlobal(sizeof(float)); IntPtr yPtr = Marshal.AllocHGlobal(sizeof(float)); IntPtr zPtr = Marshal.AllocHGlobal(sizeof(float)); parts.Q.GetEuler(xPtr, yPtr, zPtr); Marshal.Copy(xPtr, rotate, 0, 1); Marshal.Copy(yPtr, rotate, 1, 1); Marshal.Copy(zPtr, rotate, 2, 1); Marshal.FreeHGlobal(xPtr); Marshal.FreeHGlobal(yPtr); Marshal.FreeHGlobal(zPtr); var temp = rotate[1]; rotate[0] = -rotate[0] * parts.F; rotate[1] = -rotate[2] * parts.F; rotate[2] = -temp * parts.F; babylonMesh.rotation = rotate; } babylonMesh.scaling = parts.K.ToArraySwitched(); return(wm); }
private List <GlobalVertex> ExtractVertices(BabylonAbstractMesh babylonAbstractMesh, IIGameNode maxMorphTarget, bool optimizeVertices, List <int> faceIndexes) { var gameMesh = maxMorphTarget.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 mtl = maxMorphTarget.NodeMaterial; var multiMatsCount = 1; if (mtl != null) { multiMatsCount = Math.Max(mtl.SubMaterialCount, 1); } var invertedWorldMatrix = GetInvertWorldTM(maxMorphTarget, 0); var vertices = new List <GlobalVertex>(); ExtractGeometry(babylonAbstractMesh, vertices, new List <int>(), new List <BabylonSubMesh>(), null, null, gameMesh, invertedWorldMatrix, false, false, false, false, optimizeVertices, multiMatsCount, maxMorphTarget, ref faceIndexes); return(vertices); }
private void ExportTransform(BabylonAbstractMesh babylonAbstractMesh, MFnTransform mFnTransform) { // Position / rotation / scaling RaiseVerbose("BabylonExporter.Mesh | ExportTransform", 2); float[] position = null; float[] rotationQuaternion = null; float[] rotation = null; float[] scaling = null; GetTransform(mFnTransform, ref position, ref rotationQuaternion, ref rotation, ref scaling); babylonAbstractMesh.position = position; if (_exportQuaternionsInsteadOfEulers) { babylonAbstractMesh.rotationQuaternion = rotationQuaternion; } else { babylonAbstractMesh.rotation = rotation; } babylonAbstractMesh.scaling = scaling; }
private GLTFNode ExportAbstractMesh(ref GLTFNode gltfNode, BabylonAbstractMesh babylonAbstractMesh, GLTF gltf, GLTFNode gltfParentNode, BabylonScene babylonScene) { logger.RaiseMessage("GLTFExporter.AbstractMesh | Export abstract mesh named: " + babylonAbstractMesh.name, 2); // Mesh var gltfMesh = gltf.MeshesList.Find(_gltfMesh => _gltfMesh.idGroupInstance == babylonAbstractMesh.idGroupInstance); if (gltfMesh != null) { gltfNode.mesh = gltfMesh.index; // Skin if (gltfMesh.idBabylonSkeleton.HasValue) { var babylonSkeleton = babylonScene.skeletons[gltfMesh.idBabylonSkeleton.Value]; // Export a new skeleton if necessary and a new skin var gltfSkin = ExportSkin(babylonSkeleton, gltf, gltfNode, gltfMesh); gltfNode.skin = gltfSkin.index; } } return(gltfNode); }
private BabylonAbstractMesh ConvertUnityMeshToInstance(GameObject gameObject) { BabylonAbstractMesh babylonMesh = new BabylonAbstractMesh(); Transform transform = gameObject.transform; babylonMesh.name = gameObject.name; babylonMesh.position = new float[3]; babylonMesh.position[0] = transform.position.x; babylonMesh.position[1] = transform.position.y; babylonMesh.position[2] = transform.position.z; babylonMesh.rotation = new float[3]; babylonMesh.rotation[0] = transform.rotation.eulerAngles.x * (float)Math.PI / 180; babylonMesh.rotation[1] = transform.rotation.eulerAngles.y * (float)Math.PI / 180; babylonMesh.rotation[2] = transform.rotation.eulerAngles.z * (float)Math.PI / 180; babylonMesh.scaling = new float[3]; babylonMesh.scaling[0] = transform.localScale.x; babylonMesh.scaling[1] = transform.localScale.y; babylonMesh.scaling[2] = transform.localScale.z; return(babylonMesh); }
private void ConvertTransform(BabylonMesh babylonMesh, Transform transform, GameObject gameObject, BabylonAbstractMesh[] instances = null) { Action SetTransformFromGameobject = () => { 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(); }; Action SetTransformFromFirstInstance = () => { BabylonAbstractMesh first = instances[0]; babylonMesh.position = new float[3]; babylonMesh.position[0] = first.position[0]; babylonMesh.position[1] = first.position[1]; babylonMesh.position[2] = first.position[2]; babylonMesh.rotation = new float[3]; babylonMesh.rotation[0] = first.rotation[0]; babylonMesh.rotation[1] = first.rotation[1]; babylonMesh.rotation[2] = first.rotation[2]; babylonMesh.scaling = new float[3]; babylonMesh.scaling[0] = first.scaling[0]; babylonMesh.scaling[1] = first.scaling[1]; babylonMesh.scaling[2] = first.scaling[2]; }; //Check if this is a prefab if (instances != null) { /* * Unity3D prefabs don't have transforms (position, rotation, scale) because they are just a template and are not drawn on screen * but Babylon.js meshes must have a transform because they are drawn on the screen * so what we do is take the first instance * copy its transform (position, rotation, scale) into the prefab mesh * then remove that first instance */ babylonMesh.instances = new BabylonAbstractMesh[instances.Length - 1]; //Effectively remove first instance from list of all instances for (int i = 0; i < instances.Length - 1; i++) { babylonMesh.instances[i] = instances[i + 1]; } //If this is the root object then copy values directly from first instance if (GetParentID(transform) == null) { SetTransformFromFirstInstance(); } else { GameObject parent = gameObject.transform.parent.gameObject; if ((parent.GetComponent <Light>() == null) && (parent.GetComponent <Camera>() == null)) { SetTransformFromGameobject(); } else { SetTransformFromFirstInstance(); } } } else { SetTransformFromGameobject(); } }
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); }
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); }
/// <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 = new MFnTransform(mDagPath); // Mesh direct child of the transform // TODO get the original one rather than the modified? 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(), visibility = Loader.GetVisibility(mFnTransform.fullPathName) }; // Instance // 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 as a master mesh BabylonMesh babylonMasterMesh = GetMasterMesh(mFnMesh, babylonMesh); if (babylonMasterMesh != null) { RaiseMessage($"The master mesh {babylonMasterMesh.name} was already exported. This one will be exported as an instance.", 2); // Export this node as instance var babylonInstanceMesh = new BabylonAbstractMesh { name = mFnTransform.name, id = mFnTransform.uuid().asString() }; //// Add instance to master mesh List <BabylonAbstractMesh> instances = babylonMasterMesh.instances != null?babylonMasterMesh.instances.ToList() : new List <BabylonAbstractMesh>(); instances.Add(babylonInstanceMesh); babylonMasterMesh.instances = instances.ToArray(); // Export transform / hierarchy / animations ExportNode(babylonInstanceMesh, mFnTransform, babylonScene); // Animations ExportNodeAnimation(babylonInstanceMesh, mFnTransform); return(babylonInstanceMesh); } // Position / rotation / scaling / hierarchy ExportNode(babylonMesh, mFnTransform, babylonScene); // Misc. // TODO - Retreive from Maya //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); } // Animations ExportNodeAnimation(babylonMesh, mFnTransform); // 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; } // skin if (_exportSkin) { mFnSkinCluster = getMFnSkinCluster(mFnMesh); } int maxNbBones = 0; if (mFnSkinCluster != null) { RaiseMessage($"mFnSkinCluster.name | {mFnSkinCluster.name}", 2); Print(mFnSkinCluster, 3, $"Print {mFnSkinCluster.name}"); // Get the bones dictionary<name, index> => it represents all the bones in the skeleton indexByNodeName = GetIndexByFullPathNameDictionary(mFnSkinCluster); // Get the joint names that influence this mesh allMayaInfluenceNames = GetBoneFullPathName(mFnSkinCluster, mFnTransform); // Get the max number of joints acting on a vertex int maxNumInfluences = GetMaxInfluence(mFnSkinCluster, mFnTransform, mFnMesh); RaiseMessage($"Max influences : {maxNumInfluences}", 2); if (maxNumInfluences > 8) { RaiseWarning($"Too many bones influences per vertex: {maxNumInfluences}. Babylon.js only support up to 8 bones influences per vertex.", 2); RaiseWarning("The result may not be as expected.", 2); } maxNbBones = Math.Min(maxNumInfluences, 8); if (indexByNodeName != null && allMayaInfluenceNames != null) { babylonMesh.skeletonId = GetSkeletonIndex(mFnSkinCluster); } else { mFnSkinCluster = null; } } // Export tangents if option is checked and mesh have tangents bool isTangentExportSuccess = _exportTangents; // 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"); var optimizeVertices = _optimizeVertices; // global option // Compute normals var subMeshes = new List <BabylonSubMesh>(); ExtractGeometry(mFnMesh, vertices, indices, subMeshes, uvSetNames, ref isUVExportSuccess, ref isTangentExportSuccess, 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(); // export the skin if (mFnSkinCluster != 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(); } } // Tangent if (isTangentExportSuccess) { babylonMesh.tangents = vertices.SelectMany(v => v.Tangent).ToArray(); } // Color string colorSetName; mFnMesh.getCurrentColorSetName(out colorSetName); if (mFnMesh.numColors(colorSetName) > 0) { babylonMesh.colors = vertices.SelectMany(v => v.Color).ToArray(); } // UVs 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 GLTFNode ExportAbstractMesh(BabylonAbstractMesh babylonAbstractMesh, GLTF gltf, GLTFNode gltfParentNode, BabylonScene babylonScene) { RaiseMessage("GLTFExporter.AbstractMesh | Export abstract mesh named: " + babylonAbstractMesh.name, 1); // Node var gltfNode = new GLTFNode(); gltfNode.name = babylonAbstractMesh.name; gltfNode.index = gltf.NodesList.Count; gltf.NodesList.Add(gltfNode); // Hierarchy if (gltfParentNode != null) { RaiseMessage("GLTFExporter.AbstractMesh | Add " + babylonAbstractMesh.name + " as child to " + gltfParentNode.name, 2); gltfParentNode.ChildrenList.Add(gltfNode.index); gltfNode.parent = gltfParentNode; } else { // It's a root node // Only root nodes are listed in a gltf scene RaiseMessage("GLTFExporter.AbstractMesh | Add " + babylonAbstractMesh.name + " as root node to scene", 2); gltf.scenes[0].NodesList.Add(gltfNode.index); } // Transform gltfNode.translation = babylonAbstractMesh.position; if (babylonAbstractMesh.rotationQuaternion != null) { gltfNode.rotation = babylonAbstractMesh.rotationQuaternion; } else { // Convert rotation vector to quaternion BabylonVector3 rotationVector3 = new BabylonVector3 { X = babylonAbstractMesh.rotation[0], Y = babylonAbstractMesh.rotation[1], Z = babylonAbstractMesh.rotation[2] }; gltfNode.rotation = rotationVector3.toQuaternion().ToArray(); } gltfNode.scale = babylonAbstractMesh.scaling; // Switch coordinate system at object level gltfNode.translation[2] *= -1; gltfNode.rotation[0] *= -1; gltfNode.rotation[1] *= -1; // Mesh var gltfMesh = gltf.MeshesList.Find(_gltfMesh => _gltfMesh.idGroupInstance == babylonAbstractMesh.idGroupInstance); if (gltfMesh != null) { gltfNode.mesh = gltfMesh.index; // Skin if (gltfMesh.idBabylonSkeleton.HasValue) { var babylonSkeleton = babylonScene.skeletons[gltfMesh.idBabylonSkeleton.Value]; // Export a new skeleton if necessary and a new skin var gltfSkin = ExportSkin(babylonSkeleton, gltf, gltfNode); gltfNode.skin = gltfSkin.index; } } // Animations ExportNodeAnimation(babylonAbstractMesh, gltf, gltfNode, babylonScene); return(gltfNode); }
private void ExtractFace(IIGameNode meshNode, IIGameSkin skin, IIGameMesh unskinnedMesh, BabylonAbstractMesh babylonAbstractMesh, IMatrix3 invertedWorldMatrix, List <GlobalVertex> vertices, List <int> indices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, ref int indexCount, ref int minVertexIndex, ref int maxVertexIndex, IFaceEx face, List <int> boneIds) { int a, b, c; // parity is TRUE, if determinant negative ( counter-intuitive convention of 3ds max, see docs... :/ ) // fix for cesium: currently, cesium does not expect a reversed winding order for negative scales //if (false) // for threejs and babylonjs (handle negative scales correctly (reversed winding order expected) if (invertedWorldMatrix.Parity) { // flipped case: reverse winding order a = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); b = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); c = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); } else { // normal case a = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 0, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); b = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 2, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); c = CreateGlobalVertex(meshNode, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, face, 1, vertices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, skin, boneIds); } indices.Add(a); indices.Add(b); indices.Add(c); if (a < minVertexIndex) { minVertexIndex = a; } if (b < minVertexIndex) { minVertexIndex = b; } if (c < minVertexIndex) { minVertexIndex = c; } if (a > maxVertexIndex) { maxVertexIndex = a; } if (b > maxVertexIndex) { maxVertexIndex = b; } if (c > maxVertexIndex) { maxVertexIndex = c; } indexCount += 3; CheckCancelled(); }
private void ExtractGeometry(BabylonAbstractMesh babylonAbstractMesh, List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, List <int> boneIds, IIGameSkin skin, IIGameMesh unskinnedMesh, IMatrix3 invertedWorldMatrix, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, bool optimizeVertices, int multiMatsCount, IIGameNode meshNode, ref List <int> faceIndexes) { List <GlobalVertex>[] verticesAlreadyExported = null; if (optimizeVertices) { verticesAlreadyExported = new List <GlobalVertex> [unskinnedMesh.NumberOfVerts]; } var indexStart = 0; // Whether or not to store order in which faces are exported // Storage is used when exporting Morph Targets geometry // To ensure face order is identical, especially with multimaterials involved bool storeFaceIndexes = faceIndexes == null; if (storeFaceIndexes) { faceIndexes = new List <int>(); } int indexInFaceIndexesArray = 0; for (int i = 0; i < multiMatsCount; ++i) { int materialId = meshNode.NodeMaterial?.GetMaterialID(i) ?? 0; var indexCount = 0; var minVertexIndex = int.MaxValue; var maxVertexIndex = int.MinValue; var subMesh = new BabylonSubMesh { indexStart = indexStart, materialIndex = i }; if (multiMatsCount == 1) { for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j) { IFaceEx face = null; if (storeFaceIndexes) { face = unskinnedMesh.GetFace(j); // Store face index (j = face.MeshFaceIndex) faceIndexes.Add(j); } else { face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]); } ExtractFace(meshNode, skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } else { ITab <IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId); for (int j = 0; j < materialFaces.Count; ++j) { IFaceEx face = null; if (storeFaceIndexes) { // Retreive face #if MAX2017 || MAX2018 face = materialFaces[j]; #else face = materialFaces[new IntPtr(j)]; #endif // Store face index faceIndexes.Add(face.MeshFaceIndex); } else { face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]); } ExtractFace(meshNode, skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } if (indexCount != 0) { subMesh.indexCount = indexCount; subMesh.verticesStart = minVertexIndex; subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1; indexStart += indexCount; subMeshes.Add(subMesh); } } }
private void ExtractGeometry(BabylonAbstractMesh babylonAbstractMesh, List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, List <int> boneIds, IIGameSkin skin, IIGameMesh unskinnedMesh, IMatrix3 invertedWorldMatrix, IMatrix3 offsetTM, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, bool optimizeVertices, int multiMatsCount, IIGameNode meshNode, ref List <int> faceIndexes) { Dictionary <GlobalVertex, List <GlobalVertex> > verticesAlreadyExported = null; if (optimizeVertices) { verticesAlreadyExported = new Dictionary <GlobalVertex, List <GlobalVertex> >(); } var indexStart = 0; // Whether or not to store order in which faces are exported // Storage is used when exporting Morph Targets geometry // To ensure face order is identical, especially with multimaterials involved bool storeFaceIndexes = faceIndexes == null; if (storeFaceIndexes) { faceIndexes = new List <int>(); } int indexInFaceIndexesArray = 0; for (int i = 0; i < multiMatsCount; ++i) { int materialId = i; var indexCount = 0; var minVertexIndex = int.MaxValue; var maxVertexIndex = int.MinValue; var subMesh = new BabylonSubMesh { indexStart = indexStart, materialIndex = i }; if (multiMatsCount == 1) { for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j) { IFaceEx face = null; if (storeFaceIndexes) { face = unskinnedMesh.GetFace(j); // Store face index (j = face.MeshFaceIndex) faceIndexes.Add(j); } else { face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]); } ExtractFace(skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, offsetTM, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } else { if (i == 0 || isMaterialDoubleSided == false) { ITab <IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId); for (int j = 0; j < materialFaces.Count; ++j) { IFaceEx face = null; if (storeFaceIndexes) { // Retreive face #if MAX2017 || MAX2018 || MAX2019 || MAX2020 face = materialFaces[j]; #else face = materialFaces[new IntPtr(j)]; #endif // Store face index faceIndexes.Add(face.MeshFaceIndex); } else { face = unskinnedMesh.GetFace(faceIndexes[indexInFaceIndexesArray++]); } ExtractFace(skin, unskinnedMesh, babylonAbstractMesh, invertedWorldMatrix, offsetTM, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } else { // It's a double sided material // The back faces are created at runtime // WARNING - Nested multimaterial and double sided material are not supported minVertexIndex = vertices.Count; maxVertexIndex = vertices.Count * 2 - 1; // Vertices int nbVertices = vertices.Count; for (int index = 0; index < nbVertices; index++) { GlobalVertex vertexOrg = vertices[index]; // Duplicate vertex GlobalVertex vertexNew = new GlobalVertex(vertexOrg); // Inverse back vertices normal vertexNew.Normal = vertexNew.Normal.MultiplyBy(-1); vertexNew.Tangent = vertexNew.Tangent.MultiplyBy(-1); vertices.Add(vertexNew); } // Faces int nbIndices = indices.Count; for (int index = 0; index < nbIndices; index += 3) { // Duplicate and flip faces indices.Add(indices[index + 2] + nbIndices); indices.Add(indices[index + 1] + nbIndices); indices.Add(indices[index] + nbIndices); indexCount += 3; } } } if (indexCount != 0) { subMesh.indexCount = indexCount; subMesh.verticesStart = minVertexIndex; subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1; indexStart += indexCount; subMeshes.Add(subMesh); } } }
public void ConvertFromUnity() { ExporterWindow.ReportProgress(0, "Starting Babylon.js exportation process..."); gameObjects = Object.FindObjectsOfType(typeof(GameObject)) as GameObject[]; if (gameObjects.Length == 0) { ExporterWindow.ShowMessage("No gameobject! - Please add at least a gameobject to export"); return; } var itemsCount = gameObjects.Length; var index = 0; //Dictionary to store prefabs and their instances Dictionary <GameObject, List <BabylonAbstractMesh> > dicPrefabs = new Dictionary <GameObject, List <BabylonAbstractMesh> >(); foreach (var gameObject in gameObjects) { var progress = ((float)index / itemsCount); index++; /* * The order of processing is important here. * We will only check if this is a mesh prefab if it is not a light or camera */ // Light var light = gameObject.GetComponent <Light>(); if (light != null) { ConvertUnityLightToBabylon(light, progress); continue; } // Camera var camera = gameObject.GetComponent <Camera>(); if (camera != null) { ConvertUnityCameraToBabylon(camera, progress); continue; } // Check if this is a prefab instance GameObject gobjPrefab = (GameObject)PrefabUtility.GetPrefabParent(gameObject); if (gobjPrefab != null) { //Add prefab to dictionary if it doesn't already exist if (!dicPrefabs.ContainsKey(gobjPrefab)) { dicPrefabs[gobjPrefab] = new List <BabylonAbstractMesh>(); } List <BabylonAbstractMesh> lstInstances = dicPrefabs[gobjPrefab]; BabylonAbstractMesh instance = ConvertUnityMeshToInstance(gameObject); lstInstances.Add(instance); continue; } // Static meshes var meshFilter = gameObject.GetComponent <MeshFilter>(); if (meshFilter != null) { ConvertUnityMeshToBabylon(meshFilter.sharedMesh, meshFilter.transform, gameObject, progress); continue; } // Skinned meshes var skinnedMesh = gameObject.GetComponent <SkinnedMeshRenderer>(); if (skinnedMesh != null) { ConvertUnityMeshToBabylon(skinnedMesh.sharedMesh, skinnedMesh.transform, gameObject, progress); continue; } // Empty ConvertUnityEmptyObjectToBabylon(gameObject); } index = 0; itemsCount = dicPrefabs.Count; //Convert prefabs foreach (KeyValuePair <GameObject, List <BabylonAbstractMesh> > pair in dicPrefabs) { var progress = ((float)index / itemsCount); index++; List <BabylonAbstractMesh> lstValue = pair.Value; GameObject prefab = pair.Key; BabylonAbstractMesh[] lstInstance = lstValue.ToArray(); // Static meshes var meshFilter = prefab.GetComponent <MeshFilter>(); if (meshFilter != null) { ConvertUnityMeshToBabylon(meshFilter.sharedMesh, meshFilter.transform, prefab, progress, lstInstance); continue; } // Skinned meshes var skinnedMesh = prefab.GetComponent <SkinnedMeshRenderer>(); if (skinnedMesh != null) { ConvertUnityMeshToBabylon(skinnedMesh.sharedMesh, skinnedMesh.transform, prefab, progress, lstInstance); continue; } // Empty ConvertUnityEmptyObjectToBabylon(prefab, lstInstance); } // Materials foreach (var mat in materialsDictionary) { babylonScene.MaterialsList.Add(mat.Value); } foreach (var multiMat in multiMatDictionary) { babylonScene.MultiMaterialsList.Add(multiMat.Value); } // Collisions if (exportationOptions.ExportCollisions) { babylonScene.gravity = exportationOptions.Gravity.ToFloat(); } }
int CreateGlobalVertex(IIGameMesh mesh, BabylonAbstractMesh babylonAbstractMesh, IMatrix3 invertedWorldMatrix, IMatrix3 offsetTM, IFaceEx face, int facePart, List <GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List <int> boneIds) { var vertexIndex = (int)face.Vert[facePart]; // Position can by retrieved in world space or object space // Unfortunately, this value can't be retrieved in local space var vertex = new GlobalVertex { BaseIndex = vertexIndex, Position = mesh.GetVertex(vertexIndex, false), // world space Normal = mesh.GetNormal((int)face.Norm[facePart], true) // object space (world space was somehow bugged for normal) }; //System.Diagnostics.Debug.WriteLine("vertex normal: " + string.Join(", ", vertex.Normal.ToArray().Select(v => Math.Round(v, 3)))); // position (from world to local/node space) vertex.Position = invertedWorldMatrix.PointTransform(vertex.Position); // normal (from object to local/node space) vertex.Normal = offsetTM.VectorTransform(vertex.Normal).Normalize; // tangent if (exportParameters.exportTangents) { int indexTangentBinormal = mesh.GetFaceVertexTangentBinormal(face.MeshFaceIndex, facePart, 1); IPoint3 normal = vertex.Normal.Normalize; IPoint3 tangent = mesh.GetTangent(indexTangentBinormal, 1).Normalize; IPoint3 bitangent = mesh.GetBinormal(indexTangentBinormal, 1).Normalize; int w = GetW(normal, tangent, bitangent); vertex.Tangent = new float[] { tangent.X, tangent.Y, tangent.Z, w }; } if (hasUV) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(1, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(1, indices[facePart]); vertex.UV = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasUV2) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(2, indices[facePart]); vertex.UV2 = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasColor) { var vertexColorIndex = (int)face.Color[facePart]; var vertexColor = mesh.GetColorVertex(vertexColorIndex); float alpha = 1; if (hasAlpha) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(-2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var color = mesh.GetMapVertex(-2, indices[facePart]); alpha = color.X; } vertex.Color = new[] { vertexColor.X, vertexColor.Y, vertexColor.Z, alpha }; } if (skin != null) { float[] weight = new float[4] { 0, 0, 0, 0 }; int[] bone = new int[4] { 0, 0, 0, 0 }; var nbBones = skin.GetNumberOfBones(vertexIndex); int currentVtxBone = 0; int currentSkinBone = 0; // process skin bones until we have 4 bones for this vertex or we run out of skin bones for (currentSkinBone = 0; currentSkinBone < nbBones && currentVtxBone < 4; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } bone[currentVtxBone] = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, currentSkinBone).NodeID); weight[currentVtxBone] = skin.GetWeight(vertexIndex, currentSkinBone); ++currentVtxBone; } // if we didnt have any bones with a weight > 0 if (currentVtxBone == 0) { weight[0] = 1.0f; bone[0] = 0; } vertex.Weights = Loader.Global.Point4.Create(weight); vertex.BonesIndices = (bone[3] << 24) | (bone[2] << 16) | (bone[1] << 8) | bone[0]; if (currentVtxBone >= 4 && currentSkinBone < nbBones) { weight = new float[4] { 0, 0, 0, 0 }; bone = new int[4] { 0, 0, 0, 0 }; // process remaining skin bones until we have a total of 8 bones for this vertex or we run out of skin bones for (; currentSkinBone < nbBones && currentVtxBone < 8; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } if (isGltfExported) { RaiseError("Too many bone influences per vertex for vertexIndex: " + vertexIndex + ". glTF only supports up to 4 bone influences per vertex.", 2); break; } bone[currentVtxBone - 4] = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, currentSkinBone).NodeID); weight[currentVtxBone - 4] = skin.GetWeight(vertexIndex, currentSkinBone); ++currentVtxBone; } // if we have any extra bone weights if (currentVtxBone > 4) { vertex.WeightsExtra = Loader.Global.Point4.Create(weight); vertex.BonesIndicesExtra = (bone[3] << 24) | (bone[2] << 16) | (bone[1] << 8) | bone[0]; if (currentSkinBone < nbBones) { // if we have more skin bones left, this means we have used up all our bones for this vertex // check if any of the remaining bones has a weight > 0 for (; currentSkinBone < nbBones; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } RaiseError("Too many bone influences per vertex for vertexIndex: " + vertexIndex + ". Babylon.js only supports up to 8 bone influences per vertex.", 2); break; } } } } } if (verticesAlreadyExported != null) { if (verticesAlreadyExported[vertexIndex] != null) { var index = verticesAlreadyExported[vertexIndex].IndexOf(vertex); if (index > -1) { return(verticesAlreadyExported[vertexIndex][index].CurrentIndex); } } else { verticesAlreadyExported[vertexIndex] = new List <GlobalVertex>(); } vertex.CurrentIndex = vertices.Count; verticesAlreadyExported[vertexIndex].Add(vertex); } vertices.Add(vertex); return(vertices.Count - 1); }
private void ExportMesh(IINode meshNode, BabylonScene babylonScene) { if (meshNode.IsInstance()) { return; } if (meshNode.GetBoolProperty("babylonjs_noexport")) { return; } if (!ExportHiddenObjects && meshNode.IsHidden(NodeHideFlags.None, false)) { return; } var babylonMesh = new BabylonMesh(); int vx1, vx2, vx3; babylonMesh.name = meshNode.Name; babylonMesh.id = meshNode.GetGuid().ToString(); if (meshNode.HasParent()) { babylonMesh.parentId = meshNode.ParentNode.GetGuid().ToString(); } // Misc. babylonMesh.isVisible = meshNode.Renderable == 1; babylonMesh.pickable = meshNode.GetBoolProperty("babylonjs_checkpickable"); babylonMesh.receiveShadows = meshNode.RcvShadows == 1; babylonMesh.showBoundingBox = meshNode.GetBoolProperty("babylonjs_showboundingbox"); babylonMesh.showSubMeshesBoundingBox = meshNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox"); // Collisions babylonMesh.checkCollisions = meshNode.GetBoolProperty("babylonjs_checkcollisions"); // Skin var skin = GetSkinModifier(meshNode); if (skin != null) { babylonMesh.skeletonId = skins.IndexOf(skin); bonesCount = skin.NumBones; } // Position / rotation / scaling var wm = Tools.ExtractCoordinates(meshNode, babylonMesh, exportQuaternionsInsteadOfEulers); if (wm.Parity) { vx1 = 2; vx2 = 1; vx3 = 0; } else { vx1 = 0; vx2 = 1; vx3 = 2; } // Pivot var pivotMatrix = Tools.Identity; pivotMatrix.PreTranslate(meshNode.ObjOffsetPos); Loader.Global.PreRotateMatrix(pivotMatrix, meshNode.ObjOffsetRot); Loader.Global.ApplyScaling(pivotMatrix, meshNode.ObjOffsetScale); babylonMesh.pivotMatrix = pivotMatrix.ToArray(); // Mesh var objectState = meshNode.EvalWorldState(0, false); var triObject = objectState.Obj.GetMesh(); var mesh = triObject != null ? triObject.Mesh : null; RaiseMessage(meshNode.Name, 1); if (mesh != null) { mesh.BuildNormals(); if (mesh.NumFaces < 1) { RaiseError(string.Format("Mesh {0} has no face", babylonMesh.name), 2); } if (mesh.NumVerts < 3) { RaiseError(string.Format("Mesh {0} has not enough vertices", babylonMesh.name), 2); } if (mesh.NumVerts >= 65536) { RaiseError(string.Format("Mesh {0} has too many vertices (more than 65535)", babylonMesh.name), 2); } // Material var mtl = meshNode.Mtl; var multiMatsCount = 1; if (mtl != null) { babylonMesh.materialId = mtl.GetGuid().ToString(); if (!referencedMaterials.Contains(mtl)) { referencedMaterials.Add(mtl); } multiMatsCount = Math.Max(mtl.NumSubMtls, 1); } babylonMesh.visibility = meshNode.GetVisibility(0, Tools.Forever); var vertices = new List <GlobalVertex>(); var indices = new List <int>(); var matIDs = new List <int>(); var hasUV = mesh.NumTVerts > 0; var hasUV2 = mesh.GetNumMapVerts(2) > 0; var optimizeVertices = meshNode.GetBoolProperty("babylonjs_optimizevertices"); // Skin IISkinContextData skinContext = null; if (skin != null) { skinContext = skin.GetContextInterface(meshNode); } // Compute normals VNormal[] vnorms = Tools.ComputeNormals(mesh, optimizeVertices); List <GlobalVertex>[] verticesAlreadyExported = null; if (optimizeVertices) { verticesAlreadyExported = new List <GlobalVertex> [mesh.NumVerts]; } for (var face = 0; face < mesh.NumFaces; face++) { indices.Add(CreateGlobalVertex(mesh, face, vx1, vertices, hasUV, hasUV2, vnorms, verticesAlreadyExported, skinContext)); indices.Add(CreateGlobalVertex(mesh, face, vx2, vertices, hasUV, hasUV2, vnorms, verticesAlreadyExported, skinContext)); indices.Add(CreateGlobalVertex(mesh, face, vx3, vertices, hasUV, hasUV2, vnorms, verticesAlreadyExported, skinContext)); matIDs.Add(mesh.Faces[face].MatID % multiMatsCount); CheckCancelled(); } if (vertices.Count >= 65536) { RaiseError(string.Format("Mesh {0} has too many vertices: {1} (limit is 65535)", babylonMesh.name, vertices.Count), 2); if (!optimizeVertices) { RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2); } } RaiseMessage(string.Format("{0} vertices, {1} faces", vertices.Count, indices.Count / 3), 2); // Buffers babylonMesh.positions = vertices.SelectMany(v => v.Position.ToArraySwitched()).ToArray(); babylonMesh.normals = vertices.SelectMany(v => v.Normal.ToArraySwitched()).ToArray(); if (hasUV) { babylonMesh.uvs = vertices.SelectMany(v => v.UV.ToArray()).ToArray(); } if (hasUV2) { babylonMesh.uvs2 = vertices.SelectMany(v => v.UV2.ToArray()).ToArray(); } if (skin != null) { babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray(); babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray(); } // Submeshes var sortedIndices = new List <int>(); var subMeshes = new List <BabylonSubMesh>(); var indexStart = 0; for (var index = 0; index < multiMatsCount; index++) { var subMesh = new BabylonSubMesh(); var indexCount = 0; var minVertexIndex = int.MaxValue; var maxVertexIndex = int.MinValue; subMesh.indexStart = indexStart; subMesh.materialIndex = index; for (var face = 0; face < matIDs.Count; face++) { if (matIDs[face] == index) { var a = indices[3 * face]; var b = indices[3 * face + 1]; var c = indices[3 * face + 2]; sortedIndices.Add(a); sortedIndices.Add(b); sortedIndices.Add(c); indexCount += 3; if (a < minVertexIndex) { minVertexIndex = a; } if (b < minVertexIndex) { minVertexIndex = b; } if (c < minVertexIndex) { minVertexIndex = c; } if (a > maxVertexIndex) { maxVertexIndex = a; } if (b > maxVertexIndex) { maxVertexIndex = b; } if (c > maxVertexIndex) { maxVertexIndex = c; } } } if (indexCount != 0) { subMesh.indexCount = indexCount; subMesh.verticesStart = minVertexIndex; subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1; indexStart += indexCount; subMeshes.Add(subMesh); } CheckCancelled(); } babylonMesh.subMeshes = subMeshes.ToArray(); // Buffers - Indices babylonMesh.indices = sortedIndices.ToArray(); triObject.Dispose(); } // Instances var tabs = Loader.Global.NodeTab.Create(); Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode, tabs); var instances = new List <BabylonAbstractMesh>(); for (var index = 0; index < tabs.Count; index++) { var indexer = new IntPtr(index); var tab = tabs[indexer]; Marshal.FreeHGlobal(indexer); if (meshNode.GetGuid() == tab.GetGuid()) { continue; } tab.MarkAsInstance(); var instance = new BabylonAbstractMesh { name = tab.Name }; Tools.ExtractCoordinates(tab, instance, exportQuaternionsInsteadOfEulers); var instanceAnimations = new List <BabylonAnimation>(); GenerateCoordinatesAnimations(tab, instanceAnimations); instance.animations = instanceAnimations.ToArray(); instances.Add(instance); } babylonMesh.instances = instances.ToArray(); // Animations var animations = new List <BabylonAnimation>(); GenerateCoordinatesAnimations(meshNode, animations); if (!ExportFloatController(meshNode.VisController, "visibility", animations)) { ExportFloatAnimation("visibility", animations, key => new[] { meshNode.GetVisibility(key, Tools.Forever) }); } babylonMesh.animations = animations.ToArray(); if (meshNode.GetBoolProperty("babylonjs_autoanimate", 1)) { babylonMesh.autoAnimate = true; babylonMesh.autoAnimateFrom = (int)meshNode.GetFloatProperty("babylonjs_autoanimate_from"); babylonMesh.autoAnimateTo = (int)meshNode.GetFloatProperty("babylonjs_autoanimate_to", 100); babylonMesh.autoAnimateLoop = meshNode.GetBoolProperty("babylonjs_autoanimateloop", 1); } babylonScene.MeshesList.Add(babylonMesh); }
int CreateGlobalVertex(IIGameNode meshNode, IIGameMesh mesh, BabylonAbstractMesh babylonAbstractMesh, IMatrix3 invertedWorldMatrix, IFaceEx face, int facePart, List <GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List <int> boneIds) { var vertexIndex = (int)face.Vert[facePart]; // Position can by retreived in world space or object space // Unfortunately, this value can't be retreived in local space var vertex = new GlobalVertex { BaseIndex = vertexIndex, Position = mesh.GetVertex(vertexIndex, false), // world space Normal = mesh.GetNormal((int)face.Norm[facePart], false) // world space }; if (exportParameters.exportTangents) { int indexTangentBinormal = mesh.GetFaceVertexTangentBinormal(face.MeshFaceIndex, facePart, 1); IPoint3 normal = vertex.Normal.Normalize; IPoint3 tangent = mesh.GetTangent(indexTangentBinormal, 1).Normalize; IPoint3 bitangent = mesh.GetBinormal(indexTangentBinormal, 1).Normalize; int w = GetW(normal, tangent, bitangent); vertex.Tangent = new float[] { tangent.X, tangent.Y, tangent.Z, w }; } // Convert position and normal to local space vertex.Position = invertedWorldMatrix.PointTransform(vertex.Position); vertex.Normal = invertedWorldMatrix.VectorTransform(vertex.Normal); // 1. scale normals with node scales var nodeScaling = BabylonVector3.FromArray(babylonAbstractMesh.scaling); vertex.Normal = vertex.Normal.Multiply(Loader.Global.Point3.Create(Math.Abs(nodeScaling.X), Math.Abs(nodeScaling.Y), Math.Abs(nodeScaling.Z))); // 2. scale normals with objectOffsetScales (unrotate by objectOffsetRot, then scale, then rotate again) // note: LH coordinate system => flip y and z var objOffsetScale = Loader.Global.Point3.Create(meshNode.MaxNode.ObjOffsetScale.S); var scaleX = Math.Abs(objOffsetScale.X); var scaleY = Math.Abs(objOffsetScale.Y); var scaleZ = Math.Abs(objOffsetScale.Z); var objOffsetScaleFlipYZInv = Loader.Global.Point3.Create(1 / scaleX, 1 / scaleZ, 1 / scaleY); var objOffsetQuat = meshNode.MaxNode.ObjOffsetRot; var qFlippedYZ = objOffsetQuat; var tmpSwap = objOffsetQuat.Y; qFlippedYZ.Y = objOffsetQuat.Z; qFlippedYZ.Z = tmpSwap; var nUnrotated = RotateVectorByQuaternion(vertex.Normal, qFlippedYZ); var nUnrotatedScaled = nUnrotated.Multiply(objOffsetScaleFlipYZInv); nUnrotatedScaled = nUnrotatedScaled.Normalize; var nRerotatedScaled = RotateVectorByQuaternion(nUnrotatedScaled, qFlippedYZ.Conjugate); vertex.Normal = nRerotatedScaled; if (hasUV) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(1, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(1, indices[facePart]); vertex.UV = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasUV2) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(2, indices[facePart]); vertex.UV2 = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasColor) { var vertexColorIndex = (int)face.Color[facePart]; var vertexColor = mesh.GetColorVertex(vertexColorIndex); float alpha = 1; if (hasAlpha) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(-2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var color = mesh.GetMapVertex(-2, indices[facePart]); alpha = color.X; } vertex.Color = new[] { vertexColor.X, vertexColor.Y, vertexColor.Z, alpha }; } if (skin != null) { float weight0 = 0; float weight1 = 0; float weight2 = 0; float weight3 = 0; int bone0 = bonesCount; int bone1 = bonesCount; int bone2 = bonesCount; int bone3 = bonesCount; var nbBones = skin.GetNumberOfBones(vertexIndex); if (nbBones > 0) { bone0 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 0).NodeID); weight0 = skin.GetWeight(vertexIndex, 0); } if (nbBones > 1) { bone1 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 1).NodeID); weight1 = skin.GetWeight(vertexIndex, 1); } if (nbBones > 2) { bone2 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 2).NodeID); weight2 = skin.GetWeight(vertexIndex, 2); } if (nbBones > 3) { bone3 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 3).NodeID); weight3 = skin.GetWeight(vertexIndex, 3); } if (nbBones == 0) { weight0 = 1.0f; bone0 = bonesCount; } vertex.Weights = Loader.Global.Point4.Create(weight0, weight1, weight2, weight3); vertex.BonesIndices = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0; if (nbBones > 4) { bone0 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 4).NodeID); weight0 = skin.GetWeight(vertexIndex, 4); weight1 = 0; weight2 = 0; weight3 = 0; if (nbBones > 5) { bone1 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 5).NodeID); weight1 = skin.GetWeight(vertexIndex, 5); } if (nbBones > 6) { bone2 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 6).NodeID); weight2 = skin.GetWeight(vertexIndex, 6); } if (nbBones > 7) { bone3 = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, 7).NodeID); weight3 = skin.GetWeight(vertexIndex, 7); } vertex.WeightsExtra = Loader.Global.Point4.Create(weight0, weight1, weight2, weight3); vertex.BonesIndicesExtra = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0; if (nbBones > 8) { RaiseError("Too many bones influences per vertex: " + nbBones + ". Babylon.js only support 8 bones influences per vertex.", 2); } } } if (verticesAlreadyExported != null) { if (verticesAlreadyExported[vertexIndex] != null) { var index = verticesAlreadyExported[vertexIndex].IndexOf(vertex); if (index > -1) { return(verticesAlreadyExported[vertexIndex][index].CurrentIndex); } } else { verticesAlreadyExported[vertexIndex] = new List <GlobalVertex>(); } vertex.CurrentIndex = vertices.Count; verticesAlreadyExported[vertexIndex].Add(vertex); } vertices.Add(vertex); return(vertices.Count - 1); }
int CreateGlobalVertex(IIGameNode meshNode, IIGameMesh mesh, BabylonAbstractMesh babylonAbstractMesh, IMatrix3 invertedWorldMatrix, IFaceEx face, int facePart, List <GlobalVertex> vertices, bool hasUV, bool hasUV2, bool hasColor, bool hasAlpha, List <GlobalVertex>[] verticesAlreadyExported, IIGameSkin skin, List <int> boneIds) { var vertexIndex = (int)face.Vert[facePart]; // Position can by retreived in world space or object space // Unfortunately, this value can't be retreived in local space var vertex = new GlobalVertex { BaseIndex = vertexIndex, Position = mesh.GetVertex(vertexIndex, false), // world space Normal = mesh.GetNormal((int)face.Norm[facePart], false) // world space }; if (exportParameters.exportTangents) { int indexTangentBinormal = mesh.GetFaceVertexTangentBinormal(face.MeshFaceIndex, facePart, 1); IPoint3 normal = vertex.Normal.Normalize; IPoint3 tangent = mesh.GetTangent(indexTangentBinormal, 1).Normalize; IPoint3 bitangent = mesh.GetBinormal(indexTangentBinormal, 1).Normalize; int w = GetW(normal, tangent, bitangent); vertex.Tangent = new float[] { tangent.X, tangent.Y, tangent.Z, w }; } // Convert position and normal to local space vertex.Position = invertedWorldMatrix.PointTransform(vertex.Position); vertex.Normal = invertedWorldMatrix.VectorTransform(vertex.Normal); // 1. scale normals with node scales var nodeScaling = BabylonVector3.FromArray(babylonAbstractMesh.scaling); vertex.Normal = vertex.Normal.Multiply(Loader.Global.Point3.Create(Math.Abs(nodeScaling.X), Math.Abs(nodeScaling.Y), Math.Abs(nodeScaling.Z))); // 2. scale normals with objectOffsetScales (unrotate by objectOffsetRot, then scale, then rotate again) // note: LH coordinate system => flip y and z var objOffsetScale = Loader.Global.Point3.Create(meshNode.MaxNode.ObjOffsetScale.S); var scaleX = Math.Abs(objOffsetScale.X); var scaleY = Math.Abs(objOffsetScale.Y); var scaleZ = Math.Abs(objOffsetScale.Z); var objOffsetScaleFlipYZInv = Loader.Global.Point3.Create(1 / scaleX, 1 / scaleZ, 1 / scaleY); var objOffsetQuat = meshNode.MaxNode.ObjOffsetRot; var qFlippedYZ = objOffsetQuat; var tmpSwap = objOffsetQuat.Y; qFlippedYZ.Y = objOffsetQuat.Z; qFlippedYZ.Z = tmpSwap; var nUnrotated = RotateVectorByQuaternion(vertex.Normal, qFlippedYZ); var nUnrotatedScaled = nUnrotated.Multiply(objOffsetScaleFlipYZInv); nUnrotatedScaled = nUnrotatedScaled.Normalize; var nRerotatedScaled = RotateVectorByQuaternion(nUnrotatedScaled, qFlippedYZ.Conjugate); vertex.Normal = nRerotatedScaled; if (hasUV) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(1, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(1, indices[facePart]); vertex.UV = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasUV2) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var texCoord = mesh.GetMapVertex(2, indices[facePart]); vertex.UV2 = Loader.Global.Point2.Create(texCoord.X, -texCoord.Y); } if (hasColor) { var vertexColorIndex = (int)face.Color[facePart]; var vertexColor = mesh.GetColorVertex(vertexColorIndex); float alpha = 1; if (hasAlpha) { var indices = new int[3]; unsafe { fixed(int *indicesPtr = indices) { mesh.GetMapFaceIndex(-2, face.MeshFaceIndex, new IntPtr(indicesPtr)); } } var color = mesh.GetMapVertex(-2, indices[facePart]); alpha = color.X; } vertex.Color = new[] { vertexColor.X, vertexColor.Y, vertexColor.Z, alpha }; } if (skin != null) { float[] weight = new float[4] { 0, 0, 0, 0 }; int[] bone = new int[4] { bonesCount, bonesCount, bonesCount, bonesCount }; var nbBones = skin.GetNumberOfBones(vertexIndex); int currentVtxBone = 0; int currentSkinBone = 0; // process skin bones until we have 4 bones for this vertex or we run out of skin bones for (currentSkinBone = 0; currentSkinBone < nbBones && currentVtxBone < 4; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } bone[currentVtxBone] = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, currentSkinBone).NodeID); weight[currentVtxBone] = skin.GetWeight(vertexIndex, currentSkinBone); ++currentVtxBone; } // if we didnt have any bones with a weight > 0 if (currentVtxBone == 0) { weight[0] = 1.0f; bone[0] = bonesCount; } vertex.Weights = Loader.Global.Point4.Create(weight); vertex.BonesIndices = (bone[3] << 24) | (bone[2] << 16) | (bone[1] << 8) | bone[0]; if (currentVtxBone >= 4 && currentSkinBone < nbBones) { weight = new float[4] { 0, 0, 0, 0 }; bone = new int[4] { bonesCount, bonesCount, bonesCount, bonesCount }; // process remaining skin bones until we have a total of 8 bones for this vertex or we run out of skin bones for (; currentSkinBone < nbBones && currentVtxBone < 8; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } if (isGltfExported) { RaiseError("Too many bone influences per vertex for vertexIndex: " + vertexIndex + ". glTF only supports up to 4 bone influences per vertex.", 2); break; } bone[currentVtxBone - 4] = boneIds.IndexOf(skin.GetIGameBone(vertexIndex, currentSkinBone).NodeID); weight[currentVtxBone - 4] = skin.GetWeight(vertexIndex, currentSkinBone); ++currentVtxBone; } // if we have any extra bone weights if (currentVtxBone > 4) { vertex.WeightsExtra = Loader.Global.Point4.Create(weight); vertex.BonesIndicesExtra = (bone[3] << 24) | (bone[2] << 16) | (bone[1] << 8) | bone[0]; if (currentSkinBone < nbBones) { // if we have more skin bones left, this means we have used up all our bones for this vertex // check if any of the remaining bones has a weight > 0 for (; currentSkinBone < nbBones; ++currentSkinBone) { float boneWeight = skin.GetWeight(vertexIndex, currentSkinBone); if (boneWeight <= 0) { continue; } RaiseError("Too many bone influences per vertex for vertexIndex: " + vertexIndex + ". Babylon.js only supports up to 8 bone influences per vertex.", 2); break; } } } } } if (verticesAlreadyExported != null) { if (verticesAlreadyExported[vertexIndex] != null) { var index = verticesAlreadyExported[vertexIndex].IndexOf(vertex); if (index > -1) { return(verticesAlreadyExported[vertexIndex][index].CurrentIndex); } } else { verticesAlreadyExported[vertexIndex] = new List <GlobalVertex>(); } vertex.CurrentIndex = vertices.Count; verticesAlreadyExported[vertexIndex].Add(vertex); } vertices.Add(vertex); return(vertices.Count - 1); }