public static void GenerateCoordinatesAnimations(IIGameNode meshNode, List <BabylonAnimation> animations) { if (meshNode.IGameControl.IsAnimated(IGameControlType.Pos) || meshNode.IGameControl.IsAnimated(IGameControlType.PosX) || meshNode.IGameControl.IsAnimated(IGameControlType.PosY) || meshNode.IGameControl.IsAnimated(IGameControlType.PosZ)) { ExportVector3Animation("position", animations, key => { var worldMatrix = meshNode.GetObjectTM(key); if (meshNode.NodeParent != null) { var parentWorld = meshNode.NodeParent.GetObjectTM(key); worldMatrix.MultiplyBy(parentWorld.Inverse); } var trans = worldMatrix.Translation; return(new[] { trans.X, trans.Y, trans.Z }); }); } if (meshNode.IGameControl.IsAnimated(IGameControlType.Rot) || meshNode.IGameControl.IsAnimated(IGameControlType.EulerX) || meshNode.IGameControl.IsAnimated(IGameControlType.EulerY) || meshNode.IGameControl.IsAnimated(IGameControlType.EulerZ)) { ExportQuaternionAnimation("rotationQuaternion", animations, key => { var worldMatrix = meshNode.GetObjectTM(key); if (meshNode.NodeParent != null) { var parentWorld = meshNode.NodeParent.GetObjectTM(key); worldMatrix.MultiplyBy(parentWorld.Inverse); } var rot = worldMatrix.Rotation; return(new[] { rot.X, rot.Y, rot.Z, -rot.W }); }); } if (meshNode.IGameControl.IsAnimated(IGameControlType.Scale)) { ExportVector3Animation("scaling", animations, key => { var worldMatrix = meshNode.GetObjectTM(key); if (meshNode.NodeParent != null) { var parentWorld = meshNode.NodeParent.GetObjectTM(key); worldMatrix.MultiplyBy(parentWorld.Inverse); } var scale = worldMatrix.Scaling; return(new[] { scale.X, scale.Y, scale.Z }); }); } }
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 IGMatrix GetLocalTM(IIGameNode gameNode, int key) { var worldMatrix = gameNode.GetObjectTM(key); if (gameNode.NodeParent != null) { var parentWorld = gameNode.NodeParent.GetObjectTM(key); worldMatrix.MultiplyBy(parentWorld.Inverse); } return(worldMatrix); }
private void ExportCamera(IIGameScene scene, IIGameNode cameraNode, BabylonScene babylonScene) { if (cameraNode.MaxNode.GetBoolProperty("babylonjs_noexport")) { return; } var gameCamera = cameraNode.IGameObject.AsGameCamera(); var maxCamera = gameCamera.MaxObject as ICameraObject; var initialized = gameCamera.InitializeData; var babylonCamera = new BabylonCamera(); RaiseMessage(cameraNode.Name, 1); babylonCamera.name = cameraNode.Name; babylonCamera.id = cameraNode.MaxNode.GetGuid().ToString(); if (cameraNode.NodeParent != null) { babylonCamera.parentId = GetParentID(cameraNode.NodeParent, babylonScene, scene); } babylonCamera.fov = Tools.ConvertFov(maxCamera.GetFOV(0, Tools.Forever)); if (maxCamera.ManualClip == 1) { babylonCamera.minZ = maxCamera.GetClipDist(0, 1, Tools.Forever); babylonCamera.maxZ = maxCamera.GetClipDist(0, 2, Tools.Forever); } else { babylonCamera.minZ = 0.1f; babylonCamera.maxZ = 10000.0f; } if (babylonCamera.minZ == 0.0f) { babylonCamera.minZ = 0.1f; } // Type babylonCamera.type = cameraNode.MaxNode.GetStringProperty("babylonjs_type", "FreeCamera"); // Control babylonCamera.speed = cameraNode.MaxNode.GetFloatProperty("babylonjs_speed", 1.0f); babylonCamera.inertia = cameraNode.MaxNode.GetFloatProperty("babylonjs_inertia", 0.9f); // Collisions babylonCamera.checkCollisions = cameraNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions"); babylonCamera.applyGravity = cameraNode.MaxNode.GetBoolProperty("babylonjs_applygravity"); babylonCamera.ellipsoid = cameraNode.MaxNode.GetVector3Property("babylonjs_ellipsoid"); // Position / rotation var localTM = cameraNode.GetObjectTM(0); if (cameraNode.NodeParent != null) { var parentWorld = cameraNode.NodeParent.GetObjectTM(0); localTM.MultiplyBy(parentWorld.Inverse); } var position = localTM.Translation; var rotation = localTM.Rotation; var exportQuaternions = Loader.Core.RootNode.GetBoolProperty("babylonjs_exportquaternions"); babylonCamera.position = new[] { position.X, position.Y, position.Z }; if (exportQuaternions) { babylonCamera.rotationQuaternion = new[] { rotation.X, rotation.Y, rotation.Z, -rotation.W }; } else { babylonCamera.rotation = QuaternionToEulerAngles(rotation); } // Target var target = gameCamera.CameraTarget; if (target != null) { babylonCamera.lockedTargetId = target.MaxNode.GetGuid().ToString(); } else { var dir = localTM.GetRow(3); babylonCamera.target = new [] { position.X - dir.X, position.Y - dir.Y, position.Z - dir.Z }; } // Animations var animations = new List <BabylonAnimation>(); ExportVector3Animation("position", animations, key => { var tm = cameraNode.GetLocalTM(key); if (cameraNode.NodeParent != null) { var parentWorld = cameraNode.NodeParent.GetObjectTM(key); tm.MultiplyBy(parentWorld.Inverse); } var translation = tm.Translation; return(new [] { translation.X, translation.Y, translation.Z }); }); if (gameCamera.CameraTarget == null) { ExportVector3Animation("target", animations, key => { var tm = cameraNode.GetLocalTM(key); if (cameraNode.NodeParent != null) { var parentWorld = cameraNode.NodeParent.GetObjectTM(key); tm.MultiplyBy(parentWorld.Inverse); } var translation = tm.Translation; var dir = tm.GetRow(3); return(new float[] { translation.X - dir.X, translation.Y - dir.Y, translation.Z - dir.Z }); }); } ExportFloatAnimation("fov", animations, key => new[] { Tools.ConvertFov((gameCamera.MaxObject as ICameraObject).GetFOV(key, Tools.Forever)) }); babylonCamera.animations = animations.ToArray(); if (cameraNode.MaxNode.GetBoolProperty("babylonjs_autoanimate")) { babylonCamera.autoAnimate = true; babylonCamera.autoAnimateFrom = (int)cameraNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_from"); babylonCamera.autoAnimateTo = (int)cameraNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_to"); babylonCamera.autoAnimateLoop = cameraNode.MaxNode.GetBoolProperty("babylonjs_autoanimateloop"); } babylonScene.CamerasList.Add(babylonCamera); }
private void ExportLight(IIGameScene scene, IIGameNode lightNode, BabylonScene babylonScene) { if (lightNode.MaxNode.GetBoolProperty("babylonjs_noexport")) { return; } var gameLight = lightNode.IGameObject.AsGameLight(); var initialized = gameLight.InitializeData; var babylonLight = new BabylonLight(); RaiseMessage(lightNode.Name, 1); babylonLight.name = lightNode.Name; babylonLight.id = lightNode.MaxNode.GetGuid().ToString(); if (lightNode.NodeParent != null) { babylonLight.parentId = GetParentID(lightNode.NodeParent, babylonScene, scene); } // Type var maxLight = (lightNode.MaxNode.ObjectRef as ILightObject); var lightState = Loader.Global.LightState.Create(); maxLight.EvalLightState(0, Tools.Forever, lightState); switch (lightState.Type) { case LightType.OmniLgt: babylonLight.type = 0; break; case LightType.SpotLgt: babylonLight.type = 2; babylonLight.angle = (float)(maxLight.GetFallsize(0, Tools.Forever) * Math.PI / 180.0f); babylonLight.exponent = 1; break; case LightType.DirectLgt: babylonLight.type = 1; break; case LightType.AmbientLgt: babylonLight.type = 3; babylonLight.groundColor = new float[] { 0, 0, 0 }; break; } // Shadows if (maxLight.ShadowMethod == 1) { if (lightState.Type == LightType.DirectLgt || lightState.Type == LightType.SpotLgt || lightState.Type == LightType.OmniLgt) { ExportShadowGenerator(lightNode.MaxNode, babylonScene); } else { RaiseWarning("Shadows maps are only supported for point, directional and spot lights", 2); } } // Position var wm = lightNode.GetObjectTM(0); if (lightNode.NodeParent != null) { var parentWorld = lightNode.NodeParent.GetObjectTM(0); wm.MultiplyBy(parentWorld.Inverse); } var position = wm.Translation; babylonLight.position = new[] { position.X, position.Y, position.Z }; // Direction var target = gameLight.LightTarget; if (target != null) { var targetWm = target.GetObjectTM(0); var targetPosition = targetWm.Translation; var direction = targetPosition.Subtract(position).Normalize; babylonLight.direction = new[] { direction.X, direction.Y, direction.Z }; } else { var vDir = Loader.Global.Point3.Create(0, -1, 0); vDir = wm.ExtractMatrix3().VectorTransform(vDir).Normalize; babylonLight.direction = new[] { vDir.X, vDir.Y, vDir.Z }; } var maxScene = Loader.Core.RootNode; // Exclusion var inclusion = maxLight.ExclList.TestFlag(1); //NT_INCLUDE var checkExclusionList = maxLight.ExclList.TestFlag(2); //NT_AFFECT_ILLUM if (checkExclusionList) { var excllist = new List <string>(); var incllist = new List <string>(); foreach (var meshNode in maxScene.NodesListBySuperClass(SClass_ID.Geomobject)) { if (meshNode.CastShadows == 1) { var inList = maxLight.ExclList.FindNode(meshNode) != -1; if (inList) { if (inclusion) { incllist.Add(meshNode.GetGuid().ToString()); } else { excllist.Add(meshNode.GetGuid().ToString()); } } } } babylonLight.includedOnlyMeshesIds = incllist.ToArray(); babylonLight.excludedMeshesIds = excllist.ToArray(); } // Other fields babylonLight.intensity = maxLight.GetIntensity(0, Tools.Forever); babylonLight.diffuse = lightState.AffectDiffuse ? maxLight.GetRGBColor(0, Tools.Forever).ToArray() : new float[] { 0, 0, 0 }; babylonLight.specular = lightState.AffectDiffuse ? maxLight.GetRGBColor(0, Tools.Forever).ToArray() : new float[] { 0, 0, 0 }; if (maxLight.UseAtten) { babylonLight.range = maxLight.GetAtten(0, 3, Tools.Forever); } // Animations var animations = new List <BabylonAnimation>(); ExportVector3Animation("position", animations, key => { var mat = lightNode.GetObjectTM(key); if (lightNode.NodeParent != null) { var parentWorld = lightNode.NodeParent.GetObjectTM(key); mat.MultiplyBy(parentWorld.Inverse); } var pos = mat.Translation; return(new[] { pos.X, pos.Y, pos.Z }); }); ExportVector3Animation("direction", animations, key => { var wmLight = lightNode.GetObjectTM(key); if (lightNode.NodeParent != null) { var parentWorld = lightNode.NodeParent.GetObjectTM(key); wmLight.MultiplyBy(parentWorld.Inverse); } var positionLight = wmLight.Translation; var lightTarget = gameLight.LightTarget; if (lightTarget != null) { var targetWm = lightTarget.GetObjectTM(key); var targetPosition = targetWm.Translation; var direction = targetPosition.Subtract(positionLight).Normalize; return(new[] { direction.X, direction.Y, direction.Z }); } else { var vDir = Loader.Global.Point3.Create(0, -1, 0); vDir = wmLight.ExtractMatrix3().VectorTransform(vDir).Normalize; return(new[] { vDir.X, vDir.Y, vDir.Z }); } }); ExportFloatAnimation("intensity", animations, key => new[] { maxLight.GetIntensity(key, Tools.Forever) }); ExportColor3Animation("diffuse", animations, key => { return(lightState.AffectDiffuse? maxLight.GetRGBColor(key, Tools.Forever).ToArray() : new float[] { 0, 0, 0 }); }); babylonLight.animations = animations.ToArray(); if (lightNode.MaxNode.GetBoolProperty("babylonjs_autoanimate")) { babylonLight.autoAnimate = true; babylonLight.autoAnimateFrom = (int)lightNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_from"); babylonLight.autoAnimateTo = (int)lightNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_to"); babylonLight.autoAnimateLoop = lightNode.MaxNode.GetBoolProperty("babylonjs_autoanimateloop"); } babylonScene.LightsList.Add(babylonLight); }
private void ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene) { if (meshNode.MaxNode.IsInstance()) { return; } if (meshNode.MaxNode.GetBoolProperty("babylonjs_noexport")) { return; } if (!ExportHiddenObjects && meshNode.MaxNode.IsHidden(NodeHideFlags.None, false)) { return; } var gameMesh = meshNode.IGameObject.AsGameMesh(); bool initialized = gameMesh.InitializeData; //needed, the property is in fact a method initializing the exporter that has wrongly been auto // translated into a property because it has no parameters var babylonMesh = new BabylonMesh { name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString() }; if (meshNode.NodeParent != null) { babylonMesh.parentId = GetParentID(meshNode.NodeParent, babylonScene, scene); } // Sounds var soundName = meshNode.MaxNode.GetStringProperty("babylonjs_sound_filename", ""); if (!string.IsNullOrEmpty(soundName)) { var filename = Path.GetFileName(soundName); var meshSound = new BabylonSound { name = filename, autoplay = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_autoplay", 1), loop = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_loop", 1), volume = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_volume", 1.0f), playbackRate = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_playbackrate", 1.0f), connectedMeshId = babylonMesh.id, isDirectional = false, spatialSound = false, distanceModel = meshNode.MaxNode.GetStringProperty("babylonjs_sound_distancemodel", "linear"), maxDistance = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_maxdistance", 100f), rolloffFactor = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_rolloff", 1.0f), refDistance = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_refdistance", 1.0f), }; var isDirectional = meshNode.MaxNode.GetBoolProperty("babylonjs_sound_directional"); if (isDirectional) { meshSound.isDirectional = true; meshSound.coneInnerAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneinnerangle", 360f); meshSound.coneOuterAngle = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneouterangle", 360f); meshSound.coneOuterGain = meshNode.MaxNode.GetFloatProperty("babylonjs_sound_coneoutergain", 1.0f); } babylonScene.SoundsList.Add(meshSound); try { File.Copy(soundName, Path.Combine(babylonScene.OutputPath, filename), true); } catch { } } // Misc. #if MAX2017 babylonMesh.isVisible = meshNode.MaxNode.Renderable; babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows; babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics; #else babylonMesh.isVisible = meshNode.MaxNode.Renderable == 1; babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1; babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1; #endif babylonMesh.pickable = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable"); babylonMesh.showBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showboundingbox"); babylonMesh.showSubMeshesBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox"); babylonMesh.alphaIndex = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_alphaindex", 1000); // Actions babylonMesh.actions = ExportNodeAction(meshNode); // Collisions babylonMesh.checkCollisions = meshNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions"); var isSkinned = gameMesh.IsObjectSkinned; var skin = gameMesh.IGameSkin; var unskinnedMesh = gameMesh; IGMatrix skinInitPoseMatrix = Loader.Global.GMatrix.Create(Loader.Global.Matrix3.Create(true)); List <int> boneIds = null; int maxNbBones = 0; if (isSkinned) { bonesCount = skin.TotalSkinBoneCount; skins.Add(skin); skinnedNodes.Add(meshNode); babylonMesh.skeletonId = skins.IndexOf(skin); skin.GetInitSkinTM(skinInitPoseMatrix); boneIds = SortBones(skin); skinSortedBones[skin] = boneIds; } // Position / rotation / scaling var localTM = meshNode.GetObjectTM(0); if (meshNode.NodeParent != null) { var parentWorld = meshNode.NodeParent.GetObjectTM(0); localTM.MultiplyBy(parentWorld.Inverse); } var meshTrans = localTM.Translation; var meshRotation = localTM.Rotation; var meshScale = localTM.Scaling; var exportQuaternions = Loader.Core.RootNode.GetBoolProperty("babylonjs_exportquaternions"); babylonMesh.position = new[] { meshTrans.X, meshTrans.Y, meshTrans.Z }; if (exportQuaternions) { babylonMesh.rotationQuaternion = new[] { meshRotation.X, meshRotation.Y, meshRotation.Z, -meshRotation.W }; } else { RotationToEulerAngles(babylonMesh, meshRotation); } babylonMesh.scaling = new[] { meshScale.X, meshScale.Y, meshScale.Z }; // Mesh RaiseMessage(meshNode.Name, 1); if (unskinnedMesh.IGameType == Autodesk.Max.IGameObject.ObjectTypes.Mesh && unskinnedMesh.MaxMesh != null) { if (unskinnedMesh.NumberOfFaces < 1) { RaiseError($"Mesh {babylonMesh.name} has no face", 2); } if (unskinnedMesh.NumberOfVerts < 3) { RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2); } if (unskinnedMesh.NumberOfVerts >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has tmore than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", 2); } if (skin != null) { for (var vertexIndex = 0; vertexIndex < unskinnedMesh.NumberOfVerts; vertexIndex++) { maxNbBones = Math.Max(maxNbBones, skin.GetNumberOfBones(vertexIndex)); } } // Physics var impostorText = meshNode.MaxNode.GetStringProperty("babylonjs_impostor", "None"); if (impostorText != "None") { switch (impostorText) { case "Sphere": babylonMesh.physicsImpostor = 1; break; case "Box": babylonMesh.physicsImpostor = 2; break; case "Plane": babylonMesh.physicsImpostor = 3; break; default: babylonMesh.physicsImpostor = 0; break; } babylonMesh.physicsMass = meshNode.MaxNode.GetFloatProperty("babylonjs_mass"); babylonMesh.physicsFriction = meshNode.MaxNode.GetFloatProperty("babylonjs_friction", 0.2f); babylonMesh.physicsRestitution = meshNode.MaxNode.GetFloatProperty("babylonjs_restitution", 0.2f); } // Material var mtl = meshNode.NodeMaterial; var multiMatsCount = 1; if (mtl != null) { babylonMesh.materialId = mtl.MaxMaterial.GetGuid().ToString(); if (!referencedMaterials.Contains(mtl)) { referencedMaterials.Add(mtl); } multiMatsCount = Math.Max(mtl.SubMaterialCount, 1); } babylonMesh.visibility = meshNode.MaxNode.GetVisibility(0, Tools.Forever); var vertices = new List <GlobalVertex>(); var indices = new List <int>(); var mappingChannels = unskinnedMesh.ActiveMapChannelNum; bool hasUV = false; bool hasUV2 = false; for (int i = 0; i < mappingChannels.Count; ++i) { #if MAX2017 var indexer = i; #else var indexer = new IntPtr(i); #endif var channelNum = mappingChannels[indexer]; if (channelNum == 1) { hasUV = true; } else if (channelNum == 2) { hasUV2 = true; } } var hasColor = unskinnedMesh.NumberOfColorVerts > 0; var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0; var optimizeVertices = meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices"); // Compute normals List <GlobalVertex>[] verticesAlreadyExported = null; if (optimizeVertices) { verticesAlreadyExported = new List <GlobalVertex> [unskinnedMesh.NumberOfVerts]; } var subMeshes = new List <BabylonSubMesh>(); var indexStart = 0; for (int i = 0; i < multiMatsCount; ++i) { int materialId = meshNode.NodeMaterial?.GetMaterialID(i) ?? 0; var indexCount = 0; var minVertexIndex = int.MaxValue; var maxVertexIndex = int.MinValue; var subMesh = new BabylonSubMesh { indexStart = indexStart, materialIndex = i }; if (multiMatsCount == 1) { for (int j = 0; j < unskinnedMesh.NumberOfFaces; ++j) { var face = unskinnedMesh.GetFace(j); ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } else { ITab <IFaceEx> materialFaces = unskinnedMesh.GetFacesFromMatID(materialId); for (int j = 0; j < materialFaces.Count; ++j) { #if MAX2017 var faceIndexer = j; #else var faceIndexer = new IntPtr(j); #endif var face = materialFaces[faceIndexer]; #if !MAX2017 Marshal.FreeHGlobal(faceIndexer); #endif ExtractFace(skin, unskinnedMesh, vertices, indices, hasUV, hasUV2, hasColor, hasAlpha, verticesAlreadyExported, ref indexCount, ref minVertexIndex, ref maxVertexIndex, face, boneIds); } } if (indexCount != 0) { subMesh.indexCount = indexCount; subMesh.verticesStart = minVertexIndex; subMesh.verticesCount = maxVertexIndex - minVertexIndex + 1; indexStart += indexCount; subMeshes.Add(subMesh); } } if (vertices.Count >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has {vertices.Count} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 2); if (!optimizeVertices) { RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2); } } RaiseMessage($"{vertices.Count} vertices, {indices.Count/3} faces", 2); // Buffers babylonMesh.positions = vertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToArray(); babylonMesh.normals = vertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray(); if (hasUV) { babylonMesh.uvs = vertices.SelectMany(v => new[] { v.UV.X, 1 - v.UV.Y }).ToArray(); } if (hasUV2) { babylonMesh.uvs2 = vertices.SelectMany(v => new[] { v.UV2.X, 1 - v.UV2.Y }).ToArray(); } if (skin != null) { babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray(); babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray(); babylonMesh.numBoneInfluencers = maxNbBones; if (maxNbBones > 4) { babylonMesh.matricesWeightsExtra = vertices.SelectMany(v => v.WeightsExtra != null ? v.WeightsExtra.ToArray() : new[] { 0.0f, 0.0f, 0.0f, 0.0f }).ToArray(); babylonMesh.matricesIndicesExtra = vertices.Select(v => v.BonesIndicesExtra).ToArray(); } } if (hasColor) { babylonMesh.colors = vertices.SelectMany(v => v.Color.ToArray()).ToArray(); babylonMesh.hasVertexAlpha = hasAlpha; } babylonMesh.subMeshes = subMeshes.ToArray(); // Buffers - Indices babylonMesh.indices = indices.ToArray(); } // Instances var tabs = Loader.Global.NodeTab.Create(); Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode.MaxNode, tabs); var instances = new List <BabylonAbstractMesh>(); for (var index = 0; index < tabs.Count; index++) { #if MAX2017 var indexer = index; #else var indexer = new IntPtr(index); #endif var tab = tabs[indexer]; #if !MAX2017 Marshal.FreeHGlobal(indexer); #endif if (meshNode.MaxNode.GetGuid() == tab.GetGuid()) { continue; } var instanceGameNode = scene.GetIGameNode(tab); if (instanceGameNode == null) { continue; } tab.MarkAsInstance(); var instance = new BabylonAbstractMesh { name = tab.Name }; { var instanceLocalTM = instanceGameNode.GetObjectTM(0); var instanceTrans = instanceLocalTM.Translation; var instanceRotation = instanceLocalTM.Rotation; var instanceScale = instanceLocalTM.Scaling; instance.position = new[] { instanceTrans.X, instanceTrans.Y, instanceTrans.Z }; if (exportQuaternions) { instance.rotationQuaternion = new[] { instanceRotation.X, instanceRotation.Y, instanceRotation.Z, -instanceRotation.W }; } else { RotationToEulerAngles(instance, instanceRotation); } instance.scaling = new[] { instanceScale.X, instanceScale.Y, instanceScale.Z }; } var instanceAnimations = new List <BabylonAnimation>(); GenerateCoordinatesAnimations(meshNode, instanceAnimations); instance.animations = instanceAnimations.ToArray(); instances.Add(instance); } babylonMesh.instances = instances.ToArray(); // Animations var animations = new List <BabylonAnimation>(); GenerateCoordinatesAnimations(meshNode, animations); if (!ExportFloatController(meshNode.MaxNode.VisController, "visibility", animations)) { ExportFloatAnimation("visibility", animations, key => new[] { meshNode.MaxNode.GetVisibility(key, Tools.Forever) }); } babylonMesh.animations = animations.ToArray(); if (meshNode.MaxNode.GetBoolProperty("babylonjs_autoanimate", 1)) { babylonMesh.autoAnimate = true; babylonMesh.autoAnimateFrom = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_from"); babylonMesh.autoAnimateTo = (int)meshNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_to", 100); babylonMesh.autoAnimateLoop = meshNode.MaxNode.GetBoolProperty("babylonjs_autoanimateloop", 1); } babylonScene.MeshesList.Add(babylonMesh); }
private 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", 0); 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. babylonMesh.isVisible = meshNode.MaxNode.Renderable == 1; babylonMesh.pickable = meshNode.MaxNode.GetBoolProperty("babylonjs_checkpickable"); babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1; babylonMesh.showBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showboundingbox"); babylonMesh.showSubMeshesBoundingBox = meshNode.MaxNode.GetBoolProperty("babylonjs_showsubmeshesboundingbox"); babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1; 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; 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(string.Format("Mesh {0} has no face", babylonMesh.name), 2); } if (unskinnedMesh.NumberOfVerts < 3) { RaiseError(string.Format("Mesh {0} has not enough vertices", babylonMesh.name), 2); } if (unskinnedMesh.NumberOfVerts >= 65536) { RaiseWarning(string.Format("Mesh {0} 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.", babylonMesh.name), 2); } // 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) { var indexer = new IntPtr(i); var channelNum = mappingChannels[indexer]; if (channelNum == 1) { hasUV = true; } else if (channelNum == 2) { hasUV2 = true; } } var hasColor = unskinnedMesh.NumberOfColorVerts > 0; var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0; 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 == null ? 0 : meshNode.NodeMaterial.GetMaterialID(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) { 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) { var faceIndexer = new IntPtr(j); var face = materialFaces[faceIndexer]; Marshal.FreeHGlobal(faceIndexer); 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(string.Format("Mesh {0} has {1} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 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 => 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(); } 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++) { var indexer = new IntPtr(index); var tab = tabs[indexer]; Marshal.FreeHGlobal(indexer); 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); }
public static void GenerateCoordinatesAnimations(IIGameNode meshNode, List<BabylonAnimation> animations) { if (meshNode.IGameControl.IsAnimated(IGameControlType.Pos) || meshNode.IGameControl.IsAnimated(IGameControlType.PosX) || meshNode.IGameControl.IsAnimated(IGameControlType.PosY) || meshNode.IGameControl.IsAnimated(IGameControlType.PosZ)) { ExportVector3Animation("position", animations, key => { var worldMatrix = meshNode.GetObjectTM(key); if (meshNode.NodeParent != null) { var parentWorld = meshNode.NodeParent.GetObjectTM(key); worldMatrix.MultiplyBy(parentWorld.Inverse); } var trans = worldMatrix.Translation; return new[] { trans.X, trans.Y, trans.Z }; }); } if (meshNode.IGameControl.IsAnimated(IGameControlType.Rot) || meshNode.IGameControl.IsAnimated(IGameControlType.EulerX) || meshNode.IGameControl.IsAnimated(IGameControlType.EulerY) || meshNode.IGameControl.IsAnimated(IGameControlType.EulerZ)) { ExportQuaternionAnimation("rotationQuaternion", animations, key => { var worldMatrix = meshNode.GetObjectTM(key); if (meshNode.NodeParent != null) { var parentWorld = meshNode.NodeParent.GetObjectTM(key); worldMatrix.MultiplyBy(parentWorld.Inverse); } var rot = worldMatrix.Rotation; return new[] { rot.X, rot.Y, rot.Z, -rot.W }; }); } if (meshNode.IGameControl.IsAnimated(IGameControlType.Scale)) { ExportVector3Animation("scaling", animations, key => { var worldMatrix = meshNode.GetObjectTM(key); if (meshNode.NodeParent != null) { var parentWorld = meshNode.NodeParent.GetObjectTM(key); worldMatrix.MultiplyBy(parentWorld.Inverse); } var scale = worldMatrix.Scaling; return new[] { scale.X, scale.Y, scale.Z }; }); } }
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 || MAX2018 var tab = tabs[index]; #else var tab = tabs[new IntPtr(index)]; #endif 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(); try { 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 } catch (Exception e) { RaiseWarning($"Mesh {meshNode.Name} failed to initialize. Mesh is exported as dummy.", 2); return(ExportDummy(scene, meshNode, babylonScene)); } 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 || MAX2018 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); // Collisions babylonMesh.checkCollisions = meshNode.MaxNode.GetBoolProperty("babylonjs_checkcollisions"); // Skin 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 && GetRevelantNodes(skin).Count > 0) // if the mesh has a skin with at least one bone { bonesCount = skin.TotalSkinBoneCount; var skinAlreadyStored = skins.Find(_skin => IsSkinEqualTo(_skin, skin)); if (skinAlreadyStored == null) { skins.Add(skin); } else { skin = skinAlreadyStored; } babylonMesh.skeletonId = skins.IndexOf(skin); skin.GetInitSkinTM(skinInitPoseMatrix); boneIds = GetNodeIndices(skin); } else { skin = null; } // 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) { IIGameMaterial unsupportedMaterial = isMaterialSupported(mtl); if (unsupportedMaterial == null) { babylonMesh.materialId = mtl.MaxMaterial.GetGuid().ToString(); if (!referencedMaterials.Contains(mtl)) { referencedMaterials.Add(mtl); } multiMatsCount = Math.Max(mtl.SubMaterialCount, 1); } else { if (mtl.SubMaterialCount == 0 || mtl == unsupportedMaterial) { RaiseWarning("Unsupported material type '" + unsupportedMaterial.MaterialClass + "'. Material is ignored.", 2); } else { RaiseWarning("Unsupported sub-material type '" + unsupportedMaterial.MaterialClass + "'. Material is ignored.", 2); } } } 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 || MAX2018 var channelNum = mappingChannels[i]; #else var channelNum = mappingChannels[new IntPtr(i)]; #endif 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"); var invertedWorldMatrix = GetInvertWorldTM(meshNode, 0); // Compute normals var subMeshes = new List <BabylonSubMesh>(); List <int> faceIndexes = null; ExtractGeometry(babylonMesh, vertices, indices, subMeshes, boneIds, skin, unskinnedMesh, invertedWorldMatrix, hasUV, hasUV2, hasColor, hasAlpha, optimizeVertices, multiMatsCount, meshNode, ref faceIndexes); 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); } } // Tangent // Export tangents if option is checked and mesh have tangents if (exportParameters.exportTangents) { babylonMesh.tangents = vertices.SelectMany(v => v.Tangent).ToArray(); } 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(); // flip normals depending on parity var parityObject = meshNode.GetObjectTM(0).ExtractMatrix3().Parity; // for cesium, threejs and babylonjs (all the same) if (parityObject) { // flipped case: reverse normals babylonMesh.normals = vertices.SelectMany(v => new[] { -v.Normal.X, -v.Normal.Y, -v.Normal.Z }).ToArray(); } else { // normal case 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(babylonMesh, maxMorphTarget, optimizeVertices, faceIndexes); 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(); // Tangent if (exportParameters.exportTangents) { babylonMorphTarget.tangents = targetVertices.SelectMany(v => v.Tangent).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(); } } // World Modifiers ExportWorldModifiers(meshNode, babylonScene, babylonMesh); // Animations // Done last to avoid '0 vertex found' error (unkown cause) exportAnimation(babylonMesh, meshNode); babylonScene.MeshesList.Add(babylonMesh); return(babylonMesh); }
private void ExportLight(IIGameScene scene, IIGameNode lightNode, BabylonScene babylonScene) { if (lightNode.MaxNode.GetBoolProperty("babylonjs_noexport")) { return; } var gameLight = lightNode.IGameObject.AsGameLight(); var initialized = gameLight.InitializeData; var babylonLight = new BabylonLight(); RaiseMessage(lightNode.Name, 1); babylonLight.name = lightNode.Name; babylonLight.id = lightNode.MaxNode.GetGuid().ToString(); if (lightNode.NodeParent != null) { babylonLight.parentId = GetParentID(lightNode.NodeParent, babylonScene, scene); } // Type var maxLight = (lightNode.MaxNode.ObjectRef as ILightObject); var lightState = Loader.Global.LightState.Create(); maxLight.EvalLightState(0, Tools.Forever, lightState); switch (lightState.Type) { case LightType.OmniLgt: babylonLight.type = 0; break; case LightType.SpotLgt: babylonLight.type = 2; babylonLight.angle = (float)(maxLight.GetFallsize(0, Tools.Forever) * Math.PI / 180.0f); babylonLight.exponent = 1; break; case LightType.DirectLgt: babylonLight.type = 1; break; case LightType.AmbientLgt: babylonLight.type = 3; babylonLight.groundColor = new float[] { 0, 0, 0 }; break; } // Shadows if (maxLight.ShadowMethod == 1) { if (lightState.Type == LightType.DirectLgt || lightState.Type == LightType.SpotLgt) { ExportShadowGenerator(lightNode.MaxNode, babylonScene); } else { RaiseWarning("Shadows maps are only supported for directional and spot lights", 2); } } // Position var wm = lightNode.GetObjectTM(0); if (lightNode.NodeParent != null) { var parentWorld = lightNode.NodeParent.GetObjectTM(0); wm.MultiplyBy(parentWorld.Inverse); } var position = wm.Translation; babylonLight.position = new[] { position.X, position.Y, position.Z }; // Direction var target = gameLight.LightTarget; if (target != null) { var targetWm = target.GetObjectTM(0); var targetPosition = targetWm.Translation; var direction = targetPosition.Subtract(position).Normalize; babylonLight.direction = new[] { direction.X, direction.Y, direction.Z }; } else { var vDir = Loader.Global.Point3.Create(0, -1, 0); vDir = wm.ExtractMatrix3().VectorTransform(vDir).Normalize; babylonLight.direction = new[] { vDir.X, vDir.Y, vDir.Z }; } var maxScene = Loader.Core.RootNode; // Exclusion var inclusion = maxLight.ExclList.TestFlag(1); //NT_INCLUDE var checkExclusionList = maxLight.ExclList.TestFlag(2); //NT_AFFECT_ILLUM if (checkExclusionList) { var excllist = new List<string>(); var incllist = new List<string>(); foreach (var meshNode in maxScene.NodesListBySuperClass(SClass_ID.Geomobject)) { if (meshNode.CastShadows == 1) { var inList = maxLight.ExclList.FindNode(meshNode) != -1; if (inList) { if (inclusion) { incllist.Add(meshNode.GetGuid().ToString()); } else { excllist.Add(meshNode.GetGuid().ToString()); } } } } babylonLight.includedOnlyMeshesIds = incllist.ToArray(); babylonLight.excludedMeshesIds = excllist.ToArray(); } // Other fields babylonLight.intensity = maxLight.GetIntensity(0, Tools.Forever); babylonLight.diffuse = lightState.AffectDiffuse ? maxLight.GetRGBColor(0, Tools.Forever).ToArray() : new float[] { 0, 0, 0 }; babylonLight.specular = lightState.AffectDiffuse ? maxLight.GetRGBColor(0, Tools.Forever).ToArray() : new float[] { 0, 0, 0 }; if (maxLight.UseAtten) { babylonLight.range = maxLight.GetAtten(0, 1, Tools.Forever); } // Animations var animations = new List<BabylonAnimation>(); ExportVector3Animation("position", animations, key => { var mat = lightNode.GetObjectTM(key); if (lightNode.NodeParent != null) { var parentWorld = lightNode.NodeParent.GetObjectTM(key); mat.MultiplyBy(parentWorld.Inverse); } var pos = mat.Translation; return new[] { pos.X, pos.Y, pos.Z }; }); ExportVector3Animation("direction", animations, key => { var wmLight = lightNode.GetObjectTM(key); if (lightNode.NodeParent != null) { var parentWorld = lightNode.NodeParent.GetObjectTM(key); wmLight.MultiplyBy(parentWorld.Inverse); } var positionLight = wmLight.Translation; var lightTarget = gameLight.LightTarget; if (lightTarget != null) { var targetWm = lightTarget.GetObjectTM(key); var targetPosition = targetWm.Translation; var direction = targetPosition.Subtract(positionLight).Normalize; return new[] { direction.X, direction.Y, direction.Z }; } else { var vDir = Loader.Global.Point3.Create(0, -1, 0); vDir = wmLight.ExtractMatrix3().VectorTransform(vDir).Normalize; return new[] { vDir.X, vDir.Y, vDir.Z }; } }); ExportFloatAnimation("intensity", animations, key => new[] { maxLight.GetIntensity(key, Tools.Forever) }); babylonLight.animations = animations.ToArray(); if (lightNode.MaxNode.GetBoolProperty("babylonjs_autoanimate")) { babylonLight.autoAnimate = true; babylonLight.autoAnimateFrom = (int)lightNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_from"); babylonLight.autoAnimateTo = (int)lightNode.MaxNode.GetFloatProperty("babylonjs_autoanimate_to"); babylonLight.autoAnimateLoop = lightNode.MaxNode.GetBoolProperty("babylonjs_autoanimateloop"); } babylonScene.LightsList.Add(babylonLight); }