예제 #1
0
            public void CopyMeshes(MeshFilter meshFilter)
            {
                var mesh = meshFilter.sharedMesh;

                reduceMeshName(mesh);
                if (
                    meshAssets.ContainsKey(mesh.name) &&
                    meshAssets[mesh.name].ContainsKey(mesh.vertexCount)
                    )
                {
                    return;
                }
                // IMPORTANT: Default meshes may be in use and are not in Assets path
                if (!AssetDatabase.GetAssetPath(mesh).StartsWith("Assets/"))
                {
                    return;
                }
                expandMeshName(mesh);
                var copyMesh = EP.CopyAssetToPath(mesh, meshPath.Substring("Assets/".Length), ".asset");

                reduceMeshName(mesh);
                if (!meshAssets.ContainsKey(mesh.name))
                {
                    meshAssets.Add(mesh.name, new Dictionary <int, Mesh>());
                }
                meshAssets[mesh.name].Add(mesh.vertexCount, copyMesh);
            }
예제 #2
0
        static void AddCollider(GameObject gameObject, bool hasPhysics)
        {
            // If no mesh is defined skip this GameObject
            var meshFilter = gameObject.GetComponent <MeshFilter>();
            var sharedMesh = meshFilter ? meshFilter.sharedMesh : null;

            if (!sharedMesh)
            {
                return;
            }

            var colliderTarget = gameObject;
            var lodGroup       = gameObject.GetComponentInParent <LODGroup>();

            if (lodGroup)
            {
                colliderTarget = lodGroup.gameObject;
            }

            // If target already has colliders do not modify
            if (colliderTarget.GetComponentsInChildren <Collider>().Length > 0)
            {
                return;
            }

            // Add a mesh collider to this object
            var meshCollider = EP.AddComponent <MeshCollider>(colliderTarget);

            meshCollider.sharedMesh = sharedMesh;
            meshCollider.convex     = hasPhysics;
        }
예제 #3
0
        // PROBLEM: Light source meshes are generally small and could use a lower level of detail
        // in most cases. Prefabs with custom meshes could address this for sphere and cylinder

        // OPTION: Provide a component to monitor the light intensity and update the emissive material
        // accordingly - both static and dynamic. This could also tag actual child light sources.

        public static GameObject CreatePrimitiveSource(Light light, PrimitiveType primitiveType)
        {
            var source = GameObject.CreatePrimitive(primitiveType);

            source.SetActive(light.enabled);
            source.layer = light.gameObject.layer;
            // Make source constitent with search
            source.name = ConfigureName(light.name);
            EP.SetParent(source.transform, light.transform);

            // Position source around light
            source.transform.localPosition = Vector3.zero;
            source.transform.localRotation = Quaternion.identity;
            // Source scale depends on light type

            Object.DestroyImmediate(source.GetComponent <Collider>());

            if (light.gameObject.isStatic)
            {
                var staticFlags = (StaticEditorFlags) ~0;
                staticFlags &= ~StaticEditorFlags.OccluderStatic;
                staticFlags &= ~StaticEditorFlags.ContributeGI;
                GameObjectUtility.SetStaticEditorFlags(source, staticFlags);
            }
            else
            {
                source.isStatic = false;
            }

            LightSourceMeshRenderer(light, source.GetComponent <MeshRenderer>());

            Undo.RegisterCreatedObjectUndo(source, "Create Primitive Light Source");
            return(source);
        }
예제 #4
0
        // PROBLEM: If multiple intermediate levels of the hierarchy have the same name
        // then children will be randomly assigned. This could happen if Av0 has children,
        // Av1 does not and is added, but then Av2 has children, which could be parented to
        // either Av1 or Av0. Or, if the original model uses a name multiple times.
        // TODO: Identify problem and warn.

        static void Merge(Transform mergeFrom, Transform mergeTo)
        {
            // When names match, merge
            var mergeChildren = new List <Transform>();

            foreach (var childFrom in mergeFrom.Children())
            {
                var childTo = mergeTo.NameFindInChildren(childFrom.name);
                if (childTo.Length == 0)
                {
                    // ChildFrom is not in the hierarchy
                    mergeChildren.Add(childFrom);
                    continue;
                }
                if (PrefabUtility.GetPrefabAssetType(childFrom) != PrefabAssetType.NotAPrefab)
                {
                    // ChildFrom is a Prefab and is already present in hierarchy since childTo != null
                    continue;
                }
                if (childFrom.transform.childCount == 0)
                {
                    // ChildFrom is a distinct instance even if childTo is present
                    mergeChildren.Add(childFrom);
                    continue;
                }
                // ChildFrom and ChildTo match, so merge children instead
                Merge(childFrom, childTo[0].transform);
            }
            foreach (var childFrom in mergeChildren)
            {
                EP.SetParent(childFrom, mergeTo);
            }
        }
예제 #5
0
        public static float playerPositionStep = 1f;         // meters

        static void CreatePlayer(Bounds sceneBounds)
        {
            var playerAsset = AssetDatabase.LoadAssetAtPath <GameObject>(playerPrefabPath);

            if (!playerAsset)
            {
                Debug.LogWarning($"Missing asset: {playerPrefabPath}");
                return;
            }
            var player = EP.Instantiate(playerAsset);

            // Place the player
            // NOTE: This could be managed by a configuration component
            var position = Vector3.zero;

            position.y = sceneBounds.max.y + playerPositionStep;
            for (position.x = sceneBounds.min.x + playerPositionStep; position.x < sceneBounds.max.x - playerPositionStep; position.x += playerPositionStep)
            {
                for (position.z = sceneBounds.min.z + playerPositionStep; position.z < sceneBounds.max.z - playerPositionStep; position.z += playerPositionStep)
                {
                    if (Physics.Raycast(position, Vector3.down, out var hitInfo, sceneBounds.size.y + playerPositionStep * 2f))
                    {
                        player.transform.position = hitInfo.point;
                        position = sceneBounds.max;                         // Break from both loops
                    }
                }
            }
        }
예제 #6
0
        public static void CreateSource(Light light)
        {
            // ASSUME: All children of light are sources
            foreach (var source in light.gameObject.Children())
            {
                EP.Destroy(source);
            }

            // OPTION: Move light forward to prevent Z fighting when coplanar

            switch (light.type)
            {
            case LightType.Point:
                CreatePointSource(light);
                break;

            case LightType.Spot:
                CreateSpotSource(light);
                break;

            case LightType.Disc:
                CreateDiskSource(light);
                break;

            case LightType.Rectangle:
                CreateRectangleSource(light);
                break;

            default:             // Directional light has no position source
                break;
            }
        }
예제 #7
0
            public MeshGatherer(string pathRoot)
            {
                meshPath = pathRoot + "/" + meshFolder;
                if (EP.CreatePersistentPath(meshPath.Substring("Assets/".Length), false) > 0)
                {
                    return;
                }
                var meshGUIDs = AssetDatabase.FindAssets("t:Mesh", new[] { meshPath });

                foreach (var guid in meshGUIDs)
                {
                    var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                    var mesh      = AssetDatabase.LoadAssetAtPath <Mesh>(assetPath);
                    reduceMeshName(mesh);
                    if (!meshAssets.ContainsKey(mesh.name))
                    {
                        meshAssets.Add(mesh.name, new Dictionary <int, Mesh>());
                    }
                    if (meshAssets[mesh.name].ContainsKey(mesh.vertexCount))
                    {
                        Debug.LogWarning($"MeshGatherer repeated asset name: {mesh.name}({mesh.vertexCount}) at {assetPath} in {meshPath}");
                        continue;
                    }
                    meshAssets[mesh.name][mesh.vertexCount] = mesh;
                }
            }
예제 #8
0
        // Model export generates meshes in world coordinates
        // In order to retain information, each prefab is replaced with a transformed tetrahedron
        static void ConfigurePrefab(Transform placeholder, CachedPrefab cached)
        {
            var prefab = (PrefabUtility.InstantiatePrefab(cached.prefab) as GameObject).transform;

            EP.SetParent(prefab, placeholder.parent);
            prefab.localPosition = placeholder.localPosition;
            prefab.localRotation = placeholder.localRotation;
            prefab.localScale    = placeholder.localScale;
            prefab.name          = placeholder.name;
        }
예제 #9
0
        // Derive transform information from a placeholder
        public static void ImportPlaceholder(MeshFilter meshFilter, bool rhinoBasis)
        {
            var sharedMesh = meshFilter.sharedMesh;
            var vertices   = sharedMesh?.vertices;

            if (vertices == null || vertices.Length != 4)
            {
                Debug.LogWarning($"Inconsistent placeholder mesh: {meshFilter.Path()}.{sharedMesh.name}");
                return;
            }
            var placeholder = meshFilter.transform;
            // OBSERVATION: At this stage of the import the layer parent transform is present
            // but on completion the layer may be absent, although the transform will persist.
            // NOTE: This also applies to the default import path.

            // Derive Rhino transform block basis in world coordinates
            var origin = placeholder.TransformPoint(vertices[0]);
            var basisX = placeholder.TransformPoint(vertices[1]) - origin;
            var basisY = placeholder.TransformPoint(vertices[2]) - origin;
            var basisZ = placeholder.TransformPoint(vertices[3]) - origin;

            if (rhinoBasis)
            {
                // Mesh basis describes transformation of Rhino basis
                var unityX = new Vector3(-basisX.x, basisX.y, -basisX.z);
                var unityY = new Vector3(-basisZ.x, basisZ.y, -basisZ.z);
                var unityZ = new Vector3(-basisY.x, basisY.y, -basisY.z);
                basisX = unityX;
                basisY = unityY;
                basisZ = unityZ;
            }

            // TODO: Use SVD to construct transform, which can include shear
            // TEMP: Assume transform is axial scaling followed by rotation only
            // NOTE: The origin and bases are simply the columns of an affine (3x4) transform matrix
            placeholder.localScale = new Vector3(basisX.magnitude, basisY.magnitude, basisZ.magnitude);
            placeholder.rotation   = Quaternion.LookRotation(basisZ, basisY);
            placeholder.position   = origin;

            // Remove meshes from placeholders
            // IMPORTANT: MeshRenderers must also be removed, in order to avoid
            // the application of renderer configations that could modify or remove the placeholder.
            var meshRenderer = meshFilter.GetComponent <MeshRenderer>();

            if (meshRenderer)
            {
                EP.Destroy(meshRenderer);
            }
            EP.Destroy(meshFilter);
        }
예제 #10
0
        // TODO: This, like a grid, is a standard layout
        // IDEA: When TransformData is included, provide utilities to generate layouts, and to replicate gameobjects over them
        static GameObject[] RotateCopies(GameObject original, Quaternion rotation, int count)
        {
            var copyList = new GameObject[count];

            copyList[0] = original;
            for (int c = 1; c < count; ++c)
            {
                var copy = EP.Instantiate(original);
                EP.SetParent(copy.transform, original.transform.parent);
                copy.transform.localPosition = rotation * copyList[c - 1].transform.localPosition;
                copy.transform.localRotation = rotation * copyList[c - 1].transform.localRotation;
                copyList[c] = copy;
            }
            return(copyList);
        }
예제 #11
0
        /// <summary>
        /// Recursively configure all objects and components in hierarchy to be static
        /// </summary>
        /// <param name="gameObject">Root of hierarchy to be converted to static configuration</param>
        /// <param name="prefabs">Apply static conversion overrides to prefabs in hierarchy</param>
        public static void ApplyTo(GameObject gameObject, bool prefabs = false)
        {
            using (var editScope = new EP.EditGameObject(gameObject)) {
                var editObject = editScope.editObject;

                // Set static configurations
                GameObjectSetStatic(editObject);
                foreach (var renderer in editObject.GetComponents <Renderer>())
                {
                    RendererSetStatic(renderer);
                }
                foreach (var collider in editObject.GetComponents <Collider>())
                {
                    ColliderSetStatic(collider);
                }
                foreach (var light in editObject.GetComponents <Light>())
                {
                    LightSetStatic(light);
                }

                // Remove dynamic components
                foreach (var monoBehavior in editObject.GetComponents <MonoBehaviour>())
                {
                    EP.Destroy(monoBehavior);
                }
                foreach (var physics in editObject.GetComponents <Rigidbody>())
                {
                    EP.Destroy(physics);
                }

                foreach (var child in editObject.Children())
                {
                    // Limit prefab recursion
                    var prefabAssetType = PrefabUtility.GetPrefabAssetType(child);
                    if (prefabAssetType == PrefabAssetType.MissingAsset)
                    {
                        continue;
                    }
                    if (prefabAssetType != PrefabAssetType.NotAPrefab && !prefabs)
                    {
                        continue;
                    }

                    ApplyTo(child);
                }
            }
        }
예제 #12
0
        // TODO: ExtractTextures should support list parameters for batched importing

        /// <summary>
        /// Extracts and remaps textures for use by materials
        /// </summary>
        /// <returns>True if reimport is requied</returns>
        /// <remarks>
        /// IMPORTANT: In order for extracted textures to be remapped to materials
        /// this must be called when AssetDatabase.StartAssetEditing() does not pertain
        /// so that textures can be synchronously imported for remapping.
        ///
        /// WARNING: In order to update model materials the model must be remiported:
        ///  AssetDatabase.ImportAsset(modelPath)
        ///
        /// For the implementation of the "Extract Textures" button
        /// in the "Materials" tab of the "Import Settings" Inspector panel, see:
        /// https://github.com/Unity-Technologies/UnityCsReference/
        /// Modules/AssetPipelineEditor/ImportSettings/ModelImporterMaterialEditor.cs
        /// private void ExtractTexturesGUI()
        /// </remarks>
        public static bool ExtractTextures(string modelPath)
        {
            var modelImporter = AssetImporter.GetAtPath(modelPath) as ModelImporter;

            if (modelImporter == null)
            {
                return(false);
            }

            // Extract textures
            var texturesPath = modelPath.Substring(0, modelPath.LastIndexOf('.')) + "/Textures";
            var success      = false;        // success, not extraction count

            try {
                AssetDatabase.StartAssetEditing();
                success = modelImporter.ExtractTextures(texturesPath);
            } finally {
                AssetDatabase.StopAssetEditing();
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
                // Import textures to AssetDatabase
            }

            // If no textures were imported remove folder & skip the reimport
            // NOTE: ExtractTextures will only create the texturesPath if there are textures to be extracted
            if (!success || EP.CreatePersistentPath(texturesPath.Substring("Assets/".Length), false) > 0)
            {
                return(false);
            }

            // Remap textures and reimport model
            // NOTE: Remapping will fail while StartAssetEditing() pertains (during model import)
            // since extracted textures will not be immediately imported, and so will not be found.
            var guids = AssetDatabase.FindAssets("t:Texture", new string[] { texturesPath });

            foreach (var guid in guids)
            {
                var path    = AssetDatabase.GUIDToAssetPath(guid);
                var texture = AssetDatabase.LoadAssetAtPath <Texture>(path);
                if (texture == null)
                {
                    continue;
                }
                var identifier = new AssetImporter.SourceAssetIdentifier(texture);
                modelImporter.AddRemap(identifier, texture);
            }
            return(true);
        }
예제 #13
0
        public static LightProbeGroup CreateLightProbes(GameObject gameObject)
        {
            // Light probe positions are evaluated relative to LightProbeGroup
            // https://docs.unity3d.com/ScriptReference/LightProbeGroup-probePositions.html
            var group = gameObject.GetComponent <LightProbeGroup>();

            if (!group)
            {
                group = EP.AddComponent <LightProbeGroup>(gameObject);
            }

            // IDEA: Find child LightProbeGroups and exclude probes in those volumes
            // This would enable adjusting the probe density in different areas.

            // TODO: Enable configuration to use other placement strategies
            group.probePositions = LightProbeGrid(gameObject, probeSpaces);
            return(group);
        }
예제 #14
0
        static void CreateLinearYSource(Light light)
        {
            // Create a self-illuminated cylinder
            var source = CreatePrimitiveSource(light, PrimitiveType.Cylinder);

            source.transform.localScale = new Vector3(pointDiameter, light.areaSize.y / 2f, pointDiameter);

            // Create planes covering all emission directions
            var side0 = MakeAreaCopy(light, new Vector2(pointDiameter, light.areaSize.y));

            EP.SetParent(side0.transform, light.transform);
            var sideList = RotateCopies(side0, Quaternion.Euler(360f / linearSources, 0f, 0f), linearSources);

            for (int s = 0; s < sideList.Length; ++s)
            {
                sideList[s].name = light.name + "_Side" + s;
            }
        }
예제 #15
0
        /// <summary>
        /// Make a copy of light source as area light, with equivalent illumination
        /// </summary>
        /// <remarks>
        /// Intensity is scaled relative to the area of the light
        /// </remarks>
        public static GameObject MakeAreaCopy(Light light, Vector2 areaSize)
        {
            var gameObject = EP.Instantiate();

            GameObjectUtility.SetStaticEditorFlags(gameObject, (StaticEditorFlags) ~0);
            EP.SetParent(gameObject.transform, light.transform.parent);
            gameObject.transform.localPosition = light.transform.localPosition;
            gameObject.transform.localRotation = light.transform.localRotation;
            var areaLight = EP.AddComponent <Light>(gameObject);

            areaLight.lightmapBakeType = LightmapBakeType.Baked;
            areaLight.type             = LightType.Rectangle;
            areaLight.areaSize         = areaSize;
            areaLight.intensity        = light.intensity / (areaSize.x * areaSize.y);
            areaLight.color            = light.color;
            areaLight.range            = light.range;
            return(gameObject);
        }
예제 #16
0
        /// <summary>
        /// Removes all empty branches in the hierarchy of a GameObject
        /// </summary>
        /// <remarks>
        /// Excluding cameras or lights from a model import removes components,
        /// but their associated GameObjects will persist in the hierarchy.
        /// </remarks>
        public static void RemoveEmpty(GameObject gameObject)
        {
            // IMPORTANT: Before counting children, apply RemoveEmpty to children
            // since their removal could result in children being empty
            var children = gameObject.transform.Children();

            foreach (var child in children)
            {
                RemoveEmpty(child.gameObject);
            }

            // IMPORTANT: Before counting components, remove empty meshes
            // since this could result in components being empty
            var meshFilter = gameObject.GetComponent <MeshFilter>();

            if (meshFilter)
            {
                var sharedMesh = meshFilter.sharedMesh;
                if (sharedMesh == null || sharedMesh.vertexCount == 0)
                {
                    var meshRenderer = gameObject.GetComponent <MeshRenderer>();
                    if (meshRenderer != null)
                    {
                        EP.Destroy(meshRenderer);
                    }
                    var meshCollider = gameObject.GetComponent <MeshCollider>();
                    if (meshCollider != null)
                    {
                        EP.Destroy(meshCollider);
                    }
                    EP.Destroy(meshFilter);
                }
            }

            // Remove if no children and no components other than transform
            if (
                gameObject.transform.childCount == 0 &&
                gameObject.GetComponents <Component>().Length == 1
                )
            {
                EP.Destroy(gameObject);
                return;
            }
        }
예제 #17
0
 // Find or make a prefab adjacent to the merged asset folder
 static void CreateMerged(string path, Dictionary <string, GameObject> mergedPrefabs)
 {
     if (!mergedPrefabs.ContainsKey(path))
     {
         // Find or make a merged prefab
         var mergedPath = path + ".prefab";
         var merged     = AssetDatabase.LoadAssetAtPath <GameObject>(mergedPath);
         if (!merged)
         {
             var empty = EP.Instantiate();
             empty.name = path.Substring((path.LastIndexOf('/') + 1));
             // WARNING: SaveAsPrefabAsset will return null while AssetDatabase.StartAssetEditing() pertains
             merged = PrefabUtility.SaveAsPrefabAsset(empty, mergedPath);
             EP.Destroy(empty);
             //Debug.Log($"Created empty merged object: {pathPart}.prefab");
         }
         mergedPrefabs.Add(path, merged);
     }
 }
예제 #18
0
        // WARNING: If there is a name override, PathName will not resolve!

        // TODO: Find a way to clone prefab with overrides intact.
        // QUESTION: Is there a way to accomplish instatiation using object serializations?
        // Ideally, this would handle the connection and override persistence.
        // https://docs.unity3d.com/ScriptReference/SerializedObject.html
        // Construct, then iterate & copy?

        static GameObject InstantiateChild(GameObject original, GameObject parent)
        {
            GameObject child = null;
            // IMPORTANT: PrefabUtility.InstantiatePrefab applies only to assets, not to instances
            // IMPORTANT: PrefabUtility.GetCorrespondingObjectFromSource applies only to instances, not to assets
            var        asset      = PrefabUtility.GetCorrespondingObjectFromSource(parent);
            GameObject copy_asset = null;

            if (asset)
            {
                copy_asset = asset;
            }
            else
            {
                copy_asset = original;
            }
            var copy = PrefabUtility.InstantiatePrefab(copy_asset) as GameObject;

            if (parent != original)
            {
                var path = new PathName(original, parent);
                var find = path.Find(copy.transform);
                if (find.Length == 1)
                {
                    child = find[0].gameObject;
                    // Unpack to enable orphaning, only once since nearest root was instantiated
                    var unpack = PrefabUtility.GetOutermostPrefabInstanceRoot(child);
                    while (unpack)
                    {
                        PrefabUtility.UnpackPrefabInstance(unpack, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction);
                        unpack = PrefabUtility.GetOutermostPrefabInstanceRoot(child);
                    }
                    child.transform.SetParent(null);
                }
                EP.Destroy(copy);
            }
            else
            {
                child = copy;
            }
            return(child);
        }
예제 #19
0
        static void CreateSun(Bounds sceneBounds)
        {
            var sunAsset = AssetDatabase.LoadAssetAtPath <GameObject>(sunPrefabPath);

            if (!sunAsset)
            {
                Debug.LogWarning($"Missing asset: {sunPrefabPath}");
                return;
            }
            var sun = EP.Instantiate(sunAsset);

            var light = sun.GetComponentInChildren <Light>();

            RenderSettings.sun = light;

            // Position the sun source outside of the model
            // NOTE: This could be managed by a configuration component
            sun.transform.position        = sceneBounds.center;
            light.transform.localPosition = new Vector3(0f, 0f, -sceneBounds.extents.magnitude);
        }
예제 #20
0
        public static void ApplyTo(GameObject mergeTarget, params GameObject[] mergeSources)
        {
            using (var editScope = new EP.EditGameObject(mergeTarget)) {
                var targetEdit = editScope.editObject;
                foreach (var mergeSource in mergeSources)
                {
                    var sourceCopy = EP.Instantiate(mergeSource);

                    // Unpack only root model prefab, constituent prefab links will be retained
                    // NOTE: Applying UnpackPrefabInstance to a non-prefab object results in a crash
                    if (PrefabUtility.GetPrefabAssetType(sourceCopy) != PrefabAssetType.NotAPrefab)
                    {
                        PrefabUtility.UnpackPrefabInstance(sourceCopy, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction);
                    }
                    Merge(sourceCopy.transform, targetEdit.transform);

                    EP.Destroy(sourceCopy);
                }
            }
        }
예제 #21
0
        public static string CreateScene(string path, params GameObject[] gameObjects)
        {
            var scenePath = path + ".unity";

            // PROBLEM: When using NewSceneMode.Single during import assertion "GetApplication().MayUpdate()" fails
            // SOLUTION: During import, using Additive loading works.
            // PROBLEM: InvalidOperationException: Cannot create a new scene additively with an untitled scene unsaved.
            // NOTE: This can occur when the previously opened scene has ceased to exist, in particular when a project is opened.
            var scene  = EditorSceneManager.GetActiveScene();
            var addNew = scene.name.Length > 0 && scene.path.Length > 0 && scene.path != scenePath;             // scene.IsValid() will be true even when path and name are empty

            if (addNew)
            {
                scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive);
            }
            else
            {
                // Remove all default scene objects
                foreach (var rootObject in scene.GetRootGameObjects())
                {
                    EP.Destroy(rootObject);
                }
            }
            EditorSceneManager.SetActiveScene(scene);

            // Add objects to scene
            foreach (var gameObject in gameObjects)
            {
                EP.Instantiate(gameObject);
            }
            // WARNING: If scene is created during asset import physics computations will not be initialized

            // PROBLEM: At end of import the open scene will have been modified, so a pop-up will appear.
            // SOLUTION: After loading the scene in additive mode, close it.
            EditorSceneManager.SaveScene(scene, scenePath);
            if (addNew)
            {
                EditorSceneManager.CloseScene(scene, true);
            }
            return(scenePath);
        }
예제 #22
0
            public MaterialGatherer(string pathRoot)
            {
                materialPath = pathRoot + "/" + materialFolder;
                if (EP.CreatePersistentPath(materialPath.Substring("Assets/".Length), false) > 0)
                {
                    return;
                }
                var materialGUIDs = AssetDatabase.FindAssets("t:Material", new[] { materialPath });

                foreach (var guid in materialGUIDs)
                {
                    var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                    var material  = AssetDatabase.LoadAssetAtPath <Material>(assetPath);
                    if (materialAssets.ContainsKey(material.name))
                    {
                        Debug.LogWarning($"MaterialGatherer repeated asset name: {material.name} at {assetPath} in {materialPath}");
                        continue;
                    }
                    materialAssets.Add(material.name, material);
                }
            }
예제 #23
0
            public TextureGatherer(string pathRoot)
            {
                texturePath = pathRoot + "/" + textureFolder;
                if (EP.CreatePersistentPath(texturePath.Substring("Assets/".Length), false) > 0)
                {
                    return;
                }
                var textureGUIDs = AssetDatabase.FindAssets("t:Texture", new[] { texturePath });

                foreach (var guid in textureGUIDs)
                {
                    var assetPath = AssetDatabase.GUIDToAssetPath(guid);
                    var texture   = AssetDatabase.LoadAssetAtPath <Texture>(assetPath);
                    if (textureAssets.ContainsKey(texture.name))
                    {
                        Debug.LogWarning($"TextureGatherer repeated asset name: {texture.name} at {assetPath} in {texturePath}");
                        continue;
                    }
                    textureAssets.Add(texture.name, texture);
                }
            }
예제 #24
0
        public static void ApplyTo(GameObject gameObject, string pathRoot)
        {
            var assetType = PrefabUtility.GetPrefabAssetType(gameObject);

            if (assetType == PrefabAssetType.MissingAsset)
            {
                return;
            }
            if (assetType == PrefabAssetType.Model)
            {
                // In the case of a model created an editable prefab
                var prefab = EP.Instantiate(gameObject);
                PrefabUtility.UnpackPrefabInstance(prefab, PrefabUnpackMode.OutermostRoot, InteractionMode.AutomatedAction);
                gameObject = PrefabUtility.SaveAsPrefabAsset(prefab, pathRoot + ".prefab");
                EP.Destroy(prefab);
            }

            // NOTE: Calling CopyAssets will be extremely slow if each asset is imported individually.
            // Instead, all of the copying will be done in one batch, after which assets will be imported.
            // Then, a new instance of AssetGatherer will find those copies and make replacements in
            // a second batch.
            try {
                AssetDatabase.StartAssetEditing();
                var assertGatherer = new AssetGatherer(pathRoot);
                assertGatherer.CopyAssets(gameObject);
            } finally {
                AssetDatabase.StopAssetEditing();
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
            }
            try {
                AssetDatabase.StartAssetEditing();
                var assertGatherer = new AssetGatherer(pathRoot);
                assertGatherer.SwapAssets(gameObject);
            } finally {
                AssetDatabase.StopAssetEditing();
                AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
            }
        }
예제 #25
0
        static void ReplacePlaceholders(string prefabPath, GameObject gameObject)
        {
            Dictionary <string, CachedPrefab> prefabs = GetPrefabs(prefabPath);
            var children = gameObject.Children(true);

            foreach (var child in children)
            {
                // Do not modify existing child prefabs
                var childPrefab = PrefabUtility.GetNearestPrefabInstanceRoot(child);
                if (childPrefab != null && childPrefab != gameObject)
                {
                    continue;
                }

                // Placeholder names are constructed as "prefab_name=object_name"
                var name_parts = child.name.Split('=');
                if (name_parts.Length == 1)
                {
                    continue;
                }

                // Create an placeholder prefab that can be modified after import
                if (!prefabs.ContainsKey(name_parts[0]))
                {
                    var placeholder = EP.Instantiate();
                    placeholder.name = name_parts[0];
                    var placeholderPath  = prefabPath + "/" + placeholder.name + ".prefab";
                    var placeholderAsset = PrefabUtility.SaveAsPrefabAsset(placeholder, placeholderPath);
                    prefabs[name_parts[0]] = new CachedPrefab(placeholderAsset);
                    EP.Destroy(placeholder);
                    //Debug.Log($"Missing prefab in {gameObject.name} for {child.Path()} -> created placeholder");
                }

                ConfigurePrefab(child.transform, prefabs[name_parts[0]]);
                EP.Destroy(child);
            }
        }
예제 #26
0
            public void CopyMaterials(Renderer renderer)
            {
                var sharedMaterials = renderer.sharedMaterials;

                for (var m = 0; m < sharedMaterials.Length; ++m)
                {
                    var material = sharedMaterials[m];
                    if (!material)
                    {
                        continue;
                    }
                    if (materialAssets.ContainsKey(material.name))
                    {
                        continue;
                    }
                    // IMPORTANT: Default materials may be in use and are not in Assets path
                    if (!AssetDatabase.GetAssetPath(material).StartsWith("Assets/"))
                    {
                        continue;
                    }
                    var copyMaterial = EP.CopyAssetToPath(material, materialPath.Substring("Assets/".Length), ".mat");
                    materialAssets.Add(material.name, copyMaterial);
                }
            }
예제 #27
0
            public void CopyTextures(Material material)
            {
                if (material.shader.name != "Standard")
                {
                    return;
                }
                var shaderTextures = GetStandardMaterialTextures(material);

                foreach (var shaderTexture in shaderTextures)
                {
                    var texture = shaderTexture.Value;
                    if (textureAssets.ContainsKey(texture.name))
                    {
                        continue;
                    }
                    // IMPORTANT: Default textures may be in use and are not in Assets path
                    if (!AssetDatabase.GetAssetPath(texture).StartsWith("Assets/"))
                    {
                        continue;
                    }
                    var copyTexture = EP.CopyAssetToPath(texture, texturePath.Substring("Assets/".Length));
                    textureAssets.Add(texture.name, copyTexture);
                }
            }
예제 #28
0
        /// <summary>
        /// Configure the light probe proxy volume for lower levels of detail in group
        /// </summary>
        public static void ConfigureLODGroup(LODGroup lodGroup)
        {
            // TODO: Check if group is static - if not, also configure the lowest level of detail.

            // Only lower levels of detail will use probes
            // IMPORTANT: Proxy volumes always update, so only generate them if they will be used.
            var lods = lodGroup.GetLODs();

            if (lods.Length < 2)
            {
                return;
            }

            // FIXME: Local bounds are needed
            var worldBounds = RendererWorldBounds(lodGroup.gameObject);
            // TEMP: Assume that only axis swaps pertain
            var localBounds = new Bounds();

            localBounds.center = lodGroup.transform.InverseTransformPoint(worldBounds.center);
            localBounds.size   = lodGroup.transform.InverseTransformDirection(worldBounds.size);
            localBounds.size   = new Vector3(Mathf.Abs(localBounds.size.x), Mathf.Abs(localBounds.size.y), Mathf.Abs(localBounds.size.z));

            // If object bounds > probe spacing in any dimension use a proxy volume
            var useProxy = false;

            for (var i = 0; i < 3; ++i)
            {
                useProxy |= localBounds.size[i] > probeSpaces[i];
            }
            var proxy = lodGroup.gameObject.GetComponent <LightProbeProxyVolume>();

            if (useProxy)
            {
                if (!proxy)
                {
                    proxy = EP.AddComponent <LightProbeProxyVolume>(lodGroup.gameObject);
                }

                // Configure proxy bounds
                proxy.boundingBoxMode = LightProbeProxyVolume.BoundingBoxMode.Custom;
                proxy.originCustom    = localBounds.center;
                proxy.sizeCustom      = localBounds.size;

                // Configure spacing
                proxy.probePositionMode = LightProbeProxyVolume.ProbePositionMode.CellCorner;
                proxy.resolutionMode    = LightProbeProxyVolume.ResolutionMode.Custom;
                proxy.gridResolutionX   = ProxyResolution(localBounds.size.x / probeSpaces.x);
                proxy.gridResolutionY   = ProxyResolution(localBounds.size.y / probeSpaces.y);
                proxy.gridResolutionZ   = ProxyResolution(localBounds.size.z / probeSpaces.z);

                // Remaining settings
                proxy.qualityMode = LightProbeProxyVolume.QualityMode.Normal;
                proxy.refreshMode = LightProbeProxyVolume.RefreshMode.Automatic;
            }
            else
            {
                if (proxy)
                {
                    EP.Destroy(proxy);
                }
                proxy = null;
            }

            // Configure all lower levels of detail to use probes
            for (var l = 1; l < lods.Length; ++l)
            {
                foreach (var renderer in lods[l].renderers)
                {
                    renderer.lightProbeUsage = useProxy ? UnityEngine.Rendering.LightProbeUsage.UseProxyVolume : UnityEngine.Rendering.LightProbeUsage.BlendProbes;
                    renderer.lightProbeProxyVolumeOverride = lodGroup.gameObject;

                    var meshRender = renderer as MeshRenderer;
                    if (!meshRender)
                    {
                        continue;
                    }
                    meshRender.receiveGI = ReceiveGI.LightProbes;
                }
            }
        }
예제 #29
0
        static void MergeGroup(string pathName, List <GameObject> group)
        {
            // Gather LODGroup and Renderers
            LODGroup lodGroup  = null;
            var      renderers = new List <RendererSort>();

            foreach (var gameObject in group)
            {
                var renderer = gameObject.GetComponent <Renderer>();
                if (renderer)
                {
                    renderers.Add(new RendererSort(renderer));
                }
                var isGroup = gameObject.GetComponent <LODGroup>();
                if (isGroup)
                {
                    var lods = isGroup.GetLODs();
                    foreach (var lod in lods)
                    {
                        foreach (var lodRenderer in lod.renderers)
                        {
                            // Renderers must begin as siblings of LODGroup
                            EP.SetParent(lodRenderer.transform, isGroup.transform.parent);
                            renderers.Add(new RendererSort(lodRenderer));
                        }
                    }

                    if (!!lodGroup || !!renderer)
                    {
                        // LODGroup manager cannot be duplicated, and cannot have renderer component
                        Debug.LogWarning($"Removing LODGroup found on {gameObject.Path()}");
                        EP.Destroy(isGroup);
                        continue;
                    }
                    lodGroup = isGroup;
                }
            }
            if (!lodGroup)
            {
                lodGroup = EP.AddComponent <LODGroup>(EP.Instantiate());
            }

            // renderers[0] has the lowest vertex count
            renderers.Sort((l, m) => l.vertexCount - m.vertexCount);
            // Remove missing meshes and duplicate levels of detail
            var vertexCount     = 0;
            var removeRenderers = new List <RendererSort>();

            foreach (var renderer in renderers)
            {
                if (vertexCount == renderer.vertexCount)
                {
                    removeRenderers.Add(renderer);
                    continue;
                }
                vertexCount = renderer.vertexCount;
            }
            foreach (var renderer in removeRenderers)
            {
                renderers.Remove(renderer);
                EP.Destroy(renderer.renderer.gameObject);
                // NOTE: Duplicate mesh asset could be removed
            }
            if (renderers.Count == 0)
            {
                EP.Destroy(lodGroup.gameObject);
                return;
            }
            // renderers[0] has the highest vertrex count
            renderers.Reverse();

            // Configure manager in hierarchy
            lodGroup.gameObject.name = pathName.Substring(pathName.LastIndexOf('/') + 1);
            EP.SetParent(lodGroup.transform, renderers[0].renderer.transform.parent);
            lodGroup.transform.localPosition = renderers[0].renderer.transform.localPosition;
            lodGroup.transform.localRotation = renderers[0].renderer.transform.localRotation;
            lodGroup.transform.localScale    = renderers[0].renderer.transform.localScale;
            for (var r = 0; r < renderers.Count; ++r)
            {
                var renderer = renderers[r].renderer;
                // TODO: Used PathNameExtension for this!
                var lodIndex = renderer.gameObject.name.LastIndexOf(lodSuffix);
                if (lodIndex >= 0)
                {
                    renderer.gameObject.name = renderer.gameObject.name.Substring(0, lodIndex);
                }
                renderer.gameObject.name += lodSuffix + r.ToString();
                EP.SetParent(renderer.transform, lodGroup.transform);
            }

            // Configure the group
            var lodList = new LOD[renderers.Count];

            for (var r = 0; r < renderers.Count; ++r)
            {
                lodList[r].renderers = new[] { renderers[r].renderer }
            }
            ;
            ConfigureLODGroup(lodGroup, lodList);

            // Configure the renderers and materials
            foreach (var lod in lodGroup.GetLODs())
            {
                foreach (var renderer in lod.renderers)
                {
                    SetLightmapScale(renderer);
                    foreach (var material in renderer.sharedMaterials)
                    {
                        UseFadingShader(material);
                    }
                }
            }
        }
예제 #30
0
 public RePort()
 {
     // Ensure that import path exists
     EP.CreatePersistentPath(importPath.Substring("Assets/".Length));
 }