private HashSet <IIGameNode> getRootNodes(IIGameScene maxGameScene) { HashSet <IIGameNode> maxGameNodes = new HashSet <IIGameNode>(); Func <IIGameNode, IIGameNode> getMaxRootNode = delegate(IIGameNode maxGameNode) { while (maxGameNode.NodeParent != null) { maxGameNode = maxGameNode.NodeParent; } return(maxGameNode); }; Action <Autodesk.Max.IGameObject.ObjectTypes> addMaxRootNodes = delegate(Autodesk.Max.IGameObject.ObjectTypes type) { ITab <IIGameNode> maxGameNodesOfType = maxGameScene.GetIGameNodeByType(type); if (maxGameNodesOfType != null) { TabToList(maxGameNodesOfType).ForEach(maxGameNode => { var maxRootNode = getMaxRootNode(maxGameNode); maxGameNodes.Add(maxRootNode); }); } }; addMaxRootNodes(Autodesk.Max.IGameObject.ObjectTypes.Mesh); addMaxRootNodes(Autodesk.Max.IGameObject.ObjectTypes.Light); addMaxRootNodes(Autodesk.Max.IGameObject.ObjectTypes.Camera); return(maxGameNodes); }
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 BabylonNode ExportMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene) { if (IsMeshExportable(meshNode) == false) { return(null); } logger?.RaiseMessage(meshNode.Name, 1); if (!exportParameters.keepInstances) { return(ExportMasterMesh(scene, meshNode, babylonScene)); } else { // Instances #if MAX2020 || MAX2021 || MAX2022 var tabs = Loader.Global.INodeTab.Create(); #else var tabs = Loader.Global.NodeTab.Create(); #endif Loader.Global.IInstanceMgr.InstanceMgr.GetInstances(meshNode.MaxNode, tabs); if (tabs.Count > 1) { IINode Master = TabToList <IINode>(tabs)[tabs.Count - 1]; List <IINode> Instances = TabToList <IINode>(tabs).FindAll(x => x.Handle != Master.Handle); foreach (IINode instanceNode in tabs.ToIEnumerable()) { //this make sure every instance node is indexed in guid dictionary Tools.GetGuid(instanceNode); } BabylonMesh babylonMasterMesh = babylonScene.MeshesList.Find(mesh => mesh.id == Master.GetGuid().ToString()); if (babylonMasterMesh == null) { return(ExportMasterMesh(scene, meshNode, babylonScene)); } else { return(ExportInstanceMesh(scene, meshNode, babylonScene, babylonMasterMesh)); } } return(ExportMasterMesh(scene, meshNode, babylonScene)); } }
private BabylonNode ExportSubModelExtraNode(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene) { logger?.RaiseMessage(meshNode.Name, 1); var babylonMesh = new BabylonMesh { name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString() }; babylonMesh.isDummy = true; // Position / rotation / scaling / hierarchy exportNode(babylonMesh, meshNode, scene, babylonScene); babylonScene.MeshesList.Add(babylonMesh); return(babylonMesh); }
private string GetParentID(IIGameNode parentNode, BabylonScene babylonScene, IIGameScene scene) { var parentType = parentNode.IGameObject.IGameType; var parentId = parentNode.MaxNode.GetGuid().ToString(); switch (parentType) { case Autodesk.Max.IGameObject.ObjectTypes.Light: case Autodesk.Max.IGameObject.ObjectTypes.Mesh: case Autodesk.Max.IGameObject.ObjectTypes.Camera: break; default: if (babylonScene.MeshesList.All(m => m.id != parentId)) { ExportMesh(scene, parentNode, babylonScene); } break; } return parentId; }
private BabylonNode ExportDummy(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene) { RaiseMessage(meshNode.Name, 1); var gameMesh = meshNode.IGameObject.AsGameMesh(); bool initialized = gameMesh.InitializeData; // needed, the property is in fact a method initializing the exporter that has wrongly been auto // translated into a property because it has no parameters var babylonMesh = new BabylonMesh { name = meshNode.Name, id = meshNode.MaxNode.GetGuid().ToString() }; babylonMesh.isDummy = true; // Position / rotation / scaling / hierarchy exportNode(babylonMesh, meshNode, scene, babylonScene); babylonScene.MeshesList.Add(babylonMesh); return(babylonMesh); }
private string GetParentID(IIGameNode parentNode, BabylonScene babylonScene, IIGameScene scene) { var parentType = parentNode.IGameObject.IGameType; var parentId = parentNode.MaxNode.GetGuid().ToString(); switch (parentType) { case Autodesk.Max.IGameObject.ObjectTypes.Light: case Autodesk.Max.IGameObject.ObjectTypes.Mesh: case Autodesk.Max.IGameObject.ObjectTypes.Camera: break; default: if (babylonScene.MeshesList.All(m => m.id != parentId)) { ExportMesh(scene, parentNode, babylonScene); } break; } return(parentId); }
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 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 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 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 BabylonCamera ExportCamera(IIGameScene scene, IIGameNode cameraNode, BabylonScene babylonScene) { if (IsCameraExportable(cameraNode) == false) { return(null); } 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 = cameraNode.NodeParent.MaxNode.GetGuid().ToString(); } 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 = GetLocalTM(cameraNode, 0); var position = localTM.Translation; var rotation = localTM.Rotation; babylonCamera.position = new[] { position.X, position.Y, position.Z }; var rotationQuaternion = new BabylonQuaternion { X = rotation.X, Y = rotation.Y, Z = rotation.Z, W = -rotation.W }; if (ExportQuaternionsInsteadOfEulers) { babylonCamera.rotationQuaternion = rotationQuaternion.ToArray(); } else { babylonCamera.rotation = rotationQuaternion.toEulerAngles().ToArray(); } // Target var target = gameCamera.CameraTarget; if (target != null) { babylonCamera.lockedTargetId = target.MaxNode.GetGuid().ToString(); } else { // TODO - Check if should be local or world var vDir = Loader.Global.Point3.Create(0, -1, 0); vDir = localTM.ExtractMatrix3().VectorTransform(vDir).Normalize; vDir = vDir.Add(position); babylonCamera.target = new[] { vDir.X, vDir.Y, vDir.Z }; } // Animations var animations = new List <BabylonAnimation>(); GeneratePositionAnimation(cameraNode, animations); if (target == null) { // Export rotation animation //GenerateRotationAnimation(cameraNode, animations); ExportVector3Animation("target", animations, key => { var wmCam = GetLocalTM(cameraNode, key); var positionCam = wmCam.Translation; var vDir = Loader.Global.Point3.Create(0, -1, 0); vDir = wmCam.ExtractMatrix3().VectorTransform(vDir).Normalize; vDir = vDir.Add(positionCam); return(new[] { vDir.X, vDir.Y, vDir.Z }); }); } // Animation temporary stored for gltf but not exported for babylon // TODO - Will cause an issue when externalizing the glTF export process var extraAnimations = new List <BabylonAnimation>(); // Do not check if node rotation properties are animated GenerateRotationAnimation(cameraNode, extraAnimations, true); babylonCamera.extraAnimations = extraAnimations; 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); return(babylonCamera); }
private void exportNodeRec(IIGameNode maxGameNode, BabylonScene babylonScene, IIGameScene maxGameScene) { BabylonNode babylonNode = null; bool hasExporter = true; switch (maxGameNode.IGameObject.IGameType) { case Autodesk.Max.IGameObject.ObjectTypes.Mesh: babylonNode = ExportMesh(maxGameScene, maxGameNode, babylonScene); break; case Autodesk.Max.IGameObject.ObjectTypes.Camera: babylonNode = ExportCamera(maxGameScene, maxGameNode, babylonScene); break; case Autodesk.Max.IGameObject.ObjectTypes.Light: babylonNode = ExportLight(maxGameScene, maxGameNode, babylonScene); break; case Autodesk.Max.IGameObject.ObjectTypes.Unknown: // Create a dummy (empty mesh) when type is unknown // An example of unknown type object is the target of target light or camera babylonNode = ExportDummy(maxGameScene, maxGameNode, babylonScene); break; default: // The type of node is not exportable (helper, spline, xref...) hasExporter = false; break; } CheckCancelled(); // If node is not exported successfully but is significant if (babylonNode == null && isNodeRelevantToExport(maxGameNode)) { //if (!hasExporter) //{ // RaiseWarning($"Type '{maxGameNode.IGameObject.IGameType}' of node '{maxGameNode.Name}' has no exporter, an empty node is exported instead", 1); //} // Create a dummy (empty mesh) babylonNode = ExportDummy(maxGameScene, maxGameNode, babylonScene); } ; if (babylonNode != null) { // Export its children for (int i = 0; i < maxGameNode.ChildCount; i++) { var descendant = maxGameNode.GetNodeChild(i); exportNodeRec(descendant, babylonScene, maxGameScene); } } }
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 BabylonNode ExportLight(IIGameScene scene, IIGameNode lightNode, BabylonScene babylonScene) { if (IsLightExportable(lightNode) == false) { return(null); } var gameLight = lightNode.IGameObject.AsGameLight(); var initialized = gameLight.InitializeData; var babylonLight = new BabylonLight(); RaiseMessage(lightNode.Name, 1); babylonLight.name = lightNode.Name; // Export the custom attributes of this light babylonLight.metadata = ExportExtraAttributes(lightNode, babylonScene); // To preserve the position/rotation and the hierarchy, we create a dummy that will contains as direct children the light and the light children // The light will have no children. The dummy will contains the position and rotation animations. bool createDummy = lightNode.ChildCount > 0; BabylonNode dummy = null; if (createDummy) { dummy = ExportDummy(scene, lightNode, babylonScene); dummy.name = "_" + dummy.name + "_"; babylonLight.id = Guid.NewGuid().ToString(); babylonLight.parentId = dummy.id; babylonLight.hasDummy = true; } else { babylonLight.id = lightNode.MaxNode.GetGuid().ToString(); if (lightNode.NodeParent != null) { babylonLight.parentId = lightNode.NodeParent.MaxNode.GetGuid().ToString(); } } // 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, babylonLight); } else { RaiseWarning("Shadows maps are only supported for point, directional and spot lights", 2); } } // Position / rotation / scaling if (createDummy) { // The position is stored by the dummy parent and the default direction is downward and it is updated by the rotation of the parent dummy babylonLight.position = new[] { 0f, 0f, 0f }; babylonLight.direction = new[] { 0f, -1f, 0f }; } else { exportTransform(babylonLight, lightNode); // Position var localMatrix = lightNode.GetLocalTM(0); var position = localMatrix.Translation; // 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 = localMatrix.ExtractMatrix3().VectorTransform(vDir).Normalize; babylonLight.direction = new[] { vDir.X, vDir.Y, vDir.Z }; } } // The HemisphericLight simulates the ambient environment light, so the passed direction is the light reflection direction, not the incoming direction. // So we need the opposite direction if (babylonLight.type == 3) { var worldRotation = lightNode.GetWorldTM(0).Rotation; BabylonQuaternion quaternion = new BabylonQuaternion(worldRotation.X, worldRotation.Y, worldRotation.Z, worldRotation.W); babylonLight.direction = quaternion.Rotate(new BabylonVector3(0f, 1f, 0f)).ToArray(); } var maxScene = Loader.Core.RootNode; // Exclusion try { 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 MAX2017 || MAX2018 || MAX2019 || MAX2020 if (meshNode.CastShadows) #else if (meshNode.CastShadows == 1) #endif { 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(); } } catch (Exception e) { RaiseMessage("Light exclusion not supported", 2); } // 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); } if (exportParameters.exportAnimations) { // Animations var animations = new List <BabylonAnimation>(); if (createDummy) { // Position and rotation animations are stored by the parent (the dummy). The direction result from the parent rotation except for the HemisphericLight. if (babylonLight.type == 3) { BabylonVector3 direction = new BabylonVector3(0, 1, 0); ExportVector3Animation("direction", animations, key => { var worldRotation = lightNode.GetWorldTM(key).Rotation; BabylonQuaternion quaternion = new BabylonQuaternion(worldRotation.X, worldRotation.Y, worldRotation.Z, worldRotation.W); return(quaternion.Rotate(direction).ToArray()); }); } } else { GeneratePositionAnimation(lightNode, animations); ExportVector3Animation("direction", animations, key => { var localMatrixAnimDir = lightNode.GetLocalTM(key); var positionLight = localMatrixAnimDir.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 = localMatrixAnimDir.ExtractMatrix3().VectorTransform(vDir).Normalize; // The HemisphericLight (type == 3) simulates the ambient environment light, so the passed direction is the light reflection direction, not the incoming direction. // So we need the opposite direction return(babylonLight.type != 3 ? new[] { vDir.X, vDir.Y, vDir.Z } : new[] { -vDir.X, -vDir.Y, -vDir.Z }); } }); // Animation temporary stored for gltf but not exported for babylon // TODO - Will cause an issue when externalizing the glTF export process var extraAnimations = new List <BabylonAnimation>(); // Do not check if node rotation properties are animated GenerateRotationAnimation(lightNode, extraAnimations, true); babylonLight.extraAnimations = extraAnimations; } 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); return(createDummy ? dummy : babylonLight); }
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 var wm = cameraNode.GetLocalTM(0); if (cameraNode.NodeParent != null) { var parentWorld = cameraNode.NodeParent.GetObjectTM(0); wm.MultiplyBy(parentWorld.Inverse); } var position = wm.Translation; babylonCamera.position = new [] { position.X, position.Y, position.Z }; // Target var target = gameCamera.CameraTarget; if (target != null) { babylonCamera.lockedTargetId = target.MaxNode.GetGuid().ToString(); } else { var dir = wm.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 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); }
private BabylonNode exportNodeRec(IIGameNode maxGameNode, BabylonScene babylonScene, IIGameScene maxGameScene) { BabylonNode babylonNode = null; switch (maxGameNode.IGameObject.IGameType) { case Autodesk.Max.IGameObject.ObjectTypes.Mesh: babylonNode = ExportMesh(maxGameScene, maxGameNode, babylonScene); break; case Autodesk.Max.IGameObject.ObjectTypes.Camera: babylonNode = ExportCamera(maxGameScene, maxGameNode, babylonScene); break; case Autodesk.Max.IGameObject.ObjectTypes.Light: babylonNode = ExportLight(maxGameScene, maxGameNode, babylonScene); break; case Autodesk.Max.IGameObject.ObjectTypes.Unknown: // Create a dummy (empty mesh) when type is unknown // An example of unknown type object is the target of target light or camera babylonNode = ExportDummy(maxGameScene, maxGameNode, babylonScene); break; default: // The type of node is not exportable (helper, spline, xref...) break; } CheckCancelled(); // If node is not exported successfully but is significant if (babylonNode == null && isNodeRelevantToExport(maxGameNode)) { // Create a dummy (empty mesh) babylonNode = ExportDummy(maxGameScene, maxGameNode, babylonScene); } ; if (babylonNode != null) { string tag = maxGameNode.MaxNode.GetStringProperty("babylonjs_tag", ""); if (tag != "") { babylonNode.tags = tag; } // Export its children for (int i = 0; i < maxGameNode.ChildCount; i++) { var descendant = maxGameNode.GetNodeChild(i); exportNodeRec(descendant, babylonScene, maxGameScene); } } return(babylonNode); }
private BabylonCamera ExportCamera(IIGameScene scene, IIGameNode cameraNode, BabylonScene babylonScene) { if (IsCameraExportable(cameraNode) == false) { return(null); } 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 = cameraNode.NodeParent.MaxNode.GetGuid().ToString(); } // Export the custom attributes of this camera babylonCamera.metadata = ExportExtraAttributes(cameraNode, babylonScene); 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 exportTransform(babylonCamera, cameraNode); // Target var target = gameCamera.CameraTarget; if (target != null) { babylonCamera.lockedTargetId = target.MaxNode.GetGuid().ToString(); } // Animations var animations = new List <BabylonAnimation>(); GeneratePositionAnimation(cameraNode, animations); if (target == null) { // Export rotation animation GenerateRotationAnimation(cameraNode, animations); } else { // Animation temporary stored for gltf but not exported for babylon // TODO - Will cause an issue when externalizing the glTF export process var extraAnimations = new List <BabylonAnimation>(); // Do not check if node rotation properties are animated GenerateRotationAnimation(cameraNode, extraAnimations, true); babylonCamera.extraAnimations = extraAnimations; } 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); return(babylonCamera); }
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 List <BabylonMesh> babylonMasterMeshes = new List <BabylonMesh>(); var index = 0; while (index < tabs.Count) { #if MAX2017 || MAX2018 || MAX2019 var tab = tabs[index]; #else var tab = tabs[new IntPtr(index)]; #endif babylonMasterMeshes.AddRange(babylonScene.MeshesList.FindAll(_babylonMesh => { // Same id return(_babylonMesh.id == tab.GetGuid().ToString() && // Mesh is not a dummy _babylonMesh.isDummy == false); })); index++; } if (babylonMasterMeshes.Count > 0) { // Mesh already exported // Export this node as instance // Check if we need to export this instance as an instance mesh. // If there is already an exported mesh in the scene that shares this mesh's material, then export it as an instance. BabylonMesh babylonMasterMesh = null; foreach (var mesh in babylonMasterMeshes) { if (meshNode.NodeMaterial.MaxMaterial.GetGuid().ToString().Equals(mesh.materialId)) { babylonMasterMesh = mesh; } } if (babylonMasterMesh != null) { return(ExportInstanceMesh(scene, meshNode, babylonScene, babylonMasterMesh)); } return(ExportMasterMesh(scene, meshNode, babylonScene)); } } return(ExportMasterMesh(scene, meshNode, babylonScene)); }
private BabylonNode ExportMasterMesh(IIGameScene scene, IIGameNode meshNode, BabylonScene babylonScene) { 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 || MAX2019 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 && GetRelevantNodes(skin).Count > 0) // if the mesh has a skin with at least one bone { var skinAlreadyStored = skins.Find(_skin => IsSkinEqualTo(_skin, skin)); if (skinAlreadyStored == null) { skins.Add(skin); babylonMesh.skeletonId = skins.IndexOf(skin); } else { babylonMesh.skeletonId = skins.IndexOf(skinAlreadyStored); } 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; // The DirectXShader material is a passthrough to its render material. // The shell material is a passthrough to its baked material. while (mtl != null && (isShellMaterial(mtl) || isDirectXShaderMaterial(mtl))) { if (isShellMaterial(mtl)) { // Retrieve the baked material from the shell material. mtl = GetBakedMaterialFromShellMaterial(mtl); } else // isDirectXShaderMaterial(mtl) { // Retrieve the render material from the directX shader mtl = GetRenderMaterialFromDirectXShader(mtl); } } 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 || MAX2019 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); var offsetTM = GetOffsetTM(meshNode, 0); // Compute normals var subMeshes = new List <BabylonSubMesh>(); List <int> faceIndexes = null; ExtractGeometry(babylonMesh, vertices, indices, subMeshes, boneIds, skin, unskinnedMesh, invertedWorldMatrix, offsetTM, 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); } } 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(); // Export tangents if option is checked and mesh has tangents if (exportParameters.exportTangents) { babylonMesh.tangents = vertices.SelectMany(v => v.Tangent).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); var rawScene = Loader.Core.RootNode; // 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(); if (rawScene.GetBoolProperty("babylonjs_export_Morph_Normals", 1)) { babylonMorphTarget.normals = targetVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToArray(); } // Tangent if (exportParameters.exportTangents && rawScene.GetBoolProperty("babylonjs_export_Morph_Tangents")) { 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); }