initial commit

This commit is contained in:
Jo 2025-01-07 02:06:59 +01:00
parent 6715289efe
commit 788c3389af
37645 changed files with 2526849 additions and 80 deletions

View file

@ -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();
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: de0e71e5b2b399343a4870c647cb0e12
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b9f9863fa20dd8e4fbae170139cc62d9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 278aaa5095519464fa745c2e05185d5d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9f9b2a05487f93a458dee382a3776abd
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 355abbf7ca534a2499e389b1424761a9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2a44935398e27e40b2e220cabe27321
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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);
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 336ca034bcbdb604eb4b9bbcbc251c65
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2cb8a4165af65c14dab23693b66c7ddf
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 9b12913b2973b834ea2a228d4c721a2b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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
}

View file

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 949ea3dadf7e4ce4ba86c06fe3ceb09e
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2f30e1027e09fab4ebe76cf94ae19f24
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 95b1b62db7abdf6458de1c705448b2ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -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;
}
}
}
}

View file

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4f046e93f79a6e0429e1323bf887d52c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: