initial commit
This commit is contained in:
parent
6715289efe
commit
788c3389af
37645 changed files with 2526849 additions and 80 deletions
|
@ -0,0 +1,122 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
public static class BuildPlatformsMirror
|
||||
{
|
||||
public struct buildPlatformInfo
|
||||
{
|
||||
public string name;
|
||||
public Texture2D icon;
|
||||
public string tooltip;
|
||||
public NamedBuildTarget buildTarget;
|
||||
}
|
||||
|
||||
static buildPlatformInfo[] m_validBuildPlatforms;
|
||||
|
||||
public static buildPlatformInfo[] ValidBuildPlatforms
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_validBuildPlatforms == null) m_validBuildPlatforms = GetValidBuildPlatforms();
|
||||
if (m_validBuildPlatforms == null) m_validBuildPlatforms = new buildPlatformInfo[0];
|
||||
return m_validBuildPlatforms;
|
||||
}
|
||||
}
|
||||
|
||||
static buildPlatformInfo[] GetValidBuildPlatforms()
|
||||
{
|
||||
Assembly buildPlatformAssembly = Assembly.GetAssembly(typeof(BuildPlayerContext));
|
||||
if (buildPlatformAssembly == null)
|
||||
{
|
||||
Debug.LogError("Couldn't find buildPlatforms's assembly");
|
||||
return null;
|
||||
}
|
||||
Type buildPlatformsType = buildPlatformAssembly.GetType("UnityEditor.Build.BuildPlatforms");
|
||||
if (buildPlatformsType == null)
|
||||
{
|
||||
Debug.LogError("Couldn't find buildPlatforms's type");
|
||||
return null;
|
||||
}
|
||||
|
||||
PropertyInfo buildPlatformsInstance = buildPlatformsType.GetProperty("instance");
|
||||
if (buildPlatformsInstance == null)
|
||||
{
|
||||
Debug.LogError("Couldn't find buildPlatforms's instance");
|
||||
return null;
|
||||
}
|
||||
|
||||
Type buildPlatformType = buildPlatformAssembly.GetType("UnityEditor.Build.BuildPlatform");
|
||||
if (buildPlatformType == null)
|
||||
{
|
||||
Debug.LogError("Couldn't find buildPlatform's type");
|
||||
return null;
|
||||
}
|
||||
|
||||
FieldInfo buildPlatformNameField = buildPlatformType.GetField("name");
|
||||
if (buildPlatformNameField == null)
|
||||
{
|
||||
Debug.LogError("Couldn't find buildPlatform's name field");
|
||||
return null;
|
||||
}
|
||||
|
||||
FieldInfo buildPlatformTooltipField = buildPlatformType.GetField("tooltip");
|
||||
if (buildPlatformTooltipField == null)
|
||||
{
|
||||
Debug.LogError("Couldn't find buildPlatform's tooltip field");
|
||||
return null;
|
||||
}
|
||||
|
||||
FieldInfo buildPlatformTargetField = buildPlatformType.GetField("namedBuildTarget");
|
||||
if (buildPlatformTargetField == null)
|
||||
{
|
||||
Debug.LogError("Couldn't find buildPlatform's namedBuildTarget field");
|
||||
return null;
|
||||
}
|
||||
|
||||
PropertyInfo buildPlatformIconProperty = buildPlatformType.GetProperty("smallIcon");
|
||||
if (buildPlatformIconProperty == null)
|
||||
{
|
||||
Debug.LogError("Couldn't find buildPlatform's icon property");
|
||||
return null;
|
||||
}
|
||||
|
||||
MethodInfo vaildBuildPlatformMethod = buildPlatformsType.GetMethod("GetValidPlatforms", 0, new Type[0]);
|
||||
if (vaildBuildPlatformMethod == null)
|
||||
{
|
||||
Debug.LogError("Couldn't find BuildPlatforms.GetValidPlatforms method");
|
||||
return null;
|
||||
}
|
||||
|
||||
IEnumerable buildPlatformArray = (IEnumerable)vaildBuildPlatformMethod.Invoke(buildPlatformsInstance.GetValue(null), new object[0]);
|
||||
if (buildPlatformArray == null)
|
||||
{
|
||||
Debug.LogError("BuildPlatforms.GetValidPlatforms returned null");
|
||||
return null;
|
||||
}
|
||||
|
||||
IEnumerator enumerator = buildPlatformArray.GetEnumerator();
|
||||
List<buildPlatformInfo> platformInfos = new List<buildPlatformInfo>();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
object platform = enumerator.Current;
|
||||
|
||||
buildPlatformInfo info = new buildPlatformInfo()
|
||||
{
|
||||
name = buildPlatformNameField.GetValue(platform) as string,
|
||||
icon = buildPlatformIconProperty.GetValue(platform) as Texture2D,
|
||||
tooltip = buildPlatformTooltipField.GetValue(platform) as string,
|
||||
buildTarget = (NamedBuildTarget)buildPlatformTargetField.GetValue(platform),
|
||||
};
|
||||
platformInfos.Add(info);
|
||||
}
|
||||
return platformInfos.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: de0e71e5b2b399343a4870c647cb0e12
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b9f9863fa20dd8e4fbae170139cc62d9
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,113 @@
|
|||
#if DEBUG_CUSTOM_STATIC_BATCHING
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using Unity.Jobs;
|
||||
using System.Linq;
|
||||
using SLZ.CustomStaticBatching;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace SLZ.CustomStaticBatching.Editor
|
||||
{
|
||||
public class DebugSB
|
||||
{
|
||||
[MenuItem("Tools/Print SB Debug Message")]
|
||||
public static void PrintDebugMessage()
|
||||
{
|
||||
GameObject[] selection = Selection.gameObjects;
|
||||
List<MeshRenderer> renderers = new List<MeshRenderer>();
|
||||
for (int i =0; i < selection.Length; i++)
|
||||
{
|
||||
MeshRenderer[] mf2 = selection[i].GetComponentsInChildren<MeshRenderer>();
|
||||
renderers.AddRange(mf2);
|
||||
}
|
||||
int numRenderers = renderers.Count;
|
||||
int rendererIdx = 0;
|
||||
|
||||
List<MeshFilter> meshFilters = new List<MeshFilter>(numRenderers);
|
||||
for (int i = 0; i < numRenderers; i++)
|
||||
{
|
||||
MeshRenderer mr = renderers[i];
|
||||
GameObject go = mr.gameObject;
|
||||
if (!GameObjectUtility.AreStaticEditorFlagsSet(go, StaticEditorFlags.BatchingStatic))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MeshFilter mf = go.GetComponent<MeshFilter>();
|
||||
|
||||
if (mf == null || mf.sharedMesh == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (mf.sharedMesh.indexFormat == UnityEngine.Rendering.IndexFormat.UInt32)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mr.sharedMaterials.Length == 0 || mr.sharedMaterials[0] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
renderers[rendererIdx] = mr;
|
||||
meshFilters.Add(mf);
|
||||
rendererIdx++;
|
||||
}
|
||||
if (numRenderers != rendererIdx) renderers.RemoveRange(rendererIdx, numRenderers - rendererIdx);
|
||||
renderers.TrimExcess();
|
||||
meshFilters.TrimExcess();
|
||||
|
||||
RendererData[] sortedData = RendererSort.GetSortedData(renderers, meshFilters);
|
||||
|
||||
ComputeShader transferVtxCompute = SBCombineMeshEditor.GetTransferVtxComputeShader();
|
||||
SBCombineMeshList combiner = new SBCombineMeshList(transferVtxCompute);
|
||||
combiner.FetchGlobalProjectSettings();
|
||||
combiner.GenerateStaticBatches(sortedData);
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Create Hilbert Curve")]
|
||||
public static void TestHilbert()
|
||||
{
|
||||
NativeArray<Vector3> points = new NativeArray<Vector3>(16 * 16 * 16, Allocator.TempJob);
|
||||
|
||||
for (int z = 0; z < 16; z++)
|
||||
{
|
||||
for (int y = 0; y < 16; y++)
|
||||
{
|
||||
for (int x = 0; x < 16; x++)
|
||||
{
|
||||
points[256 * z + 16 * y + x] = new Vector3(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
NativeArray<ulong> sortIdxes = HilbertIndex.GetHilbertIndices(points, new Bounds(new Vector3(8, 8, 8), new Vector3(16, 16, 16)), new Vector3(1,1.0f,1), Allocator.TempJob);
|
||||
//NativeSortExtension.Sort<SortIdx>(sortIdxes);
|
||||
string message = "";
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
message += sortIdxes[i].ToString() + "\n";
|
||||
}
|
||||
Debug.Log(message);
|
||||
GameObject lineObj = new GameObject();
|
||||
LineRenderer lineRenderer = lineObj.AddComponent<LineRenderer>();
|
||||
lineRenderer.positionCount = points.Length;
|
||||
lineRenderer.widthMultiplier = 0.02f;
|
||||
lineRenderer.alignment = LineAlignment.View;
|
||||
lineRenderer.useWorldSpace = false;
|
||||
for (int i = 0; i < points.Length; i++)
|
||||
{
|
||||
lineRenderer.SetPosition(i, points[sortIdxes[i].arrayIdx]);
|
||||
}
|
||||
sortIdxes.Dispose();
|
||||
*/
|
||||
points.Dispose();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 278aaa5095519464fa745c2e05185d5d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,85 @@
|
|||
#if DEBUG_CUSTOM_STATIC_BATCHING
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using UnityEngine.Rendering;
|
||||
using System.Text;
|
||||
|
||||
public class PrintMeshLayout
|
||||
{
|
||||
[MenuItem("Tools/Print Mesh Attributes")]
|
||||
public static void PrintLayout()
|
||||
{
|
||||
|
||||
|
||||
GameObject sel = Selection.activeGameObject;
|
||||
Renderer r = sel.GetComponent<Renderer>();
|
||||
Mesh m = null;
|
||||
if (r == null) return;
|
||||
if (r.GetType() == typeof(MeshRenderer))
|
||||
{
|
||||
m = r.GetComponent<MeshFilter>().sharedMesh;
|
||||
}
|
||||
else if (r.GetType() == typeof(SkinnedMeshRenderer))
|
||||
{
|
||||
m = ((SkinnedMeshRenderer)r).sharedMesh;
|
||||
}
|
||||
if (m == null) return;
|
||||
VertexAttributeDescriptor[] attr = m.GetVertexAttributes();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine("Vertex Layout:\n");
|
||||
for (int i = 0; i < attr.Length; i++)
|
||||
{
|
||||
switch (attr[i].attribute)
|
||||
{
|
||||
case VertexAttribute.Position:
|
||||
sb.Append("Position");
|
||||
break;
|
||||
case VertexAttribute.Normal:
|
||||
sb.Append("Normal");
|
||||
break;
|
||||
case VertexAttribute.Tangent:
|
||||
sb.Append("Tangent");
|
||||
break;
|
||||
case VertexAttribute.Color:
|
||||
sb.Append("Color");
|
||||
break;
|
||||
case VertexAttribute.TexCoord0:
|
||||
sb.Append("TexCoord0");
|
||||
break;
|
||||
case VertexAttribute.TexCoord1:
|
||||
sb.Append("TexCoord1");
|
||||
break;
|
||||
case VertexAttribute.TexCoord2:
|
||||
sb.Append("TexCoord2");
|
||||
break;
|
||||
case VertexAttribute.TexCoord3:
|
||||
sb.Append("TexCoord3");
|
||||
break;
|
||||
case VertexAttribute.TexCoord4:
|
||||
sb.Append("TexCoord4");
|
||||
break;
|
||||
case VertexAttribute.TexCoord5:
|
||||
sb.Append("TexCoord5");
|
||||
break;
|
||||
case VertexAttribute.TexCoord6:
|
||||
sb.Append("TexCoord6");
|
||||
break;
|
||||
case VertexAttribute.TexCoord7:
|
||||
sb.Append("TexCoord7");
|
||||
break;
|
||||
case VertexAttribute.BlendIndices:
|
||||
sb.Append("BlendIndices");
|
||||
break;
|
||||
case VertexAttribute.BlendWeight:
|
||||
sb.Append("BlendWeight");
|
||||
break;
|
||||
}
|
||||
sb.Append(string.Format(": Stream:{0}, Format: {1}, Dimension: {2}\n", attr[i].stream, VertexAttributeFormat.GetName(typeof(VertexAttributeFormat), attr[i].format), attr[i].dimension));
|
||||
}
|
||||
Debug.Log(sb.ToString());
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9f9b2a05487f93a458dee382a3776abd
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,28 @@
|
|||
#if DEBUG_CUSTOM_STATIC_BATCHING
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
public class TestLocalKWs
|
||||
{
|
||||
[MenuItem("Tools/Print Shader Keyword Idxs")]
|
||||
public static void Test()
|
||||
{
|
||||
Shader selection = (Shader)Selection.activeObject;
|
||||
if (selection != null )
|
||||
{
|
||||
UnityEngine.Rendering.LocalKeyword[] kws = selection.keywordSpace.keywords;
|
||||
string message = "Local Keywords: \n";
|
||||
for ( int i = 0; i < kws.Length; i++ )
|
||||
{
|
||||
message += string.Format("{0} : {1}\n", ReflectKWFields.GetIndex(kws[i]), kws[i].name);
|
||||
}
|
||||
Debug.Log(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 355abbf7ca534a2499e389b1424761a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEngine;
|
||||
using static SLZ.CustomStaticBatching.PackedChannel;
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
[Serializable]
|
||||
public class EditorCombineRendererSettings
|
||||
{
|
||||
public string buildTarget;
|
||||
public bool overrideBuildTarget;
|
||||
public CombineRendererSettings settings;
|
||||
|
||||
public EditorCombineRendererSettings(string target = "Default")
|
||||
{
|
||||
this.buildTarget = target;
|
||||
overrideBuildTarget = false;
|
||||
settings = new CombineRendererSettings(false);
|
||||
}
|
||||
|
||||
static SerializedObject GetProjectSettingsAsset()
|
||||
{
|
||||
const string projectSettingsAssetPath = "ProjectSettings/ProjectSettings.asset";
|
||||
UnityEngine.Object projSettingsObj = AssetDatabase.LoadMainAssetAtPath(projectSettingsAssetPath);
|
||||
if (projSettingsObj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SerializedObject projectSettings = new SerializedObject(AssetDatabase.LoadMainAssetAtPath(projectSettingsAssetPath));
|
||||
return projectSettings;
|
||||
}
|
||||
}
|
||||
|
||||
public static CombineRendererSettings ApplyProjectSettingsCompression(EditorCombineRendererSettings crs)
|
||||
{
|
||||
SerializedObject projectSettings = GetProjectSettingsAsset();
|
||||
CombineRendererSettings outp = crs.settings;
|
||||
|
||||
int vertexCompressionFlags = 0;
|
||||
if (projectSettings == null)
|
||||
{
|
||||
Debug.LogError("Custom Static Batching: Could not find ProjectSettings.asset, will assume all channels are uncompressed");
|
||||
}
|
||||
else
|
||||
{
|
||||
SerializedProperty vertexCompression = projectSettings.FindProperty("VertexChannelCompressionMask");
|
||||
if (vertexCompression == null)
|
||||
{
|
||||
Debug.LogError("Custom Static Batching: Could not find VertexChannelCompressionMask in ProjectSettings.asset, will assume all channels are uncompressed");
|
||||
}
|
||||
else
|
||||
{
|
||||
vertexCompressionFlags = vertexCompression.intValue;
|
||||
}
|
||||
}
|
||||
outp.serializedVtxFormats = new byte[NUM_VTX_CHANNELS];
|
||||
outp.altStream = new bool[NUM_VTX_CHANNELS];
|
||||
crs.settings.altStream.CopyTo(outp.altStream, 0);
|
||||
|
||||
outp.serializedVtxFormats[0] = crs.settings.serializedVtxFormats[0] != 0 ?
|
||||
crs.settings.serializedVtxFormats[0] :
|
||||
(vertexCompressionFlags & (int)VertexChannelCompressionFlags.Position) == 0 ? (byte)VtxFormats.Float32 : (byte)VtxFormats.Float16;
|
||||
|
||||
outp.serializedVtxFormats[1] = crs.settings.serializedVtxFormats[1] != 0 ?
|
||||
crs.settings.serializedVtxFormats[1] :
|
||||
(vertexCompressionFlags & (int)VertexChannelCompressionFlags.Normal) == 0 ? (byte)VtxFormats.Float32 : (byte)VtxFormats.Float16;
|
||||
outp.serializedVtxFormats[2] = crs.settings.serializedVtxFormats[2] != 0 ?
|
||||
crs.settings.serializedVtxFormats[2] :
|
||||
(vertexCompressionFlags & (int)VertexChannelCompressionFlags.Tangent) == 0 ? (byte)VtxFormats.Float32 : (byte)VtxFormats.Float16;
|
||||
outp.serializedVtxFormats[3] = crs.settings.serializedVtxFormats[3] != 0 ?
|
||||
crs.settings.serializedVtxFormats[3] :
|
||||
(vertexCompressionFlags & (int)VertexChannelCompressionFlags.Color) == 0 ? (byte)VtxFormats.Float32 : (byte)VtxFormats.UNorm8;
|
||||
outp.serializedVtxFormats[4] = crs.settings.serializedVtxFormats[4] != 0 ?
|
||||
crs.settings.serializedVtxFormats[4] :
|
||||
(vertexCompressionFlags & (int)VertexChannelCompressionFlags.TexCoord0) == 0 ? (byte)VtxFormats.Float32 : (byte)VtxFormats.Float16;
|
||||
outp.serializedVtxFormats[5] = crs.settings.serializedVtxFormats[5] != 0 ?
|
||||
crs.settings.serializedVtxFormats[5] :
|
||||
(vertexCompressionFlags & (int)VertexChannelCompressionFlags.TexCoord1) == 0 ? (byte)VtxFormats.Float32 : (byte)VtxFormats.Float16;
|
||||
outp.serializedVtxFormats[6] = crs.settings.serializedVtxFormats[6] != 0 ?
|
||||
crs.settings.serializedVtxFormats[6] :
|
||||
(vertexCompressionFlags & (int)VertexChannelCompressionFlags.TexCoord2) == 0 ? (byte)VtxFormats.Float32 : (byte)VtxFormats.Float16;
|
||||
outp.serializedVtxFormats[7] = crs.settings.serializedVtxFormats[7] != 0 ?
|
||||
crs.settings.serializedVtxFormats[7] :
|
||||
(vertexCompressionFlags & (int)VertexChannelCompressionFlags.TexCoord3) == 0 ? (byte)VtxFormats.Float32 : (byte)VtxFormats.Float16;
|
||||
|
||||
int numVtxChannels = outp.serializedVtxFormats.Length;
|
||||
|
||||
for (int i = 8; i < numVtxChannels; i++)
|
||||
{
|
||||
outp.serializedVtxFormats[i] = crs.settings.serializedVtxFormats[i] != 0 ?
|
||||
crs.settings.serializedVtxFormats[i] : (byte)VtxFormats.Float32;
|
||||
}
|
||||
|
||||
return outp;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b2a44935398e27e40b2e220cabe27321
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,89 @@
|
|||
using SLZ.CustomStaticBatching.Editor;
|
||||
using SLZ.CustomStaticBatching;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor.Build.Reporting;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
public class ReplaceStaticBatching : IProcessSceneWithReport
|
||||
{
|
||||
public int callbackOrder { get { return 0; } }
|
||||
|
||||
public void OnProcessScene(UnityEngine.SceneManagement.Scene scene, BuildReport report)
|
||||
{
|
||||
|
||||
Debug.Log("Running static batching for " + scene.name);
|
||||
GameObject[] selection = scene.GetRootGameObjects();
|
||||
List<MeshRenderer> renderers = new List<MeshRenderer>();
|
||||
|
||||
|
||||
for (int i = 0; i < selection.Length; i++)
|
||||
{
|
||||
MeshRenderer[] mf2 = selection[i].GetComponentsInChildren<MeshRenderer>();
|
||||
renderers.AddRange(mf2);
|
||||
}
|
||||
int numRenderers = renderers.Count;
|
||||
int rendererIdx = 0;
|
||||
|
||||
ComputeShader transferVtxCompute = SBCombineMeshEditor.GetTransferVtxComputeShader();
|
||||
SBCombineMeshList combiner = new SBCombineMeshList(transferVtxCompute);
|
||||
combiner.FetchGlobalProjectSettings();
|
||||
bool allow32bitIdxBatches = combiner.settings.allow32bitIdx;
|
||||
List<MeshFilter> meshFilters = new List<MeshFilter>(numRenderers);
|
||||
for (int i = 0; i < numRenderers; i++)
|
||||
{
|
||||
MeshRenderer mr = renderers[i];
|
||||
GameObject go = mr.gameObject;
|
||||
if (!GameObjectUtility.AreStaticEditorFlagsSet(go, StaticEditorFlags.BatchingStatic))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MeshFilter mf = go.GetComponent<MeshFilter>();
|
||||
|
||||
if (mf == null || mf.sharedMesh == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!allow32bitIdxBatches && mf.sharedMesh.indexFormat == UnityEngine.Rendering.IndexFormat.UInt32)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mr.sharedMaterials.Length == 0 || mr.sharedMaterials[0] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
renderers[rendererIdx] = mr;
|
||||
meshFilters.Add(mf);
|
||||
rendererIdx++;
|
||||
}
|
||||
if (numRenderers != rendererIdx) renderers.RemoveRange(rendererIdx, numRenderers - rendererIdx);
|
||||
renderers.TrimExcess();
|
||||
meshFilters.TrimExcess();
|
||||
|
||||
if (renderers.Count < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RendererData[] sortedData = RendererSort.GetSortedData(renderers, meshFilters);
|
||||
|
||||
|
||||
combiner.GenerateStaticBatches(sortedData);
|
||||
|
||||
for (int i = 0; i < sortedData.Length; i++)
|
||||
{
|
||||
GameObject go = sortedData[i].rendererTransform.gameObject;
|
||||
GameObjectUtility.SetStaticEditorFlags(go, GameObjectUtility.GetStaticEditorFlags(go) & ~StaticEditorFlags.BatchingStatic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 336ca034bcbdb604eb4b9bbcbc251c65
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,265 @@
|
|||
//#define USE_GPU_VTX_TRANSFER
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using Unity.Profiling;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using static SLZ.CustomStaticBatching.SBCombineMeshList;
|
||||
using static SLZ.CustomStaticBatching.PackedChannel;
|
||||
using static UnityEngine.Mesh;
|
||||
using System.Reflection;
|
||||
|
||||
namespace SLZ.CustomStaticBatching.Editor
|
||||
{
|
||||
public static class SBCombineMeshEditor
|
||||
{
|
||||
|
||||
#region EditorCompresssionSettings
|
||||
public static void FetchGlobalProjectSettings(this SBCombineMeshList cml)
|
||||
{
|
||||
CombineRendererSettings settings = SBSettingsSO.GlobalSettings.GetActiveBuildTargetSettings();
|
||||
cml.settings = settings;
|
||||
}
|
||||
|
||||
public static void FetchTargetProjectSettings(this SBCombineMeshList cml, BuildTarget target)
|
||||
{
|
||||
CombineRendererSettings settings = SBSettingsSO.GlobalSettings.GetBuildTargetSettings(target);
|
||||
cml.settings = settings;
|
||||
}
|
||||
|
||||
const string transferVtxGUID = "5bae5a4c97f51964dbc10d3398312270";
|
||||
|
||||
public static ComputeShader GetTransferVtxComputeShader()
|
||||
{
|
||||
ComputeShader transferVtxBufferCompute;
|
||||
|
||||
string computePath = AssetDatabase.GUIDToAssetPath(transferVtxGUID);
|
||||
if (string.IsNullOrEmpty(computePath))
|
||||
{
|
||||
throw new Exception("SLZ Static Batching: Failed to find the TransferVertexBuffer compute shader. There is no asset corresponding to the hard-coded GUID " + transferVtxGUID + ", mesh combining failed!");
|
||||
}
|
||||
transferVtxBufferCompute = AssetDatabase.LoadAssetAtPath<ComputeShader>(computePath);
|
||||
if (transferVtxBufferCompute == null)
|
||||
{
|
||||
throw new Exception("SLZ Static Batching: Failed to find the TransferVertexBuffer compute shader. Didn't find a compute shader at the path of the hard-coded GUID " + transferVtxGUID + ", mesh combining failed!");
|
||||
}
|
||||
return transferVtxBufferCompute;
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
static readonly ProfilerMarker profilerScaleSign = new ProfilerMarker("CustomStaticBatching.RendererScaleSign");
|
||||
static readonly ProfilerMarker profilerGetUniqueMeshes = new ProfilerMarker("CustomStaticBatching.GetUniqueMeshes");
|
||||
static readonly ProfilerMarker profilerGetMeshLayout = new ProfilerMarker("CustomStaticBatching.GetMeshLayout");
|
||||
static readonly ProfilerMarker profilerGetMeshBins = new ProfilerMarker("CustomStaticBatching.GetMeshBins");
|
||||
static readonly ProfilerMarker profilerGetCombinedLayout = new ProfilerMarker("CustomStaticBatching.GetCombinedLayout");
|
||||
static readonly ProfilerMarker profilerGetCombinedMesh = new ProfilerMarker("CustomStaticBatching.GetCombinedMesh");
|
||||
static readonly ProfilerMarker profilerComputeCopyMeshes = new ProfilerMarker("CustomStaticBatching.TransferToCombinedMesh");
|
||||
static readonly ProfilerMarker profilerAssignSBCombinedMesh = new ProfilerMarker("CustomStaticBatching.AssignSBCombinedMesh");
|
||||
|
||||
/// <summary>
|
||||
/// Given a list of pre-sorted renderers, bin the renderers into groups, generate a combined mesh for each group, and assign that combined mesh to the renderers.
|
||||
/// This will ignore renderers with meshes that have unusual attibute formats in the vertex struct that don't translate well to floating point formats.
|
||||
/// These include all integer formats and 16-bit normalized formats (16-bit could be done, I originally designed this around using a compute shader to do the
|
||||
/// combining and didn't want to add more complexity to the kernel to handle more cases)
|
||||
/// Assumes that the sorted list has all 32-bit index meshes sorted at the end of the array.
|
||||
/// </summary>
|
||||
/// <param name="cml">This SBCombineMeshList object</param>
|
||||
/// <param name="sortedRenderers">List of presorted renderers, assumes all 32-bit index mesh renderers are at the end</param>
|
||||
public static void GenerateStaticBatches(this SBCombineMeshList cml, RendererData[] sortedRenderers)
|
||||
{
|
||||
List<Mesh> uniqueMeshList;
|
||||
int[] renderer2Mesh;
|
||||
MeshDataArray uniqueMeshData;
|
||||
|
||||
// Get the sign of each transform's scale. Needed for the sign of the bitangent post object to world transformation, which is stored in the tangent's 4th component
|
||||
profilerScaleSign.Begin();
|
||||
NativeArray<byte> rendererScaleSign = cml.GetRendererScaleSign(sortedRenderers);
|
||||
profilerScaleSign.End();
|
||||
|
||||
// Get a list of the meshes used in the scene, and a mapping from the list of renderers to the list of meshes
|
||||
profilerGetUniqueMeshes.Begin();
|
||||
ParallelGetUniqueMeshes(sortedRenderers, out uniqueMeshList, out uniqueMeshData, out renderer2Mesh);
|
||||
profilerGetUniqueMeshes.End();
|
||||
|
||||
|
||||
// Get the layout of the vertex struct of each unique mesh, also making an array of bools for meshes that have bad vertex structs that can be rationally merged with other meshes
|
||||
NativeArray<PackedChannel> uniqueMeshLayout;
|
||||
NativeArray<byte> invalidMeshes;
|
||||
profilerGetMeshLayout.Begin();
|
||||
ParallelGetMeshLayout(uniqueMeshData, out uniqueMeshLayout, out invalidMeshes);
|
||||
profilerGetMeshLayout.End();
|
||||
|
||||
// Shift all the renderers with valid meshes to the front of the array
|
||||
int validRendererLen = cml.CleanInvalidRenderers(invalidMeshes, sortedRenderers, renderer2Mesh);
|
||||
invalidMeshes.Dispose();
|
||||
|
||||
// Segment the mesh list into groups that will each be a single combined mesh.
|
||||
// For 16 bit index buffers, the segments are split when the number of vertices exceeds the ushort limit.
|
||||
// For 32 bit buffers, the segments are split according to a configurable max number of vertices
|
||||
ushort[] renderer2CMeshIdx;
|
||||
List<int2> cMeshIdxRange;
|
||||
int _32bitIdxStart;
|
||||
profilerGetMeshBins.Begin();
|
||||
cml.GetCombinedMeshBins(sortedRenderers, validRendererLen, out renderer2CMeshIdx, out cMeshIdxRange, out _32bitIdxStart);
|
||||
profilerGetMeshBins.End();
|
||||
|
||||
int numCombinedMeshes = cMeshIdxRange.Count;
|
||||
NativeArray<PackedChannel>[] combinedMeshLayouts = new NativeArray<PackedChannel>[numCombinedMeshes];
|
||||
Mesh[] combinedMeshes = new Mesh[numCombinedMeshes];
|
||||
#if USE_GPU_VTX_TRANSFER
|
||||
AsyncMeshReadbackData[] combinedMeshReadbacks = new AsyncMeshReadbackData[numCombinedMeshes];
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < numCombinedMeshes; i++)
|
||||
{
|
||||
profilerGetCombinedLayout.Begin();
|
||||
combinedMeshLayouts[i] = cml.GetCombinedMeshLayout(sortedRenderers, ref uniqueMeshLayout, renderer2Mesh, cMeshIdxRange[i].x, cMeshIdxRange[i].y);
|
||||
profilerGetCombinedLayout.End();
|
||||
//DebugMessage += string.Format("Combined mesh {0}: {1}\n", i, VtxStructToString(combinedMeshLayouts[i], 0));
|
||||
Mesh combinedMesh;
|
||||
profilerGetCombinedMesh.Begin();
|
||||
if (i < _32bitIdxStart)
|
||||
{
|
||||
combinedMesh = cml.GetCombinedMeshObject<ushort>(sortedRenderers, uniqueMeshData, cMeshIdxRange[i], renderer2Mesh, ref combinedMeshLayouts[i], ref rendererScaleSign, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
combinedMesh = cml.GetCombinedMeshObject<int>(sortedRenderers, uniqueMeshData, cMeshIdxRange[i], renderer2Mesh, ref combinedMeshLayouts[i], ref rendererScaleSign, false);
|
||||
}
|
||||
profilerGetCombinedMesh.End();
|
||||
combinedMesh.name = "Combined Mesh (" + i + ")";
|
||||
combinedMeshes[i] = combinedMesh;
|
||||
profilerComputeCopyMeshes.Begin();
|
||||
#if USE_GPU_VTX_TRANSFER
|
||||
combinedMeshReadbacks[i] = cml.ComputeCopyMeshes(ref uniqueMeshLayout, ref combinedMeshLayouts[i], ref rendererScaleSign, CombinedMesh, sortedRenderers, cMeshIdxRange[i], renderer2Mesh, ref invalidMeshes, uniqueMeshList);
|
||||
#else
|
||||
cml.JobCopyMeshes(ref uniqueMeshLayout, ref combinedMeshLayouts[i], ref rendererScaleSign, combinedMesh, sortedRenderers, cMeshIdxRange[i], renderer2Mesh, uniqueMeshData);
|
||||
#endif
|
||||
profilerComputeCopyMeshes.End();
|
||||
profilerAssignSBCombinedMesh.Begin();
|
||||
AssignSBCombinedMesh(combinedMesh, sortedRenderers, renderer2Mesh, cMeshIdxRange[i]);
|
||||
profilerAssignSBCombinedMesh.End();
|
||||
//combinedMeshLayouts[i].Dispose();
|
||||
}
|
||||
//Debug.Log(DebugMessage);
|
||||
#if USE_GPU_VTX_TRANSFER
|
||||
for (int i = 0; i < numCombinedMeshes; i++)
|
||||
{
|
||||
combinedMeshReadbacks[i].FinishMeshReadback(combinedMeshes[i]);
|
||||
}
|
||||
#endif
|
||||
|
||||
rendererScaleSign.Dispose();
|
||||
uniqueMeshLayout.Dispose();
|
||||
//invalidMeshes.Dispose();
|
||||
uniqueMeshData.Dispose();
|
||||
for (int i = 0; i < cMeshIdxRange.Count; i++)
|
||||
{
|
||||
combinedMeshLayouts[i].Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
delegate void dSetStaticBatchInfo(Renderer renderer, int firstSubMesh, int subMeshCount);
|
||||
static dSetStaticBatchInfo s_SetStaticBatchInfo;
|
||||
static dSetStaticBatchInfo SetStaticBatchInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
if (s_SetStaticBatchInfo == null)
|
||||
{
|
||||
MethodInfo minfo = typeof(Renderer).GetMethod("SetStaticBatchInfo", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
s_SetStaticBatchInfo = (dSetStaticBatchInfo)minfo.CreateDelegate(typeof(dSetStaticBatchInfo));
|
||||
}
|
||||
return s_SetStaticBatchInfo;
|
||||
}
|
||||
}
|
||||
|
||||
public static void AssignSBCombinedMesh(Mesh combinedMesh, RendererData[] rd, int[] renderer2Mesh, int2 rendererRange)
|
||||
{
|
||||
|
||||
int submeshIdx = 0;
|
||||
MeshRenderer[] mrArray = new MeshRenderer[1];
|
||||
for (int i = rendererRange.x; i < rendererRange.y; i++)
|
||||
{
|
||||
rd[i].meshFilter.sharedMesh = combinedMesh;
|
||||
mrArray[0] = rd[i].meshRenderer;
|
||||
SerializedObject so = new SerializedObject(mrArray); // Pass this an array instead of the renderer directly, otherwise every time we call this it internally allocates a 1-long array!
|
||||
|
||||
SerializedProperty spFirst = so.FindProperty("m_StaticBatchInfo.firstSubMesh");
|
||||
spFirst.intValue = submeshIdx;
|
||||
|
||||
int submeshCount = rd[i].mesh.subMeshCount;
|
||||
SerializedProperty spCount = so.FindProperty("m_StaticBatchInfo.subMeshCount");
|
||||
spCount.intValue = submeshCount;
|
||||
|
||||
so.ApplyModifiedPropertiesWithoutUndo();
|
||||
|
||||
//SetStaticBatchInfo(rd[i].meshRenderer, submeshIdx, submeshCount);
|
||||
|
||||
GameObject go = rd[i].rendererTransform.gameObject;
|
||||
StaticEditorFlags flags = GameObjectUtility.GetStaticEditorFlags(go);
|
||||
GameObjectUtility.SetStaticEditorFlags(go, flags & ~StaticEditorFlags.BatchingStatic);
|
||||
EditorUtility.SetDirty(go);
|
||||
submeshIdx += submeshCount;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static RendererData[] GetSortedRendererData(List<MeshRenderer> renderers, SBCombineMeshList combiner)
|
||||
{
|
||||
int numRenderers = renderers.Count;
|
||||
int rendererIdx = 0;
|
||||
bool allow32bitIdxBatches = combiner.settings.allow32bitIdx;
|
||||
List<MeshFilter> meshFilters = new List<MeshFilter>(numRenderers);
|
||||
for (int i = 0; i < numRenderers; i++)
|
||||
{
|
||||
MeshRenderer mr = renderers[i];
|
||||
GameObject go = mr.gameObject;
|
||||
if (!GameObjectUtility.AreStaticEditorFlagsSet(go, StaticEditorFlags.BatchingStatic))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MeshFilter mf = go.GetComponent<MeshFilter>();
|
||||
|
||||
if (mf == null || mf.sharedMesh == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!allow32bitIdxBatches && mf.sharedMesh.indexFormat == UnityEngine.Rendering.IndexFormat.UInt32)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mr.sharedMaterials.Length == 0 || mr.sharedMaterials[0] == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
renderers[rendererIdx] = mr;
|
||||
meshFilters.Add(mf);
|
||||
rendererIdx++;
|
||||
}
|
||||
if (numRenderers != rendererIdx) renderers.RemoveRange(rendererIdx, numRenderers - rendererIdx);
|
||||
renderers.TrimExcess();
|
||||
meshFilters.TrimExcess();
|
||||
|
||||
//if (renderers.Count < 2)
|
||||
//{
|
||||
// return new RendererData[0];
|
||||
//}
|
||||
|
||||
RendererData[] sortedData = RendererSort.GetSortedData(renderers, meshFilters);
|
||||
return sortedData;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2cb8a4165af65c14dab23693b66c7ddf
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,102 @@
|
|||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEditor.Build;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
|
||||
namespace SLZ.CustomStaticBatching.Editor
|
||||
{
|
||||
public class SBSettingsSO : ScriptableObject
|
||||
{
|
||||
public const string settingsPath = "Assets/Settings/SLZStaticBatchingSettings.asset";
|
||||
private static SBSettingsSO m_globalSettings;
|
||||
|
||||
const int currentSettingsVersion = 1;
|
||||
public int thisSettingsVersion = currentSettingsVersion;
|
||||
public EditorCombineRendererSettings defaultSettings;
|
||||
public List<EditorCombineRendererSettings> platformOverrideSettings;
|
||||
|
||||
public SBSettingsSO()
|
||||
{
|
||||
defaultSettings = new EditorCombineRendererSettings();
|
||||
platformOverrideSettings = new List<EditorCombineRendererSettings>();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
|
||||
BuildPlatformsMirror.buildPlatformInfo[] buildPlatforms = BuildPlatformsMirror.ValidBuildPlatforms;
|
||||
if (platformOverrideSettings == null || platformOverrideSettings.Count == 0)
|
||||
{
|
||||
platformOverrideSettings = new List<EditorCombineRendererSettings>(buildPlatforms.Length);
|
||||
for (int i = 0; i < buildPlatforms.Length; i++)
|
||||
{
|
||||
EditorCombineRendererSettings ps = new EditorCombineRendererSettings(buildPlatforms[i].buildTarget.TargetName);
|
||||
platformOverrideSettings.Add(ps);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
HashSet<string> oldTargets = new HashSet<string>();
|
||||
for (int i=0; i < platformOverrideSettings.Count; i++)
|
||||
{
|
||||
oldTargets.Add(platformOverrideSettings[i].buildTarget);
|
||||
}
|
||||
for (int i = 0; i < buildPlatforms.Length; i++)
|
||||
{
|
||||
if (!oldTargets.Contains(buildPlatforms[i].buildTarget.TargetName))
|
||||
platformOverrideSettings.Add(new EditorCombineRendererSettings(buildPlatforms[i].buildTarget.TargetName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CombineRendererSettings GetActiveBuildTargetSettings()
|
||||
{
|
||||
BuildTarget target = EditorUserBuildSettings.activeBuildTarget;
|
||||
return GetBuildTargetSettings(target);
|
||||
}
|
||||
|
||||
public CombineRendererSettings GetBuildTargetSettings(BuildTarget target)
|
||||
{
|
||||
string targetName = NamedBuildTarget.FromBuildTargetGroup(BuildPipeline.GetBuildTargetGroup(target)).TargetName;
|
||||
EditorCombineRendererSettings settings = defaultSettings;
|
||||
for (int i = 0; i < platformOverrideSettings.Count; i++)
|
||||
{
|
||||
if (platformOverrideSettings[i].buildTarget == targetName)
|
||||
{
|
||||
if (platformOverrideSettings[i].overrideBuildTarget)
|
||||
{
|
||||
settings = platformOverrideSettings[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return EditorCombineRendererSettings.ApplyProjectSettingsCompression(settings);
|
||||
}
|
||||
public static SBSettingsSO GlobalSettings
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_globalSettings == null)
|
||||
m_globalSettings = AssetDatabase.LoadAssetAtPath<SBSettingsSO>(settingsPath);
|
||||
if (m_globalSettings == null)
|
||||
{
|
||||
m_globalSettings = ScriptableObject.CreateInstance<SBSettingsSO>();
|
||||
string settingsDir = Path.Combine(
|
||||
Application.dataPath,
|
||||
"Settings"
|
||||
);
|
||||
if (!Directory.Exists(settingsDir))
|
||||
{
|
||||
AssetDatabase.CreateFolder("Assets", "Settings");
|
||||
}
|
||||
AssetDatabase.CreateAsset(m_globalSettings, settingsPath);
|
||||
}
|
||||
return m_globalSettings;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9b12913b2973b834ea2a228d4c721a2b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "SLZ.CustomStaticBatching.Editor",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:18839400ad0a58b4cb80e7f1117ebcba",
|
||||
"GUID:1a4848c44fae8ec4a9b68efad47aab4e",
|
||||
"GUID:e0cd26848372d4e5c891c569017e11f1",
|
||||
"GUID:d8b63aba1907145bea998dd612889d6b",
|
||||
"GUID:329b4ccd385744985bf3f83cfd77dfe7",
|
||||
"GUID:2665a8d13d1b3f18800f46e256720795",
|
||||
"GUID:b75d3cd3037d383a8d1e2f9a26d73d8a",
|
||||
"GUID:b3170da2b0ad74e4aad542674030756e"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": true,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 949ea3dadf7e4ce4ba86c06fe3ceb09e
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2f30e1027e09fab4ebe76cf94ae19f24
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,349 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine.UIElements;
|
||||
using NUnit.Framework;
|
||||
using static SLZ.CustomStaticBatching.PackedChannel;
|
||||
using static UnityEngine.UI.InputField;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using UnityEditor.UIElements.Bindings;
|
||||
using System;
|
||||
using static UnityEditor.Search.SearchValue;
|
||||
using System.Reflection;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace SLZ.CustomStaticBatching.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// Menu that controls the global settings used by the static batcher in editor
|
||||
/// </summary>
|
||||
static class SBSettingsProvider
|
||||
{
|
||||
[SettingsProvider]
|
||||
public static SettingsProvider CreateSBSettingsProvider()
|
||||
{
|
||||
|
||||
System.Action<string, VisualElement> uiAction = SBSettingsUI;
|
||||
SettingsProvider provider = new SettingsProvider("Project/SLZCustomStaticBatching", SettingsScope.Project)
|
||||
{
|
||||
label = "Custom Static Batching",
|
||||
activateHandler = uiAction,
|
||||
};
|
||||
return provider;
|
||||
}
|
||||
|
||||
public static void SBSettingsUI(string searchContext, VisualElement rootElement)
|
||||
{
|
||||
|
||||
SerializedObject globalSettings = new SerializedObject(SBSettingsSO.GlobalSettings);
|
||||
VisualElement margin = new VisualElement();
|
||||
margin.style.paddingLeft = 8;
|
||||
margin.style.paddingRight = 8;
|
||||
margin.style.paddingTop = 4;
|
||||
margin.style.flexDirection = FlexDirection.Column;
|
||||
margin.style.flexGrow = 1;
|
||||
margin.style.alignItems = Align.Stretch;
|
||||
margin.style.flexShrink = 1;
|
||||
margin.style.flexBasis = StyleKeyword.Auto;
|
||||
|
||||
Label title = new Label("<b>Custom Static Batching</b>");
|
||||
title.style.fontSize = 20;
|
||||
title.style.paddingBottom = 8;
|
||||
|
||||
|
||||
|
||||
|
||||
VisualElement testButtonStrip = new VisualElement();
|
||||
//testButtonStrip.style.marginTop = 4;
|
||||
testButtonStrip.style.flexShrink = 0;
|
||||
|
||||
testButtonStrip.style.flexDirection = FlexDirection.Row;
|
||||
testButtonStrip.style.justifyContent = Justify.Center;
|
||||
|
||||
|
||||
BuildPlatformsMirror.buildPlatformInfo[] buildPlatforms = BuildPlatformsMirror.ValidBuildPlatforms;
|
||||
|
||||
int numPlatforms = buildPlatforms.Length;
|
||||
TabBox tabBox = new TabBox(numPlatforms + 1);
|
||||
tabBox.tabs[0].text = "Default";
|
||||
|
||||
for (int i = 0; i < numPlatforms; i++)
|
||||
{
|
||||
if (buildPlatforms[i].icon != null)
|
||||
{
|
||||
tabBox.tabs[i + 1].icon = buildPlatforms[i].icon;
|
||||
}
|
||||
else
|
||||
{
|
||||
tabBox.tabs[i + 1].text = buildPlatforms[i].name;
|
||||
}
|
||||
tabBox.tabs[i + 1].tooltip = buildPlatforms[i].tooltip;
|
||||
}
|
||||
|
||||
PlatformSettingsPage[] settingsPage = new PlatformSettingsPage[numPlatforms + 1];
|
||||
|
||||
settingsPage[0] = new PlatformSettingsPage(globalSettings);
|
||||
|
||||
tabBox.tabPages[0].Add(settingsPage[0]);
|
||||
|
||||
for (int i = 1; i <= numPlatforms; i++)
|
||||
{
|
||||
settingsPage[i] = new PlatformSettingsPage(globalSettings, buildPlatforms[i - 1].buildTarget.TargetName);
|
||||
tabBox.tabPages[i].Add(settingsPage[i]);
|
||||
}
|
||||
|
||||
rootElement.Add(margin);
|
||||
margin.Add(title);
|
||||
margin.Add(tabBox);
|
||||
//EditorToolbarUtility.SetupChildrenAsButtonStrip(testButtonStrip);
|
||||
}
|
||||
|
||||
static string[] VtxFormatNames = new string[] { "Use Player Vertex Compression", "UNorm8", "SNorm8", "Float16", "Float32" };
|
||||
|
||||
class PlatformSettingsPage : VisualElement
|
||||
{
|
||||
VisualElement toggleGroup;
|
||||
public readonly int index;
|
||||
public readonly string rootBindingPath;
|
||||
|
||||
public PlatformSettingsPage(SerializedObject settings)
|
||||
{
|
||||
index = -1;
|
||||
rootBindingPath = "defaultSettings.";
|
||||
InitializePage(settings, false);
|
||||
}
|
||||
public PlatformSettingsPage(SerializedObject settings, string buildTargetName)
|
||||
{
|
||||
index = 0;
|
||||
SBSettingsSO globalSettings = SBSettingsSO.GlobalSettings;
|
||||
int settingsCount = globalSettings.platformOverrideSettings.Count;
|
||||
for (; index < settingsCount; index++)
|
||||
{
|
||||
if (buildTargetName == globalSettings.platformOverrideSettings[index].buildTarget)
|
||||
break;
|
||||
}
|
||||
|
||||
rootBindingPath = "platformOverrideSettings.Array.data[" + index + "].";
|
||||
|
||||
|
||||
InitializePage(settings, true);
|
||||
}
|
||||
|
||||
void InitializePage(SerializedObject settings, bool isOverride)
|
||||
{
|
||||
VisualElement root;
|
||||
|
||||
if (isOverride)
|
||||
{
|
||||
SBSettingsSO globalSettings = SBSettingsSO.GlobalSettings;
|
||||
Toggle enableOverride = new Toggle("Override Default Settings");
|
||||
|
||||
enableOverride.tooltip = "Use settings specific to this platform rather than the global defaults";
|
||||
enableOverride.bindingPath = rootBindingPath + "overrideBuildTarget";
|
||||
SetToggleStyle(enableOverride);
|
||||
enableOverride.BindProperty(settings);
|
||||
enableOverride.RegisterValueChangedCallback(OnOverrideToggled);
|
||||
enableOverride.style.paddingBottom = 8;
|
||||
|
||||
style.flexGrow = 1;
|
||||
style.flexDirection = FlexDirection.Column;
|
||||
style.flexGrow = 1;
|
||||
style.alignItems = Align.Stretch;
|
||||
style.flexShrink = 1;
|
||||
style.flexBasis = StyleKeyword.Auto;
|
||||
|
||||
toggleGroup = new VisualElement();
|
||||
toggleGroup.SetEnabled(globalSettings.platformOverrideSettings[index].overrideBuildTarget);
|
||||
|
||||
Add(enableOverride);
|
||||
Add(toggleGroup);
|
||||
root = toggleGroup;
|
||||
}
|
||||
else
|
||||
{
|
||||
root = this;
|
||||
}
|
||||
|
||||
root.style.flexGrow = 1;
|
||||
root.style.flexDirection = FlexDirection.Column;
|
||||
root.style.flexGrow = 1;
|
||||
root.style.alignItems = Align.Stretch;
|
||||
root.style.flexShrink = 1;
|
||||
root.style.flexBasis = StyleKeyword.Auto;
|
||||
|
||||
Toggle highPIdx = new Toggle("Allow 32 bit index combined meshes");
|
||||
highPIdx.bindingPath = rootBindingPath + "settings.allow32bitIdx";
|
||||
highPIdx.tooltip = "Allow making combined meshes of 32-bit index buffer meshes. This sorts the 32 bit meshes into their own set combined meshes, so it should not cause issues";
|
||||
SetToggleStyle(highPIdx);
|
||||
highPIdx.BindProperty(settings);
|
||||
|
||||
IntegerField highPIdxCount = new IntegerField("Max vertices per 32-bit index combined mesh");
|
||||
highPIdxCount.isDelayed = true;
|
||||
highPIdxCount.tooltip = "The maximum number of vertices that can be in a 32-bit index buffer combined mesh. Since a 32-bit index buffer can represent trillions of vertices, its a good idea to arbitrarily put a cap on how large the combined mesh can be";
|
||||
highPIdxCount.bindingPath = rootBindingPath + "settings.maxCombined32Idx";
|
||||
SetToggleStyle(highPIdxCount);
|
||||
highPIdxCount.BindProperty(settings);
|
||||
highPIdxCount.RegisterValueChangedCallback((ChangeEvent<int> e) => { highPIdxCount.value = Math.Max( 0x10000, highPIdxCount.value); });
|
||||
|
||||
Foldout vertexSettings = new Foldout();
|
||||
vertexSettings.text = "Vertex Attribute Format Settings";
|
||||
VisualElement columnLabels = new VisualElement();
|
||||
columnLabels.style.alignSelf = Align.Stretch;
|
||||
columnLabels.style.flexGrow = 1;
|
||||
columnLabels.style.flexDirection = FlexDirection.Row;
|
||||
columnLabels.style.alignItems = Align.Center;
|
||||
columnLabels.style.justifyContent = Justify.SpaceBetween;
|
||||
columnLabels.style.flexShrink = 1;
|
||||
columnLabels.style.flexBasis = StyleKeyword.Auto;
|
||||
|
||||
|
||||
Label channelLabel = new Label("<b>Attribute</b>");
|
||||
channelLabel.style.flexGrow =0.4f;
|
||||
channelLabel.style.flexBasis = 0.4f;
|
||||
channelLabel.style.flexShrink = 0.4f;
|
||||
channelLabel.style.textOverflow = TextOverflow.Ellipsis;
|
||||
channelLabel.style.overflow = Overflow.Hidden;
|
||||
channelLabel.style.unityTextOverflowPosition = TextOverflowPosition.End;
|
||||
channelLabel.tooltip = "Vertex Attribute";
|
||||
|
||||
Label altStreamLabel = new Label("<b>Secondary Stream</b>");
|
||||
altStreamLabel.style.flexGrow = 0.6f;
|
||||
altStreamLabel.style.flexBasis = 0.6f;
|
||||
altStreamLabel.style.flexShrink = 0.6f;
|
||||
altStreamLabel.style.textOverflow = TextOverflow.Ellipsis;
|
||||
altStreamLabel.style.overflow = Overflow.Hidden;
|
||||
altStreamLabel.style.unityTextOverflowPosition = TextOverflowPosition.End;
|
||||
altStreamLabel.tooltip = "Split the vertex buffer and put the marked attributes in a secondary vertex stream. Useful for many tile-based mobile GPUs, you should put any attributes that do not affect the vertex position into the secondary stream";
|
||||
|
||||
Label maxPrecisionLabel = new Label("<b>Maximum Precision</b>");
|
||||
maxPrecisionLabel.style.flexGrow = 1f;
|
||||
maxPrecisionLabel.style.flexBasis = 1f;
|
||||
maxPrecisionLabel.style.flexShrink = 1f;
|
||||
maxPrecisionLabel.style.textOverflow = TextOverflow.Ellipsis;
|
||||
maxPrecisionLabel.style.overflow = Overflow.Hidden;
|
||||
maxPrecisionLabel.style.unityTextOverflowPosition = TextOverflowPosition.End;
|
||||
|
||||
|
||||
columnLabels.Add(channelLabel);
|
||||
columnLabels.Add(altStreamLabel);
|
||||
columnLabels.Add(maxPrecisionLabel);
|
||||
|
||||
vertexSettings.Add(columnLabels);
|
||||
|
||||
// vertex channel compression
|
||||
List<int> normTanFmtsInt = new List<int>() { (int)VtxFormats.Float32, (int)VtxFormats.Float16, (int)VtxFormats.SNorm8, 0};
|
||||
VisualElement normField = VtxCompressionOption("Normal", settings, rootBindingPath, 1, normTanFmtsInt);
|
||||
|
||||
vertexSettings.contentContainer.Add(normField);
|
||||
|
||||
VisualElement tanField = VtxCompressionOption("Tangent", settings, rootBindingPath, 2, normTanFmtsInt);
|
||||
vertexSettings.contentContainer.Add(tanField);
|
||||
|
||||
List<int> colorFmtsInt = new List<int>() { (int)VtxFormats.Float32, (int)VtxFormats.Float16, (int)VtxFormats.UNorm8, 0 };
|
||||
VisualElement colorField = VtxCompressionOption("Color", settings, rootBindingPath, 3, colorFmtsInt);
|
||||
|
||||
vertexSettings.contentContainer.Add(colorField);
|
||||
|
||||
List<int> uvFmtsInt = new List<int>() { (int)VtxFormats.Float32, (int)VtxFormats.Float16, (int)VtxFormats.SNorm8, (int)VtxFormats.UNorm8, 0 };
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
VisualElement uvField = VtxCompressionOption("UV" + i, settings, rootBindingPath, i + 4, uvFmtsInt);
|
||||
vertexSettings.contentContainer.Add(uvField);
|
||||
}
|
||||
|
||||
|
||||
Label NotImplHeader = new Label("\nNot Yet Implemented");
|
||||
|
||||
Toggle splitMultiMeshes = new Toggle("Split Multi-Material Renderers (DANGEROUS!)");
|
||||
splitMultiMeshes.tooltip =
|
||||
"Splits Renderers with multi-material meshes into several single material renderers " +
|
||||
"parented to the original game object. This is slow, and will break scripts and animations " +
|
||||
"that expect to be able to change the materials on or change the state of the whole mesh";
|
||||
splitMultiMeshes.bindingPath = rootBindingPath + "settings.splitMultiMaterialMeshes";
|
||||
splitMultiMeshes.SetEnabled(false);
|
||||
SetToggleStyle(splitMultiMeshes);
|
||||
splitMultiMeshes.BindProperty(settings);
|
||||
|
||||
|
||||
|
||||
root.Add(highPIdx);
|
||||
root.Add(highPIdxCount);
|
||||
root.Add(vertexSettings);
|
||||
root.Add(NotImplHeader);
|
||||
root.Add(splitMultiMeshes);
|
||||
}
|
||||
|
||||
private void OnOverrideToggled(ChangeEvent<bool> evt)
|
||||
{
|
||||
toggleGroup.SetEnabled(evt.newValue);
|
||||
}
|
||||
|
||||
private VisualElement VtxCompressionOption(string name, SerializedObject settings, string rootPath, int index, List<int> options)
|
||||
{
|
||||
VisualElement vtxOptions = new VisualElement();
|
||||
|
||||
vtxOptions.style.flexGrow = 1;
|
||||
vtxOptions.style.flexDirection = FlexDirection.Row;
|
||||
vtxOptions.style.flexGrow = 1;
|
||||
vtxOptions.style.alignItems = Align.Stretch;
|
||||
vtxOptions.style.flexShrink = 1;
|
||||
vtxOptions.style.flexBasis = StyleKeyword.Auto;
|
||||
|
||||
Label label = new Label(name);
|
||||
label.style.flexGrow = 0.4f;
|
||||
label.style.flexBasis = 0.4f;
|
||||
label.style.flexShrink = 0.4f;
|
||||
label.style.textOverflow = TextOverflow.Ellipsis;
|
||||
label.style.overflow = Overflow.Hidden;
|
||||
label.style.unityTextOverflowPosition = TextOverflowPosition.End;
|
||||
|
||||
Toggle toggle = new Toggle();
|
||||
toggle.bindingPath = rootPath + "settings.altStream.Array.data[" + index + "]";
|
||||
toggle.BindProperty(settings);
|
||||
toggle.style.flexGrow = 0.6f;
|
||||
toggle.style.flexBasis = 0.6f;
|
||||
toggle.style.flexShrink = 0.6f;
|
||||
toggle.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
PopupField<int> popup = new PopupField<int>();
|
||||
popup.formatListItemCallback = (int b) => { return VtxFormatNames[b]; };
|
||||
popup.formatSelectedValueCallback = (int b) => { return VtxFormatNames[b]; };
|
||||
popup.choices = options;
|
||||
popup.bindingPath = rootPath + "settings.serializedVtxFormats.Array.data[" + index + "]";
|
||||
popup.BindProperty(settings);
|
||||
popup.style.flexGrow = 1f;
|
||||
popup.style.flexBasis = 1;
|
||||
popup.style.flexShrink = 1f;
|
||||
popup.style.flexDirection = FlexDirection.Row;
|
||||
|
||||
vtxOptions.Add(label);
|
||||
vtxOptions.Add(toggle);
|
||||
vtxOptions.Add(popup);
|
||||
//popup.label = name;
|
||||
|
||||
return vtxOptions;
|
||||
}
|
||||
}
|
||||
static void SetToggleStyle(VisualElement el)
|
||||
{
|
||||
el.style.alignItems = Align.Stretch;
|
||||
el.style.justifyContent = Justify.FlexStart;
|
||||
|
||||
VisualElement label = el.ElementAt(0);
|
||||
label.style.flexGrow = 1f;
|
||||
label.style.flexBasis = 1f;
|
||||
label.style.flexShrink = 1f;
|
||||
label.style.textOverflow = TextOverflow.Ellipsis;
|
||||
label.style.overflow = Overflow.Hidden;
|
||||
label.style.unityTextOverflowPosition = TextOverflowPosition.End;
|
||||
VisualElement checkmark = el.ElementAt(1);
|
||||
checkmark.style.flexGrow = 1f;
|
||||
checkmark.style.flexBasis = 1;
|
||||
checkmark.style.flexShrink = 0.25f;
|
||||
checkmark.style.flexDirection = FlexDirection.Row;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 95b1b62db7abdf6458de1c705448b2ba
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,296 @@
|
|||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
|
||||
namespace SLZ.CustomStaticBatching
|
||||
{
|
||||
public class TabBox : VisualElement
|
||||
{
|
||||
public int activeBox = 0;
|
||||
public Tab[] tabs;
|
||||
public TabPage[] tabPages;
|
||||
|
||||
|
||||
public TabBox(int tabCount)
|
||||
{
|
||||
this.tabs = new Tab[tabCount];
|
||||
this.tabPages = new TabPage[tabCount];
|
||||
VisualElement tabHeader = new VisualElement();
|
||||
int lastTab = tabCount - 1;
|
||||
for (int i = 0; i < tabCount; i++)
|
||||
{
|
||||
tabs[i] = new Tab();
|
||||
if (i == 0)
|
||||
{
|
||||
tabs[i].SetActiveState(true);
|
||||
tabs[i].SetLeftEnd();
|
||||
}
|
||||
if (i == lastTab) tabs[i].SetRightEnd();
|
||||
tabs[i].index = i;
|
||||
tabs[i].tabParent = this;
|
||||
tabHeader.Add(tabs[i]);
|
||||
}
|
||||
tabHeader.style.flexShrink = 0;
|
||||
tabHeader.style.flexDirection = FlexDirection.Row;
|
||||
tabHeader.style.justifyContent = Justify.Center;
|
||||
Add(tabHeader);
|
||||
for (int i = 0; i < tabCount; i++)
|
||||
{
|
||||
tabPages[i] = new TabPage();
|
||||
if (i != 0) tabPages[i].style.display = DisplayStyle.None;
|
||||
Add(tabPages[i]);
|
||||
}
|
||||
style.flexShrink = 0;
|
||||
style.flexDirection = FlexDirection.Column;
|
||||
style.justifyContent = Justify.Center;
|
||||
|
||||
}
|
||||
|
||||
public void SetActiveTab(int tabIndex)
|
||||
{
|
||||
for (int i = 0; i < tabs.Length; i++)
|
||||
{
|
||||
if (tabs[i].index == tabIndex)
|
||||
{
|
||||
tabPages[i].style.display = DisplayStyle.Flex;
|
||||
tabs[i].SetActiveState(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
tabPages[i].style.display = DisplayStyle.None;
|
||||
tabs[i].SetActiveState(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class TabStyle
|
||||
{
|
||||
public const int borderWidth = 1;
|
||||
public const int borderRadius = 3;
|
||||
|
||||
// --unity-colors-app_toolbar_button-background-hover
|
||||
public const uint backgroundDark = 0xFF424242;
|
||||
public const uint backgroundLight = 0xFFBBBBBB;
|
||||
public static StyleColor background = EditorGUIUtility.isProSkin ? intToColor(backgroundDark) : intToColor(backgroundLight);
|
||||
|
||||
// --unity-colors-tab-background
|
||||
public const uint backgroundDisabledDark = 0xFF353535;
|
||||
public const uint backgroundDisabledLight = 0xFFB6B6B6;
|
||||
public static StyleColor backgroundDisabled = EditorGUIUtility.isProSkin ? intToColor(backgroundDisabledDark) : intToColor(backgroundDisabledLight);
|
||||
|
||||
// --unity-colors-tab-background-hover
|
||||
public const uint hoverDisabledDark = 0xFF303030;
|
||||
public const uint hoverDisabledLight = 0xFFB0B0B0;
|
||||
public static StyleColor hoverDisabled = EditorGUIUtility.isProSkin ? intToColor(hoverDisabledDark) : intToColor(hoverDisabledLight);
|
||||
|
||||
|
||||
public const uint borderDark = 0xFF232323;
|
||||
public const uint borderLight = 0xFF999999;
|
||||
public static StyleColor border = EditorGUIUtility.isProSkin ? intToColor(borderDark) : intToColor(borderLight);
|
||||
|
||||
public static StyleColor intToColor(uint hexColor)
|
||||
{
|
||||
return new StyleColor(((Color)UnsafeUtility.As<uint, Color32>(ref hexColor)));
|
||||
}
|
||||
}
|
||||
|
||||
public class Tab : VisualElement
|
||||
{
|
||||
|
||||
bool isActive;
|
||||
bool hover;
|
||||
bool click;
|
||||
|
||||
Color32 colorInactive;
|
||||
Color32 colorActive;
|
||||
|
||||
Color32 colorInactiveHighlighted;
|
||||
Color32 colorActiveHighlighted;
|
||||
|
||||
public TabBox tabParent;
|
||||
public int index;
|
||||
|
||||
string m_text;
|
||||
Label m_label;
|
||||
|
||||
public string text
|
||||
{
|
||||
get => m_text;
|
||||
set
|
||||
{
|
||||
if (m_label == null)
|
||||
{
|
||||
m_label = new Label();
|
||||
Add(m_label);
|
||||
}
|
||||
m_label.text = value;
|
||||
}
|
||||
}
|
||||
|
||||
Image m_icon;
|
||||
public Texture icon
|
||||
{
|
||||
get => m_icon?.image;
|
||||
set
|
||||
{
|
||||
if (m_icon == null)
|
||||
{
|
||||
m_icon = new Image() { image = value };
|
||||
Add(m_icon);
|
||||
}
|
||||
else
|
||||
m_icon.image = value;
|
||||
}
|
||||
}
|
||||
|
||||
public Tab(string text)
|
||||
{
|
||||
Init();
|
||||
Add(new Label(text));
|
||||
}
|
||||
|
||||
public Tab(Texture2D image)
|
||||
{
|
||||
Init();
|
||||
Add(new Image() { image = image });
|
||||
}
|
||||
|
||||
public Tab()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
void Init()
|
||||
{
|
||||
|
||||
SetInitialStyle();
|
||||
RegisterCallback<MouseEnterEvent>(delegate { hover = true; UpdateState(); });
|
||||
RegisterCallback<MouseLeaveEvent>(delegate { hover = false; UpdateState(); });
|
||||
RegisterCallback<MouseDownEvent>(delegate { click = true; UpdateState(); });
|
||||
RegisterCallback<MouseUpEvent>(delegate { click = false; SendEventToTab(); UpdateState(); });
|
||||
}
|
||||
|
||||
void SendEventToTab()
|
||||
{
|
||||
tabParent.SetActiveTab(index);
|
||||
}
|
||||
|
||||
void UpdateState()
|
||||
{
|
||||
if (isActive)
|
||||
{
|
||||
style.borderBottomWidth = 0;
|
||||
if (click)
|
||||
{
|
||||
style.backgroundColor = TabStyle.background;
|
||||
}
|
||||
else if (hover)
|
||||
{
|
||||
style.backgroundColor = TabStyle.background;
|
||||
}
|
||||
else
|
||||
{
|
||||
style.backgroundColor = TabStyle.background;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
style.borderBottomWidth = TabStyle.borderWidth;
|
||||
if (click)
|
||||
{
|
||||
style.backgroundColor = TabStyle.backgroundDisabled;
|
||||
}
|
||||
else if (hover)
|
||||
{
|
||||
style.backgroundColor = TabStyle.hoverDisabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
style.backgroundColor = TabStyle.backgroundDisabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetInitialStyle()
|
||||
{
|
||||
style.backgroundColor = TabStyle.backgroundDisabled;
|
||||
style.borderTopColor = style.borderLeftColor = style.borderRightColor = style.borderBottomColor = TabStyle.border;
|
||||
style.borderTopLeftRadius = 0;
|
||||
style.borderTopRightRadius = 0;
|
||||
style.flexGrow = 1f;
|
||||
style.flexBasis = 0;
|
||||
style.borderBottomWidth = TabStyle.borderWidth;
|
||||
style.borderLeftWidth = TabStyle.borderWidth;
|
||||
style.borderRightWidth = 0;
|
||||
style.borderTopWidth = TabStyle.borderWidth;
|
||||
style.marginLeft = 0;
|
||||
style.marginRight = 0;
|
||||
style.paddingTop = 2;
|
||||
style.paddingBottom = 2;
|
||||
|
||||
style.alignContent = Align.Center;
|
||||
style.alignItems = Align.Center;
|
||||
}
|
||||
|
||||
internal void SetLeftEnd()
|
||||
{
|
||||
style.borderTopLeftRadius = TabStyle.borderRadius;
|
||||
}
|
||||
|
||||
internal void SetRightEnd()
|
||||
{
|
||||
style.borderTopRightRadius = TabStyle.borderRadius;
|
||||
style.borderRightWidth = TabStyle.borderWidth;
|
||||
}
|
||||
|
||||
|
||||
public void SetActiveState(bool state)
|
||||
{
|
||||
isActive = state;
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public class TabPage : VisualElement
|
||||
{
|
||||
public TabPage()
|
||||
{
|
||||
SetInitialStyle();
|
||||
}
|
||||
void SetInitialStyle()
|
||||
{
|
||||
style.backgroundColor = TabStyle.background;
|
||||
style.borderTopColor = style.borderLeftColor = style.borderRightColor = style.borderBottomColor = TabStyle.border;
|
||||
style.borderTopLeftRadius = 0;
|
||||
style.borderTopRightRadius = 0;
|
||||
style.borderBottomLeftRadius = TabStyle.borderRadius;
|
||||
style.borderBottomRightRadius = TabStyle.borderRadius;
|
||||
style.flexGrow = 1f;
|
||||
style.flexBasis = 0;
|
||||
style.borderBottomWidth = TabStyle.borderWidth;
|
||||
style.borderLeftWidth = TabStyle.borderWidth;
|
||||
style.borderRightWidth = TabStyle.borderWidth;
|
||||
style.borderTopWidth = 0;
|
||||
style.marginLeft = 0;
|
||||
style.marginRight = 0;
|
||||
style.paddingTop = 8;
|
||||
style.paddingLeft = 8;
|
||||
style.paddingRight = 8;
|
||||
style.paddingBottom = 4;
|
||||
|
||||
style.flexGrow = 1;
|
||||
style.flexDirection = FlexDirection.Column;
|
||||
style.flexGrow = 1;
|
||||
style.alignItems = Align.Stretch;
|
||||
style.flexShrink = 1;
|
||||
style.flexBasis = StyleKeyword.Auto;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4f046e93f79a6e0429e1323bf887d52c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Loading…
Add table
Add a link
Reference in a new issue