public AnimationWithSampleCurves Export(AnimationClip clip, Transform root, List <Transform> nodes) { var animation = new AnimationWithSampleCurves { animation = new GLTFAnimation(), }; List <AnimationCurveData> curveDatas = new List <AnimationCurveData>(); var bindings = AnimationUtility.GetCurveBindings(clip); foreach (var binding in bindings) { var property = PropertyToTarget(binding.propertyName); if (property == GLTFAnimationTarget.AnimationProperty.NotImplemented) { Debug.LogWarning("Not Implemented keyframe property : " + binding.propertyName); continue; } if (property == GLTFAnimationTarget.AnimationProperty.EulerRotation) { Debug.LogWarning("Interpolation setting of AnimationClip should be Quaternion"); continue; } var curve = AnimationUtility.GetEditorCurve(clip, binding); var nodeIndex = GetNodeIndex(root, nodes, binding.path); var samplerIndex = animation.animation.AddChannelAndGetSampler(nodeIndex, property); var elementCount = 0; if (property == GLTFAnimationTarget.AnimationProperty.BlendShape) { var mesh = nodes[nodeIndex].GetComponent <SkinnedMeshRenderer>().sharedMesh; elementCount = mesh.blendShapeCount; } else { elementCount = GLTFAnimationTarget.GetElementCount(property); } Debug.Log("export animation binding ... " + nodeIndex + " == " + elementCount); // 同一のsamplerIndexが割り当てられているcurveDataがある場合はそれを使用し、無ければ作る var curveData = curveDatas.FirstOrDefault(x => x.samplerIndex == samplerIndex); if (curveData == null) { curveData = new AnimationCurveData(AnimationUtility.GetKeyRightTangentMode(curve, 0), property, samplerIndex, elementCount); curveDatas.Add(curveData); } // 全てのキーフレームを回収 int elementOffset = 0; float valueFactor = 1.0f; if (property == GLTFAnimationTarget.AnimationProperty.BlendShape) { var mesh = nodes[nodeIndex].GetComponent <SkinnedMeshRenderer>().sharedMesh; var blendShapeName = binding.propertyName.Replace("blendShape.", ""); elementOffset = mesh.GetBlendShapeIndex(blendShapeName); valueFactor = 0.01f; } else { elementOffset = GetElementOffset(binding.propertyName); } if (elementOffset >= 0 && elementOffset < elementCount) { for (int i = 0; i < curve.keys.Length; i++) { curveData.SetKeyframeData(curve.keys[i].time, curve.keys[i].value * valueFactor, elementOffset); } } } //キー挿入 foreach (var curve in curveDatas) { if (curve.Keyframes.Count == 0) { continue; } curve.RecountEmptyKeyframe(); var elementNum = curve.Keyframes.First().Values.Length; var values = default(InputOutputValues); if (!animation.samplers.TryGetValue(curve.samplerIndex, out values)) { values = new InputOutputValues(); values.input = new float[curve.Keyframes.Count]; values.output = new float[curve.Keyframes.Count * elementNum]; animation.samplers[curve.samplerIndex] = values; animation.animation.samplers[curve.samplerIndex].interpolation = curve.GetInterpolation(); } int keyframeIndex = 0; foreach (var keyframe in curve.Keyframes) { values.input[keyframeIndex] = keyframe.time; Buffer.BlockCopy(keyframe.GetRightHandCoordinate(), 0, values.output, keyframeIndex * elementNum * sizeof(float), elementNum * sizeof(float)); keyframeIndex++; } } return(animation); }
void FromGameObject(GLTFRoot gltf, GameObject go, bool useSparseAccessorForMorphTarget = false, int textureFormat = 0) { var bytesBuffer = new ArrayByteBuffer(new byte[50 * 1024 * 1024]); var bufferIndex = gltf.AddBuffer(bytesBuffer); GameObject tmpParent = null; if (go.transform.childCount == 0) { tmpParent = new GameObject("tmpParent"); go.transform.SetParent(tmpParent.transform, true); go = tmpParent; } try { // exclude root object for the symmetry with the importer nodes = go.transform.Traverse().Skip(1).ToList(); #region Materials and Textures materials = nodes.SelectMany(x => x.GetSharedMaterials()).Where(x => x != null).Distinct().ToList(); var unityTextures = materials.SelectMany(x => x.GetTextures()).Where(x => x.Texture != null).Distinct().ToList(); textureManager = new TextureExportManager(unityTextures); gltf.materials = materials.Select(x => _materialExporter.ExportMaterial(x, textureManager)).ToList(); Debug.Log("image count = " + unityTextures.Count); for (int i = 0; i < unityTextures.Count; ++i) { gltf.ExportTexture(bufferIndex, textureManager.GetExportTexture(i), unityTextures[i].TextureType, textureFormat); } #endregion #region Meshes var unityMeshes = nodes .Select(x => new MeshWithRenderer { mesh = x.GetSharedMesh(), rendererer = x.GetComponent <Renderer>(), }) .Where(x => { if (x.mesh == null) { return(false); } if (x.rendererer.sharedMaterials == null || x.rendererer.sharedMaterials.Length == 0) { return(false); } return(true); }) .ToList(); Debug.Log("unityMesher...." + unityMeshes.Count); _meshExporter.Export(gltf, bufferIndex, unityMeshes, materials, useSparseAccessorForMorphTarget); meshes = unityMeshes.Select(x => x.mesh).ToList(); #endregion #region Skins var unitySkins = nodes .Select(x => x.GetComponent <SkinnedMeshRenderer>()).Where(x => x != null && x.bones != null && x.bones.Length > 0) .ToList(); gltf.nodes = nodes.Select(x => ExportNode(x, nodes, unityMeshes.Select(y => y.mesh).ToList(), unitySkins)).ToList(); gltf.scenes = new List <GLTFScene> { new GLTFScene { nodes = go.transform.GetChildren().Select(x => nodes.IndexOf(x)).ToArray(), } }; foreach (var x in unitySkins) { var matrices = x.sharedMesh.bindposes.Select(y => y.ReverseZ()).ToArray(); var accessor = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, matrices, GLTFBufferTarget.NONE); var skin = new GLTFSkin { inverseBindMatrices = accessor, joints = x.bones.Select(y => nodes.IndexOf(y)).ToArray(), skeleton = nodes.IndexOf(x.rootBone), }; var skinIndex = gltf.skins.Count; gltf.skins.Add(skin); foreach (var z in nodes.Where(y => y.HasComponent(x))) { var nodeIndex = nodes.IndexOf(z); var node = gltf.nodes[nodeIndex]; node.skin = skinIndex; } } #endregion #if UNITY_EDITOR #region Animations var clips = new List <AnimationClip>(); var animator = go.GetComponent <Animator>(); var animation = go.GetComponent <Animation>(); if (animator != null) { clips = _animationExporter.GetAnimationClips(animator); } else if (animation != null) { clips = _animationExporter.GetAnimationClips(animation); } if (clips.Any()) { Debug.Log("export clips.." + clips.Count); foreach (AnimationClip clip in clips) { var animationWithCurve = _animationExporter.Export(clip, go.transform, nodes); foreach (var kv in animationWithCurve.samplers) { var sampler = animationWithCurve.animation.samplers[kv.Key]; var inputAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, kv.Value.input); sampler.input = inputAccessorIndex; var outputAccessorIndex = gltf.ExtendBufferAndGetAccessorIndex(bufferIndex, kv.Value.output); sampler.output = outputAccessorIndex; Debug.Log(sampler.interpolation + ">>" + string.Join(",", kv.Value.output)); // modify accessors var outputAccessor = gltf.accessors[outputAccessorIndex]; var channel = animationWithCurve.animation.channels.First(x => x.sampler == kv.Key); switch (GLTFAnimationTarget.GetElementCount(channel.target.path)) { case 1: outputAccessor.type = "SCALAR"; //outputAccessor.count = ; break; case 3: outputAccessor.type = "VEC3"; outputAccessor.count /= 3; break; case 4: outputAccessor.type = "VEC4"; outputAccessor.count /= 4; break; default: throw new NotImplementedException(); } } animationWithCurve.animation.name = clip.name; gltf.animations.Add(animationWithCurve.animation); } } #endregion #endif } finally { if (tmpParent != null) { tmpParent.transform.GetChild(0).SetParent(null); if (Application.isPlaying) { GameObject.Destroy(tmpParent); } else { GameObject.DestroyImmediate(tmpParent); } } } }