private void exportNodeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null) { GLTFNode gltfNode = null; var type = babylonNode.GetType(); if (type == typeof(BabylonAbstractMesh) || type.IsSubclassOf(typeof(BabylonAbstractMesh))) { gltfNode = ExportAbstractMesh(babylonNode as BabylonAbstractMesh, gltf, gltfParentNode, babylonScene); } else if (type == typeof(BabylonCamera)) { GLTFCamera gltfCamera = ExportCamera(babylonNode as BabylonCamera, gltf, gltfParentNode); gltfNode = gltfCamera.gltfNode; } else if (type == typeof(BabylonLight)) { if (isNodeRelevantToExport(babylonNode)) { // Export light nodes as empty nodes (no lights in glTF 2.0 core) RaiseWarning($"GLTFExporter | Light named {babylonNode.name} has children but lights are not exported with glTF 2.0 core version. An empty node is used instead.", 1); gltfNode = ExportLight(babylonNode as BabylonLight, gltf, gltfParentNode); } else { RaiseMessage($"GLTFExporter | Light named {babylonNode.name} is not relevant to export", 1); } } else { RaiseError($"Node named {babylonNode.name} as no exporter", 1); } CheckCancelled(); // If node is exported successfully... if (gltfNode != null) { // ...export its children List <BabylonNode> babylonDescendants = getDescendants(babylonNode); babylonDescendants.ForEach(descendant => exportNodeRec(descendant, gltf, babylonScene, gltfNode)); } }
private GLTFCamera ExportCamera(BabylonCamera babylonCamera, GLTF gltf, GLTFNode gltfParentNode) { RaiseMessage("GLTFExporter.Camera | Export camera named: " + babylonCamera.name, 1); // -------------------------- // ---------- Node ---------- // -------------------------- RaiseMessage("GLTFExporter.Camera | Node", 2); // Node var gltfNode = new GLTFNode(); gltfNode.name = babylonCamera.name; gltfNode.index = gltf.NodesList.Count; gltf.NodesList.Add(gltfNode); // Hierarchy if (gltfParentNode != null) { RaiseMessage("GLTFExporter.Camera | Add " + babylonCamera.name + " as child to " + gltfParentNode.name, 3); gltfParentNode.ChildrenList.Add(gltfNode.index); gltfNode.parent = gltfParentNode; } else { // It's a root node // Only root nodes are listed in a gltf scene RaiseMessage("GLTFExporter.Camera | Add " + babylonCamera.name + " as root node to scene", 3); gltf.scenes[0].NodesList.Add(gltfNode.index); } // Transform gltfNode.translation = babylonCamera.position; if (babylonCamera.rotationQuaternion != null) { gltfNode.rotation = babylonCamera.rotationQuaternion; } else { // Convert rotation vector to quaternion BabylonVector3 rotationVector3 = new BabylonVector3 { X = babylonCamera.rotation[0], Y = babylonCamera.rotation[1], Z = babylonCamera.rotation[2] }; gltfNode.rotation = rotationVector3.toQuaternion().ToArray(); } // No scaling defined for babylon camera. Use identity instead. gltfNode.scale = new float[3] { 1, 1, 1 }; // Switch coordinate system at object level gltfNode.translation[2] *= -1; gltfNode.rotation[0] *= -1; gltfNode.rotation[1] *= -1; // Animations ExportNodeAnimation(babylonCamera, gltf, gltfNode); // --- prints --- #region prints RaiseVerbose("GLTFExporter.Camera | babylonCamera data", 2); RaiseVerbose("GLTFExporter.Camera | babylonCamera.type=" + babylonCamera.type, 3); RaiseVerbose("GLTFExporter.Camera | babylonCamera.fov=" + babylonCamera.fov, 3); RaiseVerbose("GLTFExporter.Camera | babylonCamera.maxZ=" + babylonCamera.maxZ, 3); RaiseVerbose("GLTFExporter.Camera | babylonCamera.minZ=" + babylonCamera.minZ, 3); #endregion // -------------------------- // ------- gltfCamera ------- // -------------------------- RaiseMessage("GLTFExporter.Camera | create gltfCamera", 2); // Camera var gltfCamera = new GLTFCamera { name = babylonCamera.name }; gltfCamera.index = gltf.CamerasList.Count; gltf.CamerasList.Add(gltfCamera); gltfNode.camera = gltfCamera.index; gltfCamera.gltfNode = gltfNode; // Camera type switch (babylonCamera.mode) { case (BabylonCamera.CameraMode.ORTHOGRAPHIC_CAMERA): var gltfCameraOrthographic = new GLTFCameraOrthographic(); gltfCameraOrthographic.xmag = 1; // Do not bother about it - still mandatory gltfCameraOrthographic.ymag = 1; // Do not bother about it - still mandatory gltfCameraOrthographic.zfar = babylonCamera.maxZ; gltfCameraOrthographic.znear = babylonCamera.minZ; gltfCamera.type = GLTFCamera.CameraType.orthographic.ToString(); gltfCamera.orthographic = gltfCameraOrthographic; break; case (BabylonCamera.CameraMode.PERSPECTIVE_CAMERA): var gltfCameraPerspective = new GLTFCameraPerspective(); gltfCameraPerspective.aspectRatio = null; // Do not bother about it - use default glTF value gltfCameraPerspective.yfov = babylonCamera.fov; // Babylon camera fov mode is assumed to be vertical (FOVMODE_VERTICAL_FIXED) gltfCameraPerspective.zfar = babylonCamera.maxZ; gltfCameraPerspective.znear = babylonCamera.minZ; gltfCamera.type = GLTFCamera.CameraType.perspective.ToString(); gltfCamera.perspective = gltfCameraPerspective; break; default: RaiseError("GLTFExporter.Camera | camera mode not found"); break; } return(gltfCamera); }
private bool ExportMorphTargetWeightAnimation(BabylonMorphTargetManager babylonMorphTargetManager, GLTF gltf, GLTFNode gltfNode, List <GLTFChannel> channelList, List <GLTFAnimationSampler> samplerList, int startFrame, int endFrame) { if (!_isBabylonMorphTargetManagerAnimationValid(babylonMorphTargetManager)) { return(false); } RaiseMessage("GLTFExporter.Animation | Export animation of morph target manager with id: " + babylonMorphTargetManager.id, 2); // Target var gltfTarget = new GLTFChannelTarget { node = gltfNode.index }; gltfTarget.path = "weights"; // Buffer var buffer = GLTFBufferService.Instance.GetBuffer(gltf); // --- Input --- var accessorInput = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewAnimationFloatScalar(gltf, buffer), "accessorAnimationInput", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.SCALAR ); // Populate accessor accessorInput.min = new float[] { float.MaxValue }; accessorInput.max = new float[] { float.MinValue }; var influencesPerFrame = _getTargetManagerAnimationsData(babylonMorphTargetManager); var frames = new List <int>(influencesPerFrame.Keys); frames.Sort(); // Mandatory otherwise gltf loader of babylon doesn't understand int numKeys = 0; foreach (var frame in frames) { if (frame < startFrame) { continue; } if (frame > endFrame) { continue; } numKeys++; var inputValue = frame / (float)Loader.Global.FrameRate; // Store values as bytes accessorInput.bytesList.AddRange(BitConverter.GetBytes(inputValue)); // Update min and max values GLTFBufferService.UpdateMinMaxAccessor(accessorInput, inputValue); } accessorInput.count = numKeys; // bail out if we have no keys to export (?) // todo [KeyInterpolation]: bail out only when there are no keyframes at all (?) and otherwise add the appropriate (interpolated) keyframes if (numKeys == 0) { return(false); } // --- Output --- GLTFAccessor accessorOutput = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewAnimationFloatScalar(gltf, buffer), "accessorAnimationWeights", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.SCALAR ); // Populate accessor foreach (var frame in frames) { if (frame < startFrame) { continue; } if (frame > endFrame) { continue; } var outputValues = influencesPerFrame[frame]; // Store values as bytes foreach (var outputValue in outputValues) { accessorOutput.count++; accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } } // Animation sampler var gltfAnimationSampler = new GLTFAnimationSampler { input = accessorInput.index, output = accessorOutput.index }; gltfAnimationSampler.index = samplerList.Count; samplerList.Add(gltfAnimationSampler); // Channel var gltfChannel = new GLTFChannel { sampler = gltfAnimationSampler.index, target = gltfTarget }; channelList.Add(gltfChannel); return(true); }
private void exportNodeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null) { GLTFNode gltfNode = null; if (babylonNode.GetType() == typeof(BabylonMesh)) { GLTFMesh gltfMesh = ExportMesh(babylonNode as BabylonMesh, gltf, gltfParentNode); if (gltfMesh != null) { gltfNode = gltfMesh.gltfNode; } } else if (babylonNode.GetType() == typeof(BabylonCamera)) { // TODO - Export camera nodes RaiseError($"TODO - Export camera node named {babylonNode.name}", 1); } else if (babylonNode.GetType() == typeof(BabylonLight)) { // TODO - Export light nodes as empty nodes (no lights in glTF 2.0 core) RaiseError($"TODO - Export light node named {babylonNode.name}", 1); RaiseWarning($"GLTFExporter.Node | Light named {babylonNode.name} has children but lights are not exported with glTF 2.0 core version. An empty node is used instead.", 1); } else { RaiseError($"Node named {babylonNode.name} as no exporter", 1); } CheckCancelled(); // If parent is exported successfully... if (gltfNode != null) { // ...export its children List <BabylonNode> babylonDescendants = getDescendants(babylonNode, babylonScene); babylonDescendants.ForEach(descendant => exportNodeRec(descendant, gltf, babylonScene, gltfNode)); } }
private void ExportNodeAnimation(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, BabylonNode babylonNode, GLTFNode gltfNode, BabylonScene babylonScene) { var channelList = gltfAnimation.ChannelList; var samplerList = gltfAnimation.SamplerList; bool exportNonAnimated = Loader.Core.RootNode.GetBoolProperty("babylonjs_animgroup_exportnonanimated"); // Combine babylon animations from .babylon file and cached ones var babylonAnimations = new List <BabylonAnimation>(); if (babylonNode.animations != null) { babylonAnimations.AddRange(babylonNode.animations); } if (babylonNode.extraAnimations != null) { babylonAnimations.AddRange(babylonNode.extraAnimations); } // Filter animations to only keep TRS ones babylonAnimations = babylonAnimations.FindAll(babylonAnimation => _getTargetPath(babylonAnimation.property) != null); // Optimize animations to only keep ones animated between start and end frames var optimizeAnimations = !Loader.Core.RootNode.GetBoolProperty("babylonjs_donotoptimizeanimations"); // reverse negation for clarity if (optimizeAnimations) { List <BabylonAnimation> babylonAnimationsOptimized = new List <BabylonAnimation>(); foreach (BabylonAnimation babylonAnimation in babylonAnimations) { // Filter animation keys to only keep frames between start and end List <BabylonAnimationKey> keysInRangeFull = babylonAnimation.keysFull.FindAll(babylonAnimationKey => babylonAnimationKey.frame >= startFrame && babylonAnimationKey.frame <= endFrame); // Optimization process always keeps first and last frames OptimizeAnimations(keysInRangeFull, true); if (IsAnimationKeysRelevant(keysInRangeFull)) { // Override animation keys babylonAnimation.keys = keysInRangeFull.ToArray(); babylonAnimationsOptimized.Add(babylonAnimation); } } // From now, use optimized animations instead babylonAnimations = babylonAnimationsOptimized; } if (babylonAnimations.Count > 0 || exportNonAnimated) { if (babylonAnimations.Count > 0) { RaiseMessage("GLTFExporter.Animation | Export animations of node named: " + babylonNode.name, 2); } else if (exportNonAnimated) { RaiseMessage("GLTFExporter.Animation | Export dummy animation for node named: " + babylonNode.name, 2); // Export a dummy animation babylonAnimations.Add(GetDummyAnimation(gltfNode, startFrame, endFrame)); } foreach (BabylonAnimation babylonAnimation in babylonAnimations) { // Target var gltfTarget = new GLTFChannelTarget { node = gltfNode.index }; gltfTarget.path = _getTargetPath(babylonAnimation.property); // --- Input --- var accessorInput = _createAndPopulateInput(gltf, babylonAnimation, startFrame, endFrame); if (accessorInput == null) { continue; } // --- Output --- GLTFAccessor accessorOutput = _createAccessorOfPath(gltfTarget.path, gltf); // Populate accessor int numKeys = 0; foreach (var babylonAnimationKey in babylonAnimation.keys) { if (babylonAnimationKey.frame < startFrame) { continue; } if (babylonAnimationKey.frame > endFrame) { continue; } numKeys++; // copy data before changing it in case animation groups overlap float[] outputValues = new float[babylonAnimationKey.values.Length]; babylonAnimationKey.values.CopyTo(outputValues, 0); // Switch coordinate system at object level if (babylonAnimation.property == "position") { outputValues[2] *= -1; } else if (babylonAnimation.property == "rotationQuaternion") { outputValues[0] *= -1; outputValues[1] *= -1; } // Store values as bytes foreach (var outputValue in outputValues) { accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } } ; accessorOutput.count = numKeys; // bail out if no keyframes to export (?) // todo [KeyInterpolation]: bail out only when there are no keyframes at all (?) and otherwise add the appropriate (interpolated) keyframes if (numKeys == 0) { continue; } // Animation sampler var gltfAnimationSampler = new GLTFAnimationSampler { input = accessorInput.index, output = accessorOutput.index }; gltfAnimationSampler.index = samplerList.Count; samplerList.Add(gltfAnimationSampler); // Channel var gltfChannel = new GLTFChannel { sampler = gltfAnimationSampler.index, target = gltfTarget }; channelList.Add(gltfChannel); } } if (babylonNode.GetType() == typeof(BabylonMesh)) { var babylonMesh = babylonNode as BabylonMesh; // Morph targets var babylonMorphTargetManager = GetBabylonMorphTargetManager(babylonScene, babylonMesh); if (babylonMorphTargetManager != null) { ExportMorphTargetWeightAnimation(babylonMorphTargetManager, gltf, gltfNode, channelList, samplerList, startFrame, endFrame); } } }
private void ExportBoneAnimation(GLTFAnimation gltfAnimation, int startFrame, int endFrame, GLTF gltf, BabylonBone babylonBone, GLTFNode gltfNode) { var channelList = gltfAnimation.ChannelList; var samplerList = gltfAnimation.SamplerList; if (babylonBone.animation != null && babylonBone.animation.property == "_matrix") { RaiseMessage("GLTFExporter.Animation | Export animation of bone named: " + babylonBone.name, 2); var babylonAnimation = babylonBone.animation; // Optimize animation var optimizeAnimations = !Loader.Core.RootNode.GetBoolProperty("babylonjs_donotoptimizeanimations"); // reverse negation for clarity if (optimizeAnimations) { // Filter animation keys to only keep frames between start and end List <BabylonAnimationKey> keysInRangeFull = babylonAnimation.keysFull.FindAll(babylonAnimationKey => babylonAnimationKey.frame >= startFrame && babylonAnimationKey.frame <= endFrame); // Optimization process always keeps first and last frames OptimizeAnimations(keysInRangeFull, false); if (IsAnimationKeysRelevant(keysInRangeFull)) { // From now, use optimized animation instead // Override animation keys babylonAnimation.keys = keysInRangeFull.ToArray(); } } // --- Input --- var accessorInput = _createAndPopulateInput(gltf, babylonAnimation, startFrame, endFrame); if (accessorInput == null) { return; } // --- Output --- var paths = new string[] { "translation", "rotation", "scale" }; var accessorOutputByPath = new Dictionary <string, GLTFAccessor>(); foreach (string path in paths) { GLTFAccessor accessorOutput = _createAccessorOfPath(path, gltf); accessorOutputByPath.Add(path, accessorOutput); } // Populate accessors foreach (var babylonAnimationKey in babylonAnimation.keys) { if (babylonAnimationKey.frame < startFrame) { continue; } if (babylonAnimationKey.frame > endFrame) { continue; } var matrix = new BabylonMatrix(); matrix.m = babylonAnimationKey.values; var translationBabylon = new BabylonVector3(); var rotationQuatBabylon = new BabylonQuaternion(); var scaleBabylon = new BabylonVector3(); matrix.decompose(scaleBabylon, rotationQuatBabylon, translationBabylon); translationBabylon.Z *= -1; rotationQuatBabylon.X *= -1; rotationQuatBabylon.Y *= -1; var outputValuesByPath = new Dictionary <string, float[]>(); outputValuesByPath.Add("translation", translationBabylon.ToArray()); outputValuesByPath.Add("rotation", rotationQuatBabylon.ToArray()); outputValuesByPath.Add("scale", scaleBabylon.ToArray()); // Store values as bytes foreach (string path in paths) { var accessorOutput = accessorOutputByPath[path]; var outputValues = outputValuesByPath[path]; foreach (var outputValue in outputValues) { accessorOutput.bytesList.AddRange(BitConverter.GetBytes(outputValue)); } accessorOutput.count++; } } ; foreach (string path in paths) { var accessorOutput = accessorOutputByPath[path]; // Animation sampler var gltfAnimationSampler = new GLTFAnimationSampler { input = accessorInput.index, output = accessorOutput.index }; gltfAnimationSampler.index = samplerList.Count; samplerList.Add(gltfAnimationSampler); // Target var gltfTarget = new GLTFChannelTarget { node = gltfNode.index }; gltfTarget.path = path; // Channel var gltfChannel = new GLTFChannel { sampler = gltfAnimationSampler.index, target = gltfTarget }; channelList.Add(gltfChannel); } } }
/// <summary> /// Create a gltf node from the babylon node. /// </summary> /// <param name="babylonNode"></param> /// <param name="gltf"></param> /// <param name="babylonScene"></param> /// <param name="gltfParentNode">The parent of the glTF node that will be created.</param> /// <returns>The gltf node created.</returns> private GLTFNode ExportNode(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode) { logger.RaiseMessage($"GLTFExporter | ExportNode {babylonNode.name}", 1); GLTFNode gltfNode = null; var type = babylonNode.GetType(); var nodeNodePair = nodeToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonNode.id)); if (nodeNodePair.Key != null) { return(nodeNodePair.Value); } // Node gltfNode = new GLTFNode { name = GetUniqueNodeName(babylonNode.name), index = gltf.NodesList.Count }; // User Custom Attributes if (babylonNode.metadata != null && babylonNode.metadata.Count != 0) { gltfNode.extras = babylonNode.metadata; } gltf.NodesList.Add(gltfNode); // add the node to the gltf list nodeToGltfNodeMap.Add(babylonNode, gltfNode); // add the node to the global map // Hierarchy if (gltfParentNode != null) { logger.RaiseMessage("GLTFExporter.Node| Add " + babylonNode.name + " as child to " + gltfParentNode.name, 2); gltfParentNode.ChildrenList.Add(gltfNode.index); gltfNode.parent = gltfParentNode; } else { // It's a root node // Only root nodes are listed in a gltf scene logger.RaiseMessage("GLTFExporter.Node | Add " + babylonNode.name + " as root node to scene", 2); gltf.scenes[0].NodesList.Add(gltfNode.index); } // TRS if (exportParameters.exportAnimationsOnly == false) { // Position gltfNode.translation = babylonNode.position; // Rotation if (babylonNode.rotationQuaternion != null) { gltfNode.rotation = babylonNode.rotationQuaternion; } else { // Convert rotation vector to quaternion BabylonVector3 rotationVector3 = new BabylonVector3 { X = babylonNode.rotation[0], Y = babylonNode.rotation[1], Z = babylonNode.rotation[2] }; gltfNode.rotation = rotationVector3.toQuaternion().ToArray(); } // Scale gltfNode.scale = babylonNode.scaling; // Switch coordinate system at object level gltfNode.translation[2] *= -1; gltfNode.translation[0] *= exportParameters.scaleFactor; gltfNode.translation[1] *= exportParameters.scaleFactor; gltfNode.translation[2] *= exportParameters.scaleFactor; gltfNode.rotation[0] *= -1; gltfNode.rotation[1] *= -1; } ExportGLTFExtension(babylonNode, ref gltfNode, gltf); return(gltfNode); }
private GLTFNode ExportAbstractMesh(ref GLTFNode gltfNode, BabylonAbstractMesh babylonAbstractMesh, GLTF gltf, GLTFNode gltfParentNode, BabylonScene babylonScene) { RaiseMessage("GLTFExporter.AbstractMesh | Export abstract mesh named: " + babylonAbstractMesh.name, 2); // Mesh var gltfMesh = gltf.MeshesList.Find(_gltfMesh => _gltfMesh.idGroupInstance == babylonAbstractMesh.idGroupInstance); if (gltfMesh != null) { gltfNode.mesh = gltfMesh.index; // Skin if (gltfMesh.idBabylonSkeleton.HasValue) { var babylonSkeleton = babylonScene.skeletons[gltfMesh.idBabylonSkeleton.Value]; // Export a new skeleton if necessary and a new skin var gltfSkin = ExportSkin(babylonSkeleton, gltf, gltfNode); gltfNode.skin = gltfSkin.index; } } return(gltfNode); }
private void exportNodeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null) { GLTFNode gltfNode = ExportNode(babylonNode, gltf, babylonScene, gltfParentNode); if (gltfNode != null) { logger.CheckCancelled(); // export its tag if (!string.IsNullOrEmpty(babylonNode.tags)) { if (gltfNode.extras == null) { gltfNode.extras = new Dictionary <string, object>(); } gltfNode.extras["tags"] = babylonNode.tags; } // ...export its children List <BabylonNode> babylonDescendants = getDescendants(babylonNode); babylonDescendants.ForEach(descendant => exportNodeRec(descendant, gltf, babylonScene, gltfNode)); } }
private void exportNodeTypeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null) { var type = babylonNode.GetType(); logger.RaiseMessage($"GLTFExporter | ExportNode {babylonNode.name} of Type {type.ToString()}", 1); var nodeNodePair = nodeToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonNode.id)); GLTFNode gltfNode = nodeNodePair.Value; if (gltfNode != null) { if (type == typeof(BabylonAbstractMesh) || type.IsSubclassOf(typeof(BabylonAbstractMesh))) { gltfNode = ExportAbstractMesh(ref gltfNode, babylonNode as BabylonAbstractMesh, gltf, gltfParentNode, babylonScene); } else if (type == typeof(BabylonCamera)) { GLTFCamera gltfCamera = ExportCamera(ref gltfNode, babylonNode as BabylonCamera, gltf, gltfParentNode); } else if (type == typeof(BabylonLight) || type.IsSubclassOf(typeof(BabylonLight))) { if (((BabylonLight)babylonNode).type != 3) { ExportLight(ref gltfNode, babylonNode as BabylonLight, gltf, gltfParentNode, babylonScene); } } else if (type == typeof(BabylonNode)) { logger.RaiseVerbose($"Node named {babylonNode.name} already exported as gltfNode", 1); } else { logger.RaiseError($"Node named {babylonNode.name} has no exporter", 1); } logger.CheckCancelled(); // ...export its children List <BabylonNode> babylonDescendants = getDescendants(babylonNode); babylonDescendants.ForEach(descendant => exportNodeTypeRec(descendant, gltf, babylonScene, gltfNode)); } }
private GLTFNode ExportLight(ref GLTFNode gltfNode, BabylonLight babylonLight, GLTF gltf, GLTFNode gltfParentNode, BabylonScene babylonScene) { // Custom user properties if (babylonLight.metadata != null && babylonLight.metadata.Count != 0) { gltfNode.extras = babylonLight.metadata; } if (exportParameters.enableKHRLightsPunctual) { if (babylonLight.type == 3) // ambient light { logger?.RaiseMessage($"GLTFExporter.Light | Ambient light {babylonLight.name} is not supported in KHR_lights_punctual."); } else { logger?.RaiseMessage("GLTFExporter.Light | Export light named: " + babylonLight.name, 2); // new light in the node extensions GLTFLight light = new GLTFLight { light = AddLightExtension(ref gltf, babylonLight) }; if (gltfNode.extensions == null) { gltfNode.extensions = new GLTFExtensions(); } gltfNode.extensions[KHR_lights_punctuals] = light; } } ExportGLTFExtension(babylonLight, ref gltfNode, gltf); return(gltfNode); }
/// <summary> /// Create a gltf node from the babylon node. /// </summary> /// <param name="babylonNode"></param> /// <param name="gltf"></param> /// <param name="babylonScene"></param> /// <param name="gltfParentNode">The parent of the glTF node that will be created.</param> /// <returns>The gltf node created.</returns> private GLTFNode ExportNode(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode) { RaiseMessage($"GLTFExporter | ExportNode {babylonNode.name}", 1); GLTFNode gltfNode = null; var type = babylonNode.GetType(); var nodeNodePair = nodeToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonNode.id)); if (nodeNodePair.Key != null) { return(nodeNodePair.Value); } var boneNodePair = boneToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonNode.id)); if (boneNodePair.Key != null) { return(boneNodePair.Value); } // Node gltfNode = new GLTFNode { name = GetUniqueNodeName(babylonNode.name), index = gltf.NodesList.Count }; gltf.NodesList.Add(gltfNode); // add the node to the gltf list nodeToGltfNodeMap.Add(babylonNode, gltfNode); // add the node to the global map // Hierarchy if (gltfParentNode != null) { RaiseMessage("GLTFExporter.Node| Add " + babylonNode.name + " as child to " + gltfParentNode.name, 2); gltfParentNode.ChildrenList.Add(gltfNode.index); gltfNode.parent = gltfParentNode; } else { // It's a root node // Only root nodes are listed in a gltf scene RaiseMessage("GLTFExporter.Node | Add " + babylonNode.name + " as root node to scene", 2); gltf.scenes[0].NodesList.Add(gltfNode.index); } // Transform // Position gltfNode.translation = babylonNode.position; // Rotation if (type == typeof(BabylonAbstractMesh) || type.IsSubclassOf(typeof(BabylonAbstractMesh)) || type == typeof(BabylonCamera)) { if (babylonNode.rotationQuaternion != null) { gltfNode.rotation = babylonNode.rotationQuaternion; } else { // Convert rotation vector to quaternion BabylonVector3 rotationVector3 = new BabylonVector3 { X = babylonNode.rotation[0], Y = babylonNode.rotation[1], Z = babylonNode.rotation[2] }; gltfNode.rotation = rotationVector3.toQuaternion().ToArray(); } } else // Light { gltfNode.rotation = new float[4] { 0, 0, 0, 1 }; } // Scale if (type == typeof(BabylonAbstractMesh) || type.IsSubclassOf(typeof(BabylonAbstractMesh))) { gltfNode.scale = babylonNode.scaling; } else // Camera and light { gltfNode.scale = new float[3] { 1, 1, 1 }; } // Switch coordinate system at object level gltfNode.translation[2] *= -1; gltfNode.rotation[0] *= -1; gltfNode.rotation[1] *= -1; return(gltfNode); }
private void exportNodeRec(BabylonNode babylonNode, GLTF gltf, BabylonScene babylonScene, GLTFNode gltfParentNode = null) { var type = babylonNode.GetType(); // Ambiant light are attached to the scene if (type == typeof(BabylonLight) && ((BabylonLight)babylonNode).type == 3) { RaiseMessage($"GLTFExporter.Light | Export light named: {babylonNode.name}", 1); // new light in the scene extensions GLTFLight light = new GLTFLight { light = AddLightExtension(ref gltf, babylonNode as BabylonLight) }; int sceneIndex = (int)gltf.scene; if (gltf.scenes[sceneIndex].extensions.ContainsKey(KHR_lights)) { RaiseWarning($"Only 1 ambient light can be referenced per scene. {babylonNode.name} has overwritten the previous one.", 2); } gltf.scenes[sceneIndex].extensions[KHR_lights] = light; return; } GLTFNode gltfNode = ExportNode(babylonNode, gltf, babylonScene, gltfParentNode); if (gltfNode != null) { if (type == typeof(BabylonAbstractMesh) || type.IsSubclassOf(typeof(BabylonAbstractMesh))) { gltfNode = ExportAbstractMesh(ref gltfNode, babylonNode as BabylonAbstractMesh, gltf, gltfParentNode, babylonScene); } else if (type == typeof(BabylonCamera)) { GLTFCamera gltfCamera = ExportCamera(ref gltfNode, babylonNode as BabylonCamera, gltf, gltfParentNode); } else if (type == typeof(BabylonLight) || type.IsSubclassOf(typeof(BabylonLight))) { ExportLight(ref gltfNode, babylonNode as BabylonLight, gltf, gltfParentNode, babylonScene); } else { RaiseError($"Node named {babylonNode.name} as no exporter", 1); } CheckCancelled(); // export its tag if (babylonNode.tag != null && babylonNode.tag != "") { if (gltfNode.extras == null) { gltfNode.extras = new Dictionary <string, object>(); } gltfNode.extras["tag"] = babylonNode.tag; } // ...export its children List <BabylonNode> babylonDescendants = getDescendants(babylonNode); babylonDescendants.ForEach(descendant => exportNodeRec(descendant, gltf, babylonScene, gltfNode)); } }
private GLTFNode ExportAbstractMesh(BabylonAbstractMesh babylonAbstractMesh, GLTF gltf, GLTFNode gltfParentNode, BabylonScene babylonScene) { RaiseMessage("GLTFExporter.AbstractMesh | Export abstract mesh named: " + babylonAbstractMesh.name, 1); // Node var gltfNode = new GLTFNode(); gltfNode.name = babylonAbstractMesh.name; gltfNode.index = gltf.NodesList.Count; gltf.NodesList.Add(gltfNode); // Hierarchy if (gltfParentNode != null) { RaiseMessage("GLTFExporter.AbstractMesh | Add " + babylonAbstractMesh.name + " as child to " + gltfParentNode.name, 2); gltfParentNode.ChildrenList.Add(gltfNode.index); gltfNode.parent = gltfParentNode; } else { // It's a root node // Only root nodes are listed in a gltf scene RaiseMessage("GLTFExporter.AbstractMesh | Add " + babylonAbstractMesh.name + " as root node to scene", 2); gltf.scenes[0].NodesList.Add(gltfNode.index); } // Transform gltfNode.translation = babylonAbstractMesh.position; if (babylonAbstractMesh.rotationQuaternion != null) { gltfNode.rotation = babylonAbstractMesh.rotationQuaternion; } else { // Convert rotation vector to quaternion BabylonVector3 rotationVector3 = new BabylonVector3 { X = babylonAbstractMesh.rotation[0], Y = babylonAbstractMesh.rotation[1], Z = babylonAbstractMesh.rotation[2] }; gltfNode.rotation = rotationVector3.toQuaternion().ToArray(); } gltfNode.scale = babylonAbstractMesh.scaling; // Mesh var gltfMesh = gltf.MeshesList.Find(_gltfMesh => _gltfMesh.idGroupInstance == babylonAbstractMesh.idGroupInstance); if (gltfMesh != null) { gltfNode.mesh = gltfMesh.index; // Skin if (gltfMesh.idBabylonSkeleton.HasValue) { var babylonSkeleton = babylonScene.skeletons[gltfMesh.idBabylonSkeleton.Value]; // Export a new skeleton if necessary and a new skin var gltfSkin = ExportSkin(babylonSkeleton, gltf, gltfNode); gltfNode.skin = gltfSkin.index; } } // Animations ExportNodeAnimation(babylonAbstractMesh, gltf, gltfNode, babylonScene); return(gltfNode); }
// TODO - Test if ok with a gltf viewer working with custom camera (babylon loader/sandbox doesn't load them) private GLTFCamera ExportCamera(BabylonCamera babylonCamera, GLTF gltf, GLTFNode gltfParentNode) { RaiseMessage("GLTFExporter.Camera | Export camera named: " + babylonCamera.name, 1); // -------------------------- // ---------- Node ---------- // -------------------------- RaiseMessage("GLTFExporter.Camera | Node", 2); // Node var gltfNode = new GLTFNode(); gltfNode.name = babylonCamera.name; gltfNode.index = gltf.NodesList.Count; gltf.NodesList.Add(gltfNode); // Hierarchy if (gltfParentNode != null) { RaiseMessage("GLTFExporter.Camera | Add " + babylonCamera.name + " as child to " + gltfParentNode.name, 3); gltfParentNode.ChildrenList.Add(gltfNode.index); } else { // It's a root node // Only root nodes are listed in a gltf scene RaiseMessage("GLTFExporter.Camera | Add " + babylonCamera.name + " as root node to scene", 3); gltf.scenes[0].NodesList.Add(gltfNode.index); } // Transform gltfNode.translation = babylonCamera.position; // Switch from left to right handed coordinate system //gltfNode.translation[0] *= -1; if (babylonCamera.rotationQuaternion != null) { gltfNode.rotation = babylonCamera.rotationQuaternion; } else { // Convert rotation vector to quaternion BabylonVector3 rotationVector3 = new BabylonVector3 { X = babylonCamera.rotation[0], Y = babylonCamera.rotation[1], Z = babylonCamera.rotation[2] }; gltfNode.rotation = rotationVector3.toQuaternion().ToArray(); } // No scaling defined for babylon camera. Use identity instead. gltfNode.scale = new float[3] { 1, 1, 1 }; // --- prints --- RaiseMessage("GLTFExporter.Camera | babylonCamera data", 2); RaiseMessage("GLTFExporter.Camera | babylonCamera.type=" + babylonCamera.type, 3); RaiseMessage("GLTFExporter.Camera | babylonCamera.fov=" + babylonCamera.fov, 3); RaiseMessage("GLTFExporter.Camera | babylonCamera.maxZ=" + babylonCamera.maxZ, 3); RaiseMessage("GLTFExporter.Camera | babylonCamera.minZ=" + babylonCamera.minZ, 3); // -------------------------- // ------- gltfCamera ------- // -------------------------- RaiseMessage("GLTFExporter.Camera | create gltfCamera", 2); // Camera var gltfCamera = new GLTFCamera { name = babylonCamera.name }; gltfCamera.index = gltf.CamerasList.Count; gltf.CamerasList.Add(gltfCamera); gltfNode.camera = gltfCamera.index; gltfCamera.gltfNode = gltfNode; // Camera type switch (babylonCamera.mode) { case (BabylonCamera.CameraMode.ORTHOGRAPHIC_CAMERA): var gltfCameraOrthographic = new GLTFCameraOrthographic(); gltfCameraOrthographic.xmag = 1; // TODO - How to retreive value from babylon? xmag:The floating-point horizontal magnification of the view gltfCameraOrthographic.ymag = 1; // TODO - How to retreive value from babylon? ymag:The floating-point vertical magnification of the view gltfCameraOrthographic.zfar = babylonCamera.maxZ; gltfCameraOrthographic.znear = babylonCamera.minZ; gltfCamera.type = GLTFCamera.CameraType.orthographic.ToString(); gltfCamera.orthographic = gltfCameraOrthographic; break; case (BabylonCamera.CameraMode.PERSPECTIVE_CAMERA): var gltfCameraPerspective = new GLTFCameraPerspective(); gltfCameraPerspective.aspectRatio = null; // 0.8f; // TODO - How to retreive value from babylon? The aspect ratio in babylon is computed based on the engine rather than set on a camera (aspectRatio = _gl.drawingBufferWidth / _gl.drawingBufferHeight) gltfCameraPerspective.yfov = babylonCamera.fov; // WARNING - Babylon camera fov mode is assumed to be vertical (FOVMODE_VERTICAL_FIXED) gltfCameraPerspective.zfar = babylonCamera.maxZ; gltfCameraPerspective.znear = babylonCamera.minZ; gltfCamera.type = GLTFCamera.CameraType.perspective.ToString(); gltfCamera.perspective = gltfCameraPerspective; break; default: RaiseError("GLTFExporter.Camera | camera mode not found"); break; } return(gltfCamera); }