private BabylonSkeleton ConvertUnitySkeletonToBabylon(Transform[] bones, Matrix4x4[] bindPoses, Transform transform, GameObject gameObject, float progress) { ExporterWindow.ReportProgress(progress, "Exporting Skeleton: " + gameObject.name); BabylonSkeleton babylonSkeleton = new BabylonSkeleton(); babylonSkeleton.name = gameObject.name; babylonSkeleton.id = Math.Abs(GetID(transform.gameObject).GetHashCode()); babylonSkeleton.needInitialSkinMatrix = false; // Prefilled to keep order and track parents. var transformToBoneMap = new Dictionary <Transform, BabylonBone>(); for (var i = 0; i < bones.Length; i++) { var unityBone = bones[i]; ExporterWindow.ReportProgress(progress, "Exporting bone: " + unityBone.name + " at index " + i); var babylonBone = new BabylonBone(); babylonBone.name = unityBone.name; babylonBone.index = i; transformToBoneMap.Add(unityBone, babylonBone); } // Attaches Matrix and parent. for (var i = 0; i < bones.Length; i++) { var unityBone = bones[i]; var babylonBone = transformToBoneMap[unityBone]; Matrix4x4 localTransform; // Unity BindPose is already inverse so take the inverse again :-) if (transformToBoneMap.ContainsKey(unityBone.parent)) { var babylonParentBone = transformToBoneMap[unityBone.parent]; babylonBone.parentBoneIndex = babylonParentBone.index; localTransform = bindPoses[babylonBone.parentBoneIndex] * bindPoses[i].inverse; } else { babylonBone.parentBoneIndex = -1; localTransform = bindPoses[i].inverse; } transformToBoneMap[unityBone].matrix = new[] { localTransform[0, 0], localTransform[1, 0], localTransform[2, 0], localTransform[3, 0], localTransform[0, 1], localTransform[1, 1], localTransform[2, 1], localTransform[3, 1], localTransform[0, 2], localTransform[1, 2], localTransform[2, 2], localTransform[3, 2], localTransform[0, 3], localTransform[1, 3], localTransform[2, 3], localTransform[3, 3] }; } // Reorder and attach the skeleton. babylonSkeleton.bones = transformToBoneMap.Values.OrderBy(b => b.index).ToArray(); babylonScene.SkeletonsList.Add(babylonSkeleton); return(babylonSkeleton); }
private GLTFNode _exportBone(BabylonBone babylonBone, GLTF gltf, BabylonSkeleton babylonSkeleton, List <BabylonBone> bones) { if (alreadyExportedBones.ContainsKey(babylonBone)) { return(alreadyExportedBones[babylonBone]); } // Node var gltfNode = new GLTFNode { name = babylonBone.name }; gltfNode.index = gltf.NodesList.Count; gltf.NodesList.Add(gltfNode); alreadyExportedBones.Add(babylonBone, gltfNode); boneToGltfNodeMap.Add(babylonBone, gltfNode); // Hierarchy if (babylonBone.parentBoneIndex >= 0) { var babylonParentBone = bones.Find(_babylonBone => _babylonBone.index == babylonBone.parentBoneIndex); var gltfParentNode = _exportBone(babylonParentBone, gltf, babylonSkeleton, bones); RaiseMessage("GLTFExporter.Skin | Add " + babylonBone.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.Skin | Add " + babylonBone.name + " as root node to scene", 3); gltf.scenes[0].NodesList.Add(gltfNode.index); } // Transform // Bones transform are exported through translation/rotation/scale (TRS) rather than matrix // Because gltf node animation can only target TRS properties, not the matrix one // Create matrix from array var babylonMatrix = new BabylonMatrix(); babylonMatrix.m = babylonBone.matrix; // Decompose matrix into TRS var translationBabylon = new BabylonVector3(); var rotationQuatBabylon = new BabylonQuaternion(); var scaleBabylon = new BabylonVector3(); babylonMatrix.decompose(scaleBabylon, rotationQuatBabylon, translationBabylon); // Store TRS values gltfNode.translation = translationBabylon.ToArray(); gltfNode.rotation = rotationQuatBabylon.ToArray(); gltfNode.scale = scaleBabylon.ToArray(); // Animations //ExportBoneAnimation(babylonBone, gltf, gltfNode); return(gltfNode); }
private static void ExportSkeletonAnimationClips(Animator animator, BabylonSkeleton skeleton, SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, UnityEditor.AnimationState animationState, ref UnityMetaData metaData) { List <AnimationClip> states = Tools.GetAnimationClips(animator); if (states != null && states.Count > 0) { ExportSkeletonAnimationClipData(animator.gameObject, skeleton, skinnedMesh, babylonMesh, animationState, ref states, ref metaData, animator); } }
private static void ExportSkeletonAnimationClips(Animator animator, BabylonSkeleton skeleton, SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, UnityEditor.AnimationState animationState, ref UnityMetaData metaData) { List <UnityEditor.AnimationParameters> aparams = Tools.GetAnimationParameters(animator); List <AnimationClip> clips = Tools.GetAnimationClips(animator); if (clips != null && clips.Count > 0) { ExportSkeletonAnimationClipData(animator.gameObject, skeleton, skinnedMesh, babylonMesh, ref clips, ref metaData, aparams); } }
/// <summary> /// Create the BabylonSkeleton from the Maya MFnSkinCluster. /// And add it to the BabylonScene. /// </summary> /// <param name="skin">The maya skin cluster</param> /// <param name="babylonScene">The scene to export</param> /// <returns></returns> private void ExportSkin(MFnSkinCluster skin, BabylonScene babylonScene) { int logRank = 1; int skinIndex = GetSkeletonIndex(skin); string name = "skeleton #" + skinIndex; RaiseMessage(name, logRank); BabylonSkeleton babylonSkeleton = new BabylonSkeleton { id = skinIndex, name = name, bones = ExportBones(skin), needInitialSkinMatrix = true }; babylonScene.SkeletonsList.Add(babylonSkeleton); }
void ParseModel(Model model, BabylonScene scene) { var effects = model.Meshes.SelectMany(m => m.Effects).ToList(); var meshes = model.Meshes.ToList(); var total = effects.Count + meshes.Count; var progress = 0; SkinningData skinningData = null; BabylonSkeleton currentSkeleton = null; if (model.Tag != null) { skinningData = model.Tag as SkinningData; } if (skinningData != null) { var skeleton = new BabylonSkeleton(); skeleton.id = scene.SkeletonsList.Count; skeleton.name = "Skeleton" + scene.SkeletonsList.Count; ParseBones(skinningData, skeleton); // Animations ParseAnimationClip(skinningData, skeleton); scene.SkeletonsList.Add(skeleton); currentSkeleton = skeleton; } foreach (Effect effect in effects) { ParseEffect(effect, scene); if (OnImportProgressChanged != null) { OnImportProgressChanged(((progress++) * 100) / total); } } foreach (var mesh in meshes) { ParseMesh(mesh, scene, currentSkeleton); if (OnImportProgressChanged != null) { OnImportProgressChanged(((progress++) * 100) / total); } } }
void ParseMesh(ModelMesh modelMesh, BabylonScene scene, BabylonSkeleton skeleton) { var proxyID = ProxyMesh.CreateBabylonMesh(modelMesh.Name, scene); int indexName = 0; foreach (var part in modelMesh.MeshParts) { var material = exportedMaterials.First(m => m.Name == part.Effect.GetHashCode().ToString()); var indices = new ushort[part.PrimitiveCount * 3]; part.IndexBuffer.GetData(part.StartIndex * 2, indices, 0, indices.Length); if (part.VertexBuffer.VertexDeclaration.VertexStride > PositionNormalTextured.Stride) { var mesh = new Mesh <PositionNormalTexturedWeights>(material); var vertices = new PositionNormalTexturedWeights[part.NumVertices]; part.VertexBuffer.GetData(part.VertexOffset * part.VertexBuffer.VertexDeclaration.VertexStride, vertices, 0, vertices.Length, part.VertexBuffer.VertexDeclaration.VertexStride); for (int index = 0; index < vertices.Length; index++) { vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y; } mesh.AddPart(indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList()); mesh.CreateBabylonMesh(scene, proxyID, skeleton); } else { var mesh = new Mesh <PositionNormalTextured>(material); var vertices = new PositionNormalTextured[part.NumVertices]; part.VertexBuffer.GetData(part.VertexOffset * PositionNormalTextured.Stride, vertices, 0, vertices.Length, PositionNormalTextured.Stride); for (int index = 0; index < vertices.Length; index++) { vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y; } mesh.AddPart(indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList()); mesh.CreateBabylonMesh(scene, proxyID, skeleton); } indexName++; } }
private void ParseBones(SkinningData skinningData, BabylonSkeleton skeleton) { // Bones var bones = new List <BabylonBone>(); for (int boneIndex = 0; boneIndex < skinningData.BindPose.Count; boneIndex++) { var newBone = new BabylonBone(); bones.Add(newBone); newBone.name = "bone" + boneIndex; newBone.index = boneIndex; newBone.matrix = skinningData.BindPose[boneIndex].ToMatrix().ToArray(); newBone.parentBoneIndex = skinningData.SkeletonHierarchy[boneIndex]; } skeleton.bones = bones.ToArray(); }
/// <summary> /// Export the skeleton /// </summary> /// <param name="skin">The skin to export</param> /// <param name="babylonScene">The exported scene that will contain the skeleton</param> private void ExportSkin(IIGameSkin skin, BabylonScene babylonScene) { int logRank = 1; int skinIndex = skins.IndexOf(skin); string name = "skeleton #" + skinIndex; logger?.RaiseMessage(name, logRank); IIGameNode skinNode = skinNodeMap[skin]; BabylonSkeleton babylonSkeleton = new BabylonSkeleton { id = skinIndex, name = name, bones = ExportBones(skin, skinNode), needInitialSkinMatrix = true }; babylonScene.SkeletonsList.Add(babylonSkeleton); }
private void ParseAnimationClip(SkinningData skinningData, BabylonSkeleton skeleton) { foreach (var clipKey in skinningData.AnimationClips.Keys) { var clip = skinningData.AnimationClips[clipKey]; var duration = clip.Duration.TotalMilliseconds; var dic = new Dictionary <int, List <BabylonAnimationKey> >(); foreach (var keyframe in clip.Keyframes) { if (!dic.ContainsKey(keyframe.Bone)) { dic.Add(keyframe.Bone, new List <BabylonAnimationKey>()); } var currentTime = (float)(keyframe.Time.TotalMilliseconds * 100.0 / duration); dic[keyframe.Bone].Add(new BabylonAnimationKey { frame = currentTime, values = keyframe.Transform.ToMatrix().ToArray() }); } foreach (var index in dic.Keys) { var bone = skeleton.bones[index]; var babylonAnimation = new BabylonAnimation { name = bone.name + "Animation", property = "_matrix", dataType = BabylonAnimation.DataType.Matrix, loopBehavior = InterpolationLoop.Cycle, framePerSecond = 60 }; babylonAnimation.keys = dic[index].ToArray(); bone.animation = babylonAnimation; } return; // Only one animation track } }
private IList <BabylonAnimationGroup> ExportAnimationGroups(BabylonScene babylonScene) { IList <BabylonAnimationGroup> animationGroups = new List <BabylonAnimationGroup>(); // Retrieve and parse animation group data AnimationGroupList animationList = AnimationGroupList.InitAnimationGroups(logger); foreach (AnimationGroup animGroup in animationList) { logger?.RaiseMessage("Exporter.animationGroups | " + animGroup.Name, 1); BabylonAnimationGroup animationGroup = new BabylonAnimationGroup { name = animGroup.Name, from = animGroup.FrameStart, to = animGroup.FrameEnd, keepNonAnimated = animGroup.KeepNonAnimated, targetedAnimations = new List <BabylonTargetedAnimation>() }; // add animations of each nodes contained in the animGroup foreach (Guid guid in animGroup.NodeGuids) { IINode maxNode = Tools.GetINodeByGuid(guid); // node could have been deleted, silently ignore it if (maxNode == null) { continue; } if (exportParameters.exportAsSubmodel && !maxNode.Selected) { continue; } // Helpers can be exported as dummies and as bones string nodeId = maxNode.GetGuid().ToString(); string boneId = isGltfExported?maxNode.GetGuid().ToString(): maxNode.GetGuid().ToString() + "-bone"; // the suffix "-bone" is added in babylon export format to assure the uniqueness of IDs // Node BabylonNode node = null; babylonScene.NodeMap.TryGetValue(nodeId, out node); if (node != null) { if (node.animations != null && node.animations.Length != 0) { IList <BabylonAnimation> animations = GetSubAnimations(node, animationGroup.from, animationGroup.to); if (!animGroup.KeepStaticAnimation) { RemoveStaticAnimations(ref animations); } foreach (BabylonAnimation animation in animations) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = animation, targetId = nodeId }; animationGroup.targetedAnimations.Add(targetedAnimation); } } } // bone BabylonBone bone = null; int index = 0; while (index < babylonScene.SkeletonsList.Count && bone == null) { BabylonSkeleton skel = babylonScene.SkeletonsList[index]; bone = skel.bones.FirstOrDefault(b => b.id == boneId); index++; } if (bone != null) { if (bone.animation != null) { IList <BabylonAnimation> animations = GetSubAnimations(bone, animationGroup.from, animationGroup.to); foreach (BabylonAnimation animation in animations) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = animation, targetId = boneId }; animationGroup.targetedAnimations.Add(targetedAnimation); } } } } // add animations of each nodes contained in the animGroup foreach (Guid guid in animGroup.MaterialGuids) { IMtl maxMtl = Tools.GetIMtlByGuid(guid); // mat could have been deleted, silently ignore it if (maxMtl == null) { continue; } string matId = maxMtl.GetGuid().ToString(); // Material BabylonMaterial material = null; material = babylonScene.MaterialsList.FirstOrDefault(x => x.id == matId); if (material != null) { if (material.animations != null && material.animations.Length != 0) { IList <BabylonAnimation> animations = GetSubAnimations(material, animationGroup.from, animationGroup.to); foreach (BabylonAnimation animation in animations) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = animation, targetId = matId }; animationGroup.targetedAnimations.Add(targetedAnimation); } } } } if (animationGroup.targetedAnimations.Count > 0) { animationGroups.Add(animationGroup); } } return(animationGroups); }
private static void ExportSkeletonAnimationClipData(GameObject source, BabylonSkeleton skeleton, SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, UnityEditor.AnimationState animationState, ref List <AnimationClip> states, ref UnityMetaData metaData, Animator animator) { ExporterWindow.ReportProgress(1, "Exporting skeleton clips: " + skinnedMesh.name); //string sourceId = GetID(source); int frameRate = 0; int firstClipEnd = 0; int totalFrameCount = 0; Transform[] bones = skinnedMesh.bones; List <string> stateNameCache = new List <string>(); if (!AnimationMode.InAnimationMode()) { AnimationMode.StartAnimationMode(); } //var anims = new List<BabylonAnimation>(); //var pxkeys = new List<BabylonAnimationKey>(); float playbackSpeed = (animationState != null) ? animationState.playbackSpeed : 1.0f; float clampFeetPositions = (animationState != null) ? animationState.clampFeetPositions : 0.0f; BabylonAnimationBaking bakeRootTransforms = (animationState != null) ? animationState.bakeRootTransforms : BabylonAnimationBaking.GameBlend; foreach (var bone in skeleton.bones) { int frameOffest = 0; var keys = new List <BabylonAnimationKey>(); Transform transform = bones.Single(b => b.name == bone.name); foreach (var state in states) { if (state == null) { continue; } AnimationClip clip = state as AnimationClip; if (frameRate <= 0) { frameRate = (int)clip.frameRate; } var frameTime = 1.0f / frameRate; int clipFrameCount = (int)(clip.length * frameRate); if (firstClipEnd <= 0) { firstClipEnd = (clipFrameCount - 1); } var settings = AnimationUtility.GetAnimationClipSettings(clip); BabylonLoopBehavior behavior = (settings.loopTime) ? BabylonLoopBehavior.Cycle : BabylonLoopBehavior.Constant; if (settings.loopTime && settings.loopBlend) { behavior = BabylonLoopBehavior.Relative; } ExporterWindow.ReportProgress(1, "Sampling: " + babylonMesh.name + " - " + bone.name + " - " + clip.name); // Set Animation State Meta Data if (!stateNameCache.Contains(clip.name)) { stateNameCache.Add(clip.name); // Animation Clip Information Dictionary <string, object> animStateInfo = new Dictionary <string, object>(); animStateInfo.Add("type", "skeleton"); animStateInfo.Add("name", clip.name); animStateInfo.Add("start", frameOffest); animStateInfo.Add("stop", (frameOffest + clipFrameCount - 1)); animStateInfo.Add("rate", frameRate); animStateInfo.Add("behavior", (int)behavior); animStateInfo.Add("playback", playbackSpeed); metaData.animationClips.Add(animStateInfo); } AnimationMode.BeginSampling(); for (var i = 0; i < clipFrameCount; i++) { Matrix4x4 local; int frameIndex = (i + frameOffest); float originalPX = transform.localPosition.x; float originalPY = transform.localPosition.y; float originalPZ = transform.localPosition.z; float originalRY = transform.localRotation.eulerAngles.y; clip.SampleAnimation(source, i * frameTime); if (transform == skinnedMesh.rootBone) { float positionX = transform.localPosition.x; float positionY = transform.localPosition.y; float positionZ = transform.localPosition.z; Quaternion rotationQT = transform.localRotation; if (settings.loopBlendOrientation || settings.keepOriginalOrientation) { if (settings.keepOriginalOrientation) { // Original Rotation - ??? rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, originalRY, rotationQT.eulerAngles.z); } else { // Body Orientation - ??? rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, settings.orientationOffsetY, rotationQT.eulerAngles.z); } } if (settings.loopBlendPositionY || settings.keepOriginalPositionY) { if (settings.keepOriginalPositionY) { // Original Position Y positionY = originalPY; } else if (settings.heightFromFeet) { // Feet Position Y positionY = (settings.level + clampFeetPositions); } else { // Center Of Mass positionY = 0.0f; } } if (settings.loopBlendPositionXZ || settings.keepOriginalPositionXZ) { if (settings.keepOriginalPositionXZ) { // Original Position XZ positionX = originalPX; positionZ = originalPZ; } else { // Center Of Mass positionX = 0.0f; positionZ = 0.0f; } } if (bakeRootTransforms == BabylonAnimationBaking.GameBlend) { positionX = 0.0f; positionZ = 0.0f; } local = Matrix4x4.TRS(new Vector3(positionX, positionY, positionZ), rotationQT, transform.localScale); } else { // DEPRECIATED: local = (transform.parent.localToWorldMatrix.inverse * transform.localToWorldMatrix); local = Matrix4x4.TRS(transform.localPosition, transform.localRotation, transform.localScale); } float[] matrix = new[] { local[0, 0], local[1, 0], local[2, 0], local[3, 0], local[0, 1], local[1, 1], local[2, 1], local[3, 1], local[0, 2], local[1, 2], local[2, 2], local[3, 2], local[0, 3], local[1, 3], local[2, 3], local[3, 3] }; var key = new BabylonAnimationKey { frame = frameIndex, values = matrix }; keys.Add(key); } AnimationMode.EndSampling(); frameOffest += clipFrameCount; totalFrameCount += clipFrameCount; } var babylonAnimation = new BabylonAnimation { name = bone.name + "Animation", property = "_matrix", dataType = (int)BabylonAnimation.DataType.Matrix, loopBehavior = (int)BabylonAnimation.LoopBehavior.Relative, enableBlending = false, blendingSpeed = 0.0f, framePerSecond = frameRate, keys = keys.ToArray() }; bone.animation = babylonAnimation; } if (AnimationMode.InAnimationMode()) { AnimationMode.StopAnimationMode(); } /* * // * // TODO: Format Custom Curve Keys * // * string property = "none"; * if (pxkeys.Count > 0) * { * property = "metadata.state.animPosition.x"; * anims.Add(new BabylonAnimation * { * dataType = (int)BabylonAnimation.DataType.Float, * name = property + " animation", * keys = pxkeys.ToArray(), * framePerSecond = frameRate, * enableBlending = false, * blendingSpeed = 0.0f, * loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, * property = property * }); * } * // * // Cache Babylon Animation Keys * // * if (anims.Count > 0) * { * List<BabylonAnimation> sourceAnimiamtions = null; * if (SceneBuilder.AnimationCurveKeys.ContainsKey(sourceId)) { * sourceAnimiamtions = SceneBuilder.AnimationCurveKeys[sourceId]; * } else { * sourceAnimiamtions = new List<BabylonAnimation>(); * SceneBuilder.AnimationCurveKeys.Add(sourceId, sourceAnimiamtions); * } * foreach (var anim in anims) { * sourceAnimiamtions.Add(anim); * } * } */ }
private void ExportSkin(IISkin skin, BabylonScene babylonScene) { var babylonSkeleton = new BabylonSkeleton { id = skins.IndexOf(skin) }; babylonSkeleton.name = "skeleton #" + babylonSkeleton.id; RaiseMessage(babylonSkeleton.name, 1); var bones = new List <BabylonBone>(); for (var index = 0; index < skin.NumBones; index++) { var bone = new BabylonBone { name = skin.GetBoneName(index), index = index }; var maxBone = skin.GetBone(index); var parentNode = maxBone.ParentNode; if (parentNode != null) { for (var recurseIndex = 0; recurseIndex < index; recurseIndex++) { if (skin.GetBone(recurseIndex).GetGuid() == parentNode.GetGuid()) { bone.parentBoneIndex = recurseIndex; break; } } } var hasParent = bone.parentBoneIndex != -1; bone.matrix = GetBoneMatrix(skin, maxBone, 0, hasParent); // Animation var babylonAnimation = new BabylonAnimation { name = bone.name + "Animation", property = "_matrix", dataType = BabylonAnimation.DataType.Matrix, loopBehavior = BabylonAnimation.LoopBehavior.Cycle, framePerSecond = Loader.Global.FrameRate }; var start = Loader.Core.AnimRange.Start; var end = Loader.Core.AnimRange.End; float[] previous = null; var keys = new List <BabylonAnimationKey>(); for (var key = start; key <= end; key += Ticks) { var current = GetBoneMatrix(skin, maxBone, key, hasParent); if (key == start || key == end || !(previous.IsEqualTo(current))) { keys.Add(new BabylonAnimationKey { frame = key / Ticks, values = current }); } previous = current; } babylonAnimation.keys = keys.ToArray(); bone.animation = babylonAnimation; bones.Add(bone); } babylonSkeleton.bones = bones.ToArray(); babylonScene.SkeletonsList.Add(babylonSkeleton); }
void ParseModel(Model model, BabylonScene scene, bool rightToLeft) { var effects = model.Meshes.SelectMany(m => m.Effects).ToList(); var meshes = model.Meshes.ToList(); var total = effects.Count + meshes.Count; var progress = 0; SkinningData skinningData = null; BabylonSkeleton currentSkeleton = null; if (model.Tag != null) { skinningData = model.Tag as SkinningData; } if (skinningData != null) { var skeleton = new BabylonSkeleton(); skeleton.id = scene.SkeletonsList.Count; skeleton.name = "Skeleton" + scene.SkeletonsList.Count; ParseBones(skinningData, skeleton); // Animations ParseAnimationClip(skinningData, skeleton); scene.SkeletonsList.Add(skeleton); currentSkeleton = skeleton; } foreach (Effect effect in effects) { ParseEffect(effect, scene); if (OnImportProgressChanged != null) OnImportProgressChanged(((progress++) * 100) / total); } foreach (var mesh in meshes) { ParseMesh(mesh, scene, currentSkeleton, rightToLeft); if (OnImportProgressChanged != null) OnImportProgressChanged(((progress++) * 100) / total); } }
private void ExportSkin(IIGameSkin skin, BabylonScene babylonScene) { var babylonSkeleton = new BabylonSkeleton { id = skins.IndexOf(skin) }; babylonSkeleton.name = "skeleton #" + babylonSkeleton.id; RaiseMessage(babylonSkeleton.name, 1); var skinIndex = skins.IndexOf(skin); var bones = new List <BabylonBone>(); var gameBones = new List <IIGameNode>(); var boneIds = new List <int>(); var bindPoseInfos = new List <BonePoseInfo>(); for (int i = 0; i < skin.TotalSkinBoneCount; ++i) { bones.Add(null); gameBones.Add(null); boneIds.Add(-1); bindPoseInfos.Add(null); } for (var index = 0; index < skin.TotalSkinBoneCount; index++) { var gameBone = skin.GetIGameBone(index, false); var sortedIndex = skinSortedBones[skin].IndexOf(gameBone.NodeID); gameBones[sortedIndex] = (gameBone); boneIds[sortedIndex] = (gameBone.NodeID); bones[sortedIndex] = (new BabylonBone { index = sortedIndex, name = gameBone.Name }); var boneInitMatrix = gameBone.GetObjectTM(0); bindPoseInfos[sortedIndex] = (new BonePoseInfo { AbsoluteTransform = boneInitMatrix }); } // fix hierarchy an generate animation keys var exportNonOptimizedAnimations = Loader.Core.RootNode.GetBoolProperty("babylonjs_exportnonoptimizedanimations"); for (var index = 0; index < skin.TotalSkinBoneCount; index++) { var gameBone = gameBones[index]; var parent = gameBone.NodeParent; var babBone = bones[index]; if (parent != null) { babBone.parentBoneIndex = boneIds.IndexOf(parent.NodeID); } if (babBone.parentBoneIndex == -1) { bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform; } else { var parentBindPoseInfos = bindPoseInfos[babBone.parentBoneIndex]; bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform.Multiply(parentBindPoseInfos.AbsoluteTransform.Inverse); } babBone.matrix = bindPoseInfos[index].LocalTransform.ToArray(); var babylonAnimation = new BabylonAnimation { name = gameBone.Name + "Animation", property = "_matrix", dataType = (int)BabylonAnimation.DataType.Matrix, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, framePerSecond = Loader.Global.FrameRate }; var start = Loader.Core.AnimRange.Start; var end = Loader.Core.AnimRange.End; float[] previous = null; var keys = new List <BabylonAnimationKey>(); for (var key = start; key <= end; key += Ticks) { var objectTM = gameBone.GetObjectTM(key); var parentNode = gameBone.NodeParent; IGMatrix mat; if (parentNode == null || babBone.parentBoneIndex == -1) { mat = objectTM; } else { mat = objectTM.Multiply(parentNode.GetObjectTM(key).Inverse); } var current = mat.ToArray(); if (key == start || key == end || exportNonOptimizedAnimations || !(previous.IsEqualTo(current))) { keys.Add(new BabylonAnimationKey { frame = key / Ticks, values = current }); } previous = current; } babylonAnimation.keys = keys.ToArray(); babBone.animation = babylonAnimation; } babylonSkeleton.needInitialSkinMatrix = true; babylonSkeleton.bones = bones.ToArray(); babylonScene.SkeletonsList.Add(babylonSkeleton); }
private void ParseBones(SkinningData skinningData, BabylonSkeleton skeleton) { // Bones var bones = new List<BabylonBone>(); for (int boneIndex = 0; boneIndex < skinningData.BindPose.Count; boneIndex++) { var newBone = new BabylonBone(); bones.Add(newBone); newBone.name = "bone" + boneIndex; newBone.index = boneIndex; newBone.matrix = skinningData.BindPose[boneIndex].ToMatrix().ToArray(); newBone.parentBoneIndex = skinningData.SkeletonHierarchy[boneIndex]; } skeleton.bones = bones.ToArray(); }
private static void ExportSkeletonAnimationClipData(Animator animator, bool autoPlay, BabylonSkeleton skeleton, Transform[] bones, BabylonMesh babylonMesh, AnimationClip clip) { var frameTime = 1.0f / clip.frameRate; int animationFrameCount = (int)(clip.length * clip.frameRate); if (autoPlay) { babylonMesh.autoAnimate = true; babylonMesh.autoAnimateFrom = 0; babylonMesh.autoAnimateTo = animationFrameCount; babylonMesh.autoAnimateLoop = true; } foreach (var bone in skeleton.bones) { var keys = new List <BabylonAnimationKey>(); var transform = bones.Single(b => b.name == bone.name); AnimationMode.BeginSampling(); for (var i = 0; i < animationFrameCount; i++) { clip.SampleAnimation(animator.gameObject, i * frameTime); var local = (transform.parent.localToWorldMatrix.inverse * transform.localToWorldMatrix); float[] matrix = new[] { local[0, 0], local[1, 0], local[2, 0], local[3, 0], local[0, 1], local[1, 1], local[2, 1], local[3, 1], local[0, 2], local[1, 2], local[2, 2], local[3, 2], local[0, 3], local[1, 3], local[2, 3], local[3, 3] }; var key = new BabylonAnimationKey { frame = i, values = matrix, }; keys.Add(key); } AnimationMode.EndSampling(); var babylonAnimation = new BabylonAnimation { name = bone.name + "Animation", property = "_matrix", dataType = (int)BabylonAnimation.DataType.Matrix, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, framePerSecond = (int)clip.frameRate, keys = keys.ToArray() }; bone.animation = babylonAnimation; } }
private static void ExportSkeletonAnimationClipData(GameObject source, BabylonSkeleton skeleton, SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, ref List <AnimationClip> clips, ref UnityMetaData metaData, List <UnityEditor.AnimationParameters> aparams) { ExporterWindow.ReportProgress(1, "Baking " + skinnedMesh.name.ToLower() + " skeleton... This may take a while."); string sourceId = GetID(source); int frameRate = 0; Transform[] bones = skinnedMesh.bones; var anims = new List <BabylonAnimation>(); List <BabylonRange> ranges = new List <BabylonRange>(); List <string> stateNameCache = new List <string>(); if (!AnimationMode.InAnimationMode()) { AnimationMode.StartAnimationMode(); } foreach (var bone in skeleton.bones) { int frameOffest = 0; var keys = new List <BabylonAnimationKey>(); Transform transform = bones.Single(b => b.name == bone.name); foreach (var clip in clips) { if (clip == null) { continue; } string clipName = FormatSafeClipName(clip.name); var settings = AnimationUtility.GetAnimationClipSettings(clip); BabylonLoopBehavior behavior = (settings.loopTime) ? BabylonLoopBehavior.Relative : BabylonLoopBehavior.Constant; if (settings.loopTime && settings.loopBlend) { behavior = BabylonLoopBehavior.Cycle; } // .. int framePadding = 1; float deltaTime = 1.0f / clip.frameRate; if (frameRate <= 0) { frameRate = (int)clip.frameRate; } float clipFrameTotal = clip.length * clip.frameRate; int clipFrameCount = (int)clipFrameTotal + framePadding; int lastFrameCount = clipFrameCount - 1; // .. AnimationMode.BeginSampling(); for (int i = 0; i < clipFrameCount; i++) { Matrix4x4 local; int frameIndex = (int)(i + frameOffest); float sampleTime = (i < lastFrameCount) ? (i * deltaTime) : clip.length; clip.SampleAnimation(source, sampleTime); if (transform == skinnedMesh.rootBone) { float positionX = transform.localPosition.x; float positionY = transform.localPosition.y; float positionZ = transform.localPosition.z; Quaternion rotationQT = transform.localRotation; if (settings.loopBlendOrientation) { if (settings.keepOriginalOrientation) { rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, (rotationQT.eulerAngles.y + settings.orientationOffsetY), rotationQT.eulerAngles.z); } else { rotationQT = Quaternion.Euler(rotationQT.eulerAngles.x, settings.orientationOffsetY, rotationQT.eulerAngles.z); } } if (settings.loopBlendPositionY) { if (settings.keepOriginalPositionY || settings.heightFromFeet) { positionY += settings.level; } else { positionY = settings.level; } } if (settings.loopBlendPositionXZ) { positionX = 0.0f; positionZ = 0.0f; } local = Matrix4x4.TRS(new Vector3(positionX, positionY, positionZ), rotationQT, transform.localScale); } else { local = Matrix4x4.TRS(transform.localPosition, transform.localRotation, transform.localScale); } float[] matrix = new[] { local[0, 0], local[1, 0], local[2, 0], local[3, 0], local[0, 1], local[1, 1], local[2, 1], local[3, 1], local[0, 2], local[1, 2], local[2, 2], local[3, 2], local[0, 3], local[1, 3], local[2, 3], local[3, 3] }; var key = new BabylonAnimationKey { frame = frameIndex, values = matrix }; keys.Add(key); } AnimationMode.EndSampling(); // .. // Set Animation State Meta Data // .. if (!stateNameCache.Contains(clipName)) { stateNameCache.Add(clipName); // Animation Clip Information int fromFrame = frameOffest, toFrame = frameOffest + lastFrameCount; Dictionary <string, object> animStateInfo = new Dictionary <string, object>(); animStateInfo.Add("type", "skeleton"); animStateInfo.Add("wrap", clip.wrapMode); animStateInfo.Add("name", clipName); animStateInfo.Add("start", fromFrame); animStateInfo.Add("stop", toFrame); animStateInfo.Add("rate", clip.frameRate); animStateInfo.Add("frames", clipFrameCount); animStateInfo.Add("weight", 1.0f); animStateInfo.Add("behavior", (int)behavior); animStateInfo.Add("apparentSpeed", clip.apparentSpeed); animStateInfo.Add("averageSpeed", clip.averageSpeed.ToFloat()); animStateInfo.Add("averageDuration", clip.averageDuration); animStateInfo.Add("averageAngularSpeed", clip.averageAngularSpeed); List <string> customCurveKeyNames = new List <string>(); // .. // UnityEditor.AnimationParameters; // .. if (aparams != null && aparams.Count > 0) { foreach (var aparam in aparams) { if (aparam.curve == true) { var curve = Tools.GetAnimationCurve(clip, aparam.name); if (curve != null) { IEnumerable <BabylonAnimationKey> cx_keys = curve.keys.Select(keyFrame => new BabylonAnimationKey { frame = (int)(keyFrame.time * frameRate), values = new[] { keyFrame.value } }); BabylonAnimationKey[] xkeys = (cx_keys != null && cx_keys.Count() > 0) ? cx_keys.ToArray() : null; if (xkeys != null && xkeys.Length > 0) { string xkey = aparam.name; string xprop = "metadata.state.floats." + xkey; string xname = "curve:" + clipName.Replace(" ", "") + ":" + System.Guid.NewGuid().ToString(); customCurveKeyNames.Add(xname); anims.Add(new BabylonAnimation { dataType = (int)BabylonAnimation.DataType.Float, name = xname, keys = xkeys, framePerSecond = frameRate, enableBlending = false, blendingSpeed = 0.0f, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, property = xprop }); } } } } } animStateInfo.Add("customCurveKeyNames", (customCurveKeyNames.Count > 0) ? customCurveKeyNames.ToArray() : null); metaData.animationClips.Add(animStateInfo); ranges.Add(new BabylonRange { name = clipName, from = fromFrame, to = toFrame }); } // .. frameOffest += clipFrameCount; } var babylonAnimation = new BabylonAnimation { name = "skeleton:" + bone.name.ToLower() + ":animation", property = "_matrix", dataType = (int)BabylonAnimation.DataType.Matrix, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, enableBlending = false, blendingSpeed = 0.0f, framePerSecond = frameRate, keys = keys.ToArray() }; bone.animation = babylonAnimation; } if (AnimationMode.InAnimationMode()) { AnimationMode.StopAnimationMode(); } // // Serialize Skeleton Clip Ranges // skeleton.ranges = (ranges.Count > 0) ? ranges.ToArray() : null; // // Cache Babylon Animation Keys // if (anims.Count > 0) { List <BabylonAnimation> sourceAnimiamtions = null; if (SceneBuilder.AnimationCurveKeys.ContainsKey(sourceId)) { sourceAnimiamtions = SceneBuilder.AnimationCurveKeys[sourceId]; } else { sourceAnimiamtions = new List <BabylonAnimation>(); SceneBuilder.AnimationCurveKeys.Add(sourceId, sourceAnimiamtions); } foreach (var anim in anims) { sourceAnimiamtions.Add(anim); } } }
private GLTFSkin ExportSkin(BabylonSkeleton babylonSkeleton, GLTF gltf, GLTFNode gltfNode) { // Skin GLTFSkin gltfSkin = new GLTFSkin { name = babylonSkeleton.name }; gltfSkin.index = gltf.SkinsList.Count; gltf.SkinsList.Add(gltfSkin); var bones = new List <BabylonBone>(babylonSkeleton.bones); // Compute and store world matrix of each bone var bonesWorldMatrices = new Dictionary <int, BabylonMatrix>(); foreach (var babylonBone in babylonSkeleton.bones) { if (!bonesWorldMatrices.ContainsKey(babylonBone.index)) { BabylonMatrix boneWorldMatrix = _getBoneWorldMatrix(babylonBone, bones); bonesWorldMatrices.Add(babylonBone.index, boneWorldMatrix); } } // Buffer var buffer = GLTFBufferService.Instance.GetBuffer(gltf); // Accessor - InverseBindMatrices var accessorInverseBindMatrices = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatMat4(gltf, buffer), "accessorInverseBindMatrices", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.MAT4 ); gltfSkin.inverseBindMatrices = accessorInverseBindMatrices.index; // World matrix of the node var invNodeWorldMatrix = BabylonMatrix.Invert(_getNodeWorldMatrix(gltfNode)); // inverted var gltfJoints = new List <int>(); alreadyExportedBones = new Dictionary <BabylonBone, GLTFNode>(); foreach (var babylonBone in babylonSkeleton.bones) { // Export bone as a new node var gltfBoneNode = _exportBone(babylonBone, gltf, babylonSkeleton, bones); gltfJoints.Add(gltfBoneNode.index); // Set this bone as skeleton if it is a root if (babylonBone.parentBoneIndex == -1) { gltfSkin.skeleton = gltfBoneNode.index; } // Compute inverseBindMatrice for this bone when attached to this node var boneLocalMatrix = new BabylonMatrix(); boneLocalMatrix.m = babylonBone.matrix; BabylonMatrix boneWorldMatrix = null; if (babylonBone.parentBoneIndex == -1) { boneWorldMatrix = boneLocalMatrix; } else { var parentWorldMatrix = bonesWorldMatrices[babylonBone.parentBoneIndex]; // Remove scale of parent // This actually enable to take into account the scale of the bones, except for the root one parentWorldMatrix = _removeScale(parentWorldMatrix); boneWorldMatrix = boneLocalMatrix * parentWorldMatrix; } var inverseBindMatrices = BabylonMatrix.Invert(boneWorldMatrix * invNodeWorldMatrix); // Populate accessor List <float> matrix = new List <float>(inverseBindMatrices.m); matrix.ForEach(n => accessorInverseBindMatrices.bytesList.AddRange(BitConverter.GetBytes(n))); accessorInverseBindMatrices.count++; } gltfSkin.joints = gltfJoints.ToArray(); return(gltfSkin); }
private static void ExportSkeletonAnimation(SkinnedMeshRenderer skinnedMesh, BabylonMesh babylonMesh, BabylonSkeleton skeleton) { var animator = skinnedMesh.rootBone.gameObject.GetComponent <Animator>(); if (animator != null) { ExportSkeletonAnimationClips(animator, true, skeleton, skinnedMesh.bones, babylonMesh); } else { var parent = skinnedMesh.rootBone.parent; while (parent != null) { animator = parent.gameObject.GetComponent <Animator>(); if (animator != null) { ExportSkeletonAnimationClips(animator, true, skeleton, skinnedMesh.bones, babylonMesh); break; } parent = parent.parent; } } }
private void ExportSkin(IIGameSkin skin, BabylonScene babylonScene) { var babylonSkeleton = new BabylonSkeleton { id = skins.IndexOf(skin) }; babylonSkeleton.name = "skeleton #" + babylonSkeleton.id; RaiseMessage(babylonSkeleton.name, 1); var skinIndex = skins.IndexOf(skin); var bones = new List <BabylonBone>(); var gameBones = new List <IIGameNode>(); var boneIds = new List <int>(); var bindPoseInfos = new List <BonePoseInfo>(); for (int i = 0; i < skin.TotalSkinBoneCount; ++i) { bones.Add(null); gameBones.Add(null); boneIds.Add(-1); bindPoseInfos.Add(null); } for (var index = 0; index < skin.TotalSkinBoneCount; index++) { var gameBone = skin.GetIGameBone(index, false); var sortedIndex = skinSortedBones[skin].IndexOf(gameBone.NodeID); gameBones[sortedIndex] = (gameBone); boneIds[sortedIndex] = (gameBone.NodeID); bones[sortedIndex] = (new BabylonBone { index = sortedIndex, name = gameBone.Name }); var boneInitMatrix = gameBone.GetObjectTM(0); bindPoseInfos[sortedIndex] = (new BonePoseInfo { AbsoluteTransform = boneInitMatrix }); } // fix hierarchy and generate animation keys for (var index = 0; index < skin.TotalSkinBoneCount; index++) { var gameBone = gameBones[index]; var parent = gameBone.NodeParent; var babBone = bones[index]; if (parent != null) { babBone.parentBoneIndex = boneIds.IndexOf(parent.NodeID); } if (babBone.parentBoneIndex == -1) { bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform; } else { var parentBindPoseInfos = bindPoseInfos[babBone.parentBoneIndex]; bindPoseInfos[index].LocalTransform = bindPoseInfos[index].AbsoluteTransform.Multiply(parentBindPoseInfos.AbsoluteTransform.Inverse); } babBone.matrix = bindPoseInfos[index].LocalTransform.ToArray(); var babylonAnimation = ExportMatrixAnimation("_matrix", key => { var objectTM = gameBone.GetObjectTM(key); var parentNode = gameBone.NodeParent; IGMatrix mat; if (parentNode == null || babBone.parentBoneIndex == -1) { mat = objectTM; } else { mat = objectTM.Multiply(parentNode.GetObjectTM(key).Inverse); } return(mat.ToArray()); }, false); // Do not remove linear animation keys for bones if (babylonAnimation != null) { babylonAnimation.name = gameBone.Name + "Animation"; // override default animation name babBone.animation = babylonAnimation; } } babylonSkeleton.needInitialSkinMatrix = true; babylonSkeleton.bones = bones.ToArray(); babylonScene.SkeletonsList.Add(babylonSkeleton); }
private GLTFSkin ExportSkin(BabylonSkeleton babylonSkeleton, GLTF gltf, GLTFNode gltfNode, GLTFMesh gltfMesh) { logger.RaiseMessage("GLTFExporter.Skin | Export skin of node '" + gltfNode.name + "' based on skeleton '" + babylonSkeleton.name + "'", 2); // Retreive gltf skeleton data if babylon skeleton has already been exported if (!alreadyExportedSkeletons.ContainsKey(babylonSkeleton)) { alreadyExportedSkeletons.Add(babylonSkeleton, new BabylonSkeletonExportData()); // Switch coordinate system at object level foreach (var babylonBone in babylonSkeleton.bones) { var boneLocalMatrix = new BabylonMatrix(); boneLocalMatrix.m = babylonBone.matrix; var translationBabylon = new BabylonVector3(); var rotationQuatBabylon = new BabylonQuaternion(); var scale = new BabylonVector3(); boneLocalMatrix.decompose(scale, rotationQuatBabylon, translationBabylon); translationBabylon *= exportParameters.scaleFactor; translationBabylon.Z *= -1; rotationQuatBabylon.X *= -1; rotationQuatBabylon.Y *= -1; boneLocalMatrix = BabylonMatrix.Compose(scale, rotationQuatBabylon, translationBabylon); babylonBone.matrix = boneLocalMatrix.m; } } var babylonSkeletonExportData = alreadyExportedSkeletons[babylonSkeleton]; // Skin // if this mesh is sharing a skin with another mesh, use the already exported skin var sharedSkinnedMeshesByOriginalPair = sharedSkinnedMeshesByOriginal.Where(skinSharingMeshPair => skinSharingMeshPair.Value.Contains(gltfMesh)).Select(kvp => (KeyValuePair <GLTFMesh, List <GLTFMesh> >?)kvp).FirstOrDefault(); if (sharedSkinnedMeshesByOriginalPair != null) { logger.RaiseMessage("GLTFExporter.Skin | Sharing skinning information from mesh '" + sharedSkinnedMeshesByOriginalPair.Value.Key.name + "'", 3); var skeletonExportData = alreadyExportedSkeletons[babylonSkeleton]; gltfNode.skin = skeletonExportData.skinIndex; return(gltf.SkinsList[(int)gltfNode.skin]); } // otherwise create a new GLTFSkin var nameSuffix = babylonSkeletonExportData.nb != 0 ? "_" + babylonSkeletonExportData.nb : ""; GLTFSkin gltfSkin = new GLTFSkin { name = babylonSkeleton.name + nameSuffix }; gltfSkin.index = gltf.SkinsList.Count; gltf.SkinsList.Add(gltfSkin); babylonSkeletonExportData.nb++; babylonSkeletonExportData.skinIndex = gltfSkin.index; var bones = new List <BabylonBone>(babylonSkeleton.bones); // Compute and store world matrix of each bone var bonesWorldMatrices = new Dictionary <int, BabylonMatrix>(); foreach (var babylonBone in babylonSkeleton.bones) { if (!bonesWorldMatrices.ContainsKey(babylonBone.index)) { var nodePair = nodeToGltfNodeMap.First(pair => pair.Key.id.Equals(babylonBone.id)); BabylonMatrix boneWorldMatrix = _getNodeWorldMatrix(nodePair.Value); bonesWorldMatrices.Add(babylonBone.index, boneWorldMatrix); } } // Buffer var buffer = GLTFBufferService.Instance.GetBuffer(gltf); // Accessor - InverseBindMatrices var accessorInverseBindMatrices = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatMat4(gltf, buffer), "accessorInverseBindMatrices", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.MAT4 ); gltfSkin.inverseBindMatrices = accessorInverseBindMatrices.index; // World matrix of the node var nodeWorldMatrix = _getNodeWorldMatrix(gltfNode); var gltfJoints = new List <int>(); foreach (var babylonBone in babylonSkeleton.bones) { GLTFNode gltfBoneNode = null; if (!babylonSkeletonExportData.nodeByBone.ContainsKey(babylonBone)) { // Export bone as a new node gltfBoneNode = nodeToGltfNodeMap.FirstOrDefault(pair => pair.Key.id.Equals(babylonBone.id)).Value;//_exportBone(babylonBone, gltf, babylonSkeleton, bones); babylonSkeletonExportData.nodeByBone.Add(babylonBone, gltfBoneNode); } gltfBoneNode = babylonSkeletonExportData.nodeByBone[babylonBone]; gltfJoints.Add(gltfBoneNode.index); // Set this bone as skeleton if it is a root // Meaning of 'skeleton' here is the top root bone if (babylonBone.parentBoneIndex == -1) { gltfSkin.skeleton = gltfBoneNode.index; } // Compute inverseBindMatrice for this bone when attached to this node var boneLocalMatrix = new BabylonMatrix(); boneLocalMatrix.m = babylonBone.matrix; //printMatrix("boneLocalMatrix[" + babylonBone.name + "]", boneLocalMatrix); BabylonMatrix boneWorldMatrix = null; if (babylonBone.parentBoneIndex == -1) { boneWorldMatrix = boneLocalMatrix; } else { var parentWorldMatrix = bonesWorldMatrices[babylonBone.parentBoneIndex]; // Remove scale of parent // This actually enable to take into account the scale of the bones, except for the root one parentWorldMatrix = _removeScale(parentWorldMatrix); boneWorldMatrix = boneLocalMatrix * parentWorldMatrix; } //printMatrix("boneWorldMatrix[" + babylonBone.name + "]", boneWorldMatrix); var inverseBindMatrices = nodeWorldMatrix * BabylonMatrix.Invert(boneWorldMatrix); // Populate accessor List <float> matrix = new List <float>(inverseBindMatrices.m); matrix.ForEach(n => accessorInverseBindMatrices.bytesList.AddRange(BitConverter.GetBytes(n))); accessorInverseBindMatrices.count++; } gltfSkin.joints = gltfJoints.ToArray(); ExportGLTFExtension(babylonSkeleton, ref gltfNode, gltf); return(gltfSkin); }
private GLTFSkin ExportSkin(BabylonSkeleton babylonSkeleton, GLTF gltf, GLTFNode gltfNode) { RaiseMessage("GLTFExporter.Skin | Export skin of node '" + gltfNode.name + "' based on skeleton '" + babylonSkeleton.name + "'", 2); // Retreive gltf skeleton data if babylon skeleton has already been exported if (!alreadyExportedSkeletons.ContainsKey(babylonSkeleton)) { alreadyExportedSkeletons.Add(babylonSkeleton, new BabylonSkeletonExportData()); } var babylonSkeletonExportData = alreadyExportedSkeletons[babylonSkeleton]; // Skin var nameSuffix = babylonSkeletonExportData.nb != 0 ? "_" + babylonSkeletonExportData.nb : ""; GLTFSkin gltfSkin = new GLTFSkin { name = babylonSkeleton.name + nameSuffix }; gltfSkin.index = gltf.SkinsList.Count; gltf.SkinsList.Add(gltfSkin); babylonSkeletonExportData.nb++; var bones = new List <BabylonBone>(babylonSkeleton.bones); // Compute and store world matrix of each bone var bonesWorldMatrices = new Dictionary <int, BabylonMatrix>(); foreach (var babylonBone in babylonSkeleton.bones) { if (!bonesWorldMatrices.ContainsKey(babylonBone.index)) { BabylonMatrix boneWorldMatrix = _getBoneWorldMatrix(babylonBone, bones); bonesWorldMatrices.Add(babylonBone.index, boneWorldMatrix); } } // Buffer var buffer = GLTFBufferService.Instance.GetBuffer(gltf); // Accessor - InverseBindMatrices var accessorInverseBindMatrices = GLTFBufferService.Instance.CreateAccessor( gltf, GLTFBufferService.Instance.GetBufferViewFloatMat4(gltf, buffer), "accessorInverseBindMatrices", GLTFAccessor.ComponentType.FLOAT, GLTFAccessor.TypeEnum.MAT4 ); gltfSkin.inverseBindMatrices = accessorInverseBindMatrices.index; // World matrix of the node var nodeWorldMatrix = _getNodeWorldMatrix(gltfNode); //printMatrix("nodeWorldMatrix[" + gltfNode.name + "]", nodeWorldMatrix); var gltfJoints = new List <int>(); alreadyExportedBones = new Dictionary <BabylonBone, GLTFNode>(); foreach (var babylonBone in babylonSkeleton.bones) { GLTFNode gltfBoneNode = null; if (!babylonSkeletonExportData.nodeByBone.ContainsKey(babylonBone)) { // Export bone as a new node gltfBoneNode = _exportBone(babylonBone, gltf, babylonSkeleton, bones); babylonSkeletonExportData.nodeByBone.Add(babylonBone, gltfBoneNode); } gltfBoneNode = babylonSkeletonExportData.nodeByBone[babylonBone]; gltfJoints.Add(gltfBoneNode.index); // Set this bone as skeleton if it is a root // Meaning of 'skeleton' here is the top root bone if (babylonBone.parentBoneIndex == -1) { gltfSkin.skeleton = gltfBoneNode.index; } // Compute inverseBindMatrice for this bone when attached to this node var boneLocalMatrix = new BabylonMatrix(); boneLocalMatrix.m = babylonBone.matrix; //printMatrix("boneLocalMatrix[" + babylonBone.name + "]", boneLocalMatrix); BabylonMatrix boneWorldMatrix = null; if (babylonBone.parentBoneIndex == -1) { boneWorldMatrix = boneLocalMatrix; } else { var parentWorldMatrix = bonesWorldMatrices[babylonBone.parentBoneIndex]; // Remove scale of parent // This actually enable to take into account the scale of the bones, except for the root one parentWorldMatrix = _removeScale(parentWorldMatrix); boneWorldMatrix = boneLocalMatrix * parentWorldMatrix; } //printMatrix("boneWorldMatrix[" + babylonBone.name + "]", boneWorldMatrix); var inverseBindMatrices = nodeWorldMatrix * BabylonMatrix.Invert(boneWorldMatrix); // Populate accessor List <float> matrix = new List <float>(inverseBindMatrices.m); matrix.ForEach(n => accessorInverseBindMatrices.bytesList.AddRange(BitConverter.GetBytes(n))); accessorInverseBindMatrices.count++; } gltfSkin.joints = gltfJoints.ToArray(); return(gltfSkin); }
void ParseMesh(ModelMesh modelMesh, BabylonScene scene, BabylonSkeleton skeleton, bool rightToLeft) { var proxyID = ProxyMesh.CreateBabylonMesh(modelMesh.Name, scene); int indexName = 0; foreach (var part in modelMesh.MeshParts) { var material = exportedMaterials.First(m => m.Name == part.Effect.GetHashCode().ToString()); var indices = new ushort[part.PrimitiveCount * 3]; part.IndexBuffer.GetData(part.StartIndex * 2, indices, 0, indices.Length); if (rightToLeft) { for (int ib = 0; ib < indices.Length; ib += 3) // reverse winding of triangles { ushort ti = indices[ib]; indices[ib] = indices[ib + 2]; indices[ib + 2] = ti; } } if (part.VertexBuffer.VertexDeclaration.VertexStride >= PositionNormalTexturedWeights.Stride) { var mesh = new Mesh<PositionNormalTexturedWeights>(material); var vertices = new PositionNormalTexturedWeights[part.NumVertices]; part.VertexBuffer.GetData(part.VertexOffset * part.VertexBuffer.VertexDeclaration.VertexStride, vertices, 0, vertices.Length, part.VertexBuffer.VertexDeclaration.VertexStride); for (int index = 0; index < vertices.Length; index++) { vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y; if (rightToLeft) { vertices[index].Position.Z = -vertices[index].Position.Z; vertices[index].Normal.Z = -vertices[index].Normal.Z; } } mesh.AddPart(modelMesh.Name+"#"+indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList()); mesh.CreateBabylonMesh(scene, proxyID, skeleton); } else { if (part.VertexBuffer.VertexDeclaration.VertexStride < PositionNormalTextured.Stride) return; // Error: Not a PositionNormalTextured mesh! var mesh = new Mesh<PositionNormalTextured>(material); var vertices = new PositionNormalTextured[part.NumVertices]; part.VertexBuffer.GetData(part.VertexOffset * part.VertexBuffer.VertexDeclaration.VertexStride, vertices, 0, vertices.Length, part.VertexBuffer.VertexDeclaration.VertexStride); for (int index = 0; index < vertices.Length; index++) { vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y; if (rightToLeft) { vertices[index].Position.Z = -vertices[index].Position.Z; vertices[index].Normal.Z = -vertices[index].Normal.Z; } } mesh.AddPart(modelMesh.Name + "#" + indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList()); mesh.CreateBabylonMesh(scene, proxyID, skeleton); } indexName++; } }
private void ParseAnimationClip(SkinningData skinningData, BabylonSkeleton skeleton) { foreach (var clipKey in skinningData.AnimationClips.Keys) { var clip = skinningData.AnimationClips[clipKey]; var duration = clip.Duration.TotalMilliseconds; var dic = new Dictionary<int, List<BabylonAnimationKey>>(); foreach (var keyframe in clip.Keyframes) { if (!dic.ContainsKey(keyframe.Bone)) { dic.Add(keyframe.Bone, new List<BabylonAnimationKey>()); } var currentTime = (float)(keyframe.Time.TotalMilliseconds * 100.0 / duration); dic[keyframe.Bone].Add(new BabylonAnimationKey { frame = currentTime, values = keyframe.Transform.ToMatrix().ToArray() }); } foreach (var index in dic.Keys) { var bone = skeleton.bones[index]; var babylonAnimation = new BabylonAnimation { name = bone.name + "Animation", property = "_matrix", dataType = BabylonAnimation.DataType.Matrix, loopBehavior = InterpolationLoop.Cycle, framePerSecond = 60 }; babylonAnimation.keys = dic[index].ToArray(); bone.animation = babylonAnimation; } return; // Only one animation track } }
private static void ExportSkeletonAnimationClips(Animator animator, bool autoPlay, BabylonSkeleton skeleton, Transform[] bones, BabylonMesh babylonMesh) { AnimationClip clip = null; AnimatorController ac = animator.runtimeAnimatorController as AnimatorController; if (ac == null) { return; } var layer = ac.layers[0]; if (layer == null) { return; } AnimatorStateMachine sm = layer.stateMachine; if (sm.states.Length > 0) { // Only the first state is supported so far. var state = sm.states[0].state; clip = state.motion as AnimationClip; } if (clip == null) { return; } ExportSkeletonAnimationClipData(animator, autoPlay, skeleton, bones, babylonMesh, clip); }
private IList <BabylonAnimationGroup> ExportAnimationGroups(BabylonScene babylonScene) { IList <BabylonAnimationGroup> animationGroups = new List <BabylonAnimationGroup>(); // Retrieve and parse animation group data AnimationGroupList animationList = InitAnimationGroups(); foreach (AnimationGroup animGroup in animationList) { RaiseMessage("Exporter.animationGroups | " + animGroup.Name, 1); BabylonAnimationGroup animationGroup = new BabylonAnimationGroup { name = animGroup.Name, from = animGroup.FrameStart, to = animGroup.FrameEnd, targetedAnimations = new List <BabylonTargetedAnimation>() }; // add animations of each nodes contained in the animGroup foreach (uint nodeHandle in animGroup.NodeHandles) { IINode maxNode = Loader.Core.RootNode.FindChildNode(nodeHandle); // node could have been deleted, silently ignore it if (maxNode == null) { continue; } // Helpers can be exported as dummies and as bones string nodeId = maxNode.GetGuid().ToString(); string boneId = maxNode.GetGuid().ToString() + "-bone"; // the suffix "-bone" is added in babylon export format to assure the uniqueness of IDs // Node BabylonNode node = babylonScene.MeshesList.FirstOrDefault(m => m.id == nodeId); if (node == null) { node = babylonScene.CamerasList.FirstOrDefault(c => c.id == nodeId); } if (node == null) { node = babylonScene.LightsList.FirstOrDefault(l => l.id == nodeId); } if (node != null) { if (node.animations != null && node.animations.Length != 0) { IList <BabylonAnimation> animations = GetSubAnimations(node, animationGroup.from, animationGroup.to); foreach (BabylonAnimation animation in animations) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = animation, targetId = nodeId }; animationGroup.targetedAnimations.Add(targetedAnimation); } } else if (exportNonAnimated) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = CreatePositionAnimation(animationGroup.from, animationGroup.to, node.position), targetId = node.id }; animationGroup.targetedAnimations.Add(targetedAnimation); } } // bone BabylonBone bone = null; int index = 0; while (index < babylonScene.SkeletonsList.Count && bone == null) { BabylonSkeleton skel = babylonScene.SkeletonsList[index]; bone = skel.bones.FirstOrDefault(b => b.id == boneId); index++; } if (bone != null) { if (bone.animation != null) { IList <BabylonAnimation> animations = GetSubAnimations(bone, animationGroup.from, animationGroup.to); foreach (BabylonAnimation animation in animations) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = animation, targetId = boneId }; animationGroup.targetedAnimations.Add(targetedAnimation); } } else if (exportNonAnimated) { BabylonTargetedAnimation targetedAnimation = new BabylonTargetedAnimation { animation = CreateMatrixAnimation(animationGroup.from, animationGroup.to, bone.matrix), targetId = bone.id }; animationGroup.targetedAnimations.Add(targetedAnimation); } } } if (animationGroup.targetedAnimations.Count > 0) { animationGroups.Add(animationGroup); } } return(animationGroups); }
void ParseMesh(ModelMesh modelMesh, BabylonScene scene, BabylonSkeleton skeleton) { var proxyID = ProxyMesh.CreateBabylonMesh(modelMesh.Name, scene); int indexName = 0; foreach (var part in modelMesh.MeshParts) { var material = exportedMaterials.First(m => m.Name == part.Effect.GetHashCode().ToString()); var indices = new ushort[part.PrimitiveCount * 3]; part.IndexBuffer.GetData(part.StartIndex * 2, indices, 0, indices.Length); if (part.VertexBuffer.VertexDeclaration.VertexStride >= PositionNormalTexturedWeights.Stride) { var mesh = new Mesh<PositionNormalTexturedWeights>(material); var vertices = new PositionNormalTexturedWeights[part.NumVertices]; part.VertexBuffer.GetData(part.VertexOffset * part.VertexBuffer.VertexDeclaration.VertexStride, vertices, 0, vertices.Length, part.VertexBuffer.VertexDeclaration.VertexStride); for (int index = 0; index < vertices.Length; index++) { vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y; } mesh.AddPart(indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList()); mesh.CreateBabylonMesh(scene, proxyID, skeleton); } else { var mesh = new Mesh<PositionNormalTextured>(material); var vertices = new PositionNormalTextured[part.NumVertices]; part.VertexBuffer.GetData(part.VertexOffset * PositionNormalTextured.Stride, vertices, 0, vertices.Length, PositionNormalTextured.Stride); for (int index = 0; index < vertices.Length; index++) { vertices[index].TextureCoordinates.Y = 1.0f - vertices[index].TextureCoordinates.Y; } mesh.AddPart(indexName.ToString(), vertices.ToList(), indices.Select(i => (int)i).ToList()); mesh.CreateBabylonMesh(scene, proxyID, skeleton); } indexName++; } }