private GLTFNode ExportAbstractMesh(BabylonAbstractMesh babylonAbstractMesh, GLTF gltf, GLTFNode gltfParentNode) { RaiseMessage("GLTFExporter.AbstractMesh | Export abstract mesh named: " + babylonAbstractMesh.name, 1); // Node var gltfNode = new GLTFNode(); gltfNode.name = babylonAbstractMesh.name; gltfNode.index = gltf.NodesList.Count; gltf.NodesList.Add(gltfNode); // Hierarchy if (gltfParentNode != null) { RaiseMessage("GLTFExporter.AbstractMesh | Add " + babylonAbstractMesh.name + " as child to " + gltfParentNode.name, 2); gltfParentNode.ChildrenList.Add(gltfNode.index); } else { // It's a root node // Only root nodes are listed in a gltf scene RaiseMessage("GLTFExporter.AbstractMesh | Add " + babylonAbstractMesh.name + " as root node to scene", 2); gltf.scenes[0].NodesList.Add(gltfNode.index); } // Transform gltfNode.translation = babylonAbstractMesh.position; // TODO - Choose between this method and the extra root node // Switch from left to right handed coordinate system //gltfNode.translation[0] *= -1; if (babylonAbstractMesh.rotationQuaternion != null) { gltfNode.rotation = babylonAbstractMesh.rotationQuaternion; } else { // Convert rotation vector to quaternion BabylonVector3 rotationVector3 = new BabylonVector3 { X = babylonAbstractMesh.rotation[0], Y = babylonAbstractMesh.rotation[1], Z = babylonAbstractMesh.rotation[2] }; gltfNode.rotation = rotationVector3.toQuaternion().ToArray(); } gltfNode.scale = babylonAbstractMesh.scaling; // Mesh var gltfMesh = gltf.MeshesList.Find(_gltfMesh => _gltfMesh.idGroupInstance == babylonAbstractMesh.idGroupInstance); if (gltfMesh != null) { gltfNode.mesh = gltfMesh.index; } return(gltfNode); }
/// <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 GLTFNode ExportAbstractMesh(BabylonAbstractMesh babylonAbstractMesh, GLTF gltf, GLTFNode gltfParentNode, BabylonScene babylonScene) { RaiseMessage("GLTFExporter.AbstractMesh | Export abstract mesh named: " + babylonAbstractMesh.name, 1); // Node var gltfNode = new GLTFNode(); gltfNode.name = babylonAbstractMesh.name; gltfNode.index = gltf.NodesList.Count; gltf.NodesList.Add(gltfNode); // Hierarchy if (gltfParentNode != null) { RaiseMessage("GLTFExporter.AbstractMesh | Add " + babylonAbstractMesh.name + " as child to " + gltfParentNode.name, 2); gltfParentNode.ChildrenList.Add(gltfNode.index); gltfNode.parent = gltfParentNode; } else { // It's a root node // Only root nodes are listed in a gltf scene RaiseMessage("GLTFExporter.AbstractMesh | Add " + babylonAbstractMesh.name + " as root node to scene", 2); gltf.scenes[0].NodesList.Add(gltfNode.index); } // Transform gltfNode.translation = babylonAbstractMesh.position; if (babylonAbstractMesh.rotationQuaternion != null) { gltfNode.rotation = babylonAbstractMesh.rotationQuaternion; } else { // Convert rotation vector to quaternion BabylonVector3 rotationVector3 = new BabylonVector3 { X = babylonAbstractMesh.rotation[0], Y = babylonAbstractMesh.rotation[1], Z = babylonAbstractMesh.rotation[2] }; gltfNode.rotation = rotationVector3.toQuaternion().ToArray(); } gltfNode.scale = babylonAbstractMesh.scaling; // Switch coordinate system at object level gltfNode.translation[2] *= -1; gltfNode.rotation[0] *= -1; gltfNode.rotation[1] *= -1; // Mesh var gltfMesh = gltf.MeshesList.Find(_gltfMesh => _gltfMesh.idGroupInstance == babylonAbstractMesh.idGroupInstance); if (gltfMesh != null) { gltfNode.mesh = gltfMesh.index; // Skin if (gltfMesh.idBabylonSkeleton.HasValue) { var babylonSkeleton = babylonScene.skeletons[gltfMesh.idBabylonSkeleton.Value]; // Export a new skeleton if necessary and a new skin var gltfSkin = ExportSkin(babylonSkeleton, gltf, gltfNode); gltfNode.skin = gltfSkin.index; } } // Animations ExportNodeAnimation(babylonAbstractMesh, gltf, gltfNode, babylonScene); return(gltfNode); }
private 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 = GetUniqueNodeName(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); }
/// <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.rotation[0] *= -1; gltfNode.rotation[1] *= -1; } ExportGLTFExtension(babylonNode, ref gltfNode, gltf); return(gltfNode); }
private GLTFAnimation ExportBoneAnimation(BabylonBone babylonBone, GLTF gltf, GLTFNode gltfNode) { GLTFAnimation gltfAnimation = null; if (gltf.AnimationsList.Count > 0) { gltfAnimation = gltf.AnimationsList[0]; } else { gltfAnimation = new GLTFAnimation(); gltf.AnimationsList.Add(gltfAnimation); } 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; // --- Input --- var accessorInput = _createAndPopulateInput(gltf, babylonAnimation); // --- 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) { 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; BabylonVector3 rotationVector3 = rotationQuatBabylon.toEulerAngles(); rotationVector3.X *= -1; rotationVector3.Y *= -1; rotationQuatBabylon = rotationVector3.toQuaternion(); 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); } } return(gltfAnimation); }
/// <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 (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.rotation[0] *= -1; gltfNode.rotation[1] *= -1; // Kuesa layers if (exportParameters.kuesaExportLayers && babylonNode.kuesaLayers != null) { Dictionary <string, List <int> > layers = new Dictionary <string, List <int> >(); List <int> indexes = babylonNode.kuesaLayers.ToList <int>(); layers["layers"] = new List <int>(); layers["layers"].AddRange(indexes); if (gltfNode.extensions == null) { gltfNode.extensions = new GLTFExtensions(); } gltfNode.extensions["KDAB_Kuesa_Layers"] = layers; } return(gltfNode); }
/// <summary> /// Get TRS and visiblity animations of the transform /// </summary> /// <param name="transform">Transform above mesh/camera/light</param> /// <returns></returns> private List <BabylonAnimation> GetAnimation(MFnTransform transform) { // Animations MPlugArray connections = new MPlugArray(); MStringArray animCurvList = new MStringArray(); MIntArray keysTime = new MIntArray(); MDoubleArray keysValue = new MDoubleArray(); MFloatArray translateValues = new MFloatArray(); MFloatArray rotateValues = new MFloatArray(); MFloatArray scaleValues = new MFloatArray(); MFloatArray visibilityValues = new MFloatArray(); MFloatArray keyTimes = new MFloatArray(); List <BabylonAnimationKey> keys = new List <BabylonAnimationKey>(); List <BabylonAnimation> animationsObject = new List <BabylonAnimation>(); //Get the animCurve MGlobal.executeCommand("listConnections -type \"animCurve\" " + transform.fullPathName + ";", animCurvList); List <AnimCurvData> animCurvesData = new List <AnimCurvData>(); foreach (String animCurv in animCurvList) { AnimCurvData animCurvData = new AnimCurvData(); animCurvesData.Add(animCurvData); animCurvData.animCurv = animCurv; //Get the key time for each curves MGlobal.executeCommand("keyframe -q " + animCurv + ";", keysTime); //Get the value for each curves MGlobal.executeCommand("keyframe - q -vc -absolute " + animCurv + ";", keysValue); if (animCurv.EndsWith("translateZ") || animCurv.EndsWith("rotateX") || animCurv.EndsWith("rotateY")) { for (int index = 0; index < keysTime.Count; index++) { // Switch coordinate system at object level animCurvData.valuePerFrame.Add(keysTime[index], (float)keysValue[index] * -1.0f); } } else { for (int index = 0; index < keysTime.Count; index++) { animCurvData.valuePerFrame.Add(keysTime[index], (float)keysValue[index]); } } } string[] mayaAnimationProperties = new string[] { "translate", "rotate", "scale" }; string[] babylonAnimationProperties = new string[] { "position", "rotationQuaternion", "scaling" }; string[] axis = new string[] { "X", "Y", "Z" }; // Init TRS default values Dictionary <string, float> defaultValues = new Dictionary <string, float>(); float[] position = null; float[] rotationQuaternion = null; float[] rotation = null; float[] scaling = null; GetTransform(transform, ref position, ref rotationQuaternion, ref rotation, ref scaling); // coordinate system already switched defaultValues.Add("translateX", position[0]); defaultValues.Add("translateY", position[1]); defaultValues.Add("translateZ", position[2]); defaultValues.Add("rotateX", rotation[0]); defaultValues.Add("rotateY", rotation[1]); defaultValues.Add("rotateZ", rotation[2]); defaultValues.Add("scaleX", scaling[0]); defaultValues.Add("scaleY", scaling[1]); defaultValues.Add("scaleZ", scaling[2]); for (int indexAnimationProperty = 0; indexAnimationProperty < mayaAnimationProperties.Length; indexAnimationProperty++) { string mayaAnimationProperty = mayaAnimationProperties[indexAnimationProperty]; // Retreive animation curves data for current animation property // Ex: all "translate" data are "translateX", "translateY", "translateZ" List <AnimCurvData> animDataProperty = animCurvesData.Where(data => data.animCurv.Contains(mayaAnimationProperty)).ToList(); if (animDataProperty.Count == 0) { // Property is not animated continue; } // Get all frames for this property List <int> framesProperty = new List <int>(); foreach (var animData in animDataProperty) { framesProperty.AddRange(animData.valuePerFrame.Keys); } framesProperty = framesProperty.Distinct().ToList(); framesProperty.Sort(); // Get default values for this property BabylonAnimationKey lastBabylonAnimationKey = new BabylonAnimationKey(); lastBabylonAnimationKey.frame = 0; lastBabylonAnimationKey.values = new float[] { defaultValues[mayaAnimationProperty + "X"], defaultValues[mayaAnimationProperty + "Y"], defaultValues[mayaAnimationProperty + "Z"] }; // Compute all values for this property List <BabylonAnimationKey> babylonAnimationKeys = new List <BabylonAnimationKey>(); foreach (var frameProperty in framesProperty) { BabylonAnimationKey babylonAnimationKey = new BabylonAnimationKey(); babylonAnimationKeys.Add(babylonAnimationKey); // Frame babylonAnimationKey.frame = frameProperty; // Values float[] valuesProperty = new float[3]; for (int indexAxis = 0; indexAxis < axis.Length; indexAxis++) { AnimCurvData animCurvDataAxis = animDataProperty.Find(data => data.animCurv.EndsWith(axis[indexAxis])); float value; if (animCurvDataAxis != null && animCurvDataAxis.valuePerFrame.ContainsKey(frameProperty)) { value = animCurvDataAxis.valuePerFrame[frameProperty]; } else { value = lastBabylonAnimationKey.values[indexAxis]; } valuesProperty[indexAxis] = value; } babylonAnimationKey.values = valuesProperty.ToArray(); // Update last known values lastBabylonAnimationKey = babylonAnimationKey; } // Convert euler to quaternion angles if (indexAnimationProperty == 1) // Rotation { foreach (var babylonAnimationKey in babylonAnimationKeys) { BabylonVector3 eulerAngles = BabylonVector3.FromArray(babylonAnimationKey.values); BabylonQuaternion quaternionAngles = eulerAngles.toQuaternion(); babylonAnimationKey.values = quaternionAngles.ToArray(); } } var keysFull = new List <BabylonAnimationKey>(babylonAnimationKeys); // Optimization OptimizeAnimations(babylonAnimationKeys, true); // Ensure animation has at least 2 frames if (IsAnimationKeysRelevant(keys)) { // Create BabylonAnimation string babylonAnimationProperty = babylonAnimationProperties[indexAnimationProperty]; animationsObject.Add(new BabylonAnimation() { dataType = indexAnimationProperty == 1 ? (int)BabylonAnimation.DataType.Quaternion : (int)BabylonAnimation.DataType.Vector3, name = babylonAnimationProperty + " animation", framePerSecond = 30, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, property = babylonAnimationProperty, keys = babylonAnimationKeys.ToArray(), keysFull = keysFull }); } } return(animationsObject); }
private GLTFMesh ExportMesh(BabylonMesh babylonMesh, GLTF gltf, GLTFNode gltfParentNode) { RaiseMessage("GLTFExporter.Mesh | ExportMesh babylonMesh.name=" + babylonMesh.name, 1); // -------------------------- // ---------- Node ---------- // -------------------------- RaiseMessage("GLTFExporter.Mesh | Node", 1); // Node var gltfNode = new GLTFNode(); gltfNode.name = babylonMesh.name; gltfNode.index = gltf.NodesList.Count; gltf.NodesList.Add(gltfNode); // Hierarchy if (gltfParentNode != null) { RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as child to " + gltfParentNode.name, 2); gltfParentNode.ChildrenList.Add(gltfNode.index); } else { // It's a root node // Only root nodes are listed in a gltf scene RaiseMessage("GLTFExporter.Mesh | Add " + babylonMesh.name + " as root node to scene", 2); gltf.scenes[0].NodesList.Add(gltfNode.index); } // Transform gltfNode.translation = babylonMesh.position; if (babylonMesh.rotationQuaternion != null) { gltfNode.rotation = babylonMesh.rotationQuaternion; } else { // Convert rotation vector to quaternion // TODO - Fix it BabylonVector3 rotationVector3 = new BabylonVector3 { X = babylonMesh.rotation[0], Y = babylonMesh.rotation[1], Z = babylonMesh.rotation[2] }; gltfNode.rotation = rotationVector3.toQuaternion().ToArray(); RaiseMessage("GLTFExporter.Mesh | rotationVector3=[" + rotationVector3.X + "; " + rotationVector3.Y + "; " + rotationVector3.Z + "]", 2); RaiseMessage("GLTFExporter.Mesh | gltfNode.rotation=[" + gltfNode.rotation[0] + "; " + gltfNode.rotation[1] + "; " + gltfNode.rotation[2] + "; " + gltfNode.rotation[3] + "]", 2); } gltfNode.scale = babylonMesh.scaling; // -------------------------- // --- Mesh from babylon ---- // -------------------------- RaiseMessage("GLTFExporter.Mesh | Mesh from babylon", 1); // Retreive general data from babylon mesh int nbVertices = babylonMesh.positions.Length / 3; bool hasUV = babylonMesh.uvs != null && babylonMesh.uvs.Length > 0; bool hasUV2 = babylonMesh.uvs2 != null && babylonMesh.uvs2.Length > 0; bool hasColor = babylonMesh.colors != null && babylonMesh.colors.Length > 0; RaiseMessage("GLTFExporter.Mesh | nbVertices=" + nbVertices, 2); RaiseMessage("GLTFExporter.Mesh | hasUV=" + hasUV, 2); RaiseMessage("GLTFExporter.Mesh | hasUV2=" + hasUV2, 2); RaiseMessage("GLTFExporter.Mesh | hasColor=" + hasColor, 2); // Retreive vertices data from babylon mesh List <GLTFGlobalVertex> globalVertices = new List <GLTFGlobalVertex>(); for (int i = 0; i < nbVertices; i++) { GLTFGlobalVertex globalVertex = new GLTFGlobalVertex(); globalVertex.Position = createIPoint3(babylonMesh.positions, i); globalVertex.Normal = createIPoint3(babylonMesh.normals, i); if (hasUV) { globalVertex.UV = createIPoint2(babylonMesh.uvs, i); // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image // While for Babylon, it corresponds to the lower left corner of a texture image globalVertex.UV.Y = 1 - globalVertex.UV.Y; } if (hasUV2) { globalVertex.UV2 = createIPoint2(babylonMesh.uvs2, i); // For glTF, the origin of the UV coordinates (0, 0) corresponds to the upper left corner of a texture image // While for Babylon, it corresponds to the lower left corner of a texture image globalVertex.UV2.Y = 1 - globalVertex.UV2.Y; } if (hasColor) { globalVertex.Color = createIPoint4(babylonMesh.colors, i).ToArray(); } globalVertices.Add(globalVertex); } // Retreive indices from babylon mesh List <ushort> indices = new List <ushort>(); indices = babylonMesh.indices.ToList().ConvertAll(new Converter <int, ushort>(n => (ushort)n)); // Swap face side for (int i = 0; i < indices.Count; i += 3) { var tmp = indices[i]; indices[i] = indices[i + 2]; indices[i + 2] = tmp; } // -------------------------- // ------- Init glTF -------- // -------------------------- RaiseMessage("GLTFExporter.Mesh | Init glTF", 1); // Mesh var gltfMesh = new GLTFMesh { name = babylonMesh.name }; gltfMesh.index = gltf.MeshesList.Count; gltf.MeshesList.Add(gltfMesh); gltfNode.mesh = gltfMesh.index; gltfMesh.gltfNode = gltfNode; // MeshPrimitive var meshPrimitives = new List <GLTFMeshPrimitive>(); var meshPrimitive = new GLTFMeshPrimitive { attributes = new Dictionary <string, int>(), mode = GLTFMeshPrimitive.FillMode.TRIANGLES // TODO reteive info from babylon material }; meshPrimitives.Add(meshPrimitive); // Buffer var buffer = new GLTFBuffer { uri = gltfMesh.name + ".bin" }; buffer.index = gltf.BuffersList.Count; gltf.BuffersList.Add(buffer); // BufferView - Scalar var bufferViewScalar = new GLTFBufferView { name = "bufferViewScalar", buffer = buffer.index, Buffer = buffer }; bufferViewScalar.index = gltf.BufferViewsList.Count; gltf.BufferViewsList.Add(bufferViewScalar); // BufferView - Vector3 var bufferViewFloatVec3 = new GLTFBufferView { name = "bufferViewFloatVec3", buffer = buffer.index, Buffer = buffer, byteOffset = 0, byteStride = 12 // Field only defined for buffer views that contain vertex attributes. A vertex needs 3 * 4 bytes }; bufferViewFloatVec3.index = gltf.BufferViewsList.Count; gltf.BufferViewsList.Add(bufferViewFloatVec3); // Accessor - Indices var accessorIndices = new GLTFAccessor { name = "accessorIndices", bufferView = bufferViewScalar.index, BufferView = bufferViewScalar, componentType = GLTFAccessor.ComponentType.UNSIGNED_SHORT, type = GLTFAccessor.TypeEnum.SCALAR.ToString() }; accessorIndices.index = gltf.AccessorsList.Count; gltf.AccessorsList.Add(accessorIndices); meshPrimitive.indices = accessorIndices.index; // Accessor - Positions var accessorPositions = new GLTFAccessor { name = "accessorPositions", bufferView = bufferViewFloatVec3.index, BufferView = bufferViewFloatVec3, componentType = GLTFAccessor.ComponentType.FLOAT, type = GLTFAccessor.TypeEnum.VEC3.ToString(), min = new float[] { float.MaxValue, float.MaxValue, float.MaxValue }, max = new float[] { float.MinValue, float.MinValue, float.MinValue } }; accessorPositions.index = gltf.AccessorsList.Count; gltf.AccessorsList.Add(accessorPositions); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.POSITION.ToString(), accessorPositions.index); // Accessor - Normals var accessorNormals = new GLTFAccessor { name = "accessorNormals", bufferView = bufferViewFloatVec3.index, BufferView = bufferViewFloatVec3, componentType = GLTFAccessor.ComponentType.FLOAT, type = GLTFAccessor.TypeEnum.VEC3.ToString() }; accessorNormals.index = gltf.AccessorsList.Count; gltf.AccessorsList.Add(accessorNormals); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.NORMAL.ToString(), accessorNormals.index); // BufferView - Vector4 GLTFBufferView bufferViewFloatVec4 = null; // Accessor - Colors GLTFAccessor accessorColors = null; if (hasColor) { bufferViewFloatVec4 = new GLTFBufferView { name = "bufferViewFloatVec4", buffer = buffer.index, Buffer = buffer, byteOffset = 0, byteStride = 16 // Field only defined for buffer views that contain vertex attributes. A vertex needs 4 * 4 bytes }; bufferViewFloatVec4.index = gltf.BufferViewsList.Count; gltf.BufferViewsList.Add(bufferViewFloatVec4); accessorColors = new GLTFAccessor { name = "accessorColors", bufferView = bufferViewFloatVec4.index, BufferView = bufferViewFloatVec4, componentType = GLTFAccessor.ComponentType.FLOAT, type = GLTFAccessor.TypeEnum.VEC4.ToString() }; accessorColors.index = gltf.AccessorsList.Count; gltf.AccessorsList.Add(accessorColors); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.COLOR_0.ToString(), accessorColors.index); } // BufferView - Vector2 GLTFBufferView bufferViewFloatVec2 = null; if (hasUV || hasUV2) { bufferViewFloatVec2 = new GLTFBufferView { name = "bufferViewFloatVec2", buffer = buffer.index, Buffer = buffer, byteStride = 8 // Field only defined for buffer views that contain vertex attributes. A vertex needs 2 * 4 bytes }; bufferViewFloatVec2.index = gltf.BufferViewsList.Count; gltf.BufferViewsList.Add(bufferViewFloatVec2); } // Accessor - UV GLTFAccessor accessorUVs = null; if (hasUV) { accessorUVs = new GLTFAccessor { name = "accessorUVs", bufferView = bufferViewFloatVec2.index, BufferView = bufferViewFloatVec2, componentType = GLTFAccessor.ComponentType.FLOAT, type = GLTFAccessor.TypeEnum.VEC2.ToString() }; accessorUVs.index = gltf.AccessorsList.Count; gltf.AccessorsList.Add(accessorUVs); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_0.ToString(), accessorUVs.index); } // Accessor - UV2 GLTFAccessor accessorUV2s = null; if (hasUV2) { accessorUV2s = new GLTFAccessor { name = "accessorUV2s", bufferView = bufferViewFloatVec2.index, BufferView = bufferViewFloatVec2, componentType = GLTFAccessor.ComponentType.FLOAT, type = GLTFAccessor.TypeEnum.VEC2.ToString() }; accessorUV2s.index = gltf.AccessorsList.Count; gltf.AccessorsList.Add(accessorUV2s); meshPrimitive.attributes.Add(GLTFMeshPrimitive.Attribute.TEXCOORD_1.ToString(), accessorUV2s.index); } // -------------------------- // ------ Mesh as glTF ------ // -------------------------- RaiseMessage("GLTFExporter.Mesh | Mesh as glTF", 1); // Material //TODO - Handle multimaterials GLTFMaterial gltfMaterial = gltf.MaterialsList.Find(material => material.id == babylonMesh.materialId); if (gltfMaterial != null) { meshPrimitive.material = gltfMaterial.index; } // Update min and max vertex position for each component (X, Y, Z) globalVertices.ForEach((globalVertex) => { var positionArray = new float[] { globalVertex.Position.X, globalVertex.Position.Y, globalVertex.Position.Z }; for (int indexComponent = 0; indexComponent < positionArray.Length; indexComponent++) { if (positionArray[indexComponent] < accessorPositions.min[indexComponent]) { accessorPositions.min[indexComponent] = positionArray[indexComponent]; } if (positionArray[indexComponent] > accessorPositions.max[indexComponent]) { accessorPositions.max[indexComponent] = positionArray[indexComponent]; } } }); // Update byte length and count of accessors, bufferViews and buffers // Scalar AddElementsToAccessor(accessorIndices, indices.Count); // Vector3 bufferViewFloatVec3.byteOffset = buffer.byteLength; AddElementsToAccessor(accessorPositions, globalVertices.Count); AddElementsToAccessor(accessorNormals, globalVertices.Count); // Vector4 if (hasColor) { bufferViewFloatVec4.byteOffset = buffer.byteLength; AddElementsToAccessor(accessorColors, globalVertices.Count); } // Vector2 if (hasUV || hasUV2) { bufferViewFloatVec2.byteOffset = buffer.byteLength; if (hasUV) { AddElementsToAccessor(accessorUVs, globalVertices.Count); } if (hasUV2) { AddElementsToAccessor(accessorUV2s, globalVertices.Count); } } // -------------------------- // --------- Saving --------- // -------------------------- string outputBinaryFile = Path.Combine(gltf.OutputPath, gltfMesh.name + ".bin"); RaiseMessage("GLTFExporter.Mesh | Saving " + outputBinaryFile, 1); using (BinaryWriter writer = new BinaryWriter(File.Open(outputBinaryFile, FileMode.Create))) { // Binary arrays List <float> vertices = globalVertices.SelectMany(v => new[] { v.Position.X, v.Position.Y, v.Position.Z }).ToList(); List <float> normals = globalVertices.SelectMany(v => new[] { v.Normal.X, v.Normal.Y, v.Normal.Z }).ToList(); List <float> colors = new List <float>(); if (hasColor) { colors = globalVertices.SelectMany(v => new[] { v.Color[0], v.Color[1], v.Color[2], v.Color[3] }).ToList(); } List <float> uvs = new List <float>(); if (hasUV) { uvs = globalVertices.SelectMany(v => new[] { v.UV.X, v.UV.Y }).ToList(); // No symetry required to perform 3dsMax => gltf conversion } List <float> uvs2 = new List <float>(); if (hasUV2) { uvs2 = globalVertices.SelectMany(v => new[] { v.UV2.X, v.UV2.Y }).ToList(); // No symetry required to perform 3dsMax => gltf conversion } // Write data to binary file indices.ForEach(n => writer.Write(n)); vertices.ForEach(n => writer.Write(n)); normals.ForEach(n => writer.Write(n)); colors.ForEach(n => writer.Write(n)); uvs.ForEach(n => writer.Write(n)); } gltfMesh.primitives = meshPrimitives.ToArray(); return(gltfMesh); }
// 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); }