private static IEnumerator LoadAsync(string json, string filepath, byte[] bytefile, long binChunkStart, ImportSettings importSettings, Action <GameObject, GLTFAnimation.ImportResult[]> onFinished, Action <float> onProgress = null) { // Threaded deserialization Task <GLTFObject> deserializeTask = new Task <GLTFObject>(() => JsonConvert.DeserializeObject <GLTFObject>(json)); deserializeTask.Start(); while (!deserializeTask.IsCompleted) { yield return(null); } GLTFObject gltfObject = deserializeTask.Result; // directory root is sometimes used for loading buffers from containing file, or local images string directoryRoot = filepath != null?Directory.GetParent(filepath).ToString() + "/" : null; importSettings.shaderOverrides.CacheDefaultShaders(); // Setup import tasks List <ImportTask> importTasks = new List <ImportTask>(); GLTFBuffer.ImportTask bufferTask = new GLTFBuffer.ImportTask(gltfObject.buffers, filepath, bytefile, binChunkStart); importTasks.Add(bufferTask); GLTFBufferView.ImportTask bufferViewTask = new GLTFBufferView.ImportTask(gltfObject.bufferViews, bufferTask); importTasks.Add(bufferViewTask); GLTFAccessor.ImportTask accessorTask = new GLTFAccessor.ImportTask(gltfObject.accessors, bufferViewTask); importTasks.Add(accessorTask); GLTFImage.ImportTask imageTask = new GLTFImage.ImportTask(gltfObject.images, directoryRoot, bufferViewTask); importTasks.Add(imageTask); GLTFTexture.ImportTask textureTask = new GLTFTexture.ImportTask(gltfObject.textures, imageTask); importTasks.Add(textureTask); GLTFMaterial.ImportTask materialTask = new GLTFMaterial.ImportTask(gltfObject.materials, textureTask, importSettings); importTasks.Add(materialTask); GLTFMesh.ImportTask meshTask = new GLTFMesh.ImportTask(gltfObject.meshes, accessorTask, materialTask, importSettings); importTasks.Add(meshTask); GLTFSkin.ImportTask skinTask = new GLTFSkin.ImportTask(gltfObject.skins, accessorTask); importTasks.Add(skinTask); GLTFNode.ImportTask nodeTask = new GLTFNode.ImportTask(gltfObject.nodes, meshTask, skinTask, gltfObject.cameras); importTasks.Add(nodeTask); // Ignite for (int i = 0; i < importTasks.Count; i++) { TaskSupervisor(importTasks[i], onProgress).RunCoroutine(); } // Wait for all tasks to finish while (!importTasks.All(x => x.IsCompleted)) { yield return(null); } // Close file streams foreach (var item in bufferTask.Result) { item.Dispose(); } // Fire onFinished when all tasks have completed GameObject root = nodeTask.Result.GetRoot(); GLTFAnimation.ImportResult[] animations = gltfObject.animations.Import(accessorTask.Result, nodeTask.Result, importSettings); if (onFinished != null) { onFinished(nodeTask.Result.GetRoot(), animations); } }
public static GameObject LoadFromFile(string filepath, ImportSettings importSettings, Format format = Format.AUTO) { GLTFAnimation.ImportResult[] animations; return(LoadFromFile(filepath, importSettings, out animations, format)); }
private static GameObject LoadInternal(this GLTFObject gltfObject, string filepath, byte[] bytefile, long binChunkStart, ImportSettings importSettings, out GLTFAnimation.ImportResult[] animations) { // directory root is sometimes used for loading buffers from containing file, or local images string directoryRoot = filepath != null?Directory.GetParent(filepath).ToString() + "/" : null; importSettings.shaderOverrides.CacheDefaultShaders(); // Import tasks synchronously GLTFBuffer.ImportTask bufferTask = new GLTFBuffer.ImportTask(gltfObject.buffers, filepath, bytefile, binChunkStart); bufferTask.RunSynchronously(); GLTFBufferView.ImportTask bufferViewTask = new GLTFBufferView.ImportTask(gltfObject.bufferViews, bufferTask); bufferViewTask.RunSynchronously(); GLTFAccessor.ImportTask accessorTask = new GLTFAccessor.ImportTask(gltfObject.accessors, bufferViewTask); accessorTask.RunSynchronously(); GLTFImage.ImportTask imageTask = new GLTFImage.ImportTask(gltfObject.images, directoryRoot, bufferViewTask); imageTask.RunSynchronously(); GLTFTexture.ImportTask textureTask = new GLTFTexture.ImportTask(gltfObject.textures, imageTask); textureTask.RunSynchronously(); GLTFMaterial.ImportTask materialTask = new GLTFMaterial.ImportTask(gltfObject.materials, textureTask, importSettings); materialTask.RunSynchronously(); GLTFMesh.ImportTask meshTask = new GLTFMesh.ImportTask(gltfObject.meshes, accessorTask, materialTask, importSettings); meshTask.RunSynchronously(); GLTFSkin.ImportTask skinTask = new GLTFSkin.ImportTask(gltfObject.skins, accessorTask); skinTask.RunSynchronously(); GLTFNode.ImportTask nodeTask = new GLTFNode.ImportTask(gltfObject.nodes, meshTask, skinTask, gltfObject.cameras); nodeTask.RunSynchronously(); animations = gltfObject.animations.Import(accessorTask.Result, nodeTask.Result, importSettings); foreach (var item in bufferTask.Result) { item.Dispose(); } return(nodeTask.Result.GetRoot()); }
/// <param name="bytes">GLB file is supported</param> public static GameObject LoadFromBytes(byte[] bytes, ImportSettings importSettings, out AnimationClip[] animations, Action <GameObject, Dictionary <string, object> > handleExtensions = null) { return(ImportGLB(bytes, importSettings, out animations, handleExtensions)); }
private static GameObject LoadInternal(this GLTFObject gltfObject, string filepath, ImportSettings importSettings, out GLTFAnimation.ImportResult[] animations) { // directory root is sometimes used for loading buffers from containing file, or local images string directoryRoot = Directory.GetParent(filepath).ToString() + "/"; // Import tasks synchronously GLTFBuffer.ImportTask bufferTask = new GLTFBuffer.ImportTask(gltfObject.buffers, filepath); bufferTask.RunSynchronously(); GLTFBufferView.ImportTask bufferViewTask = new GLTFBufferView.ImportTask(gltfObject.bufferViews, bufferTask); bufferViewTask.RunSynchronously(); GLTFAccessor.ImportTask accessorTask = new GLTFAccessor.ImportTask(gltfObject.accessors, bufferViewTask); accessorTask.RunSynchronously(); GLTFImage.ImportTask imageTask = new GLTFImage.ImportTask(gltfObject.images, directoryRoot, bufferViewTask); imageTask.RunSynchronously(); GLTFTexture.ImportTask textureTask = new GLTFTexture.ImportTask(gltfObject.textures, imageTask); textureTask.RunSynchronously(); GLTFMaterial.ImportTask materialTask = new GLTFMaterial.ImportTask(gltfObject.materials, textureTask, importSettings); materialTask.RunSynchronously(); GLTFMesh.ImportTask meshTask = new GLTFMesh.ImportTask(gltfObject.meshes, accessorTask, materialTask, importSettings); meshTask.RunSynchronously(); GLTFSkin.ImportTask skinTask = new GLTFSkin.ImportTask(gltfObject.skins, accessorTask); skinTask.RunSynchronously(); GLTFNode.ImportTask nodeTask = new GLTFNode.ImportTask(gltfObject.nodes, meshTask, skinTask, gltfObject.cameras); nodeTask.RunSynchronously(); animations = gltfObject.animations.Import(accessorTask.Result, nodeTask.Result); return(nodeTask.Result.GetRoot()); }
private static GameObject LoadInternal(this GLTFObject gltfObject, string filepath, ImportSettings importSettings, out GLTFAnimation.ImportResult[] animations) { string directoryRoot = Directory.GetParent(filepath).ToString() + "/"; GLTFBuffer.ImportResult[] buffers = gltfObject.buffers.Select(x => x.Import(filepath)).ToArray(); GLTFBufferView.ImportResult[] bufferViews = gltfObject.bufferViews.Select(x => x.Import(buffers)).ToArray(); GLTFAccessor.ImportResult[] accessors = gltfObject.accessors.Select(x => x.Import(bufferViews)).ToArray(); GLTFImage.ImportResult[] images = gltfObject.images.Import(directoryRoot, bufferViews); GLTFTexture.ImportResult[] textures = gltfObject.textures.Import(images); GLTFMaterial.ImportResult materials = gltfObject.materials.Import(textures, importSettings); GLTFMesh.ImportResult[] meshes = gltfObject.meshes.Import(accessors, materials, importSettings); GLTFSkin.ImportResult[] skins = gltfObject.skins.Import(accessors); GLTFNode.ImportResult[] nodes = gltfObject.nodes.Import(meshes, skins); animations = gltfObject.animations.Import(accessors, nodes); return(nodes.GetRoot()); }
public ImportTask(List <GLTFMesh> meshes, GLTFAccessor.ImportTask accessorTask, GLTFBufferView.ImportTask bufferViewTask, GLTFMaterial.ImportTask materialTask, ImportSettings importSettings) : base(accessorTask, materialTask) { this.meshes = meshes; this.materialTask = materialTask; task = new Task(() => { if (meshes == null) { return; } meshData = new MeshData[meshes.Count]; for (int i = 0; i < meshData.Length; i++) { meshData[i] = new MeshData(meshes[i], accessorTask.Result, bufferViewTask.Result); } }); }
public static void SaveToAsset(GameObject root, AnimationClip[] animations, AssetImportContext ctx, ImportSettings settings) { #if UNITY_2018_2_OR_NEWER ctx.AddObjectToAsset("main", root); ctx.SetMainObject(root); #else ctx.SetMainAsset("main obj", root); #endif MeshRenderer[] renderers = root.GetComponentsInChildren <MeshRenderer>(true); SkinnedMeshRenderer[] skinnedRenderers = root.GetComponentsInChildren <SkinnedMeshRenderer>(true); MeshFilter[] filters = root.GetComponentsInChildren <MeshFilter>(true); AddMeshes(filters, skinnedRenderers, ctx, settings.generateLightmapUVs); AddMaterials(renderers, skinnedRenderers, ctx); AddAnimations(animations, ctx, settings.animationSettings); }
public static GLTFMesh.ImportResult[] Import(this List <GLTFMesh> meshes, GLTFAccessor.ImportResult[] accessors, GLTFMaterial.ImportResult materials, ImportSettings importSettings) { GLTFMesh.ImportResult[] results = new GLTFMesh.ImportResult[meshes.Count]; for (int i = 0; i < results.Length; i++) { results[i] = new GLTFMesh.ImportResult(); results[i].mesh = meshes[i].CreateMesh(accessors); results[i].materials = new Material[meshes[i].primitives.Count]; for (int k = 0; k < meshes[i].primitives.Count; k++) { int?matIndex = meshes[i].primitives[k].material; if (matIndex.HasValue && materials != null) { results[i].materials[k] = materials.materials[matIndex.Value]; } else { results[i].materials[k] = GLTFMaterial.defaultMaterial; } } if (meshes[i].name == null) { meshes[i].name = "mesh" + i; } } return(results); }
public ImportResult Import(GLTFAccessor.ImportResult[] accessors, GLTFNode.ImportResult[] nodes, ImportSettings importSettings) { bool multiRoots = nodes.Where(x => x.IsRoot).Count() > 1; ImportResult result = new ImportResult(); result.clip = new AnimationClip(); result.clip.name = name; result.clip.frameRate = importSettings.animationSettings.frameRate; result.clip.legacy = importSettings.animationSettings.useLegacyClips; if (result.clip.legacy && importSettings.animationSettings.looping) { result.clip.wrapMode = WrapMode.Loop; } for (int i = 0; i < channels.Length; i++) { Channel channel = channels[i]; if (samplers.Length <= channel.sampler) { Debug.LogWarning($"GLTFUtility: Animation channel points to sampler at index {channel.sampler} which doesn't exist. Skipping animation clip."); continue; } Sampler sampler = samplers[channel.sampler]; // Get interpolation mode InterpolationMode interpolationMode = importSettings.animationSettings.interpolationMode; if (interpolationMode == InterpolationMode.ImportFromFile) { interpolationMode = sampler.interpolation; } if (interpolationMode == InterpolationMode.CUBICSPLINE) { Debug.LogWarning("Animation interpolation mode CUBICSPLINE not fully supported, result might look different."); } string relativePath = ""; GLTFNode.ImportResult node = nodes[channel.target.node.Value]; while (node != null && !node.IsRoot) { if (string.IsNullOrEmpty(relativePath)) { relativePath = node.transform.name; } else { relativePath = node.transform.name + "/" + relativePath; } if (node.parent.HasValue) { node = nodes[node.parent.Value]; } else { node = null; } } // If file has multiple root nodes, a new parent will be created for them as a final step of the import process. This parent f***s up the curve relative paths. // Add node.transform.name to path if there are multiple roots. This is not the most elegant fix but it works. // See GLTFNodeExtensions.GetRoot if (multiRoots) { relativePath = node.transform.name + "/" + relativePath; } System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; float[] keyframeInput = accessors[sampler.input].ReadFloat().ToArray(); switch (channel.target.path) { case "translation": Vector3[] pos = accessors[sampler.output].ReadVec3().ToArray(); AnimationCurve posX = new AnimationCurve(); AnimationCurve posY = new AnimationCurve(); AnimationCurve posZ = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { posX.AddKey(CreateKeyframe(k, keyframeInput, pos, x => - x.x, interpolationMode)); posY.AddKey(CreateKeyframe(k, keyframeInput, pos, x => x.y, interpolationMode)); posZ.AddKey(CreateKeyframe(k, keyframeInput, pos, x => x.z, interpolationMode)); } result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.x", posX); result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.y", posY); result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.z", posZ); break; case "rotation": Vector4[] rot = accessors[sampler.output].ReadVec4().ToArray(); AnimationCurve rotX = new AnimationCurve(); AnimationCurve rotY = new AnimationCurve(); AnimationCurve rotZ = new AnimationCurve(); AnimationCurve rotW = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { // The Animation window in Unity shows keyframes incorrectly converted to euler. This is only to deceive you. The quaternions underneath work correctly rotX.AddKey(CreateKeyframe(k, keyframeInput, rot, x => x.x, interpolationMode)); rotY.AddKey(CreateKeyframe(k, keyframeInput, rot, x => - x.y, interpolationMode)); rotZ.AddKey(CreateKeyframe(k, keyframeInput, rot, x => - x.z, interpolationMode)); rotW.AddKey(CreateKeyframe(k, keyframeInput, rot, x => x.w, interpolationMode)); } result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.x", rotX); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.y", rotY); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.z", rotZ); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.w", rotW); break; case "scale": Vector3[] scale = accessors[sampler.output].ReadVec3().ToArray(); AnimationCurve scaleX = new AnimationCurve(); AnimationCurve scaleY = new AnimationCurve(); AnimationCurve scaleZ = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { scaleX.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.x, interpolationMode)); scaleY.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.y, interpolationMode)); scaleZ.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.z, interpolationMode)); } result.clip.SetCurve(relativePath, typeof(Transform), "localScale.x", scaleX); result.clip.SetCurve(relativePath, typeof(Transform), "localScale.y", scaleY); result.clip.SetCurve(relativePath, typeof(Transform), "localScale.z", scaleZ); break; case "weights": GLTFNode.ImportResult skinnedMeshNode = nodes[channel.target.node.Value]; SkinnedMeshRenderer skinnedMeshRenderer = skinnedMeshNode.transform.GetComponent <SkinnedMeshRenderer>(); int numberOfBlendShapes = skinnedMeshRenderer.sharedMesh.blendShapeCount; AnimationCurve[] blendShapeCurves = new AnimationCurve[numberOfBlendShapes]; for (int j = 0; j < numberOfBlendShapes; ++j) { blendShapeCurves[j] = new AnimationCurve(); } float[] weights = accessors[sampler.output].ReadFloat().ToArray(); float[] weightValues = new float[keyframeInput.Length]; float[] previouslyKeyedValues = new float[numberOfBlendShapes]; // Reference for my future self: // keyframeInput.Length = number of keyframes // keyframeInput[ k ] = timestamp of keyframe // weights.Length = number of keyframes * number of blendshapes // weights[ j ] = actual animated weight of a specific blend shape // (index into weights[] array accounts for keyframe index and blend shape index) for (int k = 0; k < keyframeInput.Length; ++k) { for (int j = 0; j < numberOfBlendShapes; ++j) { int weightIndex = (k * numberOfBlendShapes) + j; weightValues[k] = weights[weightIndex]; bool addKey = true; if (importSettings.animationSettings.compressBlendShapeKeyFrames) { if (k == 0 || !Mathf.Approximately(weightValues[k], previouslyKeyedValues[j])) { if (k > 0) { weightValues[k - 1] = previouslyKeyedValues[j]; blendShapeCurves[j].AddKey(CreateKeyframe(k - 1, keyframeInput, weightValues, x => x, interpolationMode)); } addKey = true; previouslyKeyedValues[j] = weightValues[k]; } else { addKey = false; } } if (addKey) { blendShapeCurves[j].AddKey(CreateKeyframe(k, keyframeInput, weightValues, x => x, interpolationMode)); } } } for (int j = 0; j < numberOfBlendShapes; ++j) { string propertyName = "blendShape." + skinnedMeshRenderer.sharedMesh.GetBlendShapeName(j); result.clip.SetCurve(relativePath, typeof(SkinnedMeshRenderer), propertyName, blendShapeCurves[j]); } break; } } return(result); }
public ImportResult Import(GLTFAccessor.ImportResult[] accessors, GLTFNode.ImportResult[] nodes, ImportSettings importSettings) { ImportResult result = new ImportResult(); result.clip = new AnimationClip(); result.clip.name = name; if (importSettings.useLegacyClips) { result.clip.legacy = true; } for (int i = 0; i < channels.Length; i++) { Channel channel = channels[i]; if (samplers.Length <= channel.sampler) { Debug.LogWarning($"GLTFUtility: Animation channel points to sampler at index {channel.sampler} which doesn't exist. Skipping animation clip."); continue; } Sampler sampler = samplers[channel.sampler]; string relativePath = ""; GLTFNode.ImportResult node = nodes[channel.target.node.Value]; while (node != null && !node.IsRoot) { if (string.IsNullOrEmpty(relativePath)) { relativePath = node.transform.name; } else { relativePath = node.transform.name + "/" + relativePath; } if (node.parent.HasValue) { node = nodes[node.parent.Value]; } else { node = null; } } float[] keyframeInput = accessors[sampler.input].ReadFloat().ToArray(); switch (channel.target.path) { case "translation": Vector3[] pos = accessors[sampler.output].ReadVec3().ToArray(); AnimationCurve posX = new AnimationCurve(); AnimationCurve posY = new AnimationCurve(); AnimationCurve posZ = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { posX.AddKey(keyframeInput[k], pos[k].x); posY.AddKey(keyframeInput[k], pos[k].y); posZ.AddKey(keyframeInput[k], -pos[k].z); } result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.x", posX); result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.y", posY); result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.z", posZ); break; case "rotation": Vector4[] rot = accessors[sampler.output].ReadVec4().ToArray(); AnimationCurve rotX = new AnimationCurve(); AnimationCurve rotY = new AnimationCurve(); AnimationCurve rotZ = new AnimationCurve(); AnimationCurve rotW = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { rotX.AddKey(keyframeInput[k], rot[k].x); rotY.AddKey(keyframeInput[k], rot[k].y); rotZ.AddKey(keyframeInput[k], -rot[k].z); rotW.AddKey(keyframeInput[k], -rot[k].w); } result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.x", rotX); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.y", rotY); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.z", rotZ); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.w", rotW); break; case "scale": Vector3[] scale = accessors[sampler.output].ReadVec3().ToArray(); AnimationCurve scaleX = new AnimationCurve(); AnimationCurve scaleY = new AnimationCurve(); AnimationCurve scaleZ = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { scaleX.AddKey(keyframeInput[k], scale[k].x); scaleY.AddKey(keyframeInput[k], scale[k].y); scaleZ.AddKey(keyframeInput[k], scale[k].z); } result.clip.SetCurve(relativePath, typeof(Transform), "localScale.x", scaleX); result.clip.SetCurve(relativePath, typeof(Transform), "localScale.y", scaleY); result.clip.SetCurve(relativePath, typeof(Transform), "localScale.z", scaleZ); break; case "weights": Debug.LogWarning("GLTFUtility: Morph weights in animation is not supported"); break; } } return(result); }
/// <param name="bytes">GLB file is supported</param> public static void LoadFromBytes(byte[] bytes, Action <GameObject, AnimationClip[]> onFinished, ImportSettings importSettings = null) { if (importSettings == null) { importSettings = new ImportSettings(); } ImportGLB(bytes, importSettings, onFinished); }
private static IEnumerator LoadInternal(this GLTFObject gltfObject, string filepath, byte[] bytefile, long binChunkStart, ImportSettings importSettings, Action <GameObject, AnimationClip[]> onFinished) { CheckExtensions(gltfObject); // directory root is sometimes used for loading buffers from containing file, or local images string directoryRoot = filepath != null?Directory.GetParent(filepath).ToString() + "/" : null; importSettings.shaderOverrides.CacheDefaultShaders(); // Import tasks synchronously GLTFBuffer.ImportTask bufferTask = new GLTFBuffer.ImportTask(gltfObject.buffers, filepath, bytefile, binChunkStart); yield return(StaticCoroutine.Start(bufferTask.RunSynchronously())); GLTFBufferView.ImportTask bufferViewTask = new GLTFBufferView.ImportTask(gltfObject.bufferViews, bufferTask); yield return(StaticCoroutine.Start(bufferViewTask.RunSynchronously())); GLTFAccessor.ImportTask accessorTask = new GLTFAccessor.ImportTask(gltfObject.accessors, bufferViewTask); yield return(StaticCoroutine.Start(accessorTask.RunSynchronously())); GLTFImage.ImportTask imageTask = new GLTFImage.ImportTask(gltfObject.images, directoryRoot, bufferViewTask); yield return(StaticCoroutine.Start(imageTask.RunSynchronously())); GLTFTexture.ImportTask textureTask = new GLTFTexture.ImportTask(gltfObject.textures, imageTask); yield return(StaticCoroutine.Start(textureTask.RunSynchronously())); GLTFMaterial.ImportTask materialTask = new GLTFMaterial.ImportTask(gltfObject.materials, textureTask, importSettings); yield return(StaticCoroutine.Start(materialTask.RunSynchronously())); GLTFMesh.ImportTask meshTask = new GLTFMesh.ImportTask(gltfObject.meshes, accessorTask, bufferViewTask, materialTask, importSettings); yield return(StaticCoroutine.Start(meshTask.RunSynchronously())); GLTFSkin.ImportTask skinTask = new GLTFSkin.ImportTask(gltfObject.skins, accessorTask); yield return(StaticCoroutine.Start(skinTask.RunSynchronously())); GLTFNode.ImportTask nodeTask = new GLTFNode.ImportTask(gltfObject.nodes, meshTask, skinTask, gltfObject.cameras); yield return(StaticCoroutine.Start(nodeTask.RunSynchronously())); GLTFAnimation.ImportResult[] animationResult = gltfObject.animations.Import(accessorTask.Result, nodeTask.Result, importSettings); AnimationClip[] animations = null; if (animationResult != null) { animations = animationResult.Select(x => x.clip).ToArray(); } else { animations = new AnimationClip[0]; } foreach (var item in bufferTask.Result) { item.Dispose(); } onFinished?.Invoke(nodeTask.Result.GetRoot(), animations); }
public static void SaveToAsset(GameObject root, AnimationClip[] animations, AssetImportContext ctx, ImportSettings settings) { #if UNITY_2018_2_OR_NEWER ctx.AddObjectToAsset("main", root); ctx.SetMainObject(root); #else ctx.SetMainAsset("main obj", root); #endif UnwrapParam?unwrapParams = new UnwrapParam() { angleError = settings.angleError, areaError = settings.areaError, hardAngle = settings.hardAngle, packMargin = settings.packMargin }; MeshRenderer[] renderers = root.GetComponentsInChildren <MeshRenderer>(true); SkinnedMeshRenderer[] skinnedRenderers = root.GetComponentsInChildren <SkinnedMeshRenderer>(true); MeshFilter[] filters = root.GetComponentsInChildren <MeshFilter>(true); AddMeshes(filters, skinnedRenderers, ctx, settings.generateLightmapUVs ? unwrapParams : null); AddMaterials(renderers, skinnedRenderers, ctx); AddAnimations(animations, ctx, settings.animationSettings); }
/// <param name="bytes">GLB file is supported</param> public static GameObject LoadFromBytes(byte[] bytes, ImportSettings importSettings, out GLTFAnimation.ImportResult[] animations) { return(ImportGLB(bytes, importSettings, out animations)); }
// hkyoo public static GameObject LoadInternal(this GLTFObject gltfObject, string filepath, int index, byte[] bytefile, long binChunkStart, ImportSettings importSettings, out AnimationClip[] animations) { // directory root is sometimes used for loading buffers from containing file, or local images string directoryRoot = filepath != null?Directory.GetParent(filepath).ToString() + "/" : null; importSettings.shaderOverrides.CacheDefaultShaders(); //// for debug //Debug.Log(gltfObject.nodes.ToString() + " " + gltfObject.nodes.Count); // 151 //Debug.Log(gltfObject.meshes.ToString() + " " + gltfObject.meshes.Count); // 151 ////Debug.Log(gltfObject.animations.ToString() + " " + gltfObject.animations.Count); // null //Debug.Log(gltfObject.buffers.ToString() + " " + gltfObject.buffers.Count); // 1 //Debug.Log(gltfObject.bufferViews.ToString() + " " + gltfObject.bufferViews.Count); // 755 //Debug.Log(gltfObject.accessors.ToString() + " " + gltfObject.accessors.Count); // 604 ////Debug.Log(gltfObject.skins.ToString() + " " + gltfObject.skins.Count); // null //Debug.Log(gltfObject.textures.ToString() + " " + gltfObject.textures.Count); // 151 //Debug.Log(gltfObject.images.ToString() + " " + gltfObject.images.Count); // 151 //Debug.Log(gltfObject.materials.ToString() + " " + gltfObject.materials.Count); // 151 ////Debug.Log(gltfObject.cameras.ToString() + " " + gltfObject.cameras.Count); // null /// Stopwatch sw = new Stopwatch(); // image/material/mesh sw.Start(); // Import tasks synchronously GLTFBuffer.ImportTask bufferTask = new GLTFBuffer.ImportTask(gltfObject.buffers, filepath, bytefile, binChunkStart); bufferTask.RunSynchronously(); sw.Stop(); UnityEngine.Debug.Log("bufferTask : " + sw.ElapsedMilliseconds.ToString() + "ms"); sw.Reset(); sw.Start(); // TODO? GLTFBufferView.ImportTask bufferViewTask = new GLTFBufferView.ImportTask(gltfObject.bufferViews, bufferTask); bufferViewTask.RunSynchronously(); sw.Stop(); UnityEngine.Debug.Log("bufferViewTask : " + sw.ElapsedMilliseconds.ToString() + "ms"); sw.Reset(); sw.Start(); GLTFAccessor.ImportTask accessorTask = new GLTFAccessor.ImportTask(gltfObject.accessors, bufferViewTask); accessorTask.RunSynchronously(); sw.Stop(); UnityEngine.Debug.Log("accessorTask : " + sw.ElapsedMilliseconds.ToString() + "ms"); sw.Reset(); sw.Start(); GLTFImage.ImportTask imageTask = new GLTFImage.ImportTask(gltfObject.images[index], directoryRoot, bufferViewTask); //GLTFImage.ImportTask imageTask = new GLTFImage.ImportTask(gltfObject.images, directoryRoot, bufferViewTask); imageTask.RunSynchronously(); sw.Stop(); UnityEngine.Debug.Log("imageTask : " + sw.ElapsedMilliseconds.ToString() + "ms"); sw.Reset(); sw.Start(); GLTFTexture.ImportTask textureTask = new GLTFTexture.ImportTask(gltfObject.textures[index], imageTask); //GLTFTexture.ImportTask textureTask = new GLTFTexture.ImportTask(gltfObject.textures, imageTask); textureTask.RunSynchronously(); UnityEngine.Debug.Log("textureTask.Result[0] " + textureTask.Result.Length + " " + textureTask.Result[0]); sw.Stop(); UnityEngine.Debug.Log("textureTask : " + sw.ElapsedMilliseconds.ToString() + "ms"); sw.Reset(); sw.Start(); // TODO : shader setting GLTFMaterial.ImportTask materialTask = new GLTFMaterial.ImportTask(gltfObject.materials[index], textureTask, importSettings); //GLTFMaterial.ImportTask materialTask = new GLTFMaterial.ImportTask(gltfObject.materials, textureTask, importSettings); materialTask.RunSynchronously(); sw.Stop(); UnityEngine.Debug.Log("materialTask : " + sw.ElapsedMilliseconds.ToString() + "ms"); sw.Reset(); sw.Start(); // TODO : GLTFMesh.ImportTask meshTask = new GLTFMesh.ImportTask(gltfObject.meshes[index], accessorTask, bufferViewTask, materialTask, importSettings); //GLTFMesh.ImportTask meshTask = new GLTFMesh.ImportTask(gltfObject.meshes, accessorTask, bufferViewTask, materialTask, importSettings); meshTask.RunSynchronously(); sw.Stop(); UnityEngine.Debug.Log("meshTask : " + sw.ElapsedMilliseconds.ToString() + "ms"); sw.Reset(); sw.Start(); GLTFSkin.ImportTask skinTask = new GLTFSkin.ImportTask(gltfObject.skins, accessorTask); skinTask.RunSynchronously(); sw.Stop(); UnityEngine.Debug.Log("skinTask : " + sw.ElapsedMilliseconds.ToString() + "ms"); sw.Reset(); sw.Start(); GLTFNode.ImportTask nodeTask = new GLTFNode.ImportTask(gltfObject.nodes[index], meshTask, skinTask, gltfObject.cameras); //GLTFNode.ImportTask nodeTask = new GLTFNode.ImportTask(gltfObject.nodes, meshTask, skinTask, gltfObject.cameras); nodeTask.RunSynchronously(); sw.Stop(); UnityEngine.Debug.Log("nodeTask : " + sw.ElapsedMilliseconds.ToString() + "ms"); sw.Reset(); GLTFAnimation.ImportResult[] animationResult = gltfObject.animations.Import(accessorTask.Result, nodeTask.Result, importSettings); if (animationResult != null) { animations = animationResult.Select(x => x.clip).ToArray(); } else { animations = new AnimationClip[0]; } foreach (var item in bufferTask.Result) { item.Dispose(); } return(nodeTask.Result.GetRoot()); }
public ImportTask(List <GLTFMaterial> materials, GLTFTexture.ImportTask textureTask, ImportSettings importSettings) : base(textureTask) { this.materials = materials; this.textureTask = textureTask; this.importSettings = importSettings; task = new Task(() => { if (materials == null) { return; } Result = new ImportResult[materials.Count]; }); }
public ImportResult Import(GLTFAccessor.ImportResult[] accessors, GLTFNode.ImportResult[] nodes, ImportSettings importSettings) { ImportResult result = new ImportResult(); result.clip = new AnimationClip(); result.clip.name = name; result.clip.frameRate = importSettings.frameRate; if (importSettings.useLegacyClips) { result.clip.legacy = true; } for (int i = 0; i < channels.Length; i++) { Channel channel = channels[i]; if (samplers.Length <= channel.sampler) { Debug.LogWarning($"GLTFUtility: Animation channel points to sampler at index {channel.sampler} which doesn't exist. Skipping animation clip."); continue; } Sampler sampler = samplers[channel.sampler]; ImportSettings.InterpolationMode GetInterpolationMode(string samplerInterpolationMode) { if (importSettings.interpolationMode == ImportSettings.InterpolationMode.ImportFromFile) { if (samplerInterpolationMode == "STEP") { return(ImportSettings.InterpolationMode.Step); } else if (samplerInterpolationMode == "LINEAR") { return(ImportSettings.InterpolationMode.Linear); } else if (samplerInterpolationMode == "CUBICSPLINE") { return(ImportSettings.InterpolationMode.CubicSpline); } else { Debug.LogWarning($"Unsupported interpolation mode: {samplerInterpolationMode}. Defaulting to STEP."); return(ImportSettings.InterpolationMode.Step); } } else { return(importSettings.interpolationMode); } } var interpolationMode = GetInterpolationMode(sampler.interpolation); string relativePath = ""; GLTFNode.ImportResult node = nodes[channel.target.node.Value]; while (node != null && !node.IsRoot) { if (string.IsNullOrEmpty(relativePath)) { relativePath = node.transform.name; } else { relativePath = node.transform.name + "/" + relativePath; } if (node.parent.HasValue) { node = nodes[node.parent.Value]; } else { node = null; } } float[] keyframeInput = accessors[sampler.input].ReadFloat().ToArray(); switch (channel.target.path) { case "translation": Vector3[] pos = accessors[sampler.output].ReadVec3().ToArray(); AnimationCurve posX = new AnimationCurve(); AnimationCurve posY = new AnimationCurve(); AnimationCurve posZ = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { posX.AddKey(CreateKeyframe(keyframeInput[k], -pos[k].x, interpolationMode)); posY.AddKey(CreateKeyframe(keyframeInput[k], pos[k].y, interpolationMode)); posZ.AddKey(CreateKeyframe(keyframeInput[k], pos[k].z, interpolationMode)); } result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.x", posX); result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.y", posY); result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.z", posZ); break; case "rotation": Vector4[] rot = accessors[sampler.output].ReadVec4().ToArray(); bool willProcessSteppedKeyframes = interpolationMode == ImportSettings.InterpolationMode.Step && Application.isEditor && !Application.isPlaying; // @HACK: Creating stepped tangent keyframes is only supported in-editor -- not at runtime (Unity API restriction) #if UNITY_EDITOR // 🤢🤮💔 if (willProcessSteppedKeyframes) { AnimationCurve rotX = new AnimationCurve(); AnimationCurve rotY = new AnimationCurve(); AnimationCurve rotZ = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { Vector3 eulerRotation = new Quaternion(rot[k].x, -rot[k].y, -rot[k].z, rot[k].w).eulerAngles; rotX.AddKey(CreateKeyframe(keyframeInput[k], eulerRotation.x, interpolationMode)); rotY.AddKey(CreateKeyframe(keyframeInput[k], eulerRotation.y, interpolationMode)); rotZ.AddKey(CreateKeyframe(keyframeInput[k], eulerRotation.z, interpolationMode)); } EditorCurveBinding GetEditorBinding(string property) { return(EditorCurveBinding.DiscreteCurve(relativePath, typeof(Transform), property)); } // Null out any other euler rotation curves on this clip, just to be safe. // https://forum.unity.com/threads/new-animationclip-property-names.367288/#post-2384172 AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAngles.x"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAngles.y"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAngles.z"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAngles.x"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAngles.y"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAngles.z"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesBaked.x"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesBaked.y"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesBaked.z"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAnglesBaked.x"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAnglesBaked.y"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("m_LocalEulerAnglesBaked.z"), null); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesRaw.x"), rotX); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesRaw.y"), rotY); AnimationUtility.SetEditorCurve(result.clip, GetEditorBinding("localEulerAnglesRaw.z"), rotZ); } #endif if (!willProcessSteppedKeyframes) { AnimationCurve rotX = new AnimationCurve(); AnimationCurve rotY = new AnimationCurve(); AnimationCurve rotZ = new AnimationCurve(); AnimationCurve rotW = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { rotX.AddKey(CreateKeyframe(keyframeInput[k], rot[k].x, interpolationMode)); rotY.AddKey(CreateKeyframe(keyframeInput[k], -rot[k].y, interpolationMode)); rotZ.AddKey(CreateKeyframe(keyframeInput[k], -rot[k].z, interpolationMode)); rotW.AddKey(CreateKeyframe(keyframeInput[k], rot[k].w, interpolationMode)); } result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.x", rotX); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.y", rotY); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.z", rotZ); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.w", rotW); } break; case "scale": Vector3[] scale = accessors[sampler.output].ReadVec3().ToArray(); AnimationCurve scaleX = new AnimationCurve(); AnimationCurve scaleY = new AnimationCurve(); AnimationCurve scaleZ = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { scaleX.AddKey(CreateKeyframe(keyframeInput[k], scale[k].x, interpolationMode)); scaleY.AddKey(CreateKeyframe(keyframeInput[k], scale[k].y, interpolationMode)); scaleZ.AddKey(CreateKeyframe(keyframeInput[k], scale[k].z, interpolationMode)); } result.clip.SetCurve(relativePath, typeof(Transform), "localScale.x", scaleX); result.clip.SetCurve(relativePath, typeof(Transform), "localScale.y", scaleY); result.clip.SetCurve(relativePath, typeof(Transform), "localScale.z", scaleZ); break; case "weights": GLTFNode.ImportResult skinnedMeshNode = nodes[channel.target.node.Value]; SkinnedMeshRenderer skinnedMeshRenderer = skinnedMeshNode.transform.GetComponent <SkinnedMeshRenderer>(); int numberOfBlendShapes = skinnedMeshRenderer.sharedMesh.blendShapeCount; AnimationCurve[] blendShapeCurves = new AnimationCurve[numberOfBlendShapes]; for (int j = 0; j < numberOfBlendShapes; ++j) { blendShapeCurves[j] = new AnimationCurve(); } float[] weights = accessors[sampler.output].ReadFloat().ToArray(); float[] previouslyKeyedValues = new float[numberOfBlendShapes]; // Reference for my future self: // keyframeInput.Length = number of keyframes // keyframeInput[ k ] = timestamp of keyframe // weights.Length = number of keyframes * number of blendshapes // weights[ j ] = actual animated weight of a specific blend shape // (index into weights[] array accounts for keyframe index and blend shape index) for (int k = 0; k < keyframeInput.Length; ++k) { for (int j = 0; j < numberOfBlendShapes; ++j) { int weightIndex = (k * numberOfBlendShapes) + j; float weightValue = weights[weightIndex]; bool addKey = true; if (importSettings.compressBlendShapeKeyFrames) { if (k == 0 || !Mathf.Approximately(weightValue, previouslyKeyedValues[j])) { previouslyKeyedValues[j] = weightValue; addKey = true; } else { addKey = false; } } if (addKey) { blendShapeCurves[j].AddKey(CreateKeyframe(keyframeInput[k], weightValue, interpolationMode)); } } } for (int j = 0; j < numberOfBlendShapes; ++j) { string propertyName = "blendShape." + skinnedMeshRenderer.sharedMesh.GetBlendShapeName(j); result.clip.SetCurve(relativePath, typeof(SkinnedMeshRenderer), propertyName, blendShapeCurves[j]); } break; } } return(result); }
public static GameObject ImportGLTF(string filepath, ImportSettings importSettings) { GLTFAnimation.ImportResult[] animations; return(ImportGLTF(filepath, importSettings, out animations)); }
public ImportResult Import(GLTFAccessor.ImportResult[] accessors, GLTFNode.ImportResult[] nodes, ImportSettings importSettings) { bool multiRoots = nodes.Where(x => x.IsRoot).Count() > 1; ImportResult result = new ImportResult(); result.clip = new AnimationClip(); result.clip.name = name; if (importSettings.useLegacyClips) { result.clip.legacy = true; } for (int i = 0; i < channels.Length; i++) { Channel channel = channels[i]; if (samplers.Length <= channel.sampler) { Debug.LogWarning($"GLTFUtility: Animation channel points to sampler at index {channel.sampler} which doesn't exist. Skipping animation clip."); continue; } Sampler sampler = samplers[channel.sampler]; // Get interpolation mode InterpolationMode interpolationMode = importSettings.interpolationMode; if (interpolationMode == InterpolationMode.ImportFromFile) { interpolationMode = sampler.interpolation; } if (interpolationMode == InterpolationMode.CUBICSPLINE) { Debug.LogWarning("Animation interpolation mode CUBICSPLINE not fully supported, result might look different."); } string relativePath = ""; GLTFNode.ImportResult node = nodes[channel.target.node.Value]; while (node != null && !node.IsRoot) { if (string.IsNullOrEmpty(relativePath)) { relativePath = node.transform.name; } else { relativePath = node.transform.name + "/" + relativePath; } if (node.parent.HasValue) { node = nodes[node.parent.Value]; } else { node = null; } } // If file has multiple root nodes, a new parent will be created for them as a final step of the import process. This parent f***s up the curve relative paths. // Add node.transform.name to path if there are multiple roots. This is not the most elegant fix but it works. // See GLTFNodeExtensions.GetRoot if (multiRoots) { relativePath = node.transform.name + "/" + relativePath; } System.Threading.Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.InvariantCulture; float[] keyframeInput = accessors[sampler.input].ReadFloat().ToArray(); switch (channel.target.path) { case "translation": Vector3[] pos = accessors[sampler.output].ReadVec3().ToArray(); AnimationCurve posX = new AnimationCurve(); AnimationCurve posY = new AnimationCurve(); AnimationCurve posZ = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { posX.AddKey(CreateKeyframe(k, keyframeInput, pos, x => - x.x, interpolationMode)); posY.AddKey(CreateKeyframe(k, keyframeInput, pos, x => x.y, interpolationMode)); posZ.AddKey(CreateKeyframe(k, keyframeInput, pos, x => x.z, interpolationMode)); } result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.x", posX); result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.y", posY); result.clip.SetCurve(relativePath, typeof(Transform), "localPosition.z", posZ); break; case "rotation": Vector4[] rot = accessors[sampler.output].ReadVec4().ToArray(); AnimationCurve rotX = new AnimationCurve(); AnimationCurve rotY = new AnimationCurve(); AnimationCurve rotZ = new AnimationCurve(); AnimationCurve rotW = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { // The Animation window in Unity shows keyframes incorrectly converted to euler. This is only to deceive you. The quaternions underneath work correctly rotX.AddKey(CreateKeyframe(k, keyframeInput, rot, x => x.x, interpolationMode)); rotY.AddKey(CreateKeyframe(k, keyframeInput, rot, x => - x.y, interpolationMode)); rotZ.AddKey(CreateKeyframe(k, keyframeInput, rot, x => - x.z, interpolationMode)); rotW.AddKey(CreateKeyframe(k, keyframeInput, rot, x => x.w, interpolationMode)); } result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.x", rotX); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.y", rotY); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.z", rotZ); result.clip.SetCurve(relativePath, typeof(Transform), "localRotation.w", rotW); break; case "scale": Vector3[] scale = accessors[sampler.output].ReadVec3().ToArray(); AnimationCurve scaleX = new AnimationCurve(); AnimationCurve scaleY = new AnimationCurve(); AnimationCurve scaleZ = new AnimationCurve(); for (int k = 0; k < keyframeInput.Length; k++) { scaleX.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.x, interpolationMode)); scaleY.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.y, interpolationMode)); scaleZ.AddKey(CreateKeyframe(k, keyframeInput, scale, x => x.z, interpolationMode)); } result.clip.SetCurve(relativePath, typeof(Transform), "localScale.x", scaleX); result.clip.SetCurve(relativePath, typeof(Transform), "localScale.y", scaleY); result.clip.SetCurve(relativePath, typeof(Transform), "localScale.z", scaleZ); break; case "weights": Debug.LogWarning("GLTFUtility: Morph weights in animation is not supported"); break; } } return(result); }
public static GLTFAnimation.ImportResult[] Import(this List <GLTFAnimation> animations, GLTFAccessor.ImportResult[] accessors, GLTFNode.ImportResult[] nodes, ImportSettings importSettings) { if (animations == null) { return(null); } GLTFAnimation.ImportResult[] results = new GLTFAnimation.ImportResult[animations.Count]; for (int i = 0; i < results.Length; i++) { results[i] = animations[i].Import(accessors, nodes, importSettings); if (string.IsNullOrEmpty(results[i].clip.name)) { results[i].clip.name = "animation" + i; } } return(results); }
public static GLTFMaterial.ImportResult Import(this List <GLTFMaterial> materials, GLTFTexture.ImportResult[] textures, ImportSettings importSettings) { if (!importSettings.materials) { return(null); } GLTFMaterial.ImportResult result = new GLTFMaterial.ImportResult(); result.materials = new Material[materials.Count]; for (int i = 0; i < materials.Count; i++) { result.materials[i] = materials[i].CreateMaterial(textures, importSettings.shaders); if (materials[i].name == null) { materials[i].name = "material" + i; } } return(result); }