< Summary

Class:UnityGLTF.GLTFImporter
Assembly:UnityGLTFEditorAssembly
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/UnityGLTF/Scripts/Editor/GLTFImporter.cs
Covered lines:0
Uncovered lines:271
Coverable lines:271
Total lines:574
Line coverage:0% (0 of 271)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
GLTFImporter()0%2100%
SimplifyMaterials(...)0%42600%
OnImportAsset(...)0%12603500%
CreateGLTFScene(...)0%56700%
CopyOrNew[T](...)0%6200%
TexMaterialMap(...)0%2100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/UnityGLTF/Scripts/Editor/GLTFImporter.cs

#LineLine coverage
 1#if UNITY_2017_1_OR_NEWER
 2using GLTF;
 3using GLTF.Schema;
 4using System;
 5using System.Collections;
 6using System.Collections.Generic;
 7using System.IO;
 8using System.Linq;
 9using UnityEditor;
 10using UnityEditor.AssetImporters;
 11using UnityEngine;
 12using UnityGLTF.Loader;
 13using UnityGLTF.Cache;
 14using Object = UnityEngine.Object;
 15
 16// using System.Threading.Tasks;
 17
 18namespace UnityGLTF
 19{
 20    [ScriptedImporter(1, new[] { "glb", "gltf" })]
 21    public class GLTFImporter : ScriptedImporter
 22    {
 023        [SerializeField] private bool _removeEmptyRootObjects = true;
 024        [SerializeField] private float _scaleFactor = 1.0f;
 025        [SerializeField] private int _maximumLod = 300;
 026        [SerializeField] private bool _readWriteEnabled = true;
 27        [SerializeField] private bool _generateColliders = false;
 28        [SerializeField] private bool _swapUvs = false;
 29        [SerializeField] private GLTFImporterNormals _importNormals = GLTFImporterNormals.Import;
 030        [SerializeField] private bool _importMaterials = true;
 31        [SerializeField] private bool _useJpgTextures = false;
 32
 033        public bool _importTextures = true;
 34
 35        static int delayCallsCount = 0;
 36
 037        public static bool finishedImporting { get { return delayCallsCount == 0; } }
 38
 39        public List<Material> SimplifyMaterials(Renderer[] renderers)
 40        {
 041            Dictionary<string, Material> matByCrc = new Dictionary<string, Material>();
 042            List<Material> materials = new List<Material>();
 43
 044            foreach (var rend in renderers)
 45            {
 046                var matList = new List<Material>(1);
 47
 048                foreach (var mat in rend.sharedMaterials)
 49                {
 050                    if (rend.sharedMaterials.Length == 0)
 51                        break;
 052                    if (mat == null)
 53                        continue;
 54
 055                    string crc = mat.ComputeCRC() + mat.name;
 56
 057                    if (!matByCrc.ContainsKey(crc))
 58                    {
 059                        matByCrc.Add(crc, mat);
 060                        materials.Add(mat);
 61                    }
 62
 063                    matList.Add(matByCrc[crc]);
 64                }
 65
 066                rend.sharedMaterials = matList.ToArray();
 67            }
 68
 069            return materials;
 70        }
 71
 72        public override void OnImportAsset(AssetImportContext ctx)
 73        {
 74            string sceneName = null;
 075            GameObject gltfScene = null;
 076            UnityEngine.Mesh[] meshes = null;
 77            try
 78            {
 079                char ps = Path.DirectorySeparatorChar;
 80
 081                string assetPath = ctx.assetPath;
 82
 083                assetPath = assetPath.Replace('/', ps);
 084                assetPath = assetPath.Replace('\\', ps);
 85
 086                sceneName = Path.GetFileNameWithoutExtension(assetPath);
 087                gltfScene = CreateGLTFScene(assetPath);
 88
 89                // Remove empty roots
 090                if (_removeEmptyRootObjects)
 91                {
 092                    var t = gltfScene.transform;
 093                    while (
 94                        gltfScene.transform.childCount == 1 &&
 95                        gltfScene.GetComponents<Component>().Length == 1)
 96                    {
 097                        var parent = gltfScene;
 098                        gltfScene = gltfScene.transform.GetChild(0).gameObject;
 099                        t = gltfScene.transform;
 0100                        t.parent = null; // To keep transform information in the new parent
 0101                        Object.DestroyImmediate(parent); // Get rid of the parent
 102                    }
 103                }
 104
 105                // Ensure there are no hide flags present (will cause problems when saving)
 0106                gltfScene.hideFlags &= ~(HideFlags.HideAndDontSave);
 0107                foreach (Transform child in gltfScene.transform)
 108                {
 0109                    child.gameObject.hideFlags &= ~(HideFlags.HideAndDontSave);
 110                }
 111
 112                // Zero position
 0113                gltfScene.transform.position = Vector3.zero;
 114
 0115                Animation animation = gltfScene.GetComponentInChildren<Animation>();
 0116                HashSet<AnimationClip> animationClips = new HashSet<AnimationClip>();
 117
 0118                if (animation != null)
 119                {
 0120                    foreach (AnimationState animationState in animation)
 121                    {
 0122                        if (!animationClips.Contains(animationState.clip))
 123                        {
 0124                            animationClips.Add(animationState.clip);
 125                        }
 126                    }
 127                }
 128
 129                // Get meshes
 0130                var meshNames = new List<string>();
 0131                var meshHash = new HashSet<UnityEngine.Mesh>();
 0132                var meshFilters = gltfScene.GetComponentsInChildren<MeshFilter>();
 0133                var vertexBuffer = new List<Vector3>();
 0134                meshes = meshFilters.Where(mf => mf.sharedMesh != null)
 135                                    .Select(mf =>
 136                                    {
 0137                                        var mesh = mf.sharedMesh;
 138
 0139                                        vertexBuffer.Clear();
 0140                                        mesh.GetVertices(vertexBuffer);
 0141                                        for (var i = 0; i < vertexBuffer.Count; ++i)
 142                                        {
 0143                                            vertexBuffer[i] *= _scaleFactor;
 144                                        }
 145
 0146                                        mesh.SetVertices(vertexBuffer);
 0147                                        if (_swapUvs)
 148                                        {
 0149                                            var uv = mesh.uv;
 0150                                            var uv2 = mesh.uv2;
 0151                                            mesh.uv = uv2;
 0152                                            mesh.uv2 = uv2;
 153                                        }
 154
 0155                                        if (_importNormals == GLTFImporterNormals.None)
 156                                        {
 0157                                            mesh.normals = new Vector3[0];
 158                                        }
 159
 0160                                        if (_importNormals == GLTFImporterNormals.Calculate)
 161                                        {
 0162                                            mesh.RecalculateNormals();
 163                                        }
 164
 0165                                        mesh.UploadMeshData(!_readWriteEnabled);
 166
 0167                                        if (_generateColliders)
 168                                        {
 0169                                            var collider = mf.gameObject.AddComponent<MeshCollider>();
 0170                                            collider.sharedMesh = mesh;
 171                                        }
 172
 0173                                        if (meshHash.Add(mesh))
 174                                        {
 0175                                            var meshName = string.IsNullOrEmpty(mesh.name) ? mf.gameObject.name : mesh.n
 0176                                            mesh.name = ObjectNames.GetUniqueName(meshNames.ToArray(), meshName);
 0177                                            meshNames.Add(mesh.name);
 178                                        }
 179
 0180                                        return mesh;
 181                                    })
 182                                    .ToArray();
 183
 0184                var renderers = gltfScene.GetComponentsInChildren<Renderer>();
 185
 0186                if (animationClips.Count > 0)
 187                {
 0188                    var folderName = Path.GetDirectoryName(ctx.assetPath);
 0189                    var animationsRoot = string.Concat(folderName, "/", "Animations/");
 0190                    Directory.CreateDirectory(animationsRoot);
 0191                    foreach (AnimationClip clip in animationClips)
 192                    {
 0193                        string fileName = clip.name;
 0194                        foreach (char c in System.IO.Path.GetInvalidFileNameChars())
 195                        {
 0196                            fileName = fileName.Replace(c, '_');
 197                        }
 198
 0199                        AssetDatabase.CreateAsset(clip, animationsRoot + fileName + ".anim");
 0200                        var importer = AssetImporter.GetAtPath(animationsRoot + fileName + ".anim");
 201                    }
 202                }
 203
 0204                if (_importMaterials)
 205                {
 0206                    var materials = SimplifyMaterials(renderers);
 207                    // Get materials
 0208                    List<string> materialNames = new List<string>();
 209
 0210                    foreach (var mat in materials)
 211                    {
 0212                        var matName = string.IsNullOrEmpty(mat.name) ? mat.shader.name : mat.name;
 0213                        if (matName == mat.shader.name)
 214                        {
 0215                            matName = matName.Substring(Mathf.Min(matName.LastIndexOf("/") + 1, matName.Length - 1));
 216                        }
 217
 218                        // Ensure name is unique
 0219                        matName = ObjectNames.NicifyVariableName(matName);
 0220                        matName = ObjectNames.GetUniqueName(materialNames.ToArray(), matName);
 221
 0222                        mat.name = matName;
 0223                        materialNames.Add(matName);
 224                    }
 225
 0226                    List<Texture2D> textures = new List<Texture2D>();
 0227                    var texMaterialMap = new Dictionary<Texture2D, List<TexMaterialMap>>();
 228
 0229                    if (_importTextures)
 230                    {
 231                        // Get textures
 0232                        var textureNames = new List<string>();
 0233                        var textureHash = new HashSet<Texture2D>();
 234
 0235                        textures = materials.SelectMany(mat =>
 236                                            {
 0237                                                var shader = mat.shader;
 0238                                                if (!shader)
 239                                                {
 0240                                                    return Enumerable.Empty<Texture2D>();
 241                                                }
 242
 0243                                                var matTextures = new List<Texture2D>();
 244
 0245                                                for (var i = 0; i < ShaderUtil.GetPropertyCount(shader); ++i)
 246                                                {
 0247                                                    if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderProper
 248                                                    {
 0249                                                        var propertyName = ShaderUtil.GetPropertyName(shader, i);
 0250                                                        var tex = mat.GetTexture(propertyName) as Texture2D;
 251
 0252                                                        if (!tex)
 253                                                            continue;
 254
 0255                                                        if (textureHash.Add(tex))
 256                                                        {
 0257                                                            var texName = tex.name;
 258
 0259                                                            if (string.IsNullOrEmpty(texName))
 260                                                            {
 0261                                                                if (propertyName.StartsWith("_"))
 262                                                                {
 0263                                                                    texName = propertyName.Substring(Mathf.Min(1, proper
 264                                                                }
 265                                                            }
 266
 267                                                            // Ensure name is unique
 0268                                                            texName = ObjectNames.NicifyVariableName(texName);
 0269                                                            texName = ObjectNames.GetUniqueName(textureNames.ToArray(), 
 270
 0271                                                            tex.name = texName;
 0272                                                            textureNames.Add(texName);
 0273                                                            matTextures.Add(tex);
 274                                                        }
 275
 276                                                        List<TexMaterialMap> materialMaps;
 277
 0278                                                        if (!texMaterialMap.TryGetValue(tex, out materialMaps))
 279                                                        {
 0280                                                            materialMaps = new List<TexMaterialMap>();
 0281                                                            texMaterialMap.Add(tex, materialMaps);
 282                                                        }
 283
 0284                                                        materialMaps.Add(new TexMaterialMap(mat, propertyName, propertyN
 285                                                    }
 286                                                }
 287
 0288                                                return matTextures;
 289                                            })
 290                                            .ToList();
 291
 0292                        var folderName = Path.GetDirectoryName(ctx.assetPath);
 293
 294                        // Save textures as separate assets and rewrite refs
 295                        // TODO: Support for other texture types
 0296                        if (textures.Count > 0)
 297                        {
 0298                            var texturesRoot = string.Concat(folderName, "/", "Textures/");
 299
 0300                            if (!Directory.Exists(texturesRoot))
 0301                                Directory.CreateDirectory(texturesRoot);
 302
 0303                            Texture2D[] cachedTextures = PersistentAssetCache.ImageCacheByUri.Values.Select((x) => { ret
 304
 0305                            foreach (var tex in textures)
 306                            {
 0307                                var ext = _useJpgTextures ? ".jpg" : ".png";
 0308                                var texPath = string.Concat(texturesRoot, tex.name, ext);
 0309                                var absolutePath = Application.dataPath + "/../" + texPath;
 310
 0311                                if (File.Exists(absolutePath) || cachedTextures.Contains(tex))
 312                                    continue;
 313
 0314                                File.WriteAllBytes(texPath, _useJpgTextures ? tex.EncodeToJPG() : tex.EncodeToPNG());
 0315                                AssetDatabase.ImportAsset(texPath, ImportAssetOptions.ForceSynchronousImport | ImportAss
 316                            }
 317
 0318                            AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUp
 319                        }
 320                    }
 321
 322
 0323                    List<Material> materialCopies = new List<Material>(materials);
 324                    // Save materials as separate assets and rewrite refs
 0325                    if (materials.Count > 0)
 326                    {
 0327                        var folderName = Path.GetDirectoryName(ctx.assetPath);
 0328                        var materialRoot = string.Concat(folderName, "/", "Materials/");
 0329                        Directory.CreateDirectory(materialRoot);
 330
 0331                        for (var matIndex = 0; matIndex < materials.Count; matIndex++)
 332                        {
 0333                            var mat = materials[matIndex];
 0334                            var materialPath = string.Concat(materialRoot, mat.name, ".mat");
 335
 0336                            CopyOrNew(mat, materialPath, m =>
 337                            {
 0338                                materialCopies[matIndex] = m;
 339
 0340                                foreach (var r in renderers)
 341                                {
 0342                                    var sharedMaterials = r.sharedMaterials;
 0343                                    for (var i = 0; i < sharedMaterials.Length; ++i)
 344                                    {
 0345                                        var sharedMaterial = sharedMaterials[i];
 0346                                        if (sharedMaterial.name == mat.name)
 347                                        {
 0348                                            sharedMaterials[i] = m;
 0349                                            EditorUtility.SetDirty(m);
 350                                        }
 351                                    }
 352
 0353                                    sharedMaterials = sharedMaterials.Where(sm => sm).ToArray();
 0354                                    r.sharedMaterials = sharedMaterials;
 355                                }
 0356                            });
 357                        }
 358
 359                        // Fix textures
 360                        // HACK: This needs to be a delayed call.
 361                        // Unity needs a frame to kick off the texture import so we can rewrite the ref
 0362                        if (textures.Count > 0)
 363                        {
 0364                            delayCallsCount++;
 0365                            EditorApplication.delayCall += () =>
 366                            {
 0367                                Texture2D[] cachedTextures = PersistentAssetCache.ImageCacheByUri.Values.Select((x) => {
 368
 0369                                delayCallsCount--;
 370
 0371                                for (var i = 0; i < textures.Count; ++i)
 372                                {
 0373                                    var tex = textures[i];
 0374                                    var materialMaps = texMaterialMap[tex];
 0375                                    bool isExternal = cachedTextures.Contains(tex);
 376
 0377                                    var texturesRoot = string.Concat(folderName, "/", "Textures/");
 0378                                    var ext = _useJpgTextures ? ".jpg" : ".png";
 0379                                    var texPath = string.Concat(texturesRoot, tex.name, ext);
 380
 0381                                    var importedTex = AssetDatabase.LoadAssetAtPath<Texture2D>(texPath);
 0382                                    var importer = (TextureImporter) TextureImporter.GetAtPath(texPath);
 383
 0384                                    if (importer != null)
 385                                    {
 0386                                        importer.isReadable = false;
 0387                                        var isNormalMap = true;
 388
 0389                                        for (var matIndex = 0; matIndex < materials.Count; matIndex++)
 390                                        {
 0391                                            var originalMaterial = materials[matIndex];
 392
 0393                                            foreach (var materialMap in materialMaps)
 394                                            {
 0395                                                if (materialMap.Material == originalMaterial)
 396                                                {
 397                                                    //NOTE(Brian): Only set as normal map if is exclusively
 398                                                    //             used for that.
 399                                                    //             We don't want DXTnm in color textures.
 0400                                                    if (!materialMap.IsNormalMap)
 0401                                                        isNormalMap = false;
 402
 0403                                                    materialCopies[matIndex].SetTexture(materialMap.Property, importedTe
 404                                                }
 405                                            }
 406                                        }
 407
 0408                                        if (isExternal)
 0409                                            isNormalMap = false;
 410
 0411                                        if (isNormalMap)
 412                                        {
 413                                            // Try to auto-detect normal maps
 0414                                            importer.textureType = TextureImporterType.NormalMap;
 0415                                        }
 0416                                        else if (importer.textureType == TextureImporterType.Sprite)
 417                                        {
 418                                            // Force disable sprite mode, even for 2D projects
 0419                                            importer.textureType = TextureImporterType.Default;
 420                                        }
 421
 0422                                        importer.crunchedCompression = true;
 0423                                        importer.compressionQuality = 100;
 0424                                        importer.textureCompression = TextureImporterCompression.CompressedHQ;
 0425                                        importer.SaveAndReimport();
 0426                                    }
 427                                    else
 428                                    {
 0429                                        Debug.LogWarning(string.Format("GLTFImporter: Unable to import texture at path: 
 430                                    }
 431
 0432                                    if (delayCallsCount == 0)
 433                                    {
 0434                                        AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport | ImportAssetOpt
 0435                                        AssetDatabase.SaveAssets();
 436                                    }
 437                                }
 0438                            };
 439                        }
 440
 0441                        AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport | ImportAssetOptions.ForceUpdate
 0442                        AssetDatabase.SaveAssets();
 0443                    }
 444                    else
 445                    {
 0446                        var temp = GameObject.CreatePrimitive(PrimitiveType.Plane);
 0447                        temp.SetActive(false);
 0448                        var defaultMat = new[] { temp.GetComponent<Renderer>().sharedMaterial };
 0449                        DestroyImmediate(temp);
 450
 0451                        foreach (var rend in renderers)
 452                        {
 0453                            rend.sharedMaterials = defaultMat;
 454                        }
 455                    }
 456
 0457                    var rootObject = gltfScene.GetComponentInChildren<InstantiatedGLTFObject>();
 458
 0459                    if (rootObject != null)
 0460                        DestroyImmediate(rootObject);
 461                }
 0462            }
 0463            catch (Exception e)
 464            {
 0465                if (gltfScene)
 466                {
 0467                    DestroyImmediate(gltfScene);
 468                }
 469
 0470                throw new Exception(e.Message + "\n" + e.StackTrace, e);
 471            }
 472
 473            // Set main asset
 0474            ctx.AddObjectToAsset("main asset", gltfScene);
 475
 476            // Add meshes
 0477            foreach (var mesh in meshes)
 478            {
 479                try
 480                {
 0481                    ctx.AddObjectToAsset("mesh " + mesh.name, mesh);
 0482                }
 483                catch (System.InvalidOperationException e)
 484                {
 0485                    Debug.LogWarning(e.ToString(), mesh);
 0486                }
 487            }
 488
 0489            ctx.SetMainObject(gltfScene);
 0490        }
 491
 492        public static event System.Action<GLTFRoot> OnGLTFRootIsConstructed;
 493        public static event System.Action<GLTFSceneImporter> OnGLTFWillLoad;
 494
 495        private GameObject CreateGLTFScene(string projectFilePath)
 496        {
 0497            ILoader fileLoader = new FileLoader(Path.GetDirectoryName(projectFilePath));
 0498            using (var stream = File.OpenRead(projectFilePath))
 499            {
 500                GLTFRoot gLTFRoot;
 0501                GLTFParser.ParseJson(stream, out gLTFRoot);
 502
 0503                OnGLTFRootIsConstructed?.Invoke(gLTFRoot);
 504
 0505                var loader = new GLTFSceneImporter(Path.GetFullPath(projectFilePath), gLTFRoot, fileLoader, null, stream
 0506                GLTFSceneImporter.budgetPerFrameInMilliseconds = float.MaxValue;
 0507                loader.addImagesToPersistentCaching = false; // Since we control the PersistentAssetCache during AB Conv
 0508                loader.addMaterialsToPersistentCaching = false;
 0509                loader.initialVisibility = true;
 0510                loader.useMaterialTransition = false;
 0511                loader.maximumLod = _maximumLod;
 0512                loader.isMultithreaded = true;
 513
 0514                OnGLTFWillLoad?.Invoke(loader);
 515
 516                // HACK: Force the coroutine to run synchronously in the editor
 0517                var stack = new Stack<IEnumerator>();
 0518                stack.Push(loader.LoadScene());
 519
 0520                while (stack.Count > 0)
 521                {
 0522                    var enumerator = stack.Pop();
 523
 524                    try
 525                    {
 0526                        if (enumerator.MoveNext())
 527                        {
 0528                            stack.Push(enumerator);
 0529                            var subEnumerator = enumerator.Current as IEnumerator;
 0530                            if (subEnumerator != null)
 531                            {
 0532                                stack.Push(subEnumerator);
 533                            }
 534                        }
 0535                    }
 0536                    catch (Exception e)
 537                    {
 0538                        Debug.Log("GLTFImporter - CreateGLTFScene Failed: " + e.Message + "\n" + e.StackTrace);
 0539                    }
 540                }
 541
 0542                return loader.lastLoadedScene;
 543            }
 0544        }
 545
 546        private void CopyOrNew<T>(T asset, string assetPath, Action<T> replaceReferences) where T : Object
 547        {
 0548            var existingAsset = AssetDatabase.LoadAssetAtPath<T>(assetPath);
 0549            if (existingAsset)
 550            {
 0551                EditorUtility.CopySerialized(asset, existingAsset);
 0552                replaceReferences(existingAsset);
 0553                return;
 554            }
 555
 0556            AssetDatabase.CreateAsset(asset, assetPath);
 0557        }
 558
 559        private class TexMaterialMap
 560        {
 0561            public UnityEngine.Material Material { get; set; }
 0562            public string Property { get; set; }
 0563            public bool IsNormalMap { get; set; }
 564
 0565            public TexMaterialMap(UnityEngine.Material material, string property, bool isNormalMap)
 566            {
 0567                Material = material;
 0568                Property = property;
 0569                IsNormalMap = isNormalMap;
 0570            }
 571        }
 572    }
 573}
 574#endif