using System.Collections.Generic; using UnityEngine; using UnityEngine.Rendering; namespace Assets.ThoMagic.Renderer { public class ObjectData { public readonly GameObject GameObject; public readonly HashSet Owners = new HashSet(); public readonly uint prefabId; public float[] LodSizes; public float[] LodTransitions; public LOD[] LODs; public Vector4[] lods = new Vector4[8]; public uint[] indirectArgsPerLodOffsets = new uint[8]; public uint[] indirectArgsPerLodCounts = new uint[8]; public uint[] indirectArgsPerLodWithShadowsCounts = new uint[8]; public List indirectArgsPerSubmeshOffsets = new List(); public uint lodCount = 1; public uint fadeLods = 0; public uint indirectArgsCount = 1; public uint count = 0; public IInstanceRenderSettings Settings; public List indirectDrawIndexedArgs = new List(); public DrawGroup[] drawGroups; public DrawGroup[] drawShadowGroups; public int contentHashCache; private bool _isDisposed; public ObjectData(GameObject gameObject, IInstanceRenderSettings settings) { GameObject = gameObject; Settings = settings; var lodGroup = GameObject.GetComponent(); LOD[] lodArray; if (lodGroup == null) lodArray = new LOD[1] { new LOD(0.0001f, GameObject.GetComponentsInChildren()) }; else lodArray = lodGroup.GetLODs(); LODs = lodArray; LodSizes = new float[lodArray.Length]; LodTransitions = new float[lodArray.Length]; lodCount = (uint)lodArray.Length; if (lodGroup != null && lodGroup.fadeMode == LODFadeMode.CrossFade) fadeLods = 1; for (int index = 0; index < lodArray.Length; ++index) { LodSizes[index] = CalculateBoundsForRenderers(lodArray[index].renderers); LodTransitions[index] = lodArray[index].screenRelativeTransitionHeight; } if (RendererPool.prefabDataFreeSlot.Count > 0) { prefabId = RendererPool.prefabDataFreeSlot.Dequeue(); RendererPool.prefabData[(int)prefabId] = new PrefabData(); } else { prefabId = (uint)RendererPool.prefabData.Count; RendererPool.prefabData.Add(new PrefabData()); } RendererPool.rebuildPrefabs = true; indirectArgsCount = BuildRenderer(indirectDrawIndexedArgs); } public void Dispose() { if (this._isDisposed) return; foreach (var drawGroup in drawGroups) { drawGroup?.Dispose(); } foreach (var drawGroup in drawShadowGroups) { drawGroup?.Dispose(); } this._isDisposed = true; } private uint BuildRenderer(List indirectDrawIndexedArgs) { uint indirectArgsCount = 0; drawGroups = new DrawGroup[lodCount]; drawShadowGroups = new DrawGroup[lodCount]; for (int index = 0; index < lodCount; ++index) { uint indirectArgsPerLodCount = 0; foreach (var renderer in LODs[index].renderers) { if (renderer != null) { if (renderer is MeshRenderer) { var meshFilter = renderer.GetComponent(); if (meshFilter != null) { var sharedMesh = meshFilter.sharedMesh; var sharedMaterials = renderer.sharedMaterials; if (sharedMesh != null && sharedMaterials != null && sharedMaterials.Length == sharedMesh.subMeshCount) { if (drawGroups[index] == null) drawGroups[index] = new DrawGroup(index); RenderParams renderParams1 = new RenderParams(); renderParams1.layer = renderer.gameObject.layer; renderParams1.receiveShadows = renderer.receiveShadows; renderParams1.rendererPriority = renderer.rendererPriority; renderParams1.renderingLayerMask = renderer.renderingLayerMask; renderParams1.shadowCastingMode = ShadowCastingMode.Off; renderParams1.reflectionProbeUsage = renderer.reflectionProbeUsage; renderParams1.motionVectorMode = renderer.motionVectorGenerationMode; var count = drawGroups[index].Add(sharedMesh, sharedMaterials, Matrix4x4.identity, //renderer.transform.localToWorldMatrix, indirectArgsCount, indirectDrawIndexedArgs, in renderParams1); indirectArgsPerLodCount += count; } } } else if (renderer is BillboardRenderer billboardRenderer) { if (billboardRenderer.billboard != null && billboardRenderer.billboard.material != null) { if (drawGroups[index] == null) drawGroups[index] = new DrawGroup(index); RenderParams renderParams1 = new RenderParams(); renderParams1.layer = renderer.gameObject.layer; renderParams1.receiveShadows = renderer.receiveShadows; renderParams1.rendererPriority = renderer.rendererPriority; renderParams1.renderingLayerMask = renderer.renderingLayerMask; renderParams1.shadowCastingMode = ShadowCastingMode.Off; renderParams1.reflectionProbeUsage = renderer.reflectionProbeUsage; renderParams1.motionVectorMode = renderer.motionVectorGenerationMode; var count = drawGroups[index].Add(billboardRenderer.billboard, billboardRenderer.material, renderer.transform.localToWorldMatrix, indirectArgsCount, indirectDrawIndexedArgs, in renderParams1); indirectArgsPerLodCount += count; } } } } indirectArgsPerLodCounts[index] = indirectArgsPerLodCount; uint indirectArgsPerLodCountShadowOffset = indirectArgsPerLodCount; foreach (var renderer in LODs[index].renderers) { if (renderer != null) { if (renderer is MeshRenderer) { var meshFilter = renderer.GetComponent(); if (meshFilter != null) { var sharedMesh = meshFilter.sharedMesh; var sharedMaterials = renderer.sharedMaterials; if (sharedMesh != null && sharedMaterials != null && sharedMaterials.Length == sharedMesh.subMeshCount) { if (renderer.shadowCastingMode > 0) { if (drawShadowGroups[index] == null) drawShadowGroups[index] = new DrawGroup(index); var renderParams1 = new RenderParams(); renderParams1.layer = renderer.gameObject.layer; renderParams1.shadowCastingMode = ShadowCastingMode.ShadowsOnly; renderParams1.renderingLayerMask = renderer.renderingLayerMask; renderParams1.rendererPriority = renderer.rendererPriority; indirectArgsPerLodCount += drawShadowGroups[index].Add(sharedMesh, sharedMaterials, Matrix4x4.identity, //renderer.transform.localToWorldMatrix, indirectArgsPerLodCountShadowOffset + indirectArgsCount, indirectDrawIndexedArgs, in renderParams1); } else { //indirectArgsPerLodCount += (uint)sharedMesh.subMeshCount; } } } } else if (renderer is BillboardRenderer billboardRenderer) { if (renderer.shadowCastingMode > 0) { if (billboardRenderer.billboard != null && billboardRenderer.billboard.material != null) { if (drawShadowGroups[index] == null) drawShadowGroups[index] = new DrawGroup(index); var renderParams1 = new RenderParams(); renderParams1.layer = renderer.gameObject.layer; renderParams1.shadowCastingMode = ShadowCastingMode.ShadowsOnly; renderParams1.renderingLayerMask = renderer.renderingLayerMask; renderParams1.rendererPriority = renderer.rendererPriority; indirectArgsPerLodCount += drawShadowGroups[index].Add(billboardRenderer.billboard, billboardRenderer.material, renderer.transform.localToWorldMatrix, indirectArgsPerLodCountShadowOffset + indirectArgsCount, indirectDrawIndexedArgs, in renderParams1); } else { //indirectArgsPerLodCount += 1; } } } } } indirectArgsPerLodWithShadowsCounts[index] = indirectArgsPerLodCount; indirectArgsCount += indirectArgsPerLodCount; } return indirectArgsCount; } private static float CalculateBoundsForRenderers(UnityEngine.Renderer[] renderers) { Bounds bounds = new Bounds(); bool first = true; foreach (var renderer in renderers) { if (renderer != null) { if (first) { bounds = renderer.bounds; first = false; } else bounds.Encapsulate(renderer.bounds); } } return Mathf.Max(Mathf.Max(bounds.size.x, bounds.size.y), bounds.size.z); } public override int GetHashCode() { return GameObject.GetHashCode(); } } }