This commit is contained in:
CortexCore
2024-11-13 17:47:45 +08:00
parent c4af12acd7
commit 416e3322db
208 changed files with 2591757 additions and 1497 deletions

View File

@@ -0,0 +1,21 @@
{
"name": "AdvancedCullingSystem.Editor",
"rootNamespace": "",
"references": [
"GUID:37151e2099022af42afa90e2ee1b768a",
"GUID:d8b63aba1907145bea998dd612889d6b",
"GUID:e0cd26848372d4e5c891c569017e11f1",
"GUID:2665a8d13d1b3f18800f46e256720795"
],
"includePlatforms": [
"Editor"
],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View File

@@ -0,0 +1,11 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace AdvancedCullingSystem
{
public class ChunkMark : MonoBehaviour
{
}
}

View File

@@ -0,0 +1,485 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
namespace AdvancedCullingSystem.ChunksMaster
{
public class ChunksMaster
{
private int _uvChannelsCount = 8;
private List<Mesh> _meshes;
private List<List<Bounds>> _bounds;
private List<List<Mesh>> _chunks;
private List<int> _maxTriangles;
public List<Mesh> meshes
{
get
{
return _meshes;
}
}
public List<List<Bounds>> bounds
{
get
{
return _bounds;
}
}
public List<int> maxTriangles
{
get
{
return _maxTriangles;
}
}
private string _saveFolder;
public ChunksMaster()
{
_meshes = new List<Mesh>();
_bounds = new List<List<Bounds>>();
_chunks = new List<List<Mesh>>();
_maxTriangles = new List<int>();
}
public void CreateChunksBounds(Mesh mesh, int maxTriangles)
{
int index = _meshes.IndexOf(mesh);
if (index >= 0)
{
if (_maxTriangles[index] == maxTriangles && _bounds[index].Count > 0)
return;
_bounds[index].Clear();
_maxTriangles[index] = maxTriangles;
}
else
{
_meshes.Add(mesh);
_bounds.Add(new List<Bounds>());
_chunks.Add(new List<Mesh>());
_maxTriangles.Add(maxTriangles);
}
ProcessChunk(mesh, GetTriangles(mesh), mesh.bounds, maxTriangles, true, false);
}
public void CreateChunks(Mesh mesh, int maxTriangles, string saveFolder)
{
_saveFolder = saveFolder;
int index = _meshes.IndexOf(mesh);
if (index >= 0)
{
if (_maxTriangles[index] == maxTriangles && _chunks[index].Count > 0 && _chunks[0] != null)
return;
_bounds[index].Clear();
_chunks[index].Clear();
_maxTriangles[index] = maxTriangles;
}
else
{
_meshes.Add(mesh);
_bounds.Add(new List<Bounds>());
_chunks.Add(new List<Mesh>());
_maxTriangles.Add(maxTriangles);
}
if (!Directory.Exists(saveFolder))
Directory.CreateDirectory(saveFolder);
ProcessChunk(mesh, GetTriangles(mesh), mesh.bounds, maxTriangles, true, true);
}
public List<GameObject> SetupChunks(MeshFilter filter)
{
int index = _meshes.IndexOf(filter.sharedMesh);
if (index < 0)
return null;
List<Mesh> chunks = _chunks[index];
List<GameObject> chunksObjects = new List<GameObject>();
for (int i = 0; i < chunks.Count; i++)
{
GameObject chunkObject = new GameObject(filter.gameObject.name + "_chunk");
ComponentUtility.CopyComponent(filter);
ComponentUtility.PasteComponentAsNew(chunkObject);
ComponentUtility.CopyComponent(filter.GetComponent<MeshRenderer>());
ComponentUtility.PasteComponentAsNew(chunkObject);
chunkObject.transform.parent = filter.transform;
chunkObject.transform.localPosition = Vector3.zero;
chunkObject.transform.localRotation = Quaternion.identity;
chunkObject.transform.localScale = Vector3.one;
chunkObject.GetComponent<MeshFilter>().sharedMesh = chunks[i];
chunkObject.AddComponent<ChunkMark>();
chunkObject.isStatic = filter.gameObject.isStatic;
chunksObjects.Add(chunkObject);
}
filter.GetComponent<MeshRenderer>().enabled = false;
return chunksObjects;
}
public void Clear()
{
_meshes.Clear();
_bounds.Clear();
_chunks.Clear();
_maxTriangles.Clear();
}
private void ProcessChunk(Mesh mesh, List<int>[] triangles, Bounds bounds, int maxTriangles,
bool createBounds, bool createChunks)
{
int count = 0;
for (int i = 0; i < triangles.Length; i++)
count += triangles[i].Count / 3;
if (count < maxTriangles)
{
if(createBounds)
_bounds[_meshes.IndexOf(mesh)].Add(bounds);
if (createChunks)
CreateAndSaveMesh(mesh, triangles);
return;
}
Vector3 size = bounds.size;
if (bounds.size.x >= bounds.size.y && bounds.size.x >= bounds.size.z)
size.x /= 2;
else if (bounds.size.y >= bounds.size.x && bounds.size.y >= bounds.size.z)
size.y /= 2;
else
size.z /= 2;
Bounds leftBounds = new Bounds(bounds.min + size / 2, size);
Bounds rightBounds = new Bounds(bounds.max - size / 2, size);
Vector3[] vertices = mesh.vertices;
List<int>[] leftTriangles = new List<int>[triangles.Length];
List<int>[] rightTriangles = new List<int>[triangles.Length];
for (int i = 0; i < triangles.Length; i++)
{
leftTriangles[i] = new List<int>();
rightTriangles[i] = new List<int>();
for (int c = 0; c < triangles[i].Count; c += 3)
{
Vector3 vec1 = vertices[triangles[i][c]];
Vector3 vec2 = vertices[triangles[i][c + 1]];
Vector3 vec3 = vertices[triangles[i][c + 2]];
if (leftBounds.Contains(vec1) || leftBounds.Contains(vec2) || leftBounds.Contains(vec3))
{
leftTriangles[i].Add(triangles[i][c]);
leftTriangles[i].Add(triangles[i][c + 1]);
leftTriangles[i].Add(triangles[i][c + 2]);
}
else
{
rightTriangles[i].Add(triangles[i][c]);
rightTriangles[i].Add(triangles[i][c + 1]);
rightTriangles[i].Add(triangles[i][c + 2]);
}
}
}
ProcessChunk(mesh, leftTriangles, leftBounds, maxTriangles, createBounds, createChunks);
ProcessChunk(mesh, rightTriangles, rightBounds, maxTriangles, createBounds, createChunks);
}
private List<int>[] GetTriangles(Mesh mesh)
{
int subMeshCount = mesh.subMeshCount;
List<int>[] triangles = new List<int>[subMeshCount];
for (int i = 0; i < subMeshCount; i++)
triangles[i] = new List<int>(mesh.GetTriangles(i));
return triangles;
}
private void GetUVs(Mesh mesh, out List<List<Vector2>> uvs, out List<int> uvChannels)
{
uvs = new List<List<Vector2>>();
uvChannels = new List<int>();
for (int i = 0; i < _uvChannelsCount; i++)
{
List<Vector2> uv = new List<Vector2>();
mesh.GetUVs(i, uv);
if (uv != null && uv.Count > 0)
{
uvs.Add(uv);
uvChannels.Add(i);
}
}
}
private void CreateAndSaveMesh(Mesh src, List<int>[] triangles)
{
Mesh chunk = CreateChunkMesh(src, triangles);
string path = _saveFolder + chunk.GetHashCode() + ".asset";
AssetDatabase.CreateAsset(chunk, path);
_chunks[_meshes.IndexOf(src)].Add(AssetDatabase.LoadAssetAtPath<Mesh>(path));
}
private Mesh CreateChunkMesh(Mesh src, List<int>[] triangles)
{
Mesh mesh = new Mesh();
GetUVs(src, out List<List<Vector2>> srcUvs, out List<int> uvChannels);
Vector3[] srcVertices = src.vertices;
Vector4[] srcTangents = src.tangents;
Vector3[] srcNormals = src.normals;
Color[] srcColors = src.colors;
Color32[] srcColors32 = src.colors32;
List<Vector3> vertices = new List<Vector3>();
List<Vector4> tangents = new List<Vector4>();
List<Vector3> normals = new List<Vector3>();
List<Color> colors = new List<Color>();
List<Color32> colors32 = new List<Color32>();
List<Vector2>[] uvs = new List<Vector2>[uvChannels.Count];
List<int>[] meshTriangles = new List<int>[triangles.Length];
for (int i = 0; i < uvs.Length; i++)
uvs[i] = new List<Vector2>();
Dictionary<int, int> srcToNewTriangles = new Dictionary<int, int>();
for (int i = 0; i < triangles.Length; i++)
{
meshTriangles[i] = new List<int>();
for (int c = 0; c < triangles[i].Count; c++)
{
int srcTriangle = triangles[i][c];
int newTriangle = -1;
if (srcToNewTriangles.ContainsKey(srcTriangle))
{
newTriangle = srcToNewTriangles[srcTriangle];
}
else
{
newTriangle = vertices.Count;
vertices.Add(srcVertices[srcTriangle]);
tangents.Add(srcVertices[srcTriangle]);
normals.Add(srcVertices[srcTriangle]);
for (int j = 0; j < uvChannels.Count; j++)
uvs[j].Add(srcUvs[j][srcTriangle]);
if (srcColors.Length > 0)
colors.Add(srcColors[srcTriangle]);
if (srcColors32.Length > 0)
colors32.Add(srcColors32[srcTriangle]);
srcToNewTriangles.Add(srcTriangle, newTriangle);
}
meshTriangles[i].Add(newTriangle);
}
}
mesh.vertices = vertices.ToArray();
mesh.tangents = tangents.ToArray();
mesh.normals = normals.ToArray();
if (colors.Count > 0)
mesh.SetColors(colors);
if (colors32.Count > 0)
mesh.SetColors(colors32);
for (int i = 0; i < uvChannels.Count; i++)
mesh.SetUVs(uvChannels[i], uvs[i]);
mesh.subMeshCount = triangles.Length;
for (int i = 0; i < triangles.Length; i++)
mesh.SetTriangles(meshTriangles[i], i);
mesh.RecalculateBounds();
mesh.RecalculateNormals();
mesh.RecalculateTangents();
return mesh;
}
}
public static class BoundsHelper
{
public static bool Intersects(this Bounds bounds, Vector3 t1, Vector3 t2, Vector3 t3)
{
float p0, p1, p2, r;
Vector3 extents = bounds.max - bounds.center;
Vector3 v0 = t1 - bounds.center,
v1 = t2 - bounds.center,
v2 = t3 - bounds.center;
Vector3 f0 = v1 - v0,
f1 = v2 - v1,
f2 = v0 - v2;
Vector3 a00 = new Vector3(0, -f0.z, f0.y),
a01 = new Vector3(0, -f1.z, f1.y),
a02 = new Vector3(0, -f2.z, f2.y),
a10 = new Vector3(f0.z, 0, -f0.x),
a11 = new Vector3(f1.z, 0, -f1.x),
a12 = new Vector3(f2.z, 0, -f2.x),
a20 = new Vector3(-f0.y, f0.x, 0),
a21 = new Vector3(-f1.y, f1.x, 0),
a22 = new Vector3(-f2.y, f2.x, 0);
// Test axis a00
p0 = Vector3.Dot(v0, a00);
p1 = Vector3.Dot(v1, a00);
p2 = Vector3.Dot(v2, a00);
r = extents.y * Mathf.Abs(f0.z) + extents.z * Mathf.Abs(f0.y);
if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r)
return false;
// Test axis a01
p0 = Vector3.Dot(v0, a01);
p1 = Vector3.Dot(v1, a01);
p2 = Vector3.Dot(v2, a01);
r = extents.y * Mathf.Abs(f1.z) + extents.z * Mathf.Abs(f1.y);
if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r)
return false;
// Test axis a02
p0 = Vector3.Dot(v0, a02);
p1 = Vector3.Dot(v1, a02);
p2 = Vector3.Dot(v2, a02);
r = extents.y * Mathf.Abs(f2.z) + extents.z * Mathf.Abs(f2.y);
if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r)
return false;
// Test axis a10
p0 = Vector3.Dot(v0, a10);
p1 = Vector3.Dot(v1, a10);
p2 = Vector3.Dot(v2, a10);
r = extents.x * Mathf.Abs(f0.z) + extents.z * Mathf.Abs(f0.x);
if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r)
return false;
// Test axis a11
p0 = Vector3.Dot(v0, a11);
p1 = Vector3.Dot(v1, a11);
p2 = Vector3.Dot(v2, a11);
r = extents.x * Mathf.Abs(f1.z) + extents.z * Mathf.Abs(f1.x);
if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r)
return false;
// Test axis a12
p0 = Vector3.Dot(v0, a12);
p1 = Vector3.Dot(v1, a12);
p2 = Vector3.Dot(v2, a12);
r = extents.x * Mathf.Abs(f2.z) + extents.z * Mathf.Abs(f2.x);
if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r)
return false;
// Test axis a20
p0 = Vector3.Dot(v0, a20);
p1 = Vector3.Dot(v1, a20);
p2 = Vector3.Dot(v2, a20);
r = extents.x * Mathf.Abs(f0.y) + extents.y * Mathf.Abs(f0.x);
if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r)
return false;
// Test axis a21
p0 = Vector3.Dot(v0, a21);
p1 = Vector3.Dot(v1, a21);
p2 = Vector3.Dot(v2, a21);
r = extents.x * Mathf.Abs(f1.y) + extents.y * Mathf.Abs(f1.x);
if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r)
return false;
// Test axis a22
p0 = Vector3.Dot(v0, a22);
p1 = Vector3.Dot(v1, a22);
p2 = Vector3.Dot(v2, a22);
r = extents.x * Mathf.Abs(f2.y) + extents.y * Mathf.Abs(f2.x);
if (Mathf.Max(-Mathf.Max(p0, p1, p2), Mathf.Min(p0, p1, p2)) > r)
return false;
if (Mathf.Max(v0.x, v1.x, v2.x) < -extents.x || Mathf.Min(v0.x, v1.x, v2.x) > extents.x)
return false;
if (Mathf.Max(v0.y, v1.y, v2.y) < -extents.y || Mathf.Min(v0.y, v1.y, v2.y) > extents.y)
return false;
if (Mathf.Max(v0.z, v1.z, v2.z) < -extents.z || Mathf.Min(v0.z, v1.z, v2.z) > extents.z)
return false;
var normal = Vector3.Cross(f1, f0).normalized;
var pl = new Plane(normal, Vector3.Dot(normal, t1));
return Intersects(bounds, pl);
}
public static bool Intersects(this Bounds bounds, Plane plane)
{
Vector3 center = bounds.center;
var extents = bounds.max - center;
var r = extents.x * Mathf.Abs(plane.normal.x) + extents.y * Mathf.Abs(plane.normal.y) + extents.z * Mathf.Abs(plane.normal.z);
var s = Vector3.Dot(plane.normal, center) - plane.distance;
return Mathf.Abs(s) <= r;
}
}
}

View File

@@ -0,0 +1,411 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEditor;
using Directory = System.IO.Directory;
namespace AdvancedCullingSystem.ChunksMaster
{
public class ChunksMasterEditorWindow : EditorWindow
{
public Vector2 size
{
get
{
return position.size;
}
}
private ChunksMaster _chunksMaster;
private string _dataSaveFolder;
private int _maxTriangles = 10000;
private bool _showChunks = true;
private Vector2 _scrollPosition;
private List<GameObject> _selectedObjects;
[MenuItem("Tools/NGSTools/Advanced Culling System/Chunks Master")]
private static void CreateWindow()
{
var window = GetWindow<ChunksMasterEditorWindow>(false, "Chunks Master", true);
window.minSize = new Vector2(490, 205);
window.Show();
}
private void OnEnable()
{
SceneView.duringSceneGui += OnSceneView;
_selectedObjects = new List<GameObject>();
_chunksMaster = new ChunksMaster();
string scenePath = SceneManager.GetActiveScene().path;
_dataSaveFolder = (scenePath == null || scenePath == "") ? "ACSData/" :
scenePath.Replace("Assets/", "").Replace(".unity", "_ACSData/");
}
private void OnGUI()
{
CheckData();
GUILayout.BeginArea(new Rect(10, 10, size.x / 2 - 20, size.y - 70));
GUILayout.BeginHorizontal();
GUILayout.Label("Data Save Folder :", GUILayout.Width(110));
_dataSaveFolder = GUILayout.TextField(_dataSaveFolder);
GUILayout.EndHorizontal();
_maxTriangles = Mathf.Max(EditorGUILayout.IntField("Max Triangles Per Chunk : ", _maxTriangles), 1000);
GUILayout.Space(10);
GUILayout.Label(_selectedObjects.Count > 0 ? "Selected Objects :" : "No objects selected");
_scrollPosition = GUILayout.BeginScrollView(_scrollPosition);
for (int i = 0; i < _selectedObjects.Count; i++)
EditorGUILayout.ObjectField(_selectedObjects[i], typeof(GameObject), false);
GUILayout.EndScrollView();
GUILayout.EndArea();
GUILayout.BeginArea(new Rect(size.x / 2 + 10, 10, size.x / 2 - 20, size.y - 70));
if (GUILayout.Button("Add Static")) AddStatic();
if (GUILayout.Button("Add Selected")) AddSelected();
if (GUILayout.Button("Remove Selected")) RemoveSelected();
if (GUILayout.Button("Remove All")) RemoveAll();
_showChunks = EditorGUILayout.ToggleLeft("Show Created Chunks", _showChunks);
GUILayout.EndArea();
GUILayout.BeginArea(new Rect(10, size.y - 50, size.x - 20, 40));
GUILayout.BeginHorizontal();
if (IsSceneContainsChunks())
if (GUILayout.Button("Delete Chunks", GUILayout.Width(100), GUILayout.Height(40)))
DeleteChunks();
GUILayout.FlexibleSpace();
if (GUILayout.Button("Split", GUILayout.Width(70), GUILayout.Height(40))) Split();
if (GUILayout.Button("Apply", GUILayout.Width(70), GUILayout.Height(40))) Apply();
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
private void OnSceneView(SceneView sceneView)
{
if (!_showChunks)
return;
CheckData();
Handles.color = Color.blue;
foreach (var go in _selectedObjects)
{
Mesh mesh = go.GetComponent<MeshFilter>().sharedMesh;
int index = _chunksMaster.meshes.IndexOf(mesh);
Handles.matrix = Matrix4x4.TRS(go.transform.position, go.transform.rotation, go.transform.lossyScale);
foreach (var bounds in _chunksMaster.bounds[index])
Handles.DrawWireCube(bounds.center, bounds.size);
}
Handles.matrix = Matrix4x4.identity;
}
private void OnDestroy()
{
SceneView.duringSceneGui -= OnSceneView;
}
private void AddStatic()
{
int saveObjectsCount = _selectedObjects.Count;
GameObject[] objects = FindObjectsOfType<Transform>()
.Select(t => t.gameObject)
.Where(go => go.isStatic)
.ToArray();
for (int i = 0; i < objects.Length; i++)
{
EditorUtility.DisplayProgressBar("Adding static objects", i + " of " + objects.Length, (float)i / objects.Length);
if (IsValidGameObject(objects[i]))
AddGameObject(objects[i]);
}
EditorUtility.ClearProgressBar();
if ((_selectedObjects.Count - saveObjectsCount) == 0)
Debug.Log("No new static objects added");
else
Debug.Log("Added " + (_selectedObjects.Count - saveObjectsCount) + " objects");
}
private void AddSelected()
{
int saveObjectsCount = _selectedObjects.Count;
GameObject[] selected = Selection.gameObjects;
if (selected == null || selected.Length == 0)
{
Debug.Log("No objects selected");
return;
}
for (int i = 0; i < selected.Length; i++)
{
EditorUtility.DisplayProgressBar("Adding selected objects", i + " of " + selected.Length, (float)i / selected.Length);
foreach (var go in selected[i].GetComponentsInChildren<Transform>().Select(t => t.gameObject))
if (IsValidGameObject(go))
AddGameObject(go);
}
EditorUtility.ClearProgressBar();
if ((_selectedObjects.Count - saveObjectsCount) == 0)
Debug.Log("No new objects added");
else
Debug.Log("Added " + (_selectedObjects.Count - saveObjectsCount) + " objects");
}
private void RemoveSelected()
{
int saveObjectsCount = _selectedObjects.Count;
GameObject[] selected = Selection.gameObjects;
if (selected == null || selected.Length == 0)
{
Debug.Log("No objects selected");
return;
}
for (int i = 0; i < selected.Length; i++)
{
EditorUtility.DisplayProgressBar("Removing selected objects", i + " of " + selected.Length, (float)i / selected.Length);
foreach (var go in selected[i].GetComponentsInChildren<Transform>().Select(t => t.gameObject))
if (_selectedObjects.Contains(go))
_selectedObjects.Remove(go);
}
EditorUtility.ClearProgressBar();
if ((saveObjectsCount - _selectedObjects.Count) == 0)
Debug.Log("No objects removed");
else
Debug.Log("Removed " + (saveObjectsCount - _selectedObjects.Count) + " objects");
}
private void RemoveAll()
{
_selectedObjects.Clear();
Debug.Log("Removed all objects");
}
private void Apply()
{
Mesh[] meshes = _selectedObjects.Select(go => go.GetComponent<MeshFilter>().sharedMesh).ToArray();
for (int i = 0; i < meshes.Length; i++)
{
try
{
EditorUtility.DisplayProgressBar("Applying...", i + " of " + meshes.Length, (float)i / meshes.Length);
_chunksMaster.CreateChunksBounds(meshes[i], _maxTriangles);
}
catch (System.Exception ex)
{
Debug.Log("Can't compute mesh " + meshes[i].name);
Debug.Log("Cause : " + ex.Message + " " + ex.StackTrace);
Debug.Log("Please write about it on e-mail(andre-orsk@yandex.ru) and I will help You");
Debug.Log("-----------------------------------");
}
}
EditorUtility.ClearProgressBar();
}
private void Split()
{
int count = _selectedObjects.Count;
for (int i = 0; i < count; i++)
{
try
{
EditorUtility.DisplayProgressBar("Splitting...", i + " of " + count, (float)i / count);
MeshFilter filter = _selectedObjects[0].GetComponent<MeshFilter>();
_chunksMaster.CreateChunks(filter.sharedMesh, _maxTriangles,
"Assets/" + _dataSaveFolder + filter.gameObject.name + "/");
_chunksMaster.SetupChunks(filter);
}
catch (System.Exception ex)
{
Debug.Log("Can't split mesh " + _selectedObjects[0].name);
Debug.Log("Cause : " + ex.Message + " " + ex.StackTrace);
Debug.Log("Please write about it on e-mail(andre-orsk@yandex.ru) and I will help You");
Debug.Log("-----------------------------------");
}
_selectedObjects.RemoveAt(0);
}
EditorUtility.ClearProgressBar();
}
private void DeleteChunks()
{
ChunkMark[] chunks = FindObjectsOfType<ChunkMark>();
for(int i = 0; i < chunks.Length; i++)
{
try
{
EditorUtility.DisplayProgressBar("Deleting...", i + " of " + chunks.Length, (float)i / chunks.Length);
Mesh mesh = chunks[i].GetComponent<MeshFilter>().sharedMesh;
if (mesh != null)
{
string path = AssetDatabase.GetAssetPath(mesh);
string parentFolder = path.Remove(path.LastIndexOf("/"), path.Length - path.LastIndexOf("/"));
if (path != null && path != "")
AssetDatabase.DeleteAsset(path);
if (Directory.GetFiles(parentFolder).Length == 0)
Directory.Delete(parentFolder, false);
}
chunks[i].transform.parent.GetComponent<MeshRenderer>().enabled = true;
DestroyImmediate(chunks[i].gameObject);
}
catch (System.Exception ex)
{
Debug.Log("Can't remove chunk " + chunks[i].name);
Debug.Log("Cause : " + ex.Message + " " + ex.StackTrace);
Debug.Log("Please write about it on e-mail(andre-orsk@yandex.ru) and I will help You");
Debug.Log("-----------------------------------");
}
}
AssetDatabase.Refresh();
EditorUtility.ClearProgressBar();
}
private void CheckData()
{
if (!_dataSaveFolder.EndsWith("/"))
_dataSaveFolder += "/";
_selectedObjects = _selectedObjects.Where(obj => obj != null && IsValidGameObject(obj)).ToList();
}
private bool IsSceneContainsChunks()
{
return FindObjectsOfType<ChunkMark>().Length > 0;
}
private bool IsValidGameObject(GameObject go)
{
try
{
if (go.GetComponent<MeshRenderer>() == null)
return false;
MeshFilter filter = go.GetComponent<MeshFilter>();
if (filter == null || filter.sharedMesh == null)
return false;
int subMeshCount = filter.sharedMesh.subMeshCount;
for (int i = 0; i < subMeshCount; i++)
if ((filter.sharedMesh.GetTriangles(i).Length / 3) >= _maxTriangles)
return true;
return false;
}
catch (System.Exception ex)
{
Debug.Log("Can't process " + go.name);
Debug.Log("Cause : " + ex.Message + " " + ex.StackTrace);
Debug.Log("Please write about it on e-mail(andre-orsk@yandex.ru) and I will help You");
Debug.Log("-----------------------------------");
return false;
}
}
private void AddGameObject(GameObject go)
{
try
{
if (!_selectedObjects.Contains(go))
{
Mesh mesh = go.GetComponent<MeshFilter>().sharedMesh;
_chunksMaster.CreateChunksBounds(mesh, _maxTriangles);
_selectedObjects.Add(go);
}
}
catch (System.Exception ex)
{
Debug.Log("Can't add " + go.name);
Debug.Log("Cause : " + ex.Message + " " + ex.StackTrace);
Debug.Log("Please write about it on e-mail(andre-orsk@yandex.ru) and I will help You");
Debug.Log("-----------------------------------");
}
}
}
}

View File

@@ -0,0 +1,22 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace NGS.AdvancedCullingSystem
{
public static class EditorHelper
{
public static void DrawSeparatorLine(float thickness, float padding)
{
Rect previousRect = GUILayoutUtility.GetLastRect();
GUILayout.Space(padding);
EditorGUILayout.LabelField("", GUILayout.Height(thickness));
Rect lineRect = GUILayoutUtility.GetLastRect();
lineRect.x = previousRect.x;
lineRect.width = previousRect.width;
EditorGUI.DrawRect(lineRect, Color.gray);
GUILayout.Space(padding);
}
}
}

View File

@@ -0,0 +1,63 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.Linq;
namespace NGS.AdvancedCullingSystem
{
public static class LayersHelper
{
public static bool IsLayerExist(string name)
{
return LayerMask.NameToLayer(name) != -1;
}
public static void CreateLayer(string name)
{
if (IsLayerExist(name))
{
Debug.Log("Layer " + name + " already exists");
return;
}
SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
SerializedProperty layers = tagManager.FindProperty("layers");
for (int j = 8; j < layers.arraySize; j++)
{
SerializedProperty newLayer = layers.GetArrayElementAtIndex(j);
if (string.IsNullOrEmpty(newLayer.stringValue))
{
newLayer.stringValue = name;
tagManager.ApplyModifiedProperties();
Debug.Log("Layer created: " + name);
return;
}
}
Debug.LogError("Failed to create layer: " + name + ", maximum number of layers reached.");
}
public static void DisableCollisions(int layer)
{
SerializedObject tagManager = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/TagManager.asset")[0]);
string[] layers = Enumerable.Range(0, 31)
.Select(index => LayerMask.LayerToName(index))
.Where(l => !string.IsNullOrEmpty(l))
.ToArray();
for (int i = 0; i < layers.Length; i++)
{
int current = LayerMask.NameToLayer(layers[i]);
Physics.IgnoreLayerCollision(layer, current);
Physics2D.IgnoreLayerCollision(layer, current);
}
tagManager.ApplyModifiedProperties();
}
}
}

View File

@@ -0,0 +1,14 @@
using UnityEditor;
namespace NGS.AdvancedCullingSystem
{
public static class SerializedObjectHelper
{
public static SerializedProperty FindAutoProperty(this SerializedObject obj, string name)
{
string path = string.Format("<{0}>k__BackingField", name);
return obj.FindProperty(path);
}
}
}

View File

@@ -0,0 +1,213 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace NGS.AdvancedCullingSystem.Dynamic
{
[CustomEditor(typeof(DC_Controller))]
public class DC_ControllerEditor : Editor
{
private static Dictionary<int, DC_SelectionTool> _indexToSelection;
private static GUIStyle TitleLabelStyle;
private static GUIStyle ButtonGUIStyle;
private new DC_Controller target
{
get
{
return base.target as DC_Controller;
}
}
private DC_SelectionTool _selectionTool;
private bool _containsReadyToBake;
private bool _containsBaked;
private bool _containsNonReadableMeshes;
private void OnEnable()
{
string layer = DC_Controller.GetCullingLayerName();
if (!LayersHelper.IsLayerExist(layer))
{
LayersHelper.CreateLayer(layer);
LayersHelper.DisableCollisions(LayerMask.NameToLayer(layer));
}
UpdateSelectionTool();
UpdateSceneInfo();
}
public override void OnInspectorGUI()
{
if (TitleLabelStyle == null)
CreateGUIStyles();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Dynamic Culling", TitleLabelStyle);
EditorHelper.DrawSeparatorLine(1, 2);
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUI.BeginChangeCheck();
EditorGUI.BeginChangeCheck();
if (Application.isPlaying && target.MergeInGroups)
{
target.DrawGizmos = EditorGUILayout.Toggle("Draw Gizmos", target.DrawGizmos);
EditorGUILayout.Space();
}
EditorGUI.BeginDisabledGroup(Application.isPlaying);
target.ControllerID = EditorGUILayout.IntField("Controller ID", target.ControllerID);
if (EditorGUI.EndChangeCheck())
UpdateSelectionTool();
target.ObjectsLifetime = EditorGUILayout.FloatField("Objects Lifetime", target.ObjectsLifetime);
EditorGUILayout.Space();
bool merge = EditorGUILayout.Toggle("Merge In Groups", target.MergeInGroups);
float cellSize = target.CellSize;
if (target.MergeInGroups)
cellSize = EditorGUILayout.FloatField("Cell Size", target.CellSize);
if (!Application.isPlaying)
{
target.MergeInGroups = merge;
target.CellSize = cellSize;
}
EditorGUI.EndDisabledGroup();
if (EditorGUI.EndChangeCheck() && !Application.isPlaying)
EditorUtility.SetDirty(base.target);
EditorGUILayout.Space();
EditorGUILayout.Space();
if (_selectionTool.Controller.gameObject != null)
{
bool sceneChanged = false;
_selectionTool.OnInspectorGUI(ref sceneChanged);
if (sceneChanged)
UpdateSceneInfo();
}
if (Application.isPlaying)
return;
EditorGUILayout.BeginHorizontal();
if (_containsReadyToBake)
{
if (GUILayout.Button("Bake", ButtonGUIStyle))
{
DC_ControllerEditorUtil.BakeScene();
UpdateSceneInfo();
}
}
if (_containsBaked)
{
if (GUILayout.Button("Clear", ButtonGUIStyle))
{
DC_ControllerEditorUtil.ClearBakedData();
UpdateSceneInfo();
}
}
EditorGUILayout.EndHorizontal();
if (_containsNonReadableMeshes)
{
EditorGUILayout.Space();
if (GUILayout.Button("Make Meshes Readable", ButtonGUIStyle))
{
DC_ControllerEditorUtil.MakeMeshesReadable();
UpdateSceneInfo();
}
}
}
private void UpdateSelectionTool()
{
if (_indexToSelection == null)
_indexToSelection = new Dictionary<int, DC_SelectionTool>();
if (!_indexToSelection.TryGetValue(target.ControllerID, out _selectionTool))
{
_selectionTool = new DC_SelectionTool(target);
_indexToSelection.Add(target.ControllerID, _selectionTool);
}
_selectionTool.Initialize(target, this);
_selectionTool.Refresh();
}
private void UpdateSceneInfo()
{
DC_SourceSettings[] settings = FindObjectsOfType<DC_SourceSettings>();
_containsReadyToBake = DC_ControllerEditorUtil.ContainsReadyToBakeSources(settings);
_containsBaked = DC_ControllerEditorUtil.ContainsBakedSources(settings);
_containsNonReadableMeshes = DC_ControllerEditorUtil.ContainsNonReadableMeshes(settings);
}
private void CreateGUIStyles()
{
if (TitleLabelStyle == null)
{
TitleLabelStyle = new GUIStyle();
TitleLabelStyle.fontSize = 24;
TitleLabelStyle.fontStyle = FontStyle.Bold;
TitleLabelStyle.alignment = TextAnchor.MiddleLeft;
TitleLabelStyle.normal.textColor = Color.white;
}
if (ButtonGUIStyle == null)
{
ButtonGUIStyle = new GUIStyle(GUI.skin.button);
ButtonGUIStyle.fontSize = 12;
ButtonGUIStyle.fixedHeight = 24;
ButtonGUIStyle.margin = new RectOffset(5, 5, 5, 5);
ButtonGUIStyle.border = new RectOffset(0, 0, 0, 0);
ButtonGUIStyle.padding = new RectOffset(5, 5, 5, 5);
}
}
[MenuItem("Tools/NGSTools/Advanced Culling System/Dynamic")]
private static void CreateDynamicCulling()
{
GameObject go = new GameObject("Dynamic Culling");
go.AddComponent<DC_Controller>();
Selection.activeGameObject = go;
}
[DrawGizmo(GizmoType.Selected | GizmoType.NonSelected)]
private static void OnDrawGizmos(DC_Controller controller, GizmoType gizmoType)
{
if (_indexToSelection == null)
_indexToSelection = new Dictionary<int, DC_SelectionTool>();
if (_indexToSelection.TryGetValue(controller.ControllerID, out DC_SelectionTool tool))
tool.OnDrawGizmos();
}
}
}

View File

@@ -0,0 +1,167 @@
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using Object = UnityEngine.Object;
namespace NGS.AdvancedCullingSystem.Dynamic
{
public static class DC_ControllerEditorUtil
{
public static bool ContainsReadyToBakeSources(DC_SourceSettings[] sources)
{
foreach (var settings in sources)
{
if (!settings.IsIncompatible && !settings.ReadyForCulling)
return true;
}
return false;
}
public static bool ContainsBakedSources(DC_SourceSettings[] sources)
{
foreach (var settings in sources)
{
if (settings.ReadyForCulling)
return true;
}
return false;
}
public static bool ContainsNonReadableMeshes(DC_SourceSettings[] sources)
{
for (int i = 0; i < sources.Length; i++)
{
DC_SourceSettings source = sources[i];
if (source.SourceType == SourceType.SingleMesh)
{
if (source.TryGetComponent(out MeshFilter filter))
{
Mesh mesh = filter.sharedMesh;
if (mesh != null && !mesh.isReadable)
return true;
}
}
else
{
foreach (MeshFilter filter in source.GetComponentsInChildren<MeshFilter>())
{
Mesh mesh = filter.sharedMesh;
if (mesh != null && !mesh.isReadable)
return true;
}
}
}
return false;
}
public static void MakeMeshesReadable()
{
DC_SourceSettings[] sources = Object.FindObjectsOfType<DC_SourceSettings>();
int count = 0;
for (int i = 0; i < sources.Length; i++)
{
DC_SourceSettings source = sources[i];
if (source.SourceType == SourceType.SingleMesh)
{
if (source.TryGetComponent(out MeshFilter filter))
{
if (filter.sharedMesh != null)
if (MakeMeshReadable(filter.sharedMesh))
count++;
}
}
else
{
foreach (MeshFilter filter in source.GetComponentsInChildren<MeshFilter>())
{
if (filter.sharedMesh != null)
if (MakeMeshReadable(filter.sharedMesh))
count++;
}
}
}
Debug.Log(count + " meshes maked readable");
}
public static void BakeScene()
{
try
{
DC_SourceSettings[] sources = Object.FindObjectsOfType<DC_SourceSettings>();
int count = sources.Length;
for (int i = 0; i < count; i++)
{
EditorUtility.DisplayProgressBar("Bake...", i + " of " + count, (float)i / count);
sources[i].Bake();
}
}
finally
{
EditorUtility.ClearProgressBar();
}
}
public static void ClearBakedData()
{
try
{
DC_SourceSettings[] sources = Object.FindObjectsOfType<DC_SourceSettings>();
int count = sources.Length;
for (int i = 0; i < count; i++)
{
EditorUtility.DisplayProgressBar("Clear...", i + " of " + count, (float)i / count);
sources[i].ClearBakedData();
}
}
finally
{
EditorUtility.ClearProgressBar();
}
}
private static bool MakeMeshReadable(Mesh mesh)
{
try
{
if (mesh.isReadable)
return false;
string path = AssetDatabase.GetAssetPath(mesh.GetInstanceID());
if (path == null || path == "")
Debug.Log("Unable find path for mesh : " + mesh.name);
ModelImporter importer = (ModelImporter)AssetImporter.GetAtPath(path);
importer.isReadable = true;
importer.SaveAndReimport();
Debug.Log(string.Format("Maked {0} mesh readable", mesh.name));
return true;
}
catch (Exception ex)
{
Debug.Log(string.Format("Unable to make mesh {0} readable. Reason : {1}{2}",
mesh.name, ex.Message, ex.StackTrace));
return false;
}
}
}
}

View File

@@ -0,0 +1,76 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace NGS.AdvancedCullingSystem.Dynamic
{
public class DC_SelectionTool
{
public DC_Controller Controller { get; private set; }
private DC_BaseSelector[] _selectors;
private DC_ControllerEditor _editor;
public DC_SelectionTool(DC_Controller controller)
{
Controller = controller;
_selectors = new DC_BaseSelector[]
{
new DC_CamerasSelector(this),
new DC_RenderersSelector(this),
new DC_LODGroupsSelector(this),
new DC_CustomSelector(this),
new DC_OccludersSelector(this),
new DC_IncompatiblesSelector(this)
};
}
public void Initialize(DC_Controller controller, DC_ControllerEditor editor)
{
Controller = controller;
_editor = editor;
}
public void OnInspectorGUI(ref bool sceneChanged)
{
for (int i = 0; i < _selectors.Length; i++)
{
if (_selectors[i].IsAvailable)
_selectors[i].OnInspectorGUI(ref sceneChanged);
}
if (sceneChanged) // || GUILayout.Button("Refresh"))
Refresh();
}
public void OnDrawGizmos()
{
for (int i = 0; i < _selectors.Length; i++)
{
DC_BaseSelector selector = _selectors[i];
if (selector.IsAvailable)
selector.OnDrawGizmos();
}
}
public void Refresh()
{
for (int i = 0; i < _selectors.Length; i++)
{
DC_BaseSelector selector = _selectors[i];
if (selector.IsAvailable)
selector.Refresh();
}
}
public void Repaint()
{
_editor?.Repaint();
}
}
}

View File

@@ -0,0 +1,873 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using UnityEditor.AnimatedValues;
namespace NGS.AdvancedCullingSystem.Dynamic
{
public abstract class DC_BaseSelector
{
protected static GUIStyle FoldoutGUIStyle { get; private set; }
protected static GUIStyle ButtonGUIStyle { get; private set; }
protected static readonly Color GreenColor = new Color(0.3f, 1f, 0.3f, 1f);
protected static readonly Color RedColor = new Color(1f, 0.3f, 0.3f, 1f);
public string Label { get; protected set; }
public abstract bool IsAvailable { get; }
protected DC_Controller Controller
{
get
{
return _parent.Controller;
}
}
private DC_SelectionTool _parent;
private AnimBool _foldout;
public DC_BaseSelector(DC_SelectionTool parent)
{
Label = "Default Label";
_parent = parent;
_foldout = new AnimBool(false);
_foldout.valueChanged.AddListener(parent.Repaint);
Refresh();
}
public abstract void Refresh();
public virtual void OnInspectorGUI(ref bool sceneChanged)
{
if (FoldoutGUIStyle == null || ButtonGUIStyle == null)
CreateGUIStyles();
_foldout.target = EditorGUILayout.Foldout(_foldout.target, Label, true, FoldoutGUIStyle);
if (EditorGUILayout.BeginFadeGroup(_foldout.faded))
{
EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
DrawContent(ref sceneChanged);
}
EditorGUILayout.EndFadeGroup();
EditorGUILayout.LabelField("", GUI.skin.horizontalSlider);
}
public abstract void OnDrawGizmos();
protected abstract void DrawContent(ref bool sceneChanged);
protected void DrawBounds(Bounds[] bounds, Color color)
{
Gizmos.color = color;
for (int i = 0; i < bounds.Length; i++)
Gizmos.DrawWireCube(bounds[i].center, bounds[i].size);
}
private void CreateGUIStyles()
{
FoldoutGUIStyle = new GUIStyle(EditorStyles.foldout);
FoldoutGUIStyle.fontSize = 15;
FoldoutGUIStyle.fontStyle = FontStyle.Bold;
FoldoutGUIStyle.normal.textColor = Color.white;
ButtonGUIStyle = new GUIStyle(GUI.skin.button);
ButtonGUIStyle.fontSize = 12;
ButtonGUIStyle.fixedHeight = 30;
ButtonGUIStyle.margin = new RectOffset(5, 5, 5, 5);
ButtonGUIStyle.border = new RectOffset(0, 0, 0, 0);
ButtonGUIStyle.padding = new RectOffset(5, 5, 5, 5);
}
}
public abstract class DC_SelectorTemplate1 : DC_BaseSelector
{
protected DC_SelectorTemplate1(DC_SelectionTool parent)
: base(parent)
{
}
protected override void DrawContent(ref bool sceneChanged)
{
Color backColor = GUI.backgroundColor;
bool picked = Selection.gameObjects.Length > 1 ||
(Selection.gameObjects.Length == 1 && Selection.activeGameObject != Controller.gameObject);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical();
GUI.backgroundColor = GreenColor;
if (GUILayout.Button("Assign Auto", ButtonGUIStyle))
{
AssignAuto();
sceneChanged = true;
}
GUI.backgroundColor = backColor;
EditorGUI.BeginDisabledGroup(!picked);
if (GUILayout.Button("Assign Selected", ButtonGUIStyle))
{
AssignSelectedGameObjects();
sceneChanged = true;
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndVertical();
if (!ShouldDrawClearOptions())
{
EditorGUILayout.EndHorizontal();
return;
}
EditorGUILayout.BeginVertical();
GUI.backgroundColor = RedColor;
if (GUILayout.Button("Clear All", ButtonGUIStyle))
{
ClearAll();
sceneChanged = true;
}
GUI.backgroundColor = backColor;
EditorGUI.BeginDisabledGroup(!picked);
if (GUILayout.Button("Clear Selected", ButtonGUIStyle))
{
ClearSelected();
sceneChanged = true;
}
EditorGUI.EndDisabledGroup();
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("Select", ButtonGUIStyle))
Select();
}
protected void AssignAuto()
{
GameObject[] gos = Object.FindObjectsOfType<GameObject>();
int count = 0;
for (int i = 0; i < gos.Length; i++)
{
GameObject go = gos[i];
if (AssignIteration(go))
count++;
}
Debug.Log("Assigned " + count + " objects");
}
protected void AssignSelectedGameObjects()
{
GameObject[] gos = Selection.gameObjects
.SelectMany(go => go.GetComponentsInChildren<Transform>()
.Select(t => t.gameObject))
.ToArray();
int count = 0;
for (int i = 0; i < gos.Length; i++)
{
GameObject go = gos[i];
if (AssignIteration(go))
count++;
}
Debug.Log("Assigned " + count + " objects");
}
protected void ClearSelected()
{
GameObject[] gos = Selection.gameObjects
.SelectMany(go => go.GetComponentsInChildren<Transform>()
.Select(t => t.gameObject))
.ToArray();
int count = 0;
for (int i = 0; i < gos.Length; i++)
{
GameObject go = gos[i];
if (ClearIteration(go))
count++;
}
Debug.Log("Cleared " + count + " objects");
}
protected abstract bool ShouldDrawClearOptions();
protected abstract bool AssignIteration(GameObject current);
protected abstract bool ClearIteration(GameObject current);
protected abstract void ClearAll();
protected abstract void Select();
}
public class DC_CamerasSelector : DC_SelectorTemplate1
{
public override bool IsAvailable
{
get
{
return !Application.isPlaying;
}
}
private DC_Camera[] _cameras;
public DC_CamerasSelector(DC_SelectionTool parent) : base(parent)
{
}
public override void Refresh()
{
_cameras = Object.FindObjectsOfType<DC_Camera>();
Label = "Cameras (" + _cameras.Length + ")";
}
public override void OnDrawGizmos()
{
}
protected override bool ShouldDrawClearOptions()
{
return _cameras.Length > 0;
}
protected override bool AssignIteration(GameObject current)
{
if (!current.activeInHierarchy)
return false;
if (current.TryGetComponent(out DC_Camera cullingCamera))
return false;
if (current.TryGetComponent(out Camera camera))
{
Controller.AddCamera(camera, 1500);
return true;
}
return false;
}
protected override void ClearAll()
{
for (int i = 0; i < _cameras.Length; i++)
{
Object.DestroyImmediate(_cameras[i]);
}
}
protected override bool ClearIteration(GameObject current)
{
if (current.TryGetComponent(out DC_Camera camera))
{
Object.DestroyImmediate(camera);
return true;
}
return false;
}
protected override void Select()
{
Selection.objects = _cameras.Select(c => c.gameObject).ToArray();
}
}
public class DC_RenderersSelector : DC_SelectorTemplate1
{
public override bool IsAvailable
{
get
{
return !Application.isPlaying;
}
}
private DC_SourceSettings[] _renderersSettings;
private Bounds[] _bounds;
private bool _drawGizmos;
public DC_RenderersSelector(DC_SelectionTool parent)
: base(parent)
{
}
public override void Refresh()
{
_renderersSettings = Object.FindObjectsOfType<DC_SourceSettings>()
.Where(s => s.SourceType == SourceType.SingleMesh && s.ControllerID == Controller.ControllerID)
.ToArray();
Bounds bounds = default;
_bounds = _renderersSettings
.Where(s => s.TryGetBounds(ref bounds))
.Select(r => bounds)
.ToArray();
Label = "Renderers (" + _renderersSettings.Length + ")";
}
protected override void DrawContent(ref bool sceneChanged)
{
_drawGizmos = EditorGUILayout.Toggle("Draw Gizmos", _drawGizmos);
base.DrawContent(ref sceneChanged);
}
public override void OnDrawGizmos()
{
if (!_drawGizmos || _bounds == null)
return;
DrawBounds(_bounds, Color.blue);
}
protected override bool ShouldDrawClearOptions()
{
return _renderersSettings.Length > 0;
}
protected override bool AssignIteration(GameObject current)
{
if (!current.activeInHierarchy)
return false;
if (current.GetComponent<DC_SourceSettings>() != null)
return false;
if (current.GetComponent<DC_Occluder>() != null)
return false;
MeshRenderer renderer = current.GetComponent<MeshRenderer>();
if (renderer == null || !renderer.enabled)
return false;
LODGroup group = current.GetComponentInParent<LODGroup>();
if (group != null && group.GetLODs().ContainsAny(r => r == renderer))
return false;
Controller.AddObjectForCulling(renderer).CheckCompatibility();
return true;
}
protected override bool ClearIteration(GameObject current)
{
if (current.TryGetComponent(out DC_SourceSettings settings))
{
if (settings.SourceType != SourceType.SingleMesh)
return false;
if (settings.ControllerID != Controller.ControllerID)
return false;
settings.ClearBakedData();
Object.DestroyImmediate(settings);
return true;
}
return false;
}
protected override void ClearAll()
{
for (int i = 0; i < _renderersSettings.Length; i++)
{
_renderersSettings[i].ClearBakedData();
Object.DestroyImmediate(_renderersSettings[i]);
}
}
protected override void Select()
{
Selection.objects = _renderersSettings.Select(s => s.gameObject).ToArray();
}
}
public class DC_LODGroupsSelector : DC_SelectorTemplate1
{
public override bool IsAvailable
{
get
{
return !Application.isPlaying;
}
}
private DC_SourceSettings[] _settings;
private Bounds[] _bounds;
private bool _drawGizmos;
public DC_LODGroupsSelector(DC_SelectionTool parent) : base(parent)
{
}
public override void Refresh()
{
_settings = Object.FindObjectsOfType<DC_SourceSettings>()
.Where(s => s.SourceType == SourceType.LODGroup && s.ControllerID == Controller.ControllerID)
.ToArray();
Bounds bounds = default;
_bounds = _settings
.Where(s => s.TryGetBounds(ref bounds))
.Select(r => bounds)
.ToArray();
Label = "LODGroups (" + _settings.Length + ")";
}
protected override void DrawContent(ref bool sceneChanged)
{
_drawGizmos = EditorGUILayout.Toggle("Draw Gizmos", _drawGizmos);
base.DrawContent(ref sceneChanged);
}
public override void OnDrawGizmos()
{
if (!_drawGizmos || _bounds == null)
return;
DrawBounds(_bounds, Color.yellow);
}
protected override bool ShouldDrawClearOptions()
{
return _settings.Length > 0;
}
protected override bool AssignIteration(GameObject current)
{
if (!current.activeInHierarchy)
return false;
if (current.GetComponent<DC_SourceSettings>() != null)
return false;
if (current.GetComponent<DC_Occluder>() != null)
return false;
LODGroup group = current.GetComponent<LODGroup>();
if (group == null)
return false;
Controller.AddObjectForCulling(group).CheckCompatibility();
return true;
}
protected override void ClearAll()
{
for (int i = 0; i < _settings.Length; i++)
{
_settings[i].ClearBakedData();
Object.DestroyImmediate(_settings[i]);
}
}
protected override bool ClearIteration(GameObject current)
{
if (current.TryGetComponent(out DC_SourceSettings settings))
{
if (settings.SourceType != SourceType.LODGroup)
return false;
if (settings.ControllerID != Controller.ControllerID)
return false;
settings.ClearBakedData();
Object.DestroyImmediate(settings);
return true;
}
return false;
}
protected override void Select()
{
Selection.objects = _settings.Select(s => s.gameObject).ToArray();
}
}
public class DC_CustomSelector : DC_BaseSelector
{
public override bool IsAvailable
{
get
{
return !Application.isPlaying;
}
}
private DC_SourceSettings[] _customSettings;
private Bounds[] _bounds;
private bool _drawGizmos;
public DC_CustomSelector(DC_SelectionTool parent) : base(parent)
{
}
public override void Refresh()
{
_customSettings = Object.FindObjectsOfType<DC_SourceSettings>()
.Where(s => s.SourceType == SourceType.Custom && s.ControllerID == Controller.ControllerID)
.ToArray();
Bounds bounds = default;
_bounds = _customSettings
.Where(s => s.TryGetBounds(ref bounds))
.Select(r => bounds)
.ToArray();
Label = "Custom (" + _customSettings.Length + ")";
}
protected override void DrawContent(ref bool sceneChanged)
{
_drawGizmos = EditorGUILayout.Toggle("Draw Gizmos", _drawGizmos);
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Select", ButtonGUIStyle))
Select();
Color backColor = GUI.backgroundColor;
GUI.backgroundColor = RedColor;
if (GUILayout.Button("Clear All", ButtonGUIStyle))
{
ClearAll();
sceneChanged = true;
}
GUI.backgroundColor = backColor;
EditorGUILayout.EndHorizontal();
}
public override void OnDrawGizmos()
{
if (!_drawGizmos || _bounds == null)
return;
DrawBounds(_bounds, Color.white);
}
private void ClearAll()
{
for (int i = 0; i < _customSettings.Length; i++)
Object.DestroyImmediate(_customSettings[i]);
}
private void Select()
{
Selection.objects = _customSettings.Select(o => o.gameObject).ToArray();
}
}
public class DC_OccludersSelector : DC_BaseSelector
{
public override bool IsAvailable
{
get
{
return !Application.isPlaying;
}
}
private DC_Occluder[] _occluders;
private Bounds[] _bounds;
private OccluderType _occluderType;
private bool _drawGizmos;
public DC_OccludersSelector(DC_SelectionTool parent) : base(parent)
{
}
public override void Refresh()
{
_occluders = Object.FindObjectsOfType<DC_Occluder>();
Bounds bounds = default;
_bounds = _occluders
.Where(o => o.TryGetBounds(ref bounds))
.Select(o => bounds)
.ToArray();
Label = "Occluders (" + _occluders.Length + ")";
}
protected override void DrawContent(ref bool sceneChanged)
{
_drawGizmos = EditorGUILayout.Toggle("Draw Gizmos", _drawGizmos);
_occluderType = (OccluderType) EditorGUILayout.EnumPopup("Occluder Type", _occluderType);
Color backColor = GUI.backgroundColor;
EditorGUILayout.BeginHorizontal();
GUI.backgroundColor = GreenColor;
if (GUILayout.Button("Assign Selected", ButtonGUIStyle))
{
AssignSelected();
sceneChanged = true;
}
GUI.backgroundColor = backColor;
if (_occluders.Length == 0)
{
EditorGUILayout.EndHorizontal();
return;
}
GUI.backgroundColor = RedColor;
if (GUILayout.Button("Clear Selected", ButtonGUIStyle))
{
ClearSelected();
sceneChanged = true;
}
GUI.backgroundColor = backColor;
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("Clear All", ButtonGUIStyle))
{
ClearAll();
sceneChanged = true;
}
if (GUILayout.Button("Select", ButtonGUIStyle))
Select();
}
public override void OnDrawGizmos()
{
if (!_drawGizmos || _bounds == null)
return;
DrawBounds(_bounds, Color.green);
}
private void AssignSelected()
{
GameObject[] gos = Selection.gameObjects
.SelectMany(go => go.GetComponentsInChildren<Transform>()
.Select(t => t.gameObject))
.ToArray();
int count = 0;
for (int i = 0; i < gos.Length; i++)
{
GameObject go = gos[i];
if (go.TryGetComponent(out DC_SourceSettings settings))
continue;
if (_occluderType == OccluderType.Collider)
{
if (go.GetComponent<Collider>() != null)
{
go.AddComponent<DC_Occluder>();
count++;
}
}
else if (_occluderType == OccluderType.Mesh)
{
if (go.GetComponent<MeshRenderer>() != null)
{
go.AddComponent<DC_Occluder>();
count++;
}
}
else
{
if (go.GetComponent<LODGroup>() != null)
{
go.AddComponent<DC_Occluder>();
count++;
}
}
}
Debug.Log("Assigned " + count + " objects");
}
private void ClearSelected()
{
GameObject[] gos = Selection.gameObjects
.SelectMany(go => go.GetComponentsInChildren<Transform>()
.Select(t => t.gameObject))
.ToArray();
int count = 0;
for (int i = 0; i < gos.Length; i++)
{
GameObject go = gos[i];
if (go.TryGetComponent(out DC_Occluder occluder))
{
Object.DestroyImmediate(occluder);
count++;
}
}
Debug.Log("Clear " + count + " objects");
}
private void ClearAll()
{
for (int i = 0; i < _occluders.Length; i++)
Object.DestroyImmediate(_occluders[i]);
}
private void Select()
{
Selection.objects = _occluders.Select(o => o.gameObject).ToArray();
}
}
public class DC_IncompatiblesSelector : DC_BaseSelector
{
public override bool IsAvailable
{
get
{
return true;
}
}
private DC_SourceSettings[] _settings;
public DC_IncompatiblesSelector(DC_SelectionTool parent)
: base(parent)
{
}
public override void Refresh()
{
_settings = Object.FindObjectsOfType<DC_SourceSettings>()
.Where(s => s.IsIncompatible)
.ToArray();
Label = "Incompatible Sources (" + _settings.Length + ")";
}
public override void OnInspectorGUI(ref bool sceneChanged)
{
if (_settings.Length == 0)
return;
base.OnInspectorGUI(ref sceneChanged);
}
protected override void DrawContent(ref bool sceneChanged)
{
if (_settings.Length == 0)
{
EditorGUILayout.HelpBox("Incompatible sources not found :)", MessageType.Info);
return;
}
if (!Application.isPlaying)
{
Color back = GUI.backgroundColor;
GUI.backgroundColor = GreenColor;
if (GUILayout.Button("Clear All", ButtonGUIStyle))
{
ClearAll();
sceneChanged = true;
}
GUI.backgroundColor = back;
}
if (GUILayout.Button("Select", ButtonGUIStyle))
Select();
if (GUILayout.Button("Print", ButtonGUIStyle))
Print();
}
public override void OnDrawGizmos()
{
}
private void ClearAll()
{
for (int i = 0; i < _settings.Length; i++)
Object.DestroyImmediate(_settings[i]);
}
private void Select()
{
Selection.objects = _settings.Select(o => o.gameObject).ToArray();
}
private void Print()
{
for (int i = 0; i < _settings.Length; i++)
Debug.Log(_settings[i].gameObject.name + " " + _settings[i].IncompatibilityReason);
}
}
}

View File

@@ -0,0 +1,343 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
namespace NGS.AdvancedCullingSystem.Dynamic
{
[CanEditMultipleObjects]
[CustomEditor(typeof(DC_SourceSettings))]
public class DC_SourceSettingsEditor : Editor
{
private new DC_SourceSettings target
{
get
{
return base.target as DC_SourceSettings;
}
}
private SerializedProperty _controllerIdProp;
private SerializedProperty _sourceTypeProp;
private SerializedProperty _isIncompatibleProp;
private SerializedProperty _incompatibilityReasonProp;
private SourceSettingsStrategyEditor _strategyEditor;
private void OnEnable()
{
_controllerIdProp = serializedObject.FindAutoProperty(nameof(target.ControllerID));
_sourceTypeProp = serializedObject.FindProperty("_sourceType");
_isIncompatibleProp = serializedObject.FindAutoProperty(nameof(target.IsIncompatible));
_incompatibilityReasonProp = serializedObject.FindAutoProperty(nameof(target.IncompatibilityReason));
}
public override void OnInspectorGUI()
{
serializedObject.Update();
_strategyEditor = GetSourceSettingsStrategyEditor();
_strategyEditor?.SetContext(serializedObject);
DrawHelpBox();
if (DrawProperties())
ApplyModifiedProperties();
EditorGUILayout.Space();
if (GUILayout.Button("Check Compatibility"))
CheckCompatibilities();
}
private void OnSceneGUI()
{
_strategyEditor?.OnSceneGUI(target);
}
private SourceSettingsStrategyEditor GetSourceSettingsStrategyEditor()
{
if (!_sourceTypeProp.hasMultipleDifferentValues)
{
SourceType sourceType = (SourceType)_sourceTypeProp.enumValueIndex;
if (sourceType == SourceType.SingleMesh)
{
if (_strategyEditor is RendererSourceSettingsStrategyEditor)
return _strategyEditor;
return new RendererSourceSettingsStrategyEditor();
}
if (sourceType == SourceType.LODGroup)
{
if (_strategyEditor is LODGroupSourceSettingsStrategyEditor)
return _strategyEditor;
return new LODGroupSourceSettingsStrategyEditor();
}
if (sourceType == SourceType.Custom)
{
if (_strategyEditor is CustomSourceSettingsStrategyEditor)
return _strategyEditor;
return new CustomSourceSettingsStrategyEditor();
}
}
return null;
}
private void DrawHelpBox()
{
if (_isIncompatibleProp.boolValue && !_isIncompatibleProp.hasMultipleDifferentValues)
{
string text = _incompatibilityReasonProp.stringValue;
EditorGUILayout.HelpBox(text, MessageType.Warning);
EditorGUILayout.Space();
}
}
private bool DrawProperties()
{
EditorGUI.BeginDisabledGroup(Application.isPlaying);
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(_controllerIdProp);
EditorGUILayout.PropertyField(_sourceTypeProp);
_strategyEditor?.DrawProperties();
EditorGUI.EndDisabledGroup();
return EditorGUI.EndChangeCheck();
}
private void ApplyModifiedProperties()
{
_strategyEditor?.ApplyModifiedProperties(targets);
foreach (var target in targets)
{
DC_SourceSettings current = target as DC_SourceSettings;
if (!_controllerIdProp.hasMultipleDifferentValues)
current.ControllerID = _controllerIdProp.intValue;
if (!_sourceTypeProp.hasMultipleDifferentValues)
current.SourceType = (SourceType) _sourceTypeProp.enumValueIndex;
EditorUtility.SetDirty(target);
}
}
private void CheckCompatibilities()
{
foreach (var target in targets)
{
DC_SourceSettings current = target as DC_SourceSettings;
if (current.CheckCompatibility())
{
Debug.Log(current.name + " is compatible!");
}
else
{
Debug.Log(current.name + " is incompatible. Reason : " + current.IncompatibilityReason);
}
}
}
private abstract class SourceSettingsStrategyEditor
{
protected SerializedObject Context { get; private set; }
private bool _propertiesChanged;
public void SetContext(SerializedObject context)
{
Context = context;
OnContextSet();
}
public void DrawProperties()
{
EditorGUI.BeginChangeCheck();
DrawPropertiesInternal();
_propertiesChanged = EditorGUI.EndChangeCheck();
}
public void ApplyModifiedProperties(Object[] targets)
{
if (!_propertiesChanged)
return;
ApplyModifiedPropertiesInternal(targets);
_propertiesChanged = false;
}
public virtual void OnSceneGUI(DC_SourceSettings target)
{
}
protected abstract void OnContextSet();
protected abstract void DrawPropertiesInternal();
protected abstract void ApplyModifiedPropertiesInternal(Object[] targets);
}
private class RendererSourceSettingsStrategyEditor : SourceSettingsStrategyEditor
{
private SerializedProperty _cullingMethodProp;
private SerializedProperty _convexColliderProp;
protected override void OnContextSet()
{
SerializedProperty strategyProp = Context.FindProperty("_strategy");
_cullingMethodProp = strategyProp.FindPropertyRelative("_cullingMethod");
_convexColliderProp = strategyProp.FindPropertyRelative("_convexCollider");
}
protected override void DrawPropertiesInternal()
{
EditorGUILayout.PropertyField(_cullingMethodProp);
EditorGUILayout.PropertyField(_convexColliderProp);
}
protected override void ApplyModifiedPropertiesInternal(Object[] targets)
{
if (!_cullingMethodProp.hasMultipleDifferentValues)
{
foreach (var target in targets)
{
(target as DC_SourceSettings).GetStrategy<DC_RendererSourceSettingsStrategy>()
.CullingMethod = (CullingMethod)_cullingMethodProp.enumValueIndex;
}
}
if (!_convexColliderProp.hasMultipleDifferentValues)
{
foreach (var target in targets)
{
(target as DC_SourceSettings).GetStrategy<DC_RendererSourceSettingsStrategy>()
.ConvexCollider = _convexColliderProp.boolValue;
}
}
}
}
private class LODGroupSourceSettingsStrategyEditor : SourceSettingsStrategyEditor
{
private SerializedProperty _cullingMethodProp;
protected override void OnContextSet()
{
_cullingMethodProp = Context
.FindProperty("_strategy")
.FindPropertyRelative("_cullingMethod");
}
protected override void DrawPropertiesInternal()
{
EditorGUILayout.PropertyField(_cullingMethodProp);
}
protected override void ApplyModifiedPropertiesInternal(Object[] targets)
{
if (!_cullingMethodProp.hasMultipleDifferentValues)
{
foreach (var target in targets)
{
(target as DC_SourceSettings).GetStrategy<DC_LODGroupSourceSettingsStrategy>()
.CullingMethod = (CullingMethod)_cullingMethodProp.enumValueIndex;
}
}
}
}
private class CustomSourceSettingsStrategyEditor : SourceSettingsStrategyEditor
{
private static BoxBoundsHandle BoundsHandle;
private SerializedProperty _strategyProp;
private SerializedProperty _localBoundsProp;
private SerializedProperty _onVisibleEventProp;
private SerializedProperty _onInvisibleEventProp;
public override void OnSceneGUI(DC_SourceSettings target)
{
if (BoundsHandle == null)
BoundsHandle = new BoxBoundsHandle();
DC_CustomSourceSettingsStrategy strategy =
target.GetStrategy<DC_CustomSourceSettingsStrategy>();
Bounds localBounds = strategy.LocalBounds;
BoundsHandle.center = target.transform.position + localBounds.center;
BoundsHandle.size = localBounds.size;
BoundsHandle.DrawHandle();
localBounds.center = BoundsHandle.center - target.transform.position;
localBounds.size = BoundsHandle.size;
strategy.LocalBounds = localBounds;
}
protected override void OnContextSet()
{
_strategyProp = Context.FindProperty("_strategy");
_localBoundsProp = _strategyProp.FindPropertyRelative("_localBounds");
_onVisibleEventProp = _strategyProp.FindPropertyRelative("_onVisible");
_onInvisibleEventProp = _strategyProp.FindPropertyRelative("_onInvisible");
}
protected override void DrawPropertiesInternal()
{
EditorGUILayout.Space();
if (_strategyProp.hasMultipleDifferentValues)
{
EditorGUILayout.HelpBox("Multiediting is not supported for such SourceType", MessageType.Info, true);
return;
}
EditorGUILayout.PropertyField(_localBoundsProp);
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.PropertyField(_onVisibleEventProp);
EditorGUILayout.Space();
EditorGUILayout.PropertyField(_onInvisibleEventProp);
}
protected override void ApplyModifiedPropertiesInternal(Object[] targets)
{
if (_strategyProp.hasMultipleDifferentValues)
return;
Context.ApplyModifiedProperties();
}
}
}
}

View File

@@ -0,0 +1,79 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
namespace NGS.AdvancedCullingSystem.Static
{
[CanEditMultipleObjects]
[CustomEditor(typeof(CameraZone))]
public class CameraZoneEditor : Editor
{
protected new CameraZone target
{
get
{
return base.target as CameraZone;
}
}
private static bool DrawGizmo = true;
private static BinaryTreeDrawer TreeDrawer;
private BoxBoundsHandle _boundsHandle;
private void OnEnable()
{
_boundsHandle = new BoxBoundsHandle();
if (TreeDrawer == null)
{
TreeDrawer = new BinaryTreeDrawer();
TreeDrawer.Color = Color.white;
}
}
public override void OnInspectorGUI()
{
if (target.VisibilityTree != null)
{
DrawGizmo = EditorGUILayout.Toggle("Draw Gizmo", DrawGizmo);
EditorGUILayout.HelpBox("Cells Count : " + target.CellsCount, MessageType.None);
if (GUILayout.Button("Clear"))
target.ClearVisibilityTree();
}
}
[DrawGizmo(GizmoType.Selected | GizmoType.Active)]
private static void OnDrawGizmos(CameraZone cameraZone, GizmoType gizmoType)
{
if (!DrawGizmo)
return;
if (cameraZone.VisibilityTree != null)
{
TreeDrawer.DrawTreeGizmos(cameraZone.VisibilityTree.Root);
}
}
private void OnSceneGUI()
{
if (target.VisibilityTree != null)
return;
Transform transform = target.transform;
_boundsHandle.center = transform.position;
_boundsHandle.size = transform.localScale;
_boundsHandle.DrawHandle();
transform.position = _boundsHandle.center;
transform.localScale = _boundsHandle.size;
}
}
}

View File

@@ -0,0 +1,74 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace NGS.AdvancedCullingSystem.Static
{
[CustomEditor(typeof(StaticCullingCamera))]
public class StaticCullingCameraEditor : Editor
{
private static bool ShowFrustum;
protected new StaticCullingCamera target
{
get
{
return base.target as StaticCullingCamera;
}
}
private SerializedProperty _drawCellsProp;
private SerializedProperty _toleranceProp;
private Camera _camera;
private void OnEnable()
{
_drawCellsProp = serializedObject.FindProperty("_drawCells");
_toleranceProp = serializedObject.FindProperty("_tolerance");
_camera = target.GetComponent<Camera>();
}
public override void OnInspectorGUI()
{
serializedObject.Update();
EditorGUI.BeginChangeCheck();
ShowFrustum = EditorGUILayout.Toggle("Show Frustum", ShowFrustum);
if (EditorGUI.EndChangeCheck())
ResetSceneCamerasCullingMatrices();
EditorGUILayout.PropertyField(_drawCellsProp);
_toleranceProp.floatValue = EditorGUILayout.Slider("Tolerance", _toleranceProp.floatValue, 0, 3);
serializedObject.ApplyModifiedProperties();
}
private void OnSceneGUI()
{
if (!Application.isPlaying)
return;
if (!ShowFrustum)
return;
foreach (var camera in SceneView.GetAllSceneCameras())
camera.cullingMatrix = _camera.cullingMatrix;
}
private void OnDisable()
{
ResetSceneCamerasCullingMatrices();
}
private void ResetSceneCamerasCullingMatrices()
{
foreach (var camera in SceneView.GetAllSceneCameras())
camera.ResetCullingMatrix();
}
}
}

View File

@@ -0,0 +1,153 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace NGS.AdvancedCullingSystem.Static
{
public class CameraZonesSelectionWindow : EditorWindow
{
private StaticCullingController _controller;
private static GUIStyle TitleLabelStyle;
private static GUIStyle HeaderLabelStyle;
private static GUIStyle ButtonGUIStyle;
public void Initialize(StaticCullingController controller)
{
_controller = controller;
}
private void OnGUI()
{
if (_controller == null)
Close();
if (Application.isPlaying)
{
Debug.Log("Can't open window in runtime");
Close();
}
if (TitleLabelStyle == null)
CreateGUIStyles();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Camera Zones", TitleLabelStyle);
EditorHelper.DrawSeparatorLine(1, 2);
EditorGUILayout.Space();
IReadOnlyList<CameraZone> cameraZones = _controller.CameraZones;
if (cameraZones != null)
{
int i = 0;
while (i < cameraZones.Count)
{
CameraZone zone = cameraZones[i];
EditorGUILayout.BeginHorizontal();
EditorGUILayout.ObjectField(cameraZones[i], typeof(CameraZone), false);
if (GUILayout.Button("Select"))
Select(zone);
if (GUILayout.Button("Clear"))
Clear(zone);
if (GUILayout.Button("Delete"))
Delete(zone);
EditorGUILayout.EndHorizontal();
i++;
}
}
EditorGUILayout.Space();
if (GUILayout.Button("Create New", ButtonGUIStyle))
CreateNew();
if (GUILayout.Button("Add Selected", ButtonGUIStyle))
AddSelected();
}
private void CreateGUIStyles()
{
TitleLabelStyle = new GUIStyle();
TitleLabelStyle.fontSize = 24;
TitleLabelStyle.fontStyle = FontStyle.Bold;
TitleLabelStyle.alignment = TextAnchor.MiddleCenter;
TitleLabelStyle.normal.textColor = Color.white;
HeaderLabelStyle = new GUIStyle();
HeaderLabelStyle.fontSize = 17;
HeaderLabelStyle.fontStyle = FontStyle.Bold;
HeaderLabelStyle.alignment = TextAnchor.MiddleLeft;
HeaderLabelStyle.normal.textColor = Color.white;
ButtonGUIStyle = new GUIStyle(GUI.skin.button);
ButtonGUIStyle.fontSize = 12;
ButtonGUIStyle.fixedHeight = 24;
ButtonGUIStyle.margin = new RectOffset(5, 5, 5, 5);
ButtonGUIStyle.border = new RectOffset(0, 0, 0, 0);
ButtonGUIStyle.padding = new RectOffset(5, 5, 5, 5);
}
private void Select(CameraZone zone)
{
Selection.activeObject = zone.gameObject;
}
private void Clear(CameraZone zone)
{
_controller.RemoveCameraZone(zone);
}
private void Delete(CameraZone zone)
{
_controller.RemoveCameraZone(zone);
DestroyImmediate(zone.gameObject);
}
private void CreateNew()
{
GameObject go = new GameObject("Camera Zone");
go.transform.localScale = Vector3.one * 10;
CameraZone zone = go.AddComponent<CameraZone>();
_controller.AddCameraZone(zone);
}
private void AddSelected()
{
int count = 0;
foreach (var go in Selection.gameObjects)
{
CameraZone[] zones = go.GetComponentsInChildren<CameraZone>();
if (zones == null)
continue;
foreach (var zone in zones)
{
if (_controller.AddCameraZone(zone))
count++;
}
}
Debug.Log("Added " + count + " new camera zones");
}
}
}

View File

@@ -0,0 +1,961 @@
using System;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
namespace NGS.AdvancedCullingSystem.Static
{
public class SourcesSelectionWindow : EditorWindow
{
private static GUIStyle TitleLabelStyle;
private static GUIStyle HeaderLabelStyle;
private static GUIStyle ButtonGUIStyle;
private StaticCullingController _controller;
private string[] _tabNames;
private ITab[] _tabs;
private int _tabIndex = 0;
public void Initialize(StaticCullingController controller)
{
_controller = controller;
}
private void OnEnable()
{
name = "Objects Selection";
_tabNames = new string[] { "Cameras", "Renderers", "LODGroups", "Lights", "Custom" };
_tabs = new ITab[]
{
new CamerasTab(),
new RenderersTab(),
new LODGroupsTab(),
new LightsTab(),
new CustomTab()
};
foreach (var tab in _tabs)
tab.Refresh();
}
private void OnGUI()
{
if (_controller == null)
Close();
if (Application.isPlaying)
{
Close();
Debug.Log("Objects selection not available in runtime");
}
if (TitleLabelStyle == null)
CreateGUIStyles();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Objects Selection", TitleLabelStyle);
EditorHelper.DrawSeparatorLine(1, 2);
EditorGUILayout.Space();
_tabIndex = GUILayout.Toolbar(_tabIndex, _tabNames, ButtonGUIStyle);
EditorGUILayout.Space();
EditorGUILayout.Space();
bool sceneChanged = false;
_tabs[_tabIndex].OnInspectorGUI(ref sceneChanged);
if (sceneChanged)
{
foreach (var tab in _tabs)
tab.Refresh();
EditorUtility.SetDirty(_controller);
}
}
private void CreateGUIStyles()
{
TitleLabelStyle = new GUIStyle();
TitleLabelStyle.fontSize = 24;
TitleLabelStyle.fontStyle = FontStyle.Bold;
TitleLabelStyle.alignment = TextAnchor.MiddleCenter;
TitleLabelStyle.normal.textColor = Color.white;
HeaderLabelStyle = new GUIStyle();
HeaderLabelStyle.fontSize = 17;
HeaderLabelStyle.fontStyle = FontStyle.Bold;
HeaderLabelStyle.alignment = TextAnchor.MiddleLeft;
HeaderLabelStyle.normal.textColor = Color.white;
ButtonGUIStyle = new GUIStyle(GUI.skin.button);
ButtonGUIStyle.fontSize = 12;
ButtonGUIStyle.fixedHeight = 24;
ButtonGUIStyle.margin = new RectOffset(5, 5, 5, 5);
ButtonGUIStyle.border = new RectOffset(0, 0, 0, 0);
ButtonGUIStyle.padding = new RectOffset(5, 5, 5, 5);
}
private void DrawCustomTab()
{
EditorGUILayout.Space();
EditorGUILayout.HelpBox("To set a custom CullingSource you need to manually attach " +
"the 'StaticCullingSource' script to the object, specify the 'SourceType' as Custom, " +
"and then configure the 'StaticCullingSource' component.", MessageType.Info);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Custom Sources : 124", HeaderLabelStyle);
EditorGUILayout.Space();
EditorGUILayout.Toggle("DrawGizmo", true);
EditorGUILayout.Space();
EditorGUILayout.BeginHorizontal();
GUILayout.Button("Clear All", ButtonGUIStyle);
GUILayout.Button("Clear Selected", ButtonGUIStyle);
EditorGUILayout.EndHorizontal();
GUILayout.Button("Select", ButtonGUIStyle);
EditorGUILayout.Space();
EditorGUILayout.LabelField("Not Valid Custom Sources : 124", HeaderLabelStyle);
EditorGUILayout.Space();
EditorGUILayout.BeginVertical();
EditorGUILayout.BeginHorizontal();
GUILayout.Button("Print", ButtonGUIStyle);
GUILayout.Button("Select", ButtonGUIStyle);
EditorGUILayout.EndHorizontal();
GUILayout.Button("Clear All", ButtonGUIStyle);
EditorGUILayout.EndVertical();
}
private interface ITab
{
void Refresh();
void OnInspectorGUI(ref bool sceneChanged);
}
private abstract class TabTemplate : ITab
{
protected abstract string SourceName { get; }
protected abstract bool CanExistsNotValidSources { get; }
protected abstract bool ShowOnlyStaticToggle { get; }
protected abstract bool ShowSelectButton { get; }
protected abstract bool ShowVerifyButton { get; }
protected bool AssignOnlyStatic { get; private set; }
protected int SourcesCount { get; private set; }
protected int NotValidSourcesCount { get; private set; }
public virtual void Refresh()
{
SourcesCount = 0;
NotValidSourcesCount = 0;
foreach (var go in FindObjectsOfType<GameObject>())
{
if (ContainsSource(go))
{
SourcesCount++;
if (CanExistsNotValidSources && GetValidationError(go) != "")
NotValidSourcesCount++;
}
}
}
public virtual void OnInspectorGUI(ref bool sceneChanged)
{
BeforeOnGUI();
EditorGUILayout.LabelField(SourceName + " : " + SourcesCount, HeaderLabelStyle);
EditorGUILayout.Space();
EditorGUILayout.Space();
if (ShowVerifyButton)
{
if (GUILayout.Button("Verify Sources", ButtonGUIStyle))
VerifySources(ref sceneChanged);
EditorGUILayout.Space();
}
BeforeDrawContent();
if (ShowOnlyStaticToggle)
AssignOnlyStatic = EditorGUILayout.Toggle("Assign Only Static", AssignOnlyStatic);
EditorGUILayout.Space();
DrawSourcesButtons(ref sceneChanged);
if (CanExistsNotValidSources)
{
EditorGUILayout.Space();
EditorGUILayout.LabelField("Not Valid " + SourceName + " : " + NotValidSourcesCount,
HeaderLabelStyle);
DrawNotValidSourcesButtons(ref sceneChanged);
}
AfterOnGUI();
}
protected virtual void BeforeOnGUI()
{
}
protected virtual void BeforeDrawContent()
{
}
protected virtual void DrawSourcesButtons(ref bool sceneChanged)
{
EditorGUILayout.BeginHorizontal();
EditorGUILayout.BeginVertical();
if (GUILayout.Button("Assign Auto", ButtonGUIStyle))
AssignAutoButtonClick(ref sceneChanged);
if (GUILayout.Button("Clear All", ButtonGUIStyle))
ClearAllButtonClick(ref sceneChanged);
EditorGUILayout.EndVertical();
EditorGUILayout.BeginVertical();
if (GUILayout.Button("Assign Selected", ButtonGUIStyle))
AssignSelectedButtonClick(ref sceneChanged);
if (GUILayout.Button("Clear Selected", ButtonGUIStyle))
ClearSelectedButtonClick(ref sceneChanged);
EditorGUILayout.EndVertical();
EditorGUILayout.EndHorizontal();
if (ShowSelectButton)
{
if (GUILayout.Button("Select", ButtonGUIStyle))
SelectButtonClick(ref sceneChanged);
}
}
protected virtual void DrawNotValidSourcesButtons(ref bool sceneChanged)
{
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.BeginVertical();
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Print", ButtonGUIStyle))
NotValidPrintButtonClick(ref sceneChanged);
if (GUILayout.Button("Select", ButtonGUIStyle))
NotValidSelectButtonClick(ref sceneChanged);
EditorGUILayout.EndHorizontal();
if (GUILayout.Button("Clear All", ButtonGUIStyle))
NotValidClearButtonClick(ref sceneChanged);
EditorGUILayout.EndVertical();
}
protected virtual void AfterOnGUI()
{
}
protected abstract bool ContainsSource(GameObject go);
protected abstract bool CanAssignTo(GameObject go);
protected abstract void AssignSourceTo(GameObject go);
protected abstract void ClearSourceFrom(GameObject go);
protected abstract bool ValidateSource(GameObject go, out string error);
protected abstract string GetValidationError(GameObject go);
protected void VerifySources(ref bool sceneChanged)
{
foreach (var go in FindObjectsOfType<GameObject>().Where(go => ContainsSource(go)))
ValidateSource(go, out string error);
sceneChanged = true;
}
protected void AssignAutoButtonClick(ref bool sceneChanged)
{
int count = 0;
foreach (var go in FindObjectsOfType<GameObject>())
{
if (ContainsSource(go))
continue;
if (AssignOnlyStatic && !go.isStatic)
continue;
if (go.GetComponent<StaticCullingSource>() != null)
continue;
if (go.GetComponent<CullingTarget>() != null)
continue;
if (CanAssignTo(go))
{
AssignSourceTo(go);
sceneChanged = true;
count++;
}
}
Debug.Log("Assigned " + count + " new sources");
}
protected void AssignSelectedButtonClick(ref bool sceneChanged)
{
GameObject[] gos = Selection.gameObjects
.SelectMany(go => go.GetComponentsInChildren<Transform>()
.Select(t => t.gameObject))
.ToArray();
int count = 0;
foreach (var go in gos)
{
if (ContainsSource(go))
continue;
if (AssignOnlyStatic && !go.isStatic)
continue;
if (go.GetComponent<StaticCullingSource>() != null)
continue;
if (go.GetComponent<CullingTarget>() != null)
continue;
if (CanAssignTo(go))
{
AssignSourceTo(go);
sceneChanged = true;
count++;
}
}
Debug.Log("Assigned " + count + " new sources");
}
protected void ClearAllButtonClick(ref bool sceneChanged)
{
int count = 0;
foreach (var go in FindObjectsOfType<GameObject>())
{
if (ContainsSource(go))
{
ClearSourceFrom(go);
sceneChanged = true;
count++;
}
}
Debug.Log("Cleared " + count + " sources");
}
protected void ClearSelectedButtonClick(ref bool sceneChanged)
{
GameObject[] gos = Selection.gameObjects
.SelectMany(go => go.GetComponentsInChildren<Transform>()
.Select(t => t.gameObject))
.ToArray();
int count = 0;
foreach (var go in gos)
{
if (ContainsSource(go))
{
ClearSourceFrom(go);
sceneChanged = true;
count++;
}
}
Debug.Log("Cleared " + count + " sources");
}
protected void SelectButtonClick(ref bool sceneChanged)
{
GameObject[] gos = FindObjectsOfType<GameObject>()
.Where(go => ContainsSource(go))
.ToArray();
Selection.objects = gos;
}
protected void NotValidPrintButtonClick(ref bool sceneChanged)
{
foreach (var go in FindObjectsOfType<GameObject>())
{
if (ContainsSource(go))
{
string error = GetValidationError(go);
if (error == "")
continue;
Debug.Log(go.name + " : " + error);
}
}
}
protected void NotValidClearButtonClick(ref bool sceneChanged)
{
int count = 0;
foreach (var go in FindObjectsOfType<GameObject>())
{
if (ContainsSource(go))
{
if (!ValidateSource(go, out string error))
{
ClearSourceFrom(go);
sceneChanged = true;
count++;
}
}
}
Debug.Log("Cleared " + count + " not valid sources");
}
protected void NotValidSelectButtonClick(ref bool sceneChanged)
{
List<UnityEngine.Object> notValidGos = new List<UnityEngine.Object>();
foreach (var go in FindObjectsOfType<GameObject>())
{
if (ContainsSource(go))
{
string error = GetValidationError(go);
if (error != "")
notValidGos.Add(go);
}
}
Selection.objects = notValidGos.ToArray();
}
}
private class CamerasTab : TabTemplate
{
protected override string SourceName
{
get
{
return "Cameras";
}
}
protected override bool ShowOnlyStaticToggle
{
get
{
return false;
}
}
protected override bool CanExistsNotValidSources
{
get
{
return false;
}
}
protected override bool ShowVerifyButton
{
get
{
return false;
}
}
protected override bool ShowSelectButton
{
get
{
return true;
}
}
protected override void BeforeDrawContent()
{
StaticCullingCamera.DrawGizmo = EditorGUILayout.Toggle("Draw Gizmo",
StaticCullingCamera.DrawGizmo);
}
protected override bool ContainsSource(GameObject go)
{
return go.GetComponent<StaticCullingCamera>() != null;
}
protected override bool CanAssignTo(GameObject go)
{
return go.activeInHierarchy && go.GetComponent<Camera>() != null;
}
protected override void AssignSourceTo(GameObject go)
{
go.AddComponent<StaticCullingCamera>();
}
protected override void ClearSourceFrom(GameObject go)
{
DestroyImmediate(go.GetComponent<StaticCullingCamera>());
}
protected override string GetValidationError(GameObject go)
{
throw new NotSupportedException();
}
protected override bool ValidateSource(GameObject go, out string error)
{
throw new NotSupportedException();
}
}
private class RenderersTab : TabTemplate
{
protected override string SourceName
{
get
{
return "Renderers";
}
}
protected override bool ShowOnlyStaticToggle
{
get
{
return true;
}
}
protected override bool CanExistsNotValidSources
{
get
{
return true;
}
}
protected override bool ShowVerifyButton
{
get
{
return true;
}
}
protected override bool ShowSelectButton
{
get
{
return false;
}
}
protected override void BeforeDrawContent()
{
StaticCullingSource.DrawGizmoRenderers = EditorGUILayout.Toggle("Draw Gizmo",
StaticCullingSource.DrawGizmoRenderers);
}
protected override void AssignSourceTo(GameObject go)
{
StaticCullingSource source = go.AddComponent<StaticCullingSource>();
source.SourceType = SourceType.MeshRenderer;
}
protected override bool CanAssignTo(GameObject go)
{
MeshRenderer renderer = go.GetComponent<MeshRenderer>();
return go.activeInHierarchy && renderer != null && renderer.enabled &&
go.GetComponentInParent<LODGroup>() == null;
}
protected override void ClearSourceFrom(GameObject go)
{
DestroyImmediate(go.GetComponent<StaticCullingSource>());
}
protected override string GetValidationError(GameObject go)
{
return go.GetComponent<StaticCullingSource>().ValidationError;
}
protected override bool ContainsSource(GameObject go)
{
StaticCullingSource source = go.GetComponent<StaticCullingSource>();
return source != null && source.SourceType == SourceType.MeshRenderer;
}
protected override bool ValidateSource(GameObject go, out string error)
{
StaticCullingSource source = go.GetComponent<StaticCullingSource>();
bool isValid = source.Validate();
error = source.ValidationError;
return isValid;
}
}
private class LODGroupsTab : TabTemplate
{
protected override string SourceName
{
get
{
return "LODGroups";
}
}
protected override bool CanExistsNotValidSources
{
get
{
return true;
}
}
protected override bool ShowOnlyStaticToggle
{
get
{
return true;
}
}
protected override bool ShowSelectButton
{
get
{
return false;
}
}
protected override bool ShowVerifyButton
{
get
{
return true;
}
}
protected override void BeforeDrawContent()
{
StaticCullingSource.DrawGizmoLODGroups = EditorGUILayout.Toggle("Draw Gizmo",
StaticCullingSource.DrawGizmoLODGroups);
}
protected override bool ValidateSource(GameObject go, out string error)
{
StaticCullingSource source = go.GetComponent<StaticCullingSource>();
bool isValid = source.Validate();
error = source.ValidationError;
return isValid;
}
protected override string GetValidationError(GameObject go)
{
return go.GetComponent<StaticCullingSource>().ValidationError;
}
protected override bool ContainsSource(GameObject go)
{
StaticCullingSource source = go.GetComponent<StaticCullingSource>();
return source != null && source.SourceType == SourceType.LODGroup;
}
protected override bool CanAssignTo(GameObject go)
{
return go.activeInHierarchy && go.GetComponent<LODGroup>() != null;
}
protected override void AssignSourceTo(GameObject go)
{
StaticCullingSource source = go.AddComponent<StaticCullingSource>();
source.SourceType = SourceType.LODGroup;
}
protected override void ClearSourceFrom(GameObject go)
{
DestroyImmediate(go.GetComponent<StaticCullingSource>());
}
}
private class LightsTab : TabTemplate
{
protected override string SourceName
{
get
{
return "Lights";
}
}
protected override bool CanExistsNotValidSources
{
get
{
return true;
}
}
protected override bool ShowOnlyStaticToggle
{
get
{
return false;
}
}
protected override bool ShowSelectButton
{
get
{
return true;
}
}
protected override bool ShowVerifyButton
{
get
{
return true;
}
}
protected override void BeforeOnGUI()
{
EditorGUILayout.HelpBox("To avoid errors, you can only add PointLights in this window. " +
"To add other light sources - you need to manually " +
"attach 'StaticCullingSource' script " +
"to your Light, then specify SourceType as 'Light' " +
"and set up the bounding box.", MessageType.Info);
EditorGUILayout.Space();
}
protected override void BeforeDrawContent()
{
StaticCullingSource.DrawGizmoLights = EditorGUILayout.Toggle("Draw Gizmo",
StaticCullingSource.DrawGizmoLights);
}
protected override bool ValidateSource(GameObject go, out string error)
{
StaticCullingSource source = go.GetComponent<StaticCullingSource>();
bool isValid = source.Validate();
error = source.ValidationError;
return isValid;
}
protected override string GetValidationError(GameObject go)
{
return go.GetComponent<StaticCullingSource>().ValidationError;
}
protected override bool ContainsSource(GameObject go)
{
StaticCullingSource source = go.GetComponent<StaticCullingSource>();
return source != null && source.SourceType == SourceType.Light;
}
protected override bool CanAssignTo(GameObject go)
{
Light light = go.GetComponent<Light>();
return light != null && light.type == LightType.Point;
}
protected override void AssignSourceTo(GameObject go)
{
StaticCullingSource source = go.AddComponent<StaticCullingSource>();
source.SourceType = SourceType.Light;
}
protected override void ClearSourceFrom(GameObject go)
{
DestroyImmediate(go.GetComponent<StaticCullingSource>());
}
}
private class CustomTab : TabTemplate
{
protected override string SourceName
{
get
{
return "Custom";
}
}
protected override bool CanExistsNotValidSources
{
get
{
return true;
}
}
protected override bool ShowOnlyStaticToggle
{
get
{
return false;
}
}
protected override bool ShowSelectButton
{
get
{
return true;
}
}
protected override bool ShowVerifyButton
{
get
{
return true;
}
}
protected override void BeforeOnGUI()
{
EditorGUILayout.HelpBox("To set a custom CullingSource you need to manually attach " +
"the 'StaticCullingSource' script to the object, specify the 'SourceType' as Custom, " +
"and then configure the 'StaticCullingSource' component.", MessageType.Info);
EditorGUILayout.Space();
}
protected override void BeforeDrawContent()
{
StaticCullingSource.DrawGizmoCustom = EditorGUILayout.Toggle("Draw Gizmo",
StaticCullingSource.DrawGizmoCustom);
}
protected override void DrawSourcesButtons(ref bool sceneChanged)
{
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Clear All", ButtonGUIStyle))
ClearAllButtonClick(ref sceneChanged);
if (GUILayout.Button("Clear Selected", ButtonGUIStyle))
ClearSelectedButtonClick(ref sceneChanged);
EditorGUILayout.EndHorizontal();
if (ShowSelectButton)
{
if (GUILayout.Button("Select", ButtonGUIStyle))
SelectButtonClick(ref sceneChanged);
}
}
protected override bool ContainsSource(GameObject go)
{
StaticCullingSource source = go.GetComponent<StaticCullingSource>();
return source != null && source.SourceType == SourceType.Custom;
}
protected override bool CanAssignTo(GameObject go)
{
throw new NotSupportedException();
}
protected override void AssignSourceTo(GameObject go)
{
throw new NotSupportedException();
}
protected override void ClearSourceFrom(GameObject go)
{
DestroyImmediate(go.GetComponent<StaticCullingSource>());
}
protected override string GetValidationError(GameObject go)
{
return go.GetComponent<StaticCullingSource>().ValidationError;
}
protected override bool ValidateSource(GameObject go, out string error)
{
StaticCullingSource source = go.GetComponent<StaticCullingSource>();
bool isValid = source.Validate();
error = source.ValidationError;
return isValid;
}
}
}
}

View File

@@ -0,0 +1,236 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.EditorTools;
namespace NGS.AdvancedCullingSystem.Static
{
[CustomEditor(typeof(StaticCullingController))]
public class StaticCullingControllerEditor : Editor
{
protected new StaticCullingController target
{
get
{
return (StaticCullingController)base.target;
}
}
private static GUIStyle TitleLabelStyle;
private static GUIStyle HeaderLabelStyle;
private static GUIStyle ButtonGUIStyle;
private bool _sceneContainsBakedData;
[MenuItem("Tools/NGSTools/Advanced Culling System/Static")]
private static void CreateController()
{
GameObject go = new GameObject("StaticCullingController");
go.AddComponent<StaticCullingController>();
Selection.activeGameObject = go;
}
private void OnEnable()
{
CreateLayerIfNotExist();
Refresh();
}
public override void OnInspectorGUI()
{
if (TitleLabelStyle == null)
CreateGUIStyles();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Static Culling", TitleLabelStyle);
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Step 1. Objects Selection", HeaderLabelStyle);
EditorHelper.DrawSeparatorLine(1, 2);
DrawStep1();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Step 2. Scene Partitioning", HeaderLabelStyle);
EditorHelper.DrawSeparatorLine(1, 2);
DrawStep2();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Step 3. Camera Zones", HeaderLabelStyle);
EditorHelper.DrawSeparatorLine(1, 2);
DrawStep3();
EditorGUILayout.Space();
EditorGUILayout.Space();
EditorGUILayout.LabelField("Step 4. Baking", HeaderLabelStyle);
EditorHelper.DrawSeparatorLine(1, 2);
DrawStep4();
}
private void Refresh()
{
_sceneContainsBakedData = false;
if (FindObjectsOfType<CullingTarget>().Length > 0)
{
_sceneContainsBakedData = true;
return;
}
foreach (var zone in FindObjectsOfType<CameraZone>())
{
if (zone.VisibilityTree != null && zone.VisibilityTree.CullingTargets != null)
{
_sceneContainsBakedData = true;
return;
}
}
}
private void CreateLayerIfNotExist()
{
string layer = StaticCullingPreferences.LayerName;
if (!LayersHelper.IsLayerExist(layer))
{
LayersHelper.CreateLayer(layer);
LayersHelper.DisableCollisions(LayerMask.NameToLayer(layer));
}
}
private void CreateGUIStyles()
{
TitleLabelStyle = new GUIStyle();
TitleLabelStyle.fontSize = 24;
TitleLabelStyle.fontStyle = FontStyle.Bold;
TitleLabelStyle.alignment = TextAnchor.MiddleCenter;
TitleLabelStyle.normal.textColor = Color.white;
HeaderLabelStyle = new GUIStyle();
HeaderLabelStyle.fontSize = 17;
HeaderLabelStyle.fontStyle = FontStyle.Bold;
HeaderLabelStyle.alignment = TextAnchor.MiddleLeft;
HeaderLabelStyle.normal.textColor = Color.white;
ButtonGUIStyle = new GUIStyle(GUI.skin.button);
ButtonGUIStyle.fontSize = 12;
ButtonGUIStyle.fixedHeight = 24;
ButtonGUIStyle.margin = new RectOffset(5, 5, 5, 5);
ButtonGUIStyle.border = new RectOffset(0, 0, 0, 0);
ButtonGUIStyle.padding = new RectOffset(5, 5, 5, 5);
}
private void DrawStep1()
{
EditorGUILayout.HelpBox("Add cameras and objects to be culled", MessageType.None);
if (GUILayout.Button("Open Selection Tool", ButtonGUIStyle))
{
EditorWindow.GetWindow<SourcesSelectionWindow>().Initialize(target);
}
}
private void DrawStep2()
{
EditorGUILayout.HelpBox("Partition the scene into cells of optimal size. " +
"If a cell is visible, all objects inside this cell will be enabled. " +
"The more cells - the more objects will be culled, " +
"but the longer the baking time will be.", MessageType.None);
target.DrawGeometryTreeGizmo = EditorGUILayout.Toggle("Draw Gizmo",
target.DrawGeometryTreeGizmo);
EditorGUI.BeginChangeCheck();
target.GeometryTreeDepth = EditorGUILayout.IntSlider("Partition",
target.GeometryTreeDepth, 7, 20);
if (EditorGUI.EndChangeCheck())
EditorUtility.SetDirty(target);
EditorGUILayout.Space();
if (GUILayout.Button("Update", ButtonGUIStyle))
target.CreatePreviewGeometryTree();
}
private void DrawStep3()
{
EditorGUILayout.HelpBox("Select the areas where cameras can be located during gameplay. " +
"Divide these areas into cells. " +
"The smaller the cell, the more accurate the objects will be culled, " +
"but the longer the baking time.", MessageType.None);
target.DrawCameraZones = EditorGUILayout.Toggle("Draw Gizmo", target.DrawCameraZones);
EditorGUI.BeginChangeCheck();
target.CellSize = EditorGUILayout.FloatField("Cell Size", target.CellSize);
if (EditorGUI.EndChangeCheck())
EditorUtility.SetDirty(target);
EditorGUILayout.HelpBox("Total Cells Count : " + target.TotalCellsCount, MessageType.None);
EditorGUILayout.Space();
if (GUILayout.Button("Update", ButtonGUIStyle))
target.CreatePreviewCameraZones();
if (GUILayout.Button("Open Selection Tool", ButtonGUIStyle))
{
var window = EditorWindow.GetWindow<CameraZonesSelectionWindow>();
window.Initialize(target);
}
}
private void DrawStep4()
{
EditorGUILayout.HelpBox("Check the settings in each step again. " +
"Specify the number of rays per unit and press bake.", MessageType.None);
EditorGUI.BeginChangeCheck();
target.RaysPerUnit = EditorGUILayout.FloatField("Rays Per Unit", target.RaysPerUnit);
target.MaxRaysPerSource = EditorGUILayout.IntField("Max Rays Per Source", target.MaxRaysPerSource);
EditorGUILayout.Space();
if (GUILayout.Button("Bake", ButtonGUIStyle))
{
target.Bake();
Refresh();
}
EditorGUI.BeginDisabledGroup(!_sceneContainsBakedData);
if (GUILayout.Button("Clear", ButtonGUIStyle))
{
target.Clear();
Refresh();
}
EditorGUI.EndDisabledGroup();
if (EditorGUI.EndChangeCheck())
EditorUtility.SetDirty(target);
}
}
}

View File

@@ -0,0 +1,198 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditor.IMGUI.Controls;
namespace NGS.AdvancedCullingSystem.Static
{
[CanEditMultipleObjects]
[CustomEditor(typeof(StaticCullingSource))]
public class StaticCullingSourceEditor : Editor
{
protected new StaticCullingSource target
{
get
{
return base.target as StaticCullingSource;
}
}
private SerializedProperty _validationErrorProp;
private SerializedProperty _sourceTypeProp;
private SerializedProperty _strategyProp;
private BoxBoundsHandle _boundsHandle;
public void OnEnable()
{
_boundsHandle = new BoxBoundsHandle();
_validationErrorProp = serializedObject.FindProperty("_validationError");
_sourceTypeProp = serializedObject.FindProperty("_sourceType");
_strategyProp = serializedObject.FindProperty("_strategy");
}
public override void OnInspectorGUI()
{
serializedObject.Update();
DrawHelpBoxes();
if (DrawProperties())
ApplyModifiedProperties();
EditorGUILayout.Space();
serializedObject.Update();
DrawStrategyProperties();
serializedObject.ApplyModifiedProperties();
EditorGUILayout.Space();
if (GUILayout.Button("Verify"))
{
foreach (var target in targets)
{
StaticCullingSource current = target as StaticCullingSource;
if (current.Validate())
Debug.Log(current.gameObject.name + " is valid");
else
Debug.Log(current.gameObject.name + " not valid");
}
}
}
private void OnSceneGUI()
{
SourceType type = target.SourceType;
Bounds localBounds = default;
if (type == SourceType.Light)
localBounds = ((LightStaticCullingSourceStrategy)target.Strategy).LocalBounds;
else if (type == SourceType.Custom)
localBounds = ((CustomStaticCullingSourceStrategy)target.Strategy).LocalBounds;
else
return;
_boundsHandle.center = target.transform.position + localBounds.center;
_boundsHandle.size = localBounds.size;
_boundsHandle.DrawHandle();
localBounds.center = _boundsHandle.center - target.transform.position;
localBounds.size = _boundsHandle.size;
if (type == SourceType.Light)
((LightStaticCullingSourceStrategy)target.Strategy).LocalBounds = localBounds;
else if (type == SourceType.Custom)
((CustomStaticCullingSourceStrategy)target.Strategy).LocalBounds = localBounds;
}
private void DrawHelpBoxes()
{
if (!_validationErrorProp.hasMultipleDifferentValues)
{
string validationError = _validationErrorProp.stringValue;
if (validationError != "")
EditorGUILayout.HelpBox(validationError, MessageType.Warning);
}
}
private bool DrawProperties()
{
EditorGUI.BeginChangeCheck();
EditorGUILayout.PropertyField(_sourceTypeProp);
return EditorGUI.EndChangeCheck();
}
private void ApplyModifiedProperties()
{
foreach (var target in targets)
{
StaticCullingSource current = target as StaticCullingSource;
if (!_sourceTypeProp.hasMultipleDifferentValues)
current.SourceType = (SourceType) _sourceTypeProp.enumValueIndex;
}
}
private void DrawStrategyProperties()
{
if (_sourceTypeProp.hasMultipleDifferentValues)
return;
SourceType type = (SourceType) _sourceTypeProp.enumValueIndex;
if (type == SourceType.MeshRenderer)
DrawMeshRendererSourceStrategy();
else if (type == SourceType.LODGroup)
DrawLODGroupSourceStrategy();
else if (type == SourceType.Light)
DrawLightSourceStrategy();
else if (type == SourceType.Custom)
DrawCustomSourceStrategy();
else
throw new System.NotSupportedException();
}
private void DrawMeshRendererSourceStrategy()
{
SerializedProperty cullingMethodProp = _strategyProp.FindPropertyRelative("_cullingMethod");
SerializedProperty isOccluderProp = _strategyProp.FindPropertyRelative("_isOccluder");
EditorGUILayout.PropertyField(cullingMethodProp);
EditorGUILayout.PropertyField(isOccluderProp);
}
private void DrawLODGroupSourceStrategy()
{
SerializedProperty isOccluderProp = _strategyProp.FindPropertyRelative("_isOccluder");
SerializedProperty cullingMethodProp = _strategyProp.FindPropertyRelative("_cullingMethod");
EditorGUILayout.PropertyField(cullingMethodProp);
EditorGUILayout.PropertyField(isOccluderProp);
}
private void DrawLightSourceStrategy()
{
}
private void DrawCustomSourceStrategy()
{
SerializedProperty isOccluderProp = _strategyProp.FindPropertyRelative("_isOccluder");
SerializedProperty onVisibleProp = _strategyProp.FindPropertyRelative("_onVisible");
SerializedProperty onInvisibleProp = _strategyProp.FindPropertyRelative("_onInvisible");
EditorGUILayout.PropertyField(isOccluderProp);
if (!isOccluderProp.hasMultipleDifferentValues && isOccluderProp.boolValue)
{
SerializedProperty collidersProp = _strategyProp.FindPropertyRelative("_colliders");
EditorGUILayout.PropertyField(collidersProp);
}
EditorGUILayout.Space();
EditorGUILayout.PropertyField(onVisibleProp);
EditorGUILayout.PropertyField(onInvisibleProp);
}
}
}