folder structure
parent
10037724e2
commit
ee5b330904
|
|
@ -1,128 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class BillboardDrawCall : DrawCall
|
|
||||||
{
|
|
||||||
private static Mesh _billboardMesh;
|
|
||||||
|
|
||||||
private static Mesh GetBillboardMesh()
|
|
||||||
{
|
|
||||||
if (_billboardMesh == null)
|
|
||||||
{
|
|
||||||
_billboardMesh = new Mesh();
|
|
||||||
_billboardMesh.name = "Billboard Mesh";
|
|
||||||
_billboardMesh.vertices = new Vector3[6];
|
|
||||||
_billboardMesh.triangles = new int[6] {
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
5
|
|
||||||
};
|
|
||||||
|
|
||||||
_billboardMesh.SetUVs(0,new Vector2[6]
|
|
||||||
{
|
|
||||||
new Vector2(0.0f, 0.0f),
|
|
||||||
new Vector2(0.0f, 1f),
|
|
||||||
new Vector2(1f, 1f),
|
|
||||||
new Vector2(0.0f, 0.0f),
|
|
||||||
new Vector2(1f, 1f),
|
|
||||||
new Vector2(1f, 0.0f)
|
|
||||||
});
|
|
||||||
|
|
||||||
_billboardMesh.SetUVs(1, new Vector4[6]
|
|
||||||
{
|
|
||||||
new Vector4(1f, 1f, 0.0f, 0.0f),
|
|
||||||
new Vector4(1f, 1f, 0.0f, 0.0f),
|
|
||||||
new Vector4(1f, 1f, 0.0f, 0.0f),
|
|
||||||
new Vector4(1f, 1f, 0.0f, 0.0f),
|
|
||||||
new Vector4(1f, 1f, 0.0f, 0.0f),
|
|
||||||
new Vector4(1f, 1f, 0.0f, 0.0f)
|
|
||||||
});
|
|
||||||
|
|
||||||
_billboardMesh.UploadMeshData(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _billboardMesh;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BillboardDrawCall(
|
|
||||||
int lodNr,
|
|
||||||
BillboardAsset billboardAsset,
|
|
||||||
Material material,
|
|
||||||
Matrix4x4 localToWorldMatrix,
|
|
||||||
uint baseIndirectArgsIndex,
|
|
||||||
List<GraphicsBuffer.IndirectDrawIndexedArgs> indirectDrawIndexedArgs,
|
|
||||||
in RenderParams renderParams)
|
|
||||||
: base(lodNr,
|
|
||||||
GetBillboardMesh(),
|
|
||||||
new Material[1]{ material },
|
|
||||||
localToWorldMatrix,
|
|
||||||
baseIndirectArgsIndex,
|
|
||||||
indirectDrawIndexedArgs,
|
|
||||||
in renderParams)
|
|
||||||
{
|
|
||||||
if (billboardAsset == null)
|
|
||||||
throw new ArgumentNullException(nameof(billboardAsset));
|
|
||||||
|
|
||||||
SetBillboardPerBatch(billboardAsset);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void Dispose() {
|
|
||||||
UnityEngine.Object.Destroy(_billboardMesh);
|
|
||||||
base.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Draw(Camera camera, ObjectData obj, GraphicsBuffer indirectDrawIndexedArgs)
|
|
||||||
{
|
|
||||||
SetBillboardPerCamera(camera);
|
|
||||||
base.Draw(camera, obj, indirectDrawIndexedArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetBillboardPerCamera(Camera camera)
|
|
||||||
{
|
|
||||||
Vector3 position = camera.transform.position;
|
|
||||||
Vector3 vector3_1 = camera.transform.forward * -1f;
|
|
||||||
vector3_1.y = 0.0f;
|
|
||||||
vector3_1.Normalize();
|
|
||||||
Vector3 vector3_2 = camera.transform.right * -1f;
|
|
||||||
vector3_2.y = 0.0f;
|
|
||||||
vector3_2.Normalize();
|
|
||||||
float num1 = Mathf.Atan2(vector3_1.z, vector3_1.x);
|
|
||||||
float num2 = num1 + (num1 < 0.0f ? 6.283185f : 0.0f);
|
|
||||||
Vector4 vector4;
|
|
||||||
vector4.x = position.x;
|
|
||||||
vector4.y = position.y;
|
|
||||||
vector4.z = position.z;
|
|
||||||
vector4.w = num2;
|
|
||||||
for (int index = 0; index < RenderParams.Length; ++index)
|
|
||||||
{
|
|
||||||
RenderParams[index].matProps.SetVector("unity_BillboardNormal", vector3_1);
|
|
||||||
RenderParams[index].matProps.SetVector("unity_BillboardTangent", vector3_2);
|
|
||||||
RenderParams[index].matProps.SetVector("unity_BillboardCameraParams", vector4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetBillboardPerBatch(BillboardAsset asset)
|
|
||||||
{
|
|
||||||
Vector4 vector4_1 = Vector4.zero;
|
|
||||||
vector4_1.x = asset.imageCount;
|
|
||||||
vector4_1.y = 1.0f / (6.28318548202515f / asset.imageCount);
|
|
||||||
Vector4 vector4_2 = Vector4.zero;
|
|
||||||
vector4_2.x = asset.width;
|
|
||||||
vector4_2.y = asset.height;
|
|
||||||
vector4_2.z = asset.bottom;
|
|
||||||
Vector4[] imageTexCoords = asset.GetImageTexCoords();
|
|
||||||
for (int index = 0; index < RenderParams.Length; ++index)
|
|
||||||
{
|
|
||||||
RenderParams[index].matProps.SetVector("unity_BillboardInfo", vector4_1);
|
|
||||||
RenderParams[index].matProps.SetVector("unity_BillboardSize", vector4_2);
|
|
||||||
RenderParams[index].matProps.SetVectorArray("unity_BillboardImageTexCoords", imageTexCoords);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 462b041640bdab24f87e7de6f8a16fb3
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,161 +0,0 @@
|
||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
[DisallowMultipleComponent]
|
|
||||||
[ExecuteAlways]
|
|
||||||
public class ThoMagicRendererCameraSettings : MonoBehaviour, IInstanceRenderSettings
|
|
||||||
{
|
|
||||||
[SerializeField]
|
|
||||||
private bool _supported;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideSupported;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _render;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideRendering;
|
|
||||||
[Min(0.0f)]
|
|
||||||
[SerializeField]
|
|
||||||
private float _renderDistance;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideRenderDistance;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _renderShadows;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideRenderShadows;
|
|
||||||
[Min(0.0f)]
|
|
||||||
[SerializeField]
|
|
||||||
private float _shadowDistance;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideShadowDistance;
|
|
||||||
[Range(0.01f, 1f)]
|
|
||||||
[SerializeField]
|
|
||||||
private float _densityInDistance;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideDensityInDistance;
|
|
||||||
[SerializeField]
|
|
||||||
private Vector2 _densityInDistanceFalloff;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideDensityInDistanceFalloff;
|
|
||||||
private ReflectionProbe _reflectionProbe;
|
|
||||||
|
|
||||||
public InstanceRenderSettings Settings => new InstanceRenderSettings()
|
|
||||||
{
|
|
||||||
Supported = !_overrideSupported || _supported,
|
|
||||||
Render = !_overrideRendering || _render,
|
|
||||||
RenderDistance = _overrideRenderDistance ? _renderDistance : -1f,
|
|
||||||
ShadowDistance = _overrideShadowDistance ? _shadowDistance : -1f,
|
|
||||||
Shadows = !_overrideRenderShadows || _renderShadows,
|
|
||||||
DensityInDistance = _overrideDensityInDistance ? _densityInDistance : 1f,
|
|
||||||
DensityInDistanceFalloff = _overrideDensityInDistanceFalloff ? _densityInDistanceFalloff : Vector2.zero
|
|
||||||
};
|
|
||||||
|
|
||||||
public bool? Supported
|
|
||||||
{
|
|
||||||
get => !_overrideSupported ? new bool?() : new bool?(_supported);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_overrideSupported = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
_supported = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool? Render
|
|
||||||
{
|
|
||||||
get => !_overrideRendering ? new bool?() : new bool?(_render);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_overrideRendering = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
_render = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float? RenderDistance
|
|
||||||
{
|
|
||||||
get => !_overrideRenderDistance ? new float?() : new float?(_renderDistance);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_overrideRenderDistance = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
_renderDistance = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool? RenderShadows
|
|
||||||
{
|
|
||||||
get => !_overrideRenderShadows ? new bool?() : new bool?(_renderShadows);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_overrideRenderShadows = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
_renderShadows = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float? ShadowDistance
|
|
||||||
{
|
|
||||||
get => !_overrideShadowDistance ? new float?() : new float?(_shadowDistance);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_overrideShadowDistance = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
_shadowDistance = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float? DensityInDistance
|
|
||||||
{
|
|
||||||
get => !_overrideDensityInDistance ? new float?() : new float?(_densityInDistance);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_overrideDensityInDistance = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
_densityInDistance = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2? DensityInDistanceFalloff
|
|
||||||
{
|
|
||||||
get => !_overrideDensityInDistanceFalloff ? new Vector2?() : new Vector2?(_densityInDistanceFalloff);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_overrideDensityInDistanceFalloff = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
_densityInDistanceFalloff = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEnable()
|
|
||||||
{
|
|
||||||
_reflectionProbe = GetComponent<ReflectionProbe>();
|
|
||||||
if (_reflectionProbe == null)
|
|
||||||
return;
|
|
||||||
CameraRenderer.ReflectionProbeSettings = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDisable()
|
|
||||||
{
|
|
||||||
if (_reflectionProbe == null || CameraRenderer.ReflectionProbeSettings as ThoMagicRendererCameraSettings == this)
|
|
||||||
return;
|
|
||||||
|
|
||||||
CameraRenderer.ReflectionProbeSettings = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnValidate()
|
|
||||||
{
|
|
||||||
if (_reflectionProbe == null || !Application.isEditor)
|
|
||||||
return;
|
|
||||||
_reflectionProbe.RenderProbe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 937c543b6caf2384898a535da09c337d
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,676 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Threading;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Rendering;
|
|
||||||
using UnityEngine.XR;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class CameraRenderer : IDisposable
|
|
||||||
{
|
|
||||||
internal static int nextCameraRendererId = 0;
|
|
||||||
int cameraRendererId;
|
|
||||||
public static IInstanceRenderSettings ReflectionProbeSettings;
|
|
||||||
public readonly Camera Camera;
|
|
||||||
public IInstanceRenderSettings cameraSettings;
|
|
||||||
|
|
||||||
private readonly bool _singlePassInstanced = false;
|
|
||||||
|
|
||||||
private int cachedBufferHash;
|
|
||||||
|
|
||||||
private Vector3 _origin;
|
|
||||||
private Light _mainLight;
|
|
||||||
|
|
||||||
List<InstanceStreamer> visibleStreams = new List<InstanceStreamer>();
|
|
||||||
|
|
||||||
List<PrefabData> prefabData = new List<PrefabData>();
|
|
||||||
List<GraphicsBuffer.IndirectDrawIndexedArgs> indirectDrawIndexedArgs = new List<GraphicsBuffer.IndirectDrawIndexedArgs>();
|
|
||||||
Dictionary<int, float> objRenderDistanceCache = new Dictionary<int, float>();
|
|
||||||
Dictionary<uint, InstanceRenderSettings> appliedSettings = new Dictionary<uint, InstanceRenderSettings>();
|
|
||||||
List<uint> cullableIndexes = new List<uint>();
|
|
||||||
|
|
||||||
public Camera ReferenceCamera { get; private set; }
|
|
||||||
|
|
||||||
private CommandBuffer commandBuffer;
|
|
||||||
private ComputeShader instancingShader;
|
|
||||||
private ComputeBuffer prefabBuffer;
|
|
||||||
|
|
||||||
private GraphicsBuffer indirectDrawIndexedArgsBuffer;
|
|
||||||
private ComputeBuffer metaBuffer;
|
|
||||||
private ComputeBuffer cullableIndexesBuffer;
|
|
||||||
private ComputeBuffer visibleIndexesBuffer;
|
|
||||||
private ComputeBuffer visibleShadowIndexesBuffer;
|
|
||||||
|
|
||||||
private float cachedFov;
|
|
||||||
private float cachedBias;
|
|
||||||
private bool lodInvalid;
|
|
||||||
|
|
||||||
SceneRenderSettings sceneSettings = new SceneRenderSettings();
|
|
||||||
private Plane[] _cachedFrustumPlanes;
|
|
||||||
private Vector4[] _cachedShaderFrustumPlanes;
|
|
||||||
internal bool instanceCountChanged = true;
|
|
||||||
internal bool rebuildPrefabs = true;
|
|
||||||
|
|
||||||
private int cullShaderId = 0, clearShaderId = 0;
|
|
||||||
|
|
||||||
private uint maxInstancesCount = 0;
|
|
||||||
private uint indexBufferOffset = 0;
|
|
||||||
|
|
||||||
private int lastCamSettingsHash = 0;
|
|
||||||
|
|
||||||
public CameraRenderer(Camera camera)
|
|
||||||
{
|
|
||||||
commandBuffer = new CommandBuffer();
|
|
||||||
cameraRendererId = Interlocked.Increment(ref nextCameraRendererId);
|
|
||||||
|
|
||||||
instancingShader = UnityEngine.Object.Instantiate(Resources.Load<ComputeShader>("ThoMagic Renderer Instancing"));
|
|
||||||
instancingShader.hideFlags = HideFlags.HideAndDontSave;
|
|
||||||
cullShaderId = instancingShader.FindKernel("Cull_64");
|
|
||||||
clearShaderId = instancingShader.FindKernel("Clear_64");
|
|
||||||
|
|
||||||
instancingShader.name = $"ThoMagic Renderer Instancing - {camera.name}";
|
|
||||||
Camera = camera != null ? camera : throw new ArgumentNullException(nameof(camera));
|
|
||||||
cameraSettings = camera.GetComponent<IInstanceRenderSettings>();
|
|
||||||
ReferenceCamera = camera;
|
|
||||||
if (camera.cameraType == CameraType.Game || camera.cameraType == CameraType.VR)
|
|
||||||
{
|
|
||||||
_singlePassInstanced = XRSettings.enabled && (XRSettings.stereoRenderingMode == XRSettings.StereoRenderingMode.SinglePassInstanced || XRSettings.stereoRenderingMode == XRSettings.StereoRenderingMode.SinglePassMultiview);
|
|
||||||
if (Application.isEditor && XRSettings.enabled && !_singlePassInstanced && XRSettings.loadedDeviceName == "MockHMD Display" && this.LoadMockHmdSetting() == "SinglePassInstanced")
|
|
||||||
_singlePassInstanced = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetFloatingOrigin(in Vector3 origin)
|
|
||||||
{
|
|
||||||
_origin = origin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetReferenceCamera(Camera camera)
|
|
||||||
{
|
|
||||||
ReferenceCamera = camera != null ? camera : Camera;
|
|
||||||
cameraSettings = ReferenceCamera.GetComponent<IInstanceRenderSettings>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetMainLight(Light light) => _mainLight = light;
|
|
||||||
|
|
||||||
private void CalculateFrustumPlanes()
|
|
||||||
{
|
|
||||||
if (_cachedFrustumPlanes == null)
|
|
||||||
_cachedFrustumPlanes = new Plane[6];
|
|
||||||
GeometryUtility.CalculateFrustumPlanes(ReferenceCamera, _cachedFrustumPlanes);
|
|
||||||
|
|
||||||
if (_cachedShaderFrustumPlanes == null)
|
|
||||||
_cachedShaderFrustumPlanes = new Vector4[6];
|
|
||||||
|
|
||||||
for (int index = 0; index < 6; ++index)
|
|
||||||
_cachedShaderFrustumPlanes[index] = new Vector4(_cachedFrustumPlanes[index].normal.x, _cachedFrustumPlanes[index].normal.y, _cachedFrustumPlanes[index].normal.z, _cachedFrustumPlanes[index].distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Render()
|
|
||||||
{
|
|
||||||
//Used in editor if selected camera is not editor scene camera. Then only the culled instances of the selected camera is rendered.
|
|
||||||
if (ReferenceCamera == null)
|
|
||||||
SetReferenceCamera(Camera);
|
|
||||||
|
|
||||||
if (_mainLight == null)
|
|
||||||
_mainLight = FindMainLight();
|
|
||||||
|
|
||||||
if (_mainLight != null)
|
|
||||||
{
|
|
||||||
sceneSettings.HasMainLight = true;
|
|
||||||
sceneSettings.HasMainLightShadows = _mainLight.shadows > 0;
|
|
||||||
sceneSettings.MainLightDirection = _mainLight.transform.forward;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sceneSettings.HasMainLight = false;
|
|
||||||
sceneSettings.HasMainLightShadows = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var finalSettings = InstanceRenderSettings.Default(ReferenceCamera);
|
|
||||||
|
|
||||||
if (ReferenceCamera.cameraType == CameraType.Reflection && ReflectionProbeSettings != null)
|
|
||||||
finalSettings.Merge(ReflectionProbeSettings.Settings);
|
|
||||||
|
|
||||||
if (cameraSettings == null && Application.isEditor && !Application.isPlaying)
|
|
||||||
cameraSettings = ReferenceCamera.GetComponent<IInstanceRenderSettings>();
|
|
||||||
|
|
||||||
if (cameraSettings != null)
|
|
||||||
finalSettings.Merge(cameraSettings.Settings);
|
|
||||||
|
|
||||||
if(finalSettings.GetHashCode() != lastCamSettingsHash)
|
|
||||||
{
|
|
||||||
lastCamSettingsHash = finalSettings.GetHashCode();
|
|
||||||
rebuildPrefabs = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rebuildPrefabs && Application.isEditor && !Application.isPlaying)
|
|
||||||
{
|
|
||||||
foreach (var renderObject in RendererPool.objectsList)
|
|
||||||
{
|
|
||||||
|
|
||||||
var settingsEditor = finalSettings;
|
|
||||||
var renderObjectSettingsNew = renderObject.GameObject.GetComponent<IInstanceRenderSettings>();
|
|
||||||
|
|
||||||
if (renderObjectSettingsNew != null)
|
|
||||||
settingsEditor.Merge(renderObjectSettingsNew.Settings);
|
|
||||||
|
|
||||||
if(!appliedSettings.ContainsKey(renderObject.prefabId) || settingsEditor.GetHashCode() != appliedSettings[renderObject.prefabId].GetHashCode())
|
|
||||||
{
|
|
||||||
renderObject.Settings = renderObjectSettingsNew;
|
|
||||||
rebuildPrefabs = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CalculateFrustumPlanes();
|
|
||||||
//Find visible streams for this frame
|
|
||||||
visibleStreams.Clear();
|
|
||||||
|
|
||||||
foreach (var streamer in RendererPool.streamers.Values)
|
|
||||||
if (streamer.IsInRange(ReferenceCamera, _cachedFrustumPlanes))
|
|
||||||
{
|
|
||||||
streamer.UpdateForCamera(ReferenceCamera, _cachedFrustumPlanes);
|
|
||||||
visibleStreams.Add(streamer);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool prefabDataChanged = false;
|
|
||||||
bool noNeedToUpdateIndirectIndexOffsets = false;
|
|
||||||
//A prefab (object) has been added, removed or changed
|
|
||||||
if (rebuildPrefabs)
|
|
||||||
{
|
|
||||||
noNeedToUpdateIndirectIndexOffsets = true;
|
|
||||||
rebuildPrefabs = false;
|
|
||||||
|
|
||||||
lodInvalid = false;
|
|
||||||
cachedFov = ReferenceCamera.fieldOfView;
|
|
||||||
cachedBias = QualitySettings.lodBias;
|
|
||||||
|
|
||||||
appliedSettings.Clear();
|
|
||||||
indirectDrawIndexedArgs.Clear();
|
|
||||||
|
|
||||||
prefabData.Clear();
|
|
||||||
prefabData.AddRange(RendererPool.prefabData);
|
|
||||||
|
|
||||||
maxInstancesCount = 0;
|
|
||||||
indexBufferOffset = 0;
|
|
||||||
|
|
||||||
uint batchIndex = 0;
|
|
||||||
uint prefabCount = 0;
|
|
||||||
uint indexArgsSize = 0;
|
|
||||||
int totalIndirectArgsIndex = 0;
|
|
||||||
|
|
||||||
foreach (var renderObject in RendererPool.objectsList)
|
|
||||||
{
|
|
||||||
var localPrefabData = prefabData[(int)renderObject.prefabId];
|
|
||||||
|
|
||||||
var finalObjSettings = finalSettings;
|
|
||||||
|
|
||||||
if (renderObject.Settings != null)
|
|
||||||
{
|
|
||||||
finalObjSettings.Merge(renderObject.Settings.Settings);
|
|
||||||
ValidateSettings(ref finalObjSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
CalculateCullingDistances(renderObject, finalObjSettings, cachedFov, cachedBias, renderObject.LodTransitions, renderObject.LodSizes, renderObject.lods);
|
|
||||||
|
|
||||||
float distance1 = RelativeHeightToDistance(finalObjSettings.DensityInDistanceFalloff.x, renderObject.LodSizes[0], cachedFov);
|
|
||||||
float distance2 = RelativeHeightToDistance(finalObjSettings.DensityInDistanceFalloff.y, renderObject.LodSizes[0], cachedFov);
|
|
||||||
|
|
||||||
var densityInDistance = new Vector4(finalObjSettings.DensityInDistance,
|
|
||||||
distance1,
|
|
||||||
distance2 - distance1,
|
|
||||||
finalObjSettings.ShadowDistance);
|
|
||||||
|
|
||||||
localPrefabData.lodCount = renderObject.lodCount;
|
|
||||||
localPrefabData.fadeLods = renderObject.fadeLods;
|
|
||||||
localPrefabData.batchIndex = batchIndex;
|
|
||||||
localPrefabData.indexBufferStartOffset = indexBufferOffset;
|
|
||||||
localPrefabData.maxCount = renderObject.count;
|
|
||||||
localPrefabData.densityInDistance = densityInDistance;
|
|
||||||
|
|
||||||
renderObject.indirectArgsPerSubmeshOffsets.Clear();
|
|
||||||
|
|
||||||
indirectDrawIndexedArgs.AddRange(renderObject.indirectDrawIndexedArgs);
|
|
||||||
|
|
||||||
for (int i = 0; i < renderObject.lodCount; i++)
|
|
||||||
{
|
|
||||||
renderObject.indirectArgsPerLodOffsets[i] = indexArgsSize;
|
|
||||||
|
|
||||||
//All indirect arguments for this lod including shadows
|
|
||||||
for (int z = 0; z < renderObject.indirectArgsPerLodWithShadowsCounts[i]; z++)
|
|
||||||
{
|
|
||||||
//Set batchoffset
|
|
||||||
var idia = indirectDrawIndexedArgs[totalIndirectArgsIndex];
|
|
||||||
idia.startInstance = indexBufferOffset + (uint)(i * renderObject.count);
|
|
||||||
indirectDrawIndexedArgs[totalIndirectArgsIndex] = idia;
|
|
||||||
//**
|
|
||||||
|
|
||||||
renderObject.indirectArgsPerSubmeshOffsets.Add(totalIndirectArgsIndex);
|
|
||||||
totalIndirectArgsIndex++;
|
|
||||||
indexArgsSize += 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
localPrefabData.SetLods(renderObject.lods, renderObject.indirectArgsPerLodOffsets, renderObject.indirectArgsPerLodCounts);
|
|
||||||
prefabData[(int)renderObject.prefabId] = localPrefabData;
|
|
||||||
appliedSettings[renderObject.prefabId] = finalObjSettings;
|
|
||||||
|
|
||||||
batchIndex += renderObject.lodCount;
|
|
||||||
indexBufferOffset += renderObject.count * renderObject.lodCount;
|
|
||||||
maxInstancesCount += renderObject.count;
|
|
||||||
prefabCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Nothing to draw yet
|
|
||||||
if (maxInstancesCount == 0)
|
|
||||||
{
|
|
||||||
rebuildPrefabs = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metaBuffer == null || metaBuffer.count < maxInstancesCount)
|
|
||||||
{
|
|
||||||
metaBuffer?.Dispose();
|
|
||||||
int nextCount = RendererPool.RESIZE_COUNT * (((int)maxInstancesCount - 1) / RendererPool.RESIZE_COUNT + 1);
|
|
||||||
metaBuffer = new ComputeBuffer(nextCount, sizeof(uint) * 4, ComputeBufferType.Structured);
|
|
||||||
metaBuffer.name = $"ThoMagic metaBuffer ({Camera.name})";
|
|
||||||
|
|
||||||
cullableIndexesBuffer?.Dispose();
|
|
||||||
cullableIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured);
|
|
||||||
cullableIndexesBuffer.name = $"ThoMagic cullingIndexesBuffer ({Camera.name})";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (indirectDrawIndexedArgsBuffer == null || indirectDrawIndexedArgsBuffer.count < indirectDrawIndexedArgs.Count)
|
|
||||||
{
|
|
||||||
indirectDrawIndexedArgsBuffer?.Dispose();
|
|
||||||
int nextCount = RendererPool.RESIZE_COUNT * (((int)indirectDrawIndexedArgs.Count - 1) / RendererPool.RESIZE_COUNT + 1);
|
|
||||||
indirectDrawIndexedArgsBuffer = new GraphicsBuffer(GraphicsBuffer.Target.IndirectArguments, nextCount, GraphicsBuffer.IndirectDrawIndexedArgs.size);
|
|
||||||
indirectDrawIndexedArgsBuffer.name = $"ThoMagic indirectDrawIndexedArgsBuffer ({Camera.name})";
|
|
||||||
}
|
|
||||||
|
|
||||||
indirectDrawIndexedArgsBuffer.SetData(indirectDrawIndexedArgs);
|
|
||||||
|
|
||||||
if (prefabBuffer == null || prefabBuffer.count < prefabCount)
|
|
||||||
{
|
|
||||||
prefabBuffer?.Dispose();
|
|
||||||
int nextCount = RendererPool.RESIZE_COUNT * (((int)prefabCount - 1) / RendererPool.RESIZE_COUNT + 1);
|
|
||||||
prefabBuffer = new ComputeBuffer(nextCount, PrefabData.Stride, ComputeBufferType.Structured);
|
|
||||||
prefabBuffer.name = $"ThoMagic prefabBuffer ({Camera.name})";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visibleIndexesBuffer == null || visibleIndexesBuffer.count < indexBufferOffset)
|
|
||||||
{
|
|
||||||
visibleIndexesBuffer?.Dispose();
|
|
||||||
int nextCount = RendererPool.RESIZE_COUNT * (((int)indexBufferOffset - 1) / RendererPool.RESIZE_COUNT + 1);
|
|
||||||
visibleIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured);
|
|
||||||
visibleIndexesBuffer.name = $"ThoMagic visibleIndexesBuffer ({Camera.name})";
|
|
||||||
|
|
||||||
visibleShadowIndexesBuffer?.Dispose();
|
|
||||||
visibleShadowIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured);
|
|
||||||
visibleShadowIndexesBuffer.name = $"ThoMagic visibleShadowIndexesBuffer ({Camera.name})";
|
|
||||||
};
|
|
||||||
|
|
||||||
prefabDataChanged = true;
|
|
||||||
instanceCountChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Nothing to draw yet
|
|
||||||
if (maxInstancesCount == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
//Either instances have been added/removed or the visible streams have changed.
|
|
||||||
//We need to rebuild the visible instances index buffer
|
|
||||||
if (instanceCountChanged || BuffersChanged(visibleStreams, ref cachedBufferHash))
|
|
||||||
{
|
|
||||||
if (instanceCountChanged)
|
|
||||||
{
|
|
||||||
if (!noNeedToUpdateIndirectIndexOffsets)//If already rebuild by prefab update, this is not needed
|
|
||||||
{
|
|
||||||
maxInstancesCount = 0;
|
|
||||||
indexBufferOffset = 0;
|
|
||||||
uint indexArgsSize = 0;
|
|
||||||
int totalIndirectArgsIndex = 0;
|
|
||||||
|
|
||||||
foreach (var renderObject in RendererPool.objectsList)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < renderObject.lodCount; i++)
|
|
||||||
{
|
|
||||||
//All indirect arguments for this lod including shadows
|
|
||||||
for (int z = 0; z < renderObject.indirectArgsPerLodWithShadowsCounts[i]; z++)
|
|
||||||
{
|
|
||||||
//Set batchoffset
|
|
||||||
var idia = indirectDrawIndexedArgs[totalIndirectArgsIndex];
|
|
||||||
idia.startInstance = indexBufferOffset + (uint)(i * renderObject.count);
|
|
||||||
indirectDrawIndexedArgs[totalIndirectArgsIndex] = idia;
|
|
||||||
//**
|
|
||||||
|
|
||||||
totalIndirectArgsIndex++;
|
|
||||||
indexArgsSize += 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var pd = prefabData[(int)renderObject.prefabId];
|
|
||||||
if (pd.maxCount != renderObject.count)
|
|
||||||
prefabDataChanged = true;
|
|
||||||
pd.indexBufferStartOffset = indexBufferOffset;
|
|
||||||
pd.maxCount = renderObject.count;
|
|
||||||
prefabData[(int)renderObject.prefabId] = pd;
|
|
||||||
|
|
||||||
indexBufferOffset += renderObject.count * renderObject.lodCount;
|
|
||||||
maxInstancesCount += renderObject.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metaBuffer == null || metaBuffer.count < maxInstancesCount)
|
|
||||||
{
|
|
||||||
metaBuffer?.Dispose();
|
|
||||||
int nextCount = RendererPool.RESIZE_COUNT * (((int)maxInstancesCount - 1) / RendererPool.RESIZE_COUNT + 1);
|
|
||||||
metaBuffer = new ComputeBuffer(nextCount, sizeof(uint) * 4, ComputeBufferType.Structured);
|
|
||||||
metaBuffer.name = $"ThoMagic metaBuffer ({Camera.name})";
|
|
||||||
|
|
||||||
cullableIndexesBuffer?.Dispose();
|
|
||||||
cullableIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured);
|
|
||||||
cullableIndexesBuffer.name = $"ThoMagic cullingIndexesBuffer ({Camera.name})";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visibleIndexesBuffer == null || visibleIndexesBuffer.count < indexBufferOffset)
|
|
||||||
{
|
|
||||||
visibleIndexesBuffer?.Dispose();
|
|
||||||
int nextCount = RendererPool.RESIZE_COUNT * (((int)indexBufferOffset - 1) / RendererPool.RESIZE_COUNT + 1);
|
|
||||||
visibleIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured);
|
|
||||||
visibleIndexesBuffer.name = $"ThoMagic visibleIndexesBuffer ({Camera.name})";
|
|
||||||
|
|
||||||
visibleShadowIndexesBuffer?.Dispose();
|
|
||||||
visibleShadowIndexesBuffer = new ComputeBuffer(nextCount, sizeof(uint), ComputeBufferType.Structured);
|
|
||||||
visibleShadowIndexesBuffer.name = $"ThoMagic visibleShadowIndexesBuffer ({Camera.name})";
|
|
||||||
};
|
|
||||||
|
|
||||||
indirectDrawIndexedArgsBuffer.SetData(indirectDrawIndexedArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
instanceCountChanged = false;
|
|
||||||
|
|
||||||
cullableIndexes.Clear();
|
|
||||||
|
|
||||||
foreach (var stream in visibleStreams)
|
|
||||||
{
|
|
||||||
foreach (var objectInstanceIds in stream.objectInstanceIds.Values)
|
|
||||||
{
|
|
||||||
cullableIndexes.AddRange(objectInstanceIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cullableIndexesBuffer.SetData(cullableIndexes);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Nothing to render
|
|
||||||
if (cullableIndexes.Count == 0 || RendererPool.globalInstanceBuffer == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
//Lod settings changed, update prefab lod data
|
|
||||||
if (cachedFov != ReferenceCamera.fieldOfView || cachedBias != QualitySettings.lodBias || lodInvalid)
|
|
||||||
{
|
|
||||||
lodInvalid = false;
|
|
||||||
cachedFov = ReferenceCamera.fieldOfView;
|
|
||||||
cachedBias = QualitySettings.lodBias;
|
|
||||||
|
|
||||||
foreach (var renderObject in RendererPool.objectsList)
|
|
||||||
{
|
|
||||||
CalculateCullingDistances(renderObject, appliedSettings[renderObject.prefabId], cachedFov, cachedBias, renderObject.LodTransitions, renderObject.LodSizes, renderObject.lods);
|
|
||||||
|
|
||||||
float distance1 = RelativeHeightToDistance(appliedSettings[renderObject.prefabId].DensityInDistanceFalloff.x, renderObject.LodSizes[0], cachedFov);
|
|
||||||
float distance2 = RelativeHeightToDistance(appliedSettings[renderObject.prefabId].DensityInDistanceFalloff.y, renderObject.LodSizes[0], cachedFov);
|
|
||||||
|
|
||||||
var densityInDistance = new Vector4(appliedSettings[renderObject.prefabId].DensityInDistance,
|
|
||||||
distance1,
|
|
||||||
distance2 - distance1,
|
|
||||||
appliedSettings[renderObject.prefabId].ShadowDistance);
|
|
||||||
|
|
||||||
var pd = prefabData[(int)renderObject.prefabId];
|
|
||||||
pd.SetLods(renderObject.lods, renderObject.indirectArgsPerLodOffsets, renderObject.indirectArgsPerLodCounts);
|
|
||||||
pd.densityInDistance = densityInDistance;
|
|
||||||
prefabData[(int)renderObject.prefabId] = pd;
|
|
||||||
}
|
|
||||||
|
|
||||||
prefabDataChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(prefabDataChanged)
|
|
||||||
prefabBuffer.SetData(prefabData);
|
|
||||||
|
|
||||||
//Compute culling
|
|
||||||
|
|
||||||
//Reset visible count of instances to zero
|
|
||||||
/*instancingShader.SetInt("_Count", indirectDrawIndexedArgs.Count);
|
|
||||||
instancingShader.SetBuffer(clearShaderId, "perCamIndirectArgumentsBuffer", indirectDrawIndexedArgsBuffer);
|
|
||||||
instancingShader.Dispatch(clearShaderId, Mathf.CeilToInt(indirectDrawIndexedArgs.Count / 64.0f), 1, 1);
|
|
||||||
|
|
||||||
//Cull
|
|
||||||
instancingShader.SetBuffer(cullShaderId, "globalInstances", RendererPool.globalInstanceBuffer);
|
|
||||||
instancingShader.SetBuffer(cullShaderId, "perCamPrefabs", prefabBuffer);
|
|
||||||
instancingShader.SetBuffer(cullShaderId, "perCamMeta", metaBuffer);
|
|
||||||
instancingShader.SetBuffer(cullShaderId, "perCamIndirectArgumentsBuffer", indirectDrawIndexedArgsBuffer);
|
|
||||||
instancingShader.SetBuffer(cullShaderId, "perCamCullableIndexesBuffer", cullableIndexesBuffer);
|
|
||||||
instancingShader.SetBuffer(cullShaderId, "perCamVisibleIndexesBuffer", visibleIndexesBuffer);
|
|
||||||
instancingShader.SetBuffer(cullShaderId, "perCamShadowVisibleIndexesBuffer", visibleShadowIndexesBuffer);
|
|
||||||
|
|
||||||
instancingShader.SetVector("_CameraPosition", ReferenceCamera.transform.position);
|
|
||||||
instancingShader.SetVectorArray("_FrustumPlanes", _cachedShaderFrustumPlanes);
|
|
||||||
instancingShader.SetVector("_ShadowDirection", sceneSettings.MainLightDirection);
|
|
||||||
instancingShader.SetInt("_Count", cullableIndexes.Count);
|
|
||||||
|
|
||||||
instancingShader.Dispatch(cullShaderId, Mathf.CeilToInt(cullableIndexes.Count / 64.0f), 1, 1);*/
|
|
||||||
|
|
||||||
commandBuffer.Clear();
|
|
||||||
|
|
||||||
commandBuffer.SetComputeIntParam(instancingShader, "_CountClear", indirectDrawIndexedArgs.Count);
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, clearShaderId, "perCamIndirectArgumentsBuffer", indirectDrawIndexedArgsBuffer);
|
|
||||||
commandBuffer.DispatchCompute(instancingShader, clearShaderId, Mathf.CeilToInt(indirectDrawIndexedArgs.Count / 64.0f), 1, 1);
|
|
||||||
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "globalInstances", RendererPool.globalInstanceBuffer);
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamPrefabs", prefabBuffer);
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamMeta", metaBuffer);
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamIndirectArgumentsBuffer", indirectDrawIndexedArgsBuffer);
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamCullableIndexesBuffer", cullableIndexesBuffer);
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamVisibleIndexesBuffer", visibleIndexesBuffer);
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, cullShaderId, "perCamShadowVisibleIndexesBuffer", visibleShadowIndexesBuffer);
|
|
||||||
commandBuffer.SetComputeVectorParam(instancingShader, "_CameraPosition", ReferenceCamera.transform.position);
|
|
||||||
commandBuffer.SetComputeVectorArrayParam(instancingShader, "_FrustumPlanes", _cachedShaderFrustumPlanes);
|
|
||||||
commandBuffer.SetComputeVectorParam(instancingShader, "_ShadowDirection", sceneSettings.MainLightDirection);
|
|
||||||
commandBuffer.SetComputeIntParam(instancingShader, "_CountCull", cullableIndexes.Count);
|
|
||||||
commandBuffer.DispatchCompute(instancingShader, cullShaderId, Mathf.CeilToInt(cullableIndexes.Count / 64.0f), 1, 1);
|
|
||||||
|
|
||||||
Graphics.ExecuteCommandBuffer(commandBuffer);
|
|
||||||
|
|
||||||
var fence = Graphics.CreateGraphicsFence(GraphicsFenceType.AsyncQueueSynchronisation, SynchronisationStageFlags.ComputeProcessing);
|
|
||||||
Graphics.WaitOnAsyncGraphicsFence(fence);
|
|
||||||
|
|
||||||
//Render all prefabs (objects)
|
|
||||||
foreach (var renderObject in RendererPool.objectsList)
|
|
||||||
if (renderObject.count > 0)//Ignore if no instance is registered
|
|
||||||
RenderObject(renderObject, appliedSettings[renderObject.prefabId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CalculateCullingDistances(
|
|
||||||
ObjectData renderObject,
|
|
||||||
InstanceRenderSettings finalSettings,
|
|
||||||
float fieldOfView,
|
|
||||||
float bias,
|
|
||||||
float[] relativeTransitionHeight,
|
|
||||||
float[] detailSize,
|
|
||||||
Vector4[] lodData)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < relativeTransitionHeight.Length; ++index)
|
|
||||||
lodData[index] = new Vector4(index > 0 ? lodData[index - 1].y : 0.0f, RelativeHeightToDistance(relativeTransitionHeight[index], detailSize[index], fieldOfView) * bias, detailSize[index], 0.0f);
|
|
||||||
|
|
||||||
var renderDistance = Mathf.Min(finalSettings.RenderDistance, RelativeHeightToDistance(renderObject.LodTransitions[renderObject.LodTransitions.Length - 1], renderObject.LodSizes[renderObject.LodTransitions.Length - 1], this.ReferenceCamera.fieldOfView, QualitySettings.lodBias));
|
|
||||||
objRenderDistanceCache[renderObject.GetHashCode()] = renderDistance;
|
|
||||||
|
|
||||||
for (int index = 0; index < renderObject.LodSizes.Length; ++index)
|
|
||||||
{
|
|
||||||
renderObject.lods[index].x = Mathf.Min(renderObject.lods[index].x, renderDistance);
|
|
||||||
renderObject.lods[index].y = Mathf.Min(renderObject.lods[index].y, renderDistance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float RelativeHeightToDistance(
|
|
||||||
float relativeHeight,
|
|
||||||
float size,
|
|
||||||
float fieldOfView)
|
|
||||||
{
|
|
||||||
if (relativeHeight <= 0.0f || relativeHeight == float.MaxValue)
|
|
||||||
return float.MaxValue;
|
|
||||||
|
|
||||||
float num = Mathf.Tan((float)(Math.PI / 180.0 * (double)fieldOfView * 0.5));
|
|
||||||
return size * 0.5f / relativeHeight / num;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RenderObject(
|
|
||||||
ObjectData obj,
|
|
||||||
InstanceRenderSettings renderSettings)
|
|
||||||
{
|
|
||||||
if (LayerIsCulled(ReferenceCamera, obj.GameObject.layer) || (/*(!Application.isEditor || Application.isPlaying) &&*/ SceneIsCulled(ReferenceCamera, obj.GameObject)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!renderSettings.Render)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach(var drawGroup in obj.drawGroups)
|
|
||||||
{
|
|
||||||
if (drawGroup == null) continue;
|
|
||||||
drawGroup.SetInstances(RendererPool.globalInstanceBuffer, visibleIndexesBuffer, metaBuffer);
|
|
||||||
drawGroup.Draw(Camera, obj, indirectDrawIndexedArgsBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!renderSettings.Shadows)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var drawGroup in obj.drawShadowGroups)
|
|
||||||
{
|
|
||||||
if (drawGroup == null) continue;
|
|
||||||
drawGroup.SetInstances(RendererPool.globalInstanceBuffer, visibleShadowIndexesBuffer, metaBuffer);
|
|
||||||
drawGroup.Draw(Camera, obj, indirectDrawIndexedArgsBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ValidateSettings(ref InstanceRenderSettings settings)
|
|
||||||
{
|
|
||||||
if (settings.RenderDistance <= 0.0f)
|
|
||||||
settings.RenderDistance = this.ReferenceCamera.farClipPlane;
|
|
||||||
if (settings.ShadowDistance > 0.0f)
|
|
||||||
return;
|
|
||||||
settings.ShadowDistance = settings.RenderDistance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool LayerIsCulled(Camera camera, int layer)
|
|
||||||
{
|
|
||||||
if (camera.cullingMask != -1)
|
|
||||||
{
|
|
||||||
int num = layer;
|
|
||||||
if ((camera.cullingMask & 1 << num) == 0)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool SceneIsCulled(Camera camera, GameObject gameObject)
|
|
||||||
{
|
|
||||||
if (camera.scene != null && camera.scene.handle != 0 && camera.scene != gameObject.scene)
|
|
||||||
return true;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string LoadMockHmdSetting()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
|
||||||
{
|
|
||||||
Type type = assembly.GetType("Unity.XR.MockHMD.MockHMDBuildSettings", false);
|
|
||||||
if (!(type == (Type)null))
|
|
||||||
{
|
|
||||||
object obj = type.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue((object)null);
|
|
||||||
return type.GetField("renderMode").GetValue(obj).ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Light FindMainLight()
|
|
||||||
{
|
|
||||||
Light currentLight = null;
|
|
||||||
foreach (Light otherLight in UnityEngine.Object.FindObjectsOfType<Light>())
|
|
||||||
{
|
|
||||||
if (otherLight.type == LightType.Directional)
|
|
||||||
{
|
|
||||||
if (otherLight.shadows > 0)
|
|
||||||
{
|
|
||||||
currentLight = otherLight;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (currentLight == null)
|
|
||||||
currentLight = otherLight;
|
|
||||||
else if (otherLight.intensity > currentLight.intensity)
|
|
||||||
currentLight = otherLight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return currentLight;
|
|
||||||
}
|
|
||||||
|
|
||||||
private float RelativeHeightToDistance(
|
|
||||||
float relativeHeight,
|
|
||||||
float size,
|
|
||||||
float fieldOfView,
|
|
||||||
float bias)
|
|
||||||
{
|
|
||||||
if ((double)relativeHeight <= 0.0 || (double)relativeHeight == 3.40282346638529E+38)
|
|
||||||
return float.MaxValue;
|
|
||||||
float num = Mathf.Tan((float)(Math.PI / 180.0 * (double)fieldOfView * 0.5));
|
|
||||||
return size * 0.5f / relativeHeight / num * bias;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool BuffersChanged(List<InstanceStreamer> buffers, ref int cachedHash)
|
|
||||||
{
|
|
||||||
int hash = ComputeHash(buffers);
|
|
||||||
if (hash == cachedHash)
|
|
||||||
return false;
|
|
||||||
cachedHash = hash;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int ComputeHash(List<InstanceStreamer> buffers)
|
|
||||||
{
|
|
||||||
if (buffers == null || buffers.Count == 0)
|
|
||||||
return 0;
|
|
||||||
int num = buffers[0].GetHashCode();
|
|
||||||
for (int index = 1; index < buffers.Count; ++index)
|
|
||||||
num = HashCode.Combine(num, buffers[index].GetHashCode());
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
UnityEngine.Object.DestroyImmediate(instancingShader);
|
|
||||||
prefabBuffer?.Dispose();
|
|
||||||
indirectDrawIndexedArgsBuffer?.Dispose();
|
|
||||||
metaBuffer?.Dispose();
|
|
||||||
cullableIndexesBuffer?.Dispose();
|
|
||||||
visibleIndexesBuffer?.Dispose();
|
|
||||||
visibleShadowIndexesBuffer?.Dispose();
|
|
||||||
commandBuffer?.Dispose();
|
|
||||||
prefabBuffer = null;
|
|
||||||
indirectDrawIndexedArgsBuffer = null;
|
|
||||||
metaBuffer = null;
|
|
||||||
visibleIndexesBuffer = null;
|
|
||||||
visibleShadowIndexesBuffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: f2218c3942cfa8745866f4503b313216
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
179
CellLayout.cs
179
CellLayout.cs
|
|
@ -1,179 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class CellLayout
|
|
||||||
{
|
|
||||||
public Vector3 Origin;
|
|
||||||
private readonly int _cellsX;
|
|
||||||
private readonly int _cellsZ;
|
|
||||||
private readonly float _cellSizeX;
|
|
||||||
private readonly float _cellSizeZ;
|
|
||||||
private readonly Cell[] _cells;
|
|
||||||
private readonly Dictionary<int, Plane[]> _cameraPlanes = new Dictionary<int, Plane[]>();
|
|
||||||
private const int _planeCount = 6;
|
|
||||||
private int _cachedFrameId;
|
|
||||||
private Camera _cachedCamera;
|
|
||||||
private Plane[] _planes = new Plane[6];
|
|
||||||
private Double3[] _absNormals = new Double3[6];
|
|
||||||
private Double3[] _planeNormal = new Double3[6];
|
|
||||||
private double[] _planeDistance = new double[6];
|
|
||||||
|
|
||||||
public CellLayout(float cellSizeX, float cellSizeZ, Bounds worldBounds)
|
|
||||||
{
|
|
||||||
Origin = worldBounds.min;
|
|
||||||
_cellSizeX = cellSizeX;
|
|
||||||
_cellSizeZ = cellSizeZ;
|
|
||||||
_cellsX = Mathf.CeilToInt(worldBounds.size.x / _cellSizeX);
|
|
||||||
_cellsZ = Mathf.CeilToInt(worldBounds.size.z / _cellSizeZ);
|
|
||||||
_cells = new Cell[_cellsX * _cellsZ];
|
|
||||||
float num1 = _cellSizeX * 0.5f;
|
|
||||||
float num2 = _cellSizeZ * 0.5f;
|
|
||||||
for (int index1 = 0; index1 < _cellsZ; ++index1)
|
|
||||||
{
|
|
||||||
for (int index2 = 0; index2 < _cellsX; ++index2)
|
|
||||||
{
|
|
||||||
int index3 = index1 * _cellsX + index2;
|
|
||||||
float num3 = (float)index2 * _cellSizeX + num1;
|
|
||||||
float num4 = (float)index1 * _cellSizeZ + num2;
|
|
||||||
_cells[index3].HeightMin = double.MaxValue;
|
|
||||||
_cells[index3].HeightMax = double.MinValue;
|
|
||||||
_cells[index3].Center = new Double3((double)num3, 0.0, (double)num4);
|
|
||||||
_cells[index3].Extends = new Double3((double)num1, double.NaN, (double)num2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cell[] GetCells() => _cells;
|
|
||||||
|
|
||||||
public int GetCellCount() => _cells.Length;
|
|
||||||
|
|
||||||
public void OverwriteCellHeightBounds()
|
|
||||||
{
|
|
||||||
int length = _cells.Length;
|
|
||||||
for (int index = 0; index < length; ++index)
|
|
||||||
_cells[index].HeightOverwrite = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetCellHeightBounds(int cellIndex, double min, double max)
|
|
||||||
{
|
|
||||||
ref Cell local = ref _cells[cellIndex];
|
|
||||||
if (local.HeightOverwrite)
|
|
||||||
{
|
|
||||||
local.HeightOverwrite = false;
|
|
||||||
local.HeightMax = max;
|
|
||||||
local.HeightMin = min;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
local.HeightMax = Math.Max(local.HeightMax, max);
|
|
||||||
local.HeightMin = Math.Min(local.HeightMin, min);
|
|
||||||
}
|
|
||||||
local.Center = new Double3(local.Center.x, (local.HeightMax + local.HeightMin) * 0.5, local.Center.z);
|
|
||||||
local.Extends = new Double3(local.Extends.x, (local.HeightMax + local.HeightMin) * 0.5, local.Extends.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(Camera camera, int frameId, bool frustumCulling)
|
|
||||||
{
|
|
||||||
if (camera == _cachedCamera && frameId == _cachedFrameId)
|
|
||||||
return;
|
|
||||||
_cachedCamera = camera;
|
|
||||||
_cachedFrameId = frameId;
|
|
||||||
Plane[] planes;
|
|
||||||
if (!_cameraPlanes.TryGetValue(((object)camera).GetHashCode(), out planes))
|
|
||||||
_cameraPlanes[((object)camera).GetHashCode()] = planes = new Plane[6];
|
|
||||||
GeometryUtility.CalculateFrustumPlanes(camera, planes);
|
|
||||||
SetPlanes(planes);
|
|
||||||
double x1 = camera.transform.position.x;
|
|
||||||
double z1 = camera.transform.position.z;
|
|
||||||
Double3 absNormal1 = _absNormals[0];
|
|
||||||
Double3 absNormal2 = _absNormals[1];
|
|
||||||
Double3 absNormal3 = _absNormals[2];
|
|
||||||
Double3 absNormal4 = _absNormals[3];
|
|
||||||
Double3 absNormal5 = _absNormals[4];
|
|
||||||
Double3 absNormal6 = _absNormals[5];
|
|
||||||
Double3 double3_1 = _planeNormal[0];
|
|
||||||
Double3 double3_2 = _planeNormal[1];
|
|
||||||
Double3 double3_3 = _planeNormal[2];
|
|
||||||
Double3 double3_4 = _planeNormal[3];
|
|
||||||
Double3 double3_5 = _planeNormal[4];
|
|
||||||
Double3 double3_6 = _planeNormal[5];
|
|
||||||
double num1 = _planeDistance[0];
|
|
||||||
double num2 = _planeDistance[1];
|
|
||||||
double num3 = _planeDistance[2];
|
|
||||||
double num4 = _planeDistance[3];
|
|
||||||
double num5 = _planeDistance[4];
|
|
||||||
double num6 = _planeDistance[5];
|
|
||||||
double x2 = (double)Origin.x;
|
|
||||||
double y = (double)Origin.y;
|
|
||||||
double z2 = (double)Origin.z;
|
|
||||||
int length = _cells.Length;
|
|
||||||
for (int index = 0; index < length; ++index)
|
|
||||||
{
|
|
||||||
ref Cell local = ref _cells[index];
|
|
||||||
double num7 = local.Center.x + x2;
|
|
||||||
double num8 = local.Center.y + y;
|
|
||||||
double num9 = local.Center.z + z2;
|
|
||||||
Double3 extends = local.Extends;
|
|
||||||
local.DistanceX = Math.Abs(x1 - num7);
|
|
||||||
local.DistanceZ = Math.Abs(z1 - num9);
|
|
||||||
if (frustumCulling && !double.IsNaN(extends.y))
|
|
||||||
{
|
|
||||||
bool flag = extends.x * absNormal1.x + extends.y * absNormal1.y + extends.z * absNormal1.z + (double3_1.x * num7 + double3_1.y * num8 + double3_1.z * num9) < -num1 || extends.x * absNormal2.x + extends.y * absNormal2.y + extends.z * absNormal2.z + (double3_2.x * num7 + double3_2.y * num8 + double3_2.z * num9) < -num2 || extends.x * absNormal3.x + extends.y * absNormal3.y + extends.z * absNormal3.z + (double3_3.x * num7 + double3_3.y * num8 + double3_3.z * num9) < -num3 || extends.x * absNormal4.x + extends.y * absNormal4.y + extends.z * absNormal4.z + (double3_4.x * num7 + double3_4.y * num8 + double3_4.z * num9) < -num4 || extends.x * absNormal5.x + extends.y * absNormal5.y + extends.z * absNormal5.z + (double3_5.x * num7 + double3_5.y * num8 + double3_5.z * num9) < -num5 || extends.x * absNormal6.x + extends.y * absNormal6.y + extends.z * absNormal6.z + (double3_6.x * num7 + double3_6.y * num8 + double3_6.z * num9) < -num6;
|
|
||||||
local.InFrustum = !flag;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
local.InFrustum = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetPlanes(Plane[] planes)
|
|
||||||
{
|
|
||||||
_planes = planes;
|
|
||||||
for (int index = 0; index < 6; ++index)
|
|
||||||
{
|
|
||||||
Plane plane = _planes[index];
|
|
||||||
Vector3 vector = plane.normal;
|
|
||||||
Double3 double3 = new Double3(in vector);
|
|
||||||
_absNormals[index] = new Double3(Math.Abs(double3.x), Math.Abs(double3.y), Math.Abs(double3.z));
|
|
||||||
_planeNormal[index] = double3;
|
|
||||||
_planeDistance[index] = plane.distance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Cell
|
|
||||||
{
|
|
||||||
public double DistanceX;
|
|
||||||
public double DistanceZ;
|
|
||||||
public bool InFrustum;
|
|
||||||
public Double3 Center;
|
|
||||||
public Double3 Extends;
|
|
||||||
public double HeightMin;
|
|
||||||
public double HeightMax;
|
|
||||||
public bool HeightOverwrite;
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly struct Double3
|
|
||||||
{
|
|
||||||
public readonly double x;
|
|
||||||
public readonly double y;
|
|
||||||
public readonly double z;
|
|
||||||
|
|
||||||
public Double3(in Vector3 vector)
|
|
||||||
{
|
|
||||||
x = (double)vector.x;
|
|
||||||
y = (double)vector.y;
|
|
||||||
z = (double)vector.z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Double3(double x, double y, double z)
|
|
||||||
{
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 4cf46f5589518ec4f9ea40aaf072cfbf
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
internal class CellLayoutPool
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<CellLayout, int> _hashLookup = new Dictionary<CellLayout, int>();
|
|
||||||
private static readonly Dictionary<int, CellLayout> _sharedCells = new Dictionary<int, CellLayout>();
|
|
||||||
private static readonly Dictionary<int, HashSet<int>> _usageTracker = new Dictionary<int, HashSet<int>>();
|
|
||||||
|
|
||||||
public static int Count => CellLayoutPool._sharedCells.Count;
|
|
||||||
|
|
||||||
internal static bool Validate() => CellLayoutPool._hashLookup.Count == CellLayoutPool._sharedCells.Count && CellLayoutPool._hashLookup.Count == CellLayoutPool._usageTracker.Count;
|
|
||||||
|
|
||||||
public static CellLayout Get(
|
|
||||||
object owner,
|
|
||||||
float cellSizeX,
|
|
||||||
float cellSizeZ,
|
|
||||||
int cellsX,
|
|
||||||
int cellsZ,
|
|
||||||
Bounds bounds)
|
|
||||||
{
|
|
||||||
if (owner == null)
|
|
||||||
throw new ArgumentNullException(nameof(owner));
|
|
||||||
int hashCode = CellLayoutPool.GetHashCode(cellSizeX, cellSizeZ, cellsX, cellsZ, bounds.min);
|
|
||||||
CellLayout key;
|
|
||||||
if (!CellLayoutPool._sharedCells.TryGetValue(hashCode, out key))
|
|
||||||
{
|
|
||||||
CellLayoutPool._sharedCells[hashCode] = key = new CellLayout(cellSizeX, cellSizeZ, bounds);
|
|
||||||
CellLayoutPool._hashLookup[key] = hashCode;
|
|
||||||
}
|
|
||||||
CellLayoutPool.IncreaseUsage(hashCode, owner);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Return(object owner, CellLayout layout)
|
|
||||||
{
|
|
||||||
if (owner == null)
|
|
||||||
throw new ArgumentNullException(nameof(owner));
|
|
||||||
int hash = layout != null ? CellLayoutPool.GetHashCode(layout) : throw new ArgumentNullException(nameof(layout));
|
|
||||||
if (CellLayoutPool.DecreaseUsage(hash, owner) != 0)
|
|
||||||
return;
|
|
||||||
CellLayoutPool.Dispose(hash, layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void Dispose(int hash, CellLayout layout)
|
|
||||||
{
|
|
||||||
CellLayoutPool._usageTracker.Remove(hash);
|
|
||||||
CellLayoutPool._sharedCells.Remove(hash);
|
|
||||||
CellLayoutPool._hashLookup.Remove(layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetHashCode(CellLayout layout) => CellLayoutPool._hashLookup[layout];
|
|
||||||
|
|
||||||
private static void IncreaseUsage(int hash, object owner)
|
|
||||||
{
|
|
||||||
HashSet<int> intSet;
|
|
||||||
if (!CellLayoutPool._usageTracker.TryGetValue(hash, out intSet) || intSet == null)
|
|
||||||
CellLayoutPool._usageTracker[hash] = intSet = new HashSet<int>();
|
|
||||||
int hashCode = owner.GetHashCode();
|
|
||||||
if (intSet.Contains(hashCode))
|
|
||||||
return;
|
|
||||||
intSet.Add(hashCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int DecreaseUsage(int hash, object owner)
|
|
||||||
{
|
|
||||||
HashSet<int> intSet = CellLayoutPool._usageTracker[hash];
|
|
||||||
intSet.Remove(owner.GetHashCode());
|
|
||||||
return intSet.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int GetHashCode(
|
|
||||||
float cellSizeX,
|
|
||||||
float cellSizeZ,
|
|
||||||
int cellsX,
|
|
||||||
int cellsZ,
|
|
||||||
Vector3 initialOrigin)
|
|
||||||
{
|
|
||||||
return HashCode.Combine<float, float, int, int, Vector3>(cellSizeX, cellSizeZ, cellsX, cellsZ, initialOrigin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: d431ea020bbd81c42b897bd3afc640cb
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
97
DrawCall.cs
97
DrawCall.cs
|
|
@ -1,97 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class DrawCall
|
|
||||||
{
|
|
||||||
public readonly Mesh Mesh;
|
|
||||||
public readonly Material[] Materials;
|
|
||||||
public readonly RenderParams[] RenderParams;
|
|
||||||
private readonly Matrix4x4 _localToWorldMatrix;
|
|
||||||
private readonly uint baseIndirectArgsIndex;
|
|
||||||
private readonly int lodNr;
|
|
||||||
public DrawCall(
|
|
||||||
int lodNr,
|
|
||||||
Mesh mesh,
|
|
||||||
Material[] materials,
|
|
||||||
Matrix4x4 localToWorldMatrix,
|
|
||||||
uint baseIndirectArgsIndex,
|
|
||||||
List<GraphicsBuffer.IndirectDrawIndexedArgs> indirectDrawIndexedArgs,
|
|
||||||
in RenderParams renderParams)
|
|
||||||
{
|
|
||||||
if (mesh == null)
|
|
||||||
throw new ArgumentNullException(nameof(mesh));
|
|
||||||
if (materials == null)
|
|
||||||
throw new ArgumentNullException(nameof(materials));
|
|
||||||
if (materials.Length != mesh.subMeshCount)
|
|
||||||
throw new IndexOutOfRangeException(nameof(materials));
|
|
||||||
|
|
||||||
this.baseIndirectArgsIndex = baseIndirectArgsIndex;
|
|
||||||
_localToWorldMatrix = localToWorldMatrix;
|
|
||||||
Mesh = mesh;
|
|
||||||
Materials = materials;
|
|
||||||
RenderParams = new RenderParams[mesh.subMeshCount];
|
|
||||||
|
|
||||||
this.lodNr = lodNr;
|
|
||||||
|
|
||||||
for (int index = 0; index < mesh.subMeshCount; ++index)
|
|
||||||
{
|
|
||||||
RenderParams[index] = renderParams;
|
|
||||||
RenderParams[index].material = materials[index];
|
|
||||||
RenderParams[index].matProps = new MaterialPropertyBlock();
|
|
||||||
RenderParams[index].matProps.SetMatrix("trInstanceMatrix", _localToWorldMatrix);
|
|
||||||
RenderParams[index].matProps.SetInteger("trLodNr", lodNr + 1);
|
|
||||||
|
|
||||||
RenderParams[index].worldBounds = new Bounds(Vector3.zero, 1000f * Vector3.one);
|
|
||||||
|
|
||||||
indirectDrawIndexedArgs.Add(new GraphicsBuffer.IndirectDrawIndexedArgs
|
|
||||||
{
|
|
||||||
indexCountPerInstance = mesh.GetIndexCount(index),
|
|
||||||
baseVertexIndex = mesh.GetBaseVertex(index),
|
|
||||||
startIndex = mesh.GetIndexStart(index),
|
|
||||||
startInstance = 0,
|
|
||||||
instanceCount = 0
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void SetInstances(ComputeBuffer instances, ComputeBuffer visibleIndexBuffer, ComputeBuffer metaBuffer)
|
|
||||||
{
|
|
||||||
for (int index = 0; index < this.RenderParams.Length; ++index)
|
|
||||||
{
|
|
||||||
RenderParams[index].matProps.SetBuffer("trInstances", instances);
|
|
||||||
RenderParams[index].matProps.SetBuffer("trPerCamVisibleIndexesBuffer", visibleIndexBuffer);
|
|
||||||
RenderParams[index].matProps.SetBuffer("trPerCamMeta", metaBuffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Draw(Camera camera, ObjectData obj, GraphicsBuffer indirectDrawIndexedArgs)
|
|
||||||
{
|
|
||||||
if (Materials == null)
|
|
||||||
throw new ObjectDisposedException("Materials");
|
|
||||||
|
|
||||||
if (Mesh == null)
|
|
||||||
throw new ObjectDisposedException("Mesh");
|
|
||||||
|
|
||||||
var bounds = new Bounds(camera.transform.position, Vector3.one * 1000f);
|
|
||||||
|
|
||||||
for (int index = 0; index < RenderParams.Length; ++index)
|
|
||||||
{
|
|
||||||
var renderParam = RenderParams[index];
|
|
||||||
if (renderParam.material != null)
|
|
||||||
{
|
|
||||||
renderParam.camera = camera;
|
|
||||||
renderParam.worldBounds = bounds;
|
|
||||||
Graphics.RenderMeshIndirect(in renderParam, Mesh, indirectDrawIndexedArgs, 1, obj.indirectArgsPerSubmeshOffsets[(int)baseIndirectArgsIndex + index]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal virtual void Dispose()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 0345ca3ff11f4114a9cb20dfdeee77a5
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
65
DrawGroup.cs
65
DrawGroup.cs
|
|
@ -1,65 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class DrawGroup
|
|
||||||
{
|
|
||||||
private List<DrawCall> _drawCalls = new List<DrawCall>();
|
|
||||||
|
|
||||||
public void Add(DrawCall drawCall) => _drawCalls.Add(drawCall);
|
|
||||||
|
|
||||||
private int lodNr;
|
|
||||||
|
|
||||||
public DrawGroup(int lodNr)
|
|
||||||
{
|
|
||||||
this.lodNr = lodNr;
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint Add(
|
|
||||||
Mesh mesh,
|
|
||||||
Material[] materials,
|
|
||||||
Matrix4x4 matrix,
|
|
||||||
uint indirectArgsCount,
|
|
||||||
List<GraphicsBuffer.IndirectDrawIndexedArgs> indirectDrawIndexedArgs,
|
|
||||||
in RenderParams renderParams)
|
|
||||||
{
|
|
||||||
var drawCall = new DrawCall(lodNr, mesh, materials, matrix, indirectArgsCount, indirectDrawIndexedArgs, in renderParams);
|
|
||||||
_drawCalls.Add(drawCall);
|
|
||||||
return (uint)drawCall.Mesh.subMeshCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint Add(
|
|
||||||
BillboardAsset mesh,
|
|
||||||
Material material,
|
|
||||||
Matrix4x4 matrix,
|
|
||||||
uint indirectArgsCount,
|
|
||||||
List<GraphicsBuffer.IndirectDrawIndexedArgs> indirectDrawIndexedArgs,
|
|
||||||
in RenderParams renderParams)
|
|
||||||
{
|
|
||||||
var drawCall = new BillboardDrawCall(lodNr, mesh, material, matrix, indirectArgsCount, indirectDrawIndexedArgs, in renderParams);
|
|
||||||
_drawCalls.Add(drawCall);
|
|
||||||
return (uint)drawCall.Mesh.subMeshCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Draw(Camera camera, ObjectData obj, GraphicsBuffer indirectDrawIndexedArgs)
|
|
||||||
{
|
|
||||||
foreach (var drawCall in _drawCalls)
|
|
||||||
drawCall?.Draw(camera, obj, indirectDrawIndexedArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void SetInstances(ComputeBuffer instances, ComputeBuffer visibleIndexBuffer, ComputeBuffer metaBuffer)
|
|
||||||
{
|
|
||||||
foreach (var drawCall in _drawCalls)
|
|
||||||
drawCall?.SetInstances(instances, visibleIndexBuffer, metaBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Dispose()
|
|
||||||
{
|
|
||||||
foreach (var drawCall in _drawCalls)
|
|
||||||
drawCall?.Dispose();
|
|
||||||
|
|
||||||
_drawCalls.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 14eaba28b1cfbac43acbf8a698ee5dbe
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,90 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
using UnityEditor;
|
|
||||||
#endif
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Rendering;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class FrameRenderer
|
|
||||||
{
|
|
||||||
[RuntimeInitializeOnLoadMethod]
|
|
||||||
public static void Initialize()
|
|
||||||
{
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
AssemblyReloadEvents.beforeAssemblyReload += AssemblyReloadEvents_beforeAssemblyReload;
|
|
||||||
#endif
|
|
||||||
RendererPool.RemoveDestroyedCameras();
|
|
||||||
RendererPool.Initialize();
|
|
||||||
Camera.onPreCull -= RenderCamera;
|
|
||||||
Camera.onPreCull += RenderCamera;
|
|
||||||
RenderPipelineManager.beginContextRendering -= OnBeginContextRendering;
|
|
||||||
RenderPipelineManager.beginContextRendering += OnBeginContextRendering;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AssemblyReloadEvents_beforeAssemblyReload()
|
|
||||||
{
|
|
||||||
RendererPool.Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnBeginContextRendering(
|
|
||||||
ScriptableRenderContext context,
|
|
||||||
List<Camera> cameras)
|
|
||||||
{
|
|
||||||
RendererPool.BuildBuffers();
|
|
||||||
RendererPool.RemoveDestroyedCameras();
|
|
||||||
foreach(var camera in cameras)
|
|
||||||
{
|
|
||||||
if (!CameraIsSupported(camera))
|
|
||||||
{
|
|
||||||
RendererPool.RemoveCamera(camera.GetHashCode());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
CameraRenderer cameraRenderer = RendererPool.GetCamera(RendererPool.RegisterCamera(camera));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
cameraRenderer.Render();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.LogException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RenderCamera(Camera camera)
|
|
||||||
{
|
|
||||||
if (camera == null)
|
|
||||||
throw new NullReferenceException(nameof(camera));
|
|
||||||
|
|
||||||
if (!CameraIsSupported(camera))
|
|
||||||
{
|
|
||||||
RendererPool.RemoveCamera(camera.GetHashCode());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RendererPool.BuildBuffers();
|
|
||||||
RendererPool.RemoveDestroyedCameras();
|
|
||||||
int cameraId = RendererPool.RegisterCamera(camera);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
RendererPool.GetCamera(cameraId)?.Render();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Debug.LogException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool CameraIsSupported(Camera camera)
|
|
||||||
{
|
|
||||||
IInstanceRenderSettings instanceRenderSettings;
|
|
||||||
return (Application.isPlaying || camera.cameraType != CameraType.Preview) && (!camera.TryGetComponent(out instanceRenderSettings) || instanceRenderSettings.Settings.Supported);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: e9b14e1d6d523834db575b4973d22c0e
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
196
GrassRenderer.cs
196
GrassRenderer.cs
|
|
@ -1,196 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class GrassRenderer : GrassStreamer
|
|
||||||
{
|
|
||||||
|
|
||||||
private readonly Terrain terrain;
|
|
||||||
private readonly TerrainData terrainData;
|
|
||||||
private bool isDirty;
|
|
||||||
|
|
||||||
public GrassRenderer(Terrain terrain)
|
|
||||||
: base(terrain)
|
|
||||||
{
|
|
||||||
this.terrain = terrain;
|
|
||||||
terrainData = terrain.terrainData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnTerrainChanged(TerrainChangedFlags flags)
|
|
||||||
{
|
|
||||||
if (flags.HasFlag(TerrainChangedFlags.DelayedHeightmapUpdate))
|
|
||||||
isDirty = true;
|
|
||||||
else if (flags.HasFlag(TerrainChangedFlags.Heightmap))
|
|
||||||
isDirty = true;
|
|
||||||
else if (flags.HasFlag(TerrainChangedFlags.HeightmapResolution))
|
|
||||||
isDirty = true;
|
|
||||||
else if (flags.HasFlag(TerrainChangedFlags.Holes))
|
|
||||||
isDirty = true;
|
|
||||||
else if (flags.HasFlag(TerrainChangedFlags.DelayedHolesUpdate))
|
|
||||||
isDirty = true;
|
|
||||||
else if (flags.HasFlag(TerrainChangedFlags.FlushEverythingImmediately))
|
|
||||||
isDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Destroy()
|
|
||||||
{
|
|
||||||
Recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LateUpdate()
|
|
||||||
{
|
|
||||||
if (isDirty)
|
|
||||||
{
|
|
||||||
isDirty = false;
|
|
||||||
Load();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Application.isEditor && !Application.isPlaying)
|
|
||||||
RebuildChangedPrototypes();
|
|
||||||
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Load()
|
|
||||||
{
|
|
||||||
DetailPrototype[] detailPrototypes = terrainData.detailPrototypes;
|
|
||||||
|
|
||||||
Recycle();
|
|
||||||
|
|
||||||
int ownerHash = GetHashCode();
|
|
||||||
|
|
||||||
if (grassPrefabs == null)
|
|
||||||
{
|
|
||||||
grassPrefabs = new List<int>();
|
|
||||||
grassPrefabSet = new List<bool>();
|
|
||||||
}
|
|
||||||
|
|
||||||
float maxDetailDistance = float.MinValue;
|
|
||||||
|
|
||||||
for (int index = 0; index < detailPrototypes.Length; ++index)
|
|
||||||
{
|
|
||||||
if (!detailPrototypes[index].usePrototypeMesh || detailPrototypes[index].prototype == null)
|
|
||||||
{
|
|
||||||
AddDummy(index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GameObject prototypeToRender = RendererUtility.GetPrototypeToRender(detailPrototypes[index]);
|
|
||||||
if (!RendererUtility.SupportsProceduralInstancing(prototypeToRender))
|
|
||||||
{
|
|
||||||
AddDummy(index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var settings = GetSettingsOrDefault(prototypeToRender);
|
|
||||||
maxDetailDistance = Mathf.Max(maxDetailDistance, settings.Settings.RenderDistance);
|
|
||||||
grassPrefabs.Add(RendererPool.RegisterObject(prototypeToRender, settings, this, ownerHash));
|
|
||||||
grassPrefabSet.Add(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Build(maxDetailDistance, detailPrototypes.Length);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddDummy(int i)
|
|
||||||
{
|
|
||||||
grassPrefabs.Add(-1);
|
|
||||||
grassPrefabSet.Add(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Recycle()
|
|
||||||
{
|
|
||||||
if (grassPrefabs == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
base.Recycle();
|
|
||||||
|
|
||||||
int ownerHash = GetHashCode();
|
|
||||||
for (int index = 0; index < grassPrefabs.Count; ++index)
|
|
||||||
{
|
|
||||||
if (grassPrefabSet[index])
|
|
||||||
{
|
|
||||||
RendererPool.RemoveObject(grassPrefabs[index], ownerHash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
grassPrefabs.Clear();
|
|
||||||
grassPrefabSet.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RebuildChangedPrototypes()
|
|
||||||
{
|
|
||||||
DetailPrototype[] detailPrototypes = terrainData.detailPrototypes;
|
|
||||||
if (detailPrototypes.Length != grassPrefabs.Count)
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int ownerHash = GetHashCode();
|
|
||||||
|
|
||||||
for (int index = 0; index < grassPrefabs.Count; ++index)
|
|
||||||
{
|
|
||||||
if (grassPrefabSet[index])
|
|
||||||
{
|
|
||||||
GameObject prototypeToRender = RendererUtility.GetPrototypeToRender(detailPrototypes[index]);
|
|
||||||
GameObject gameObject = RendererPool.GetObject(grassPrefabs[index]);
|
|
||||||
if (prototypeToRender != gameObject)
|
|
||||||
{
|
|
||||||
RendererPool.RemoveObject(grassPrefabs[index], ownerHash);
|
|
||||||
|
|
||||||
if (prototypeToRender != null)
|
|
||||||
{
|
|
||||||
grassPrefabs[index] = RendererPool.RegisterObject(prototypeToRender, GetSettingsOrDefault(prototypeToRender), this, ownerHash);
|
|
||||||
grassPrefabSet[index] = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
grassPrefabs[index] = -1;
|
|
||||||
grassPrefabSet[index] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (RendererPool.ContentHashChanged(grassPrefabs[index]))
|
|
||||||
{
|
|
||||||
RendererPool.RemoveObject(grassPrefabs[index], GetHashCode());
|
|
||||||
if (prototypeToRender != null)
|
|
||||||
{
|
|
||||||
grassPrefabs[index] = RendererPool.RegisterObject(prototypeToRender, GetSettingsOrDefault(prototypeToRender), this, ownerHash);
|
|
||||||
grassPrefabSet[index] = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
grassPrefabs[index] = -1;
|
|
||||||
grassPrefabSet[index] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (prototypeToRender != null)
|
|
||||||
RendererPool.SetObjectSettings(prototypeToRender, GetSettingsOrDefault(prototypeToRender));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IInstanceRenderSettings GetSettingsOrDefault(GameObject gameObject)
|
|
||||||
{
|
|
||||||
IInstanceRenderSettings instanceRenderSettings;
|
|
||||||
return gameObject.TryGetComponent(out instanceRenderSettings) ? instanceRenderSettings : new DefaultRenderSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DefaultRenderSettings : IInstanceRenderSettings
|
|
||||||
{
|
|
||||||
public InstanceRenderSettings Settings => new InstanceRenderSettings()
|
|
||||||
{
|
|
||||||
Render = true,
|
|
||||||
DensityInDistance = 0.125f,
|
|
||||||
DensityInDistanceFalloff = new Vector2(0.08f, 0.0075f),
|
|
||||||
RenderDistance = 150f,
|
|
||||||
ShadowDistance = 50f,
|
|
||||||
Shadows = true,
|
|
||||||
Supported = true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: cdb66824e49e1344faf75691d1c8788d
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
301
GrassStreamer.cs
301
GrassStreamer.cs
|
|
@ -1,301 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Subdivides a terrain into smaller 2d-cells in the xz-plane based on terrain settings. Only cells in range 'maxDetailDistance' of the camera
|
|
||||||
/// are alive.
|
|
||||||
/// </summary>
|
|
||||||
public class GrassStreamer : InstanceStreamer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The limit of how many instances are created per frame.
|
|
||||||
/// TODO: While the setting is global, the budget is per streamer and each
|
|
||||||
/// terrain has it's own streamer. This should be a real global budget.
|
|
||||||
/// </summary>
|
|
||||||
static int globalStreamingBudget = 10000;
|
|
||||||
|
|
||||||
protected List<int> grassPrefabs;
|
|
||||||
protected List<bool> grassPrefabSet;
|
|
||||||
|
|
||||||
private readonly Terrain terrain;
|
|
||||||
private readonly TerrainData terrainData;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to find cells in range of the camera.
|
|
||||||
/// </summary>
|
|
||||||
private float maxDetailDistance;
|
|
||||||
private Bounds worldBounds;
|
|
||||||
private int layerCount;
|
|
||||||
|
|
||||||
private Cell[] Cells;
|
|
||||||
private float cellSizeX, cellSizeZ;
|
|
||||||
private int cellsX, cellsZ;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A queue with a list of cells to load. Cells are loaded per frame
|
|
||||||
/// til the streaming budget is spent.
|
|
||||||
/// </summary>
|
|
||||||
private Queue<int> cellsToLoad = new Queue<int>();
|
|
||||||
/// <summary>
|
|
||||||
/// List of cells unloaded in a frame, only temporary list used to remove from loadedCells.
|
|
||||||
/// </summary>
|
|
||||||
private List<int> cellsToUnloaded = new List<int>();
|
|
||||||
//A map of loaded cells. The cell index is z * cellCountX + x.
|
|
||||||
private Dictionary<int, Cell> loadedCells = new Dictionary<int, Cell>();
|
|
||||||
|
|
||||||
public static void SetStreamingBudget(int budget) => globalStreamingBudget = budget;
|
|
||||||
|
|
||||||
private static float CalculatePatchSizeX(TerrainData terrainData) => (float)((double)terrainData.detailResolutionPerPatch / (double)terrainData.detailResolution * terrainData.size.x);
|
|
||||||
|
|
||||||
private static float CalculatePatchSizeZ(TerrainData terrainData) => (float)((double)terrainData.detailResolutionPerPatch / (double)terrainData.detailResolution * terrainData.size.z);
|
|
||||||
|
|
||||||
public GrassStreamer(
|
|
||||||
Terrain terrain)
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
this.terrain = terrain;
|
|
||||||
terrainData = terrain.terrainData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Update loaded cells. Called by renderer. The renderer calls this once each frame.
|
|
||||||
/// </summary>
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
//Load cells until either all cells are loaded or budget is spent.
|
|
||||||
//Returns the budget left.
|
|
||||||
int streamingBudget = UpdateLoadCellsAsync();
|
|
||||||
|
|
||||||
//Go through all loaded cells and reduce 'inRangeOfAnyCamera'.
|
|
||||||
//If 'inRangeOfAnyCamera' is equal or below zero unload the cell.
|
|
||||||
//'inRangeOfAnyCamera' is reset to x if a camera renders and this
|
|
||||||
//cell is in range.
|
|
||||||
foreach (var cellIndex in loadedCells.Keys)
|
|
||||||
{
|
|
||||||
var cell = loadedCells[cellIndex];
|
|
||||||
|
|
||||||
//If cell is out of range and budget exist, unload.
|
|
||||||
if (cell.inRangeOfAnyCamera <= 0 && streamingBudget > 0)
|
|
||||||
{
|
|
||||||
streamingBudget -= UnloadCell(cellIndex);
|
|
||||||
cellsToUnloaded.Add(cellIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
--cell.inRangeOfAnyCamera;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Remove all cells unloaded from 'loadedCells'.
|
|
||||||
if (cellsToUnloaded.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var id in cellsToUnloaded)
|
|
||||||
loadedCells.Remove(id);
|
|
||||||
cellsToUnloaded.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Rebuild the cell layout. Called on initialize and on certain terrain changes.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="maxDistance">Max distance where a cell is in range</param>
|
|
||||||
/// <param name="layerCount">Count of detail layers</param>
|
|
||||||
public void Build(float maxDistance, int layerCount)
|
|
||||||
{
|
|
||||||
maxDetailDistance = maxDistance;
|
|
||||||
this.layerCount = layerCount;
|
|
||||||
|
|
||||||
cellSizeX = CalculatePatchSizeX(terrainData);
|
|
||||||
cellSizeZ = CalculatePatchSizeZ(terrainData);
|
|
||||||
|
|
||||||
Vector3 position = terrain.GetPosition();
|
|
||||||
worldBounds = new Bounds(terrainData.bounds.center + position, terrainData.bounds.size);
|
|
||||||
|
|
||||||
cellsX = Mathf.CeilToInt(worldBounds.size.x / cellSizeX);
|
|
||||||
cellsZ = Mathf.CeilToInt(worldBounds.size.z / cellSizeZ);
|
|
||||||
Cells = new Cell[cellsX * cellsZ];
|
|
||||||
|
|
||||||
float cellSizeX_2 = cellSizeX * 0.5f;
|
|
||||||
float cellSizeZ_2 = cellSizeZ * 0.5f;
|
|
||||||
for (int indexZ = 0; indexZ < cellsZ; ++indexZ)
|
|
||||||
{
|
|
||||||
for (int indexX = 0; indexX < cellsX; ++indexX)
|
|
||||||
{
|
|
||||||
int cellIndex = indexZ * cellsX + indexX;
|
|
||||||
Cells[cellIndex] = new Cell();
|
|
||||||
float cellCenterX = indexX * cellSizeX + cellSizeX_2;
|
|
||||||
float cellCenterZ = indexZ * cellSizeZ + cellSizeZ_2;
|
|
||||||
Cells[cellIndex].CenterX = cellCenterX;
|
|
||||||
Cells[cellIndex].CenterZ = cellCenterZ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Is this streamer within range?
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="camera">The camera to check for</param>
|
|
||||||
/// <param name="planes">Planes of the camera frustum</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public override bool IsInRange(Camera camera, Plane[] planes)
|
|
||||||
{
|
|
||||||
if (layerCount == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!terrain.editorRenderFlags.HasFlag(TerrainRenderFlags.Details))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var eyePos = camera.transform.position;
|
|
||||||
|
|
||||||
if ((eyePos - worldBounds.ClosestPoint(eyePos)).magnitude <= Mathf.Min(camera.farClipPlane, maxDetailDistance))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load cells until either all cells are loaded from the cellsToLoad list
|
|
||||||
/// or the budget is spent.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Unspent budget</returns>
|
|
||||||
private int UpdateLoadCellsAsync()
|
|
||||||
{
|
|
||||||
int streamingBudget = globalStreamingBudget;
|
|
||||||
|
|
||||||
if (globalStreamingBudget <= 0 || Cells == null || Cells.Length == 0)
|
|
||||||
return streamingBudget;
|
|
||||||
|
|
||||||
while(streamingBudget > 0 && cellsToLoad.TryDequeue(out var cellIndex))
|
|
||||||
{
|
|
||||||
streamingBudget -= LoadCell(cellIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
return streamingBudget;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Recycle()
|
|
||||||
{
|
|
||||||
UnloadAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UnloadAll()
|
|
||||||
{
|
|
||||||
if (Cells == null || Cells.Length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (int cellIndex = 0; cellIndex < Cells.Length; ++cellIndex)
|
|
||||||
{
|
|
||||||
UnloadCell(cellIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
loadedCells.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Remove all instances of a cell.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cellIndex"></param>
|
|
||||||
/// <returns>The amount of deleted instances</returns>
|
|
||||||
private int UnloadCell(int cellIndex)
|
|
||||||
{
|
|
||||||
if (cellIndex < 0 || cellIndex >= Cells.Length || !Cells[cellIndex].IsLoaded)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
var cell = Cells[cellIndex];
|
|
||||||
cell.IsLoaded = false;
|
|
||||||
cell.IsLoading = false;
|
|
||||||
|
|
||||||
int deletedInstances = cell.loadedInstances.Count;
|
|
||||||
|
|
||||||
foreach (var instanceInfo in cell.loadedInstances)
|
|
||||||
RemoveInstance(instanceInfo.objectId, instanceInfo.instanceId);
|
|
||||||
|
|
||||||
cell.loadedInstances.Clear();
|
|
||||||
|
|
||||||
return deletedInstances;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Load all instances for a cell.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="cellIndex"></param>
|
|
||||||
/// <returns>The amount of loaded instances</returns>
|
|
||||||
private int LoadCell(int cellIndex)
|
|
||||||
{
|
|
||||||
if (cellIndex < 0 || cellIndex >= Cells.Length || Cells[cellIndex].IsLoaded)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
var cell = Cells[cellIndex];
|
|
||||||
cell.IsLoaded = true;
|
|
||||||
cell.IsLoading = false;
|
|
||||||
|
|
||||||
loadedCells.Add(cellIndex, cell);
|
|
||||||
int x = Mathf.FloorToInt(cell.CenterX / cellSizeX);
|
|
||||||
int z = Mathf.FloorToInt(cell.CenterZ / cellSizeZ);
|
|
||||||
Vector3 size = terrainData.size;
|
|
||||||
Vector3 terrainOffset = terrain.transform.position;
|
|
||||||
Quaternion terrainRotation = terrain.transform.rotation;
|
|
||||||
float detailObjectDensity = terrain.detailObjectDensity;
|
|
||||||
|
|
||||||
for (int layer = 0; layer < layerCount; layer++)
|
|
||||||
{
|
|
||||||
var instanceTransforms = terrainData.ComputeDetailInstanceTransforms(x, z, layer, detailObjectDensity, out var _);
|
|
||||||
|
|
||||||
for (int index = 0; index < instanceTransforms.Length; ++index)
|
|
||||||
{
|
|
||||||
var local = instanceTransforms[index];
|
|
||||||
|
|
||||||
Vector3 interpolatedNormal = terrainData.GetInterpolatedNormal((local.posX / size.x), (local.posZ / size.z));
|
|
||||||
Quaternion rotation = terrainRotation * Quaternion.FromToRotation(Vector3.up, interpolatedNormal) * Quaternion.AngleAxis((local.rotationY * 57.2957801818848f), Vector3.up);
|
|
||||||
|
|
||||||
var instanceId = AddInstance(grassPrefabs[layer], new Vector3(local.posX, local.posY, local.posZ) + terrainOffset, rotation, local.scaleXZ, local.scaleY);
|
|
||||||
cell.loadedInstances.Add(new InstanceInfo { instanceId = instanceId, objectId = grassPrefabs[layer] });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cell.loadedInstances.Count;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called by each camera at the start of a frame.
|
|
||||||
/// Finds all cells visible in range of a camera. Either
|
|
||||||
/// resets the 'inRangeOfAnyCamera' counter or adds the cell
|
|
||||||
/// to the load list.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="camera"></param>
|
|
||||||
/// <param name="planes"></param>
|
|
||||||
public override void UpdateForCamera(Camera camera, Plane[] planes)
|
|
||||||
{
|
|
||||||
if (Cells == null || Cells.Length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var range = Mathf.Min(camera.farClipPlane, maxDetailDistance);
|
|
||||||
var rangeX = Mathf.CeilToInt((range + cellSizeX * 0.5f) / cellSizeX);
|
|
||||||
var rangeZ = Mathf.CeilToInt((range + cellSizeZ * 0.5f) / cellSizeZ);
|
|
||||||
|
|
||||||
var eyePos = camera.transform.position - worldBounds.min;
|
|
||||||
var eyeCellX = Mathf.FloorToInt(eyePos.x / cellSizeX);
|
|
||||||
var eyeCellZ = Mathf.FloorToInt(eyePos.z / cellSizeZ);
|
|
||||||
|
|
||||||
for(int indexZ = eyeCellZ - rangeZ; indexZ < eyeCellZ + rangeZ; indexZ++)
|
|
||||||
for (int indexX = eyeCellX - rangeX; indexX < eyeCellX + rangeX; indexX++)
|
|
||||||
{
|
|
||||||
if(indexZ >= 0 && indexZ < cellsZ &&
|
|
||||||
indexX >= 0 && indexX < cellsX)
|
|
||||||
{
|
|
||||||
int cellIndex = indexZ * cellsX + indexX;
|
|
||||||
if (Cells[cellIndex].IsLoaded || Cells[cellIndex].IsLoading)
|
|
||||||
{
|
|
||||||
Cells[cellIndex].inRangeOfAnyCamera = 2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Cells[cellIndex].IsLoading = true;
|
|
||||||
Cells[cellIndex].inRangeOfAnyCamera = 2;
|
|
||||||
cellsToLoad.Enqueue(cellIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: a9909a9557ba0b94e9de512444a0629e
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class InstanceBuffer
|
|
||||||
{
|
|
||||||
private static int nextInstanceBufferId = 1;
|
|
||||||
private readonly int instanceBufferId;
|
|
||||||
|
|
||||||
public readonly ObjectData objectData;
|
|
||||||
public readonly Dictionary<int, InstanceData> instanceData;
|
|
||||||
|
|
||||||
public InstanceBuffer(ObjectData objectData)
|
|
||||||
{
|
|
||||||
this.objectData = objectData;
|
|
||||||
instanceData = new Dictionary<int, InstanceData>();
|
|
||||||
instanceBufferId = Interlocked.Increment(ref InstanceBuffer.nextInstanceBufferId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return instanceBufferId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 1d85f6533dc1bb84bb236fd27ab68b38
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public struct InstanceData
|
|
||||||
{
|
|
||||||
public const int Add = 1;
|
|
||||||
public const int Delete = 2;
|
|
||||||
public static readonly int Stride = 48;
|
|
||||||
public readonly Vector4 Packed1;
|
|
||||||
public readonly Vector4 Packed2;
|
|
||||||
public readonly uint prefabId;
|
|
||||||
//Used to stream out new instances
|
|
||||||
public readonly int uploadAction;
|
|
||||||
public readonly int uploadId;
|
|
||||||
public readonly int padding;
|
|
||||||
|
|
||||||
public InstanceData(
|
|
||||||
int uploadAction,
|
|
||||||
int uploadId,
|
|
||||||
uint prefabId,
|
|
||||||
float x,
|
|
||||||
float y,
|
|
||||||
float z,
|
|
||||||
in Quaternion rotation,
|
|
||||||
float scaleXZ,
|
|
||||||
float scaleY)
|
|
||||||
{
|
|
||||||
float num = rotation.w > 0.0 ? 1f : -1f;
|
|
||||||
Packed1.x = x;
|
|
||||||
Packed1.y = y;
|
|
||||||
Packed1.z = z;
|
|
||||||
Packed1.w = (rotation.x * num);
|
|
||||||
Packed2.x = scaleXZ;
|
|
||||||
Packed2.y = scaleY;
|
|
||||||
Packed2.z = (rotation.y * num);
|
|
||||||
Packed2.w = (rotation.z * num);
|
|
||||||
this.prefabId = prefabId;
|
|
||||||
this.uploadAction = uploadAction;
|
|
||||||
this.uploadId = uploadId;
|
|
||||||
padding = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InstanceData(
|
|
||||||
int uploadAction,
|
|
||||||
int uploadId)
|
|
||||||
{
|
|
||||||
Packed1.x = 0;
|
|
||||||
Packed1.y = 0;
|
|
||||||
Packed1.z = 0;
|
|
||||||
Packed1.w = 0;
|
|
||||||
Packed2.x = 0;
|
|
||||||
Packed2.y = 0;
|
|
||||||
Packed2.z = 0;
|
|
||||||
Packed2.w = 0;
|
|
||||||
this.prefabId = 0;
|
|
||||||
this.uploadAction = uploadAction;
|
|
||||||
this.uploadId = uploadId;
|
|
||||||
padding = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct PrefabData
|
|
||||||
{
|
|
||||||
public static readonly int Stride = 228;
|
|
||||||
public uint batchIndex;
|
|
||||||
public uint indexBufferStartOffset;
|
|
||||||
public uint maxCount;
|
|
||||||
public uint lodCount;
|
|
||||||
public uint fadeLods;
|
|
||||||
public Vector4 densityInDistance;
|
|
||||||
public Vector4 lodData0;
|
|
||||||
public Vector4 lodData1;
|
|
||||||
public Vector4 lodData2;
|
|
||||||
public Vector4 lodData3;
|
|
||||||
public Vector4 lodData4;
|
|
||||||
public Vector4 lodData5;
|
|
||||||
public Vector4 lodData6;
|
|
||||||
public Vector4 lodData7;
|
|
||||||
public uint indirectArgsOffset0;
|
|
||||||
public uint indirectArgsCount0;
|
|
||||||
public uint indirectArgsOffset1;
|
|
||||||
public uint indirectArgsCount1;
|
|
||||||
public uint indirectArgsOffset2;
|
|
||||||
public uint indirectArgsCount2;
|
|
||||||
public uint indirectArgsOffset3;
|
|
||||||
public uint indirectArgsCount3;
|
|
||||||
public uint indirectArgsOffset4;
|
|
||||||
public uint indirectArgsCount4;
|
|
||||||
public uint indirectArgsOffset5;
|
|
||||||
public uint indirectArgsCount5;
|
|
||||||
public uint indirectArgsOffset6;
|
|
||||||
public uint indirectArgsCount6;
|
|
||||||
public uint indirectArgsOffset7;
|
|
||||||
public uint indirectArgsCount7;
|
|
||||||
|
|
||||||
public void SetLods(Vector4[] lodData, uint[] indirectArgsPerLodOffset, uint[] indirectArgsPerLodCount)
|
|
||||||
{
|
|
||||||
this.lodData0 = lodCount > 0 ? lodData[0] : Vector4.zero;
|
|
||||||
this.lodData1 = lodCount > 1 ? lodData[1] : Vector4.zero;
|
|
||||||
this.lodData2 = lodCount > 2 ? lodData[2] : Vector4.zero;
|
|
||||||
this.lodData3 = lodCount > 3 ? lodData[3] : Vector4.zero;
|
|
||||||
this.lodData4 = lodCount > 4 ? lodData[4] : Vector4.zero;
|
|
||||||
this.lodData5 = lodCount > 5 ? lodData[5] : Vector4.zero;
|
|
||||||
this.lodData6 = lodCount > 6 ? lodData[6] : Vector4.zero;
|
|
||||||
this.lodData7 = lodCount > 7 ? lodData[7] : Vector4.zero;
|
|
||||||
|
|
||||||
this.indirectArgsOffset0 = lodCount > 0 ? indirectArgsPerLodOffset[0] : 0;
|
|
||||||
this.indirectArgsOffset1 = lodCount > 1 ? indirectArgsPerLodOffset[1] : 0;
|
|
||||||
this.indirectArgsOffset2 = lodCount > 2 ? indirectArgsPerLodOffset[2] : 0;
|
|
||||||
this.indirectArgsOffset3 = lodCount > 3 ? indirectArgsPerLodOffset[3] : 0;
|
|
||||||
this.indirectArgsOffset4 = lodCount > 4 ? indirectArgsPerLodOffset[4] : 0;
|
|
||||||
this.indirectArgsOffset5 = lodCount > 5 ? indirectArgsPerLodOffset[5] : 0;
|
|
||||||
this.indirectArgsOffset6 = lodCount > 6 ? indirectArgsPerLodOffset[6] : 0;
|
|
||||||
this.indirectArgsOffset7 = lodCount > 7 ? indirectArgsPerLodOffset[7] : 0;
|
|
||||||
|
|
||||||
this.indirectArgsCount0 = lodCount > 0 ? indirectArgsPerLodCount[0] : 0;
|
|
||||||
this.indirectArgsCount1 = lodCount > 1 ? indirectArgsPerLodCount[1] : 0;
|
|
||||||
this.indirectArgsCount2 = lodCount > 2 ? indirectArgsPerLodCount[2] : 0;
|
|
||||||
this.indirectArgsCount3 = lodCount > 3 ? indirectArgsPerLodCount[3] : 0;
|
|
||||||
this.indirectArgsCount4 = lodCount > 4 ? indirectArgsPerLodCount[4] : 0;
|
|
||||||
this.indirectArgsCount5 = lodCount > 5 ? indirectArgsPerLodCount[5] : 0;
|
|
||||||
this.indirectArgsCount6 = lodCount > 6 ? indirectArgsPerLodCount[6] : 0;
|
|
||||||
this.indirectArgsCount7 = lodCount > 7 ? indirectArgsPerLodCount[7] : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PrefabData(
|
|
||||||
uint batchIndex,
|
|
||||||
uint indexBufferStartOffset,
|
|
||||||
uint maxCount,
|
|
||||||
uint lodCount,
|
|
||||||
uint fadeLods,
|
|
||||||
Vector4 densityInDistance,
|
|
||||||
Vector4[] lodData,
|
|
||||||
uint[] indirectArgsPerLodOffset,
|
|
||||||
uint[] indirectArgsPerLodCount
|
|
||||||
)
|
|
||||||
{
|
|
||||||
this.batchIndex = batchIndex;
|
|
||||||
this.indexBufferStartOffset = indexBufferStartOffset;
|
|
||||||
this.maxCount = maxCount;
|
|
||||||
this.lodCount = lodCount;
|
|
||||||
this.fadeLods = fadeLods;
|
|
||||||
this.densityInDistance = densityInDistance;
|
|
||||||
|
|
||||||
this.lodData0 = lodCount > 0 ? lodData[0] : Vector4.zero;
|
|
||||||
this.lodData1 = lodCount > 1 ? lodData[1] : Vector4.zero;
|
|
||||||
this.lodData2 = lodCount > 2 ? lodData[2] : Vector4.zero;
|
|
||||||
this.lodData3 = lodCount > 3 ? lodData[3] : Vector4.zero;
|
|
||||||
this.lodData4 = lodCount > 4 ? lodData[4] : Vector4.zero;
|
|
||||||
this.lodData5 = lodCount > 5 ? lodData[5] : Vector4.zero;
|
|
||||||
this.lodData6 = lodCount > 6 ? lodData[6] : Vector4.zero;
|
|
||||||
this.lodData7 = lodCount > 7 ? lodData[7] : Vector4.zero;
|
|
||||||
|
|
||||||
this.indirectArgsOffset0 = lodCount > 0 ? indirectArgsPerLodOffset[0] : 0;
|
|
||||||
this.indirectArgsOffset1 = lodCount > 1 ? indirectArgsPerLodOffset[1] : 0;
|
|
||||||
this.indirectArgsOffset2 = lodCount > 2 ? indirectArgsPerLodOffset[2] : 0;
|
|
||||||
this.indirectArgsOffset3 = lodCount > 3 ? indirectArgsPerLodOffset[3] : 0;
|
|
||||||
this.indirectArgsOffset4 = lodCount > 4 ? indirectArgsPerLodOffset[4] : 0;
|
|
||||||
this.indirectArgsOffset5 = lodCount > 5 ? indirectArgsPerLodOffset[5] : 0;
|
|
||||||
this.indirectArgsOffset6 = lodCount > 6 ? indirectArgsPerLodOffset[6] : 0;
|
|
||||||
this.indirectArgsOffset7 = lodCount > 7 ? indirectArgsPerLodOffset[7] : 0;
|
|
||||||
|
|
||||||
this.indirectArgsCount0 = lodCount > 0 ? indirectArgsPerLodCount[0] : 0;
|
|
||||||
this.indirectArgsCount1 = lodCount > 1 ? indirectArgsPerLodCount[1] : 0;
|
|
||||||
this.indirectArgsCount2 = lodCount > 2 ? indirectArgsPerLodCount[2] : 0;
|
|
||||||
this.indirectArgsCount3 = lodCount > 3 ? indirectArgsPerLodCount[3] : 0;
|
|
||||||
this.indirectArgsCount4 = lodCount > 4 ? indirectArgsPerLodCount[4] : 0;
|
|
||||||
this.indirectArgsCount5 = lodCount > 5 ? indirectArgsPerLodCount[5] : 0;
|
|
||||||
this.indirectArgsCount6 = lodCount > 6 ? indirectArgsPerLodCount[6] : 0;
|
|
||||||
this.indirectArgsCount7 = lodCount > 7 ? indirectArgsPerLodCount[7] : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 80369a8a9c9a0e64daddf39789fa0f66
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public interface IInstanceRenderSettings
|
|
||||||
{
|
|
||||||
InstanceRenderSettings Settings { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct InstanceRenderSettings
|
|
||||||
{
|
|
||||||
int hashCodeCache;
|
|
||||||
public bool Supported;
|
|
||||||
public bool Render;
|
|
||||||
public float RenderDistance;
|
|
||||||
public float ShadowDistance;
|
|
||||||
public bool Shadows;
|
|
||||||
public float DensityInDistance;
|
|
||||||
public Vector2 DensityInDistanceFalloff;
|
|
||||||
public static InstanceRenderSettings Default(Camera camera) => new InstanceRenderSettings()
|
|
||||||
{
|
|
||||||
Supported = true,
|
|
||||||
Render = true,
|
|
||||||
Shadows = true,
|
|
||||||
RenderDistance = camera.farClipPlane,
|
|
||||||
ShadowDistance = camera.farClipPlane,
|
|
||||||
DensityInDistance = 1f,
|
|
||||||
DensityInDistanceFalloff = Vector2.zero,
|
|
||||||
};
|
|
||||||
|
|
||||||
public void Merge(InstanceRenderSettings other)
|
|
||||||
{
|
|
||||||
Supported = Supported && other.Supported;
|
|
||||||
Render = Render && other.Render;
|
|
||||||
Shadows = Shadows && other.Shadows;
|
|
||||||
if (RenderDistance > 0.0f && other.RenderDistance > 0.0f)
|
|
||||||
RenderDistance = Mathf.Min(RenderDistance, other.RenderDistance);
|
|
||||||
else if (other.RenderDistance > 0.0f)
|
|
||||||
RenderDistance = other.RenderDistance;
|
|
||||||
if (ShadowDistance > 0.0f && other.ShadowDistance > 0.0f)
|
|
||||||
ShadowDistance = Mathf.Min(ShadowDistance, other.ShadowDistance);
|
|
||||||
else if (other.ShadowDistance > 0.0f)
|
|
||||||
ShadowDistance = other.ShadowDistance;
|
|
||||||
DensityInDistance = Mathf.Min(DensityInDistance, other.DensityInDistance);
|
|
||||||
DensityInDistanceFalloff.x = Mathf.Max(DensityInDistanceFalloff.x, other.DensityInDistanceFalloff.x);
|
|
||||||
DensityInDistanceFalloff.y = Mathf.Max(DensityInDistanceFalloff.y, other.DensityInDistanceFalloff.y);
|
|
||||||
|
|
||||||
hashCodeCache = HashCode.Combine(Render, Shadows, RenderDistance, ShadowDistance, DensityInDistance, DensityInDistanceFalloff.x, DensityInDistanceFalloff.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
if(hashCodeCache == 0 || (Application.isEditor && !Application.isPlaying))
|
|
||||||
hashCodeCache = HashCode.Combine(Render, Shadows, RenderDistance, ShadowDistance, DensityInDistance, DensityInDistanceFalloff.x, DensityInDistanceFalloff.y);
|
|
||||||
|
|
||||||
return hashCodeCache;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: ada01dd02c566634092d55b23558e707
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class InstanceStreamer
|
|
||||||
{
|
|
||||||
public struct InstanceInfo
|
|
||||||
{
|
|
||||||
public int instanceId;
|
|
||||||
public int objectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Cell
|
|
||||||
{
|
|
||||||
public float CenterX;
|
|
||||||
public float CenterZ;
|
|
||||||
public bool IsLoaded;
|
|
||||||
public bool IsLoading;
|
|
||||||
public int inRangeOfAnyCamera;
|
|
||||||
public List<InstanceInfo> loadedInstances = new List<InstanceInfo>();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static int nextStreamerId = 1;
|
|
||||||
protected readonly int streamerInstanceId = Interlocked.Increment(ref nextStreamerId);
|
|
||||||
|
|
||||||
public readonly HashSet<int> owners = new HashSet<int>();
|
|
||||||
public Dictionary<int, HashSet<uint>> objectInstanceIds = new Dictionary<int, HashSet<uint>>();
|
|
||||||
public virtual int AddInstance(int objectId, Vector3 pos, Quaternion orientation, float scaleXZ, float scaleY)
|
|
||||||
{
|
|
||||||
var instanceId = RendererPool.AddInstance(objectId, pos, orientation, scaleXZ, scaleY);
|
|
||||||
|
|
||||||
if (!objectInstanceIds.ContainsKey(objectId))
|
|
||||||
objectInstanceIds.Add(objectId, new HashSet<uint>());
|
|
||||||
|
|
||||||
objectInstanceIds[objectId].Add((uint)instanceId);
|
|
||||||
|
|
||||||
return instanceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void RemoveInstance(int objectId, int instanceId, bool noRemove = false)
|
|
||||||
{
|
|
||||||
if (noRemove)
|
|
||||||
{
|
|
||||||
RendererPool.RemoveInstance(objectId, instanceId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!objectInstanceIds.ContainsKey(objectId))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (objectInstanceIds[objectId].Remove((uint)instanceId))
|
|
||||||
RendererPool.RemoveInstance(objectId, instanceId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Clear()
|
|
||||||
{
|
|
||||||
foreach (var obj in objectInstanceIds)
|
|
||||||
{
|
|
||||||
if (!objectInstanceIds.ContainsKey(obj.Key))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
foreach (var id in obj.Value)
|
|
||||||
{
|
|
||||||
RemoveInstance(obj.Key, (int)id, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
objectInstanceIds.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void UpdateForCamera(Camera camera, Plane[] planes)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual bool IsInRange(Camera camera, Plane[] planes)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return streamerInstanceId.GetHashCode();//HashCode.Combine(streamerInstanceId, objectInstanceIds.Count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: e591f7bae0131d548ab936dcae307d64
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
266
ObjectData.cs
266
ObjectData.cs
|
|
@ -1,266 +0,0 @@
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Rendering;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class ObjectData
|
|
||||||
{
|
|
||||||
public readonly GameObject GameObject;
|
|
||||||
public readonly HashSet<int> Owners = new HashSet<int>();
|
|
||||||
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<int> indirectArgsPerSubmeshOffsets = new List<int>();
|
|
||||||
public uint lodCount = 1;
|
|
||||||
public uint fadeLods = 0;
|
|
||||||
public uint indirectArgsCount = 1;
|
|
||||||
public uint count = 0;
|
|
||||||
public IInstanceRenderSettings Settings;
|
|
||||||
|
|
||||||
public List<GraphicsBuffer.IndirectDrawIndexedArgs> indirectDrawIndexedArgs = new List<GraphicsBuffer.IndirectDrawIndexedArgs>();
|
|
||||||
|
|
||||||
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<LODGroup>();
|
|
||||||
|
|
||||||
LOD[] lodArray;
|
|
||||||
|
|
||||||
if (lodGroup == null)
|
|
||||||
lodArray = new LOD[1]
|
|
||||||
{
|
|
||||||
new LOD(0.0001f, GameObject.GetComponentsInChildren<MeshRenderer>())
|
|
||||||
};
|
|
||||||
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<GraphicsBuffer.IndirectDrawIndexedArgs> 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<MeshFilter>();
|
|
||||||
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<MeshFilter>();
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 38c29b0618523cb44971f341d53349bf
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
541
RendererPool.cs
541
RendererPool.cs
|
|
@ -1,541 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading;
|
|
||||||
using Unity.Collections;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Rendering;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public static class RendererPool
|
|
||||||
{
|
|
||||||
public const int RESIZE_COUNT = 100000;
|
|
||||||
public const int STREAMOUT_SIZE = 10000;
|
|
||||||
|
|
||||||
public static bool isDisposed = true;
|
|
||||||
private static ComputeShader instancingShader = null;
|
|
||||||
private static CommandBuffer commandBuffer = null;
|
|
||||||
private static int uploadInstancesShaderId = 0, resizeInstanceBufferShaderId = 0;
|
|
||||||
|
|
||||||
private static object threadSecurity = new object();
|
|
||||||
private static int maxInstanceCount = 0;
|
|
||||||
private static int instanceUploadCount = 0;
|
|
||||||
private static readonly Dictionary<int, CameraRenderer> cameras = new Dictionary<int, CameraRenderer>();
|
|
||||||
public static readonly Dictionary<int, ObjectData> objects = new Dictionary<int, ObjectData>();
|
|
||||||
public static readonly Dictionary<int, InstanceStreamer> streamers = new Dictionary<int, InstanceStreamer>();
|
|
||||||
public static readonly List<ObjectData> objectsList = new List<ObjectData>();
|
|
||||||
public static readonly List<PrefabData> prefabData = new List<PrefabData>();
|
|
||||||
|
|
||||||
public static NativeArray<InstanceData> uploadInstancesArray;
|
|
||||||
|
|
||||||
public static readonly Queue<InstanceData> uploadInstancesArrayQueue = new Queue<InstanceData>();
|
|
||||||
|
|
||||||
public static readonly Queue<uint> prefabDataFreeSlot = new Queue<uint>();
|
|
||||||
public static readonly Queue<int> instanceBufferFreeSlot = new Queue<int>();
|
|
||||||
|
|
||||||
public static ComputeBuffer globalUpdateInstanceBuffer = null;
|
|
||||||
public static ComputeBuffer globalInstanceBuffer = null;
|
|
||||||
public static ComputeBuffer tempGlobalInstanceBuffer = null;
|
|
||||||
|
|
||||||
private static Vector3 _origin;
|
|
||||||
private static List<int> _keysToRemove = new List<int>();
|
|
||||||
|
|
||||||
private static bool _instanceCountChanged;
|
|
||||||
|
|
||||||
public static bool externInstanceCountChanged
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == true)
|
|
||||||
{
|
|
||||||
foreach (var camera in cameras.Values)
|
|
||||||
{
|
|
||||||
camera.instanceCountChanged = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool instanceCountChanged
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == true && !_instanceCountChanged)
|
|
||||||
{
|
|
||||||
_instanceCountChanged = true;
|
|
||||||
foreach (var camera in cameras.Values)
|
|
||||||
{
|
|
||||||
camera.instanceCountChanged = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool rebuildPrefabs
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == true)
|
|
||||||
{
|
|
||||||
foreach (var camera in cameras.Values)
|
|
||||||
{
|
|
||||||
camera.rebuildPrefabs = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Destroy()
|
|
||||||
{
|
|
||||||
if (isDisposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
isDisposed = true;
|
|
||||||
|
|
||||||
_keysToRemove.Clear();
|
|
||||||
|
|
||||||
foreach (KeyValuePair<int, CameraRenderer> camera in cameras)
|
|
||||||
{
|
|
||||||
_keysToRemove.Add(camera.Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int id in _keysToRemove)
|
|
||||||
RemoveCamera(id);
|
|
||||||
|
|
||||||
maxInstanceCount = 0;
|
|
||||||
instanceUploadCount = 0;
|
|
||||||
|
|
||||||
uploadInstancesArrayQueue.Clear();
|
|
||||||
|
|
||||||
globalInstanceBuffer?.Dispose();
|
|
||||||
globalInstanceBuffer = null;
|
|
||||||
globalUpdateInstanceBuffer?.Dispose();
|
|
||||||
globalUpdateInstanceBuffer = null;
|
|
||||||
tempGlobalInstanceBuffer?.Dispose();
|
|
||||||
tempGlobalInstanceBuffer = null;
|
|
||||||
commandBuffer?.Dispose();
|
|
||||||
commandBuffer = null;
|
|
||||||
|
|
||||||
prefabDataFreeSlot.Clear();
|
|
||||||
instanceBufferFreeSlot.Clear();
|
|
||||||
_keysToRemove.Clear();
|
|
||||||
InstanceStreamer.nextStreamerId = 1;
|
|
||||||
CameraRenderer.nextCameraRendererId = 1;
|
|
||||||
|
|
||||||
uploadInstancesArray.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void BuildBuffers()
|
|
||||||
{
|
|
||||||
Monitor.Enter(threadSecurity);
|
|
||||||
bool deleteOldBuffer = false;
|
|
||||||
//Load shader TODO: (Move this to init?)
|
|
||||||
if(instancingShader == null)
|
|
||||||
{
|
|
||||||
uploadInstancesArray = new NativeArray<InstanceData>(STREAMOUT_SIZE, Allocator.Persistent);
|
|
||||||
|
|
||||||
instancingShader = UnityEngine.Object.Instantiate(Resources.Load<ComputeShader>("ThoMagic Renderer Instancing"));
|
|
||||||
instancingShader.hideFlags = HideFlags.HideAndDontSave;
|
|
||||||
uploadInstancesShaderId = instancingShader.FindKernel("Upload_64");
|
|
||||||
resizeInstanceBufferShaderId = instancingShader.FindKernel("Resize_64");
|
|
||||||
|
|
||||||
instancingShader.name = $"ThoMagic Renderer Instancing - RendererPool";
|
|
||||||
commandBuffer = new CommandBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
commandBuffer.Clear();
|
|
||||||
|
|
||||||
//Are there queued instances ready for upload no current upload is planned?
|
|
||||||
if (instanceUploadCount == 0 && uploadInstancesArrayQueue.Count > 0)
|
|
||||||
{
|
|
||||||
int take = Math.Min(STREAMOUT_SIZE, uploadInstancesArrayQueue.Count);
|
|
||||||
for (int i = 0; i < take; i++)
|
|
||||||
{
|
|
||||||
uploadInstancesArray[instanceUploadCount++] = uploadInstancesArrayQueue.Dequeue();
|
|
||||||
}
|
|
||||||
|
|
||||||
instanceCountChanged = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Upload instances
|
|
||||||
if (_instanceCountChanged && maxInstanceCount > 0)
|
|
||||||
{
|
|
||||||
//Create update buffer if null. TODO: Move to init?
|
|
||||||
if(globalUpdateInstanceBuffer == null)
|
|
||||||
{
|
|
||||||
globalUpdateInstanceBuffer = new ComputeBuffer(STREAMOUT_SIZE, InstanceData.Stride, ComputeBufferType.Structured);//, ComputeBufferMode.SubUpdates);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Resize and copy global instance buffer in gpu
|
|
||||||
if (globalInstanceBuffer == null || globalInstanceBuffer.count < maxInstanceCount)
|
|
||||||
{
|
|
||||||
int nextCount = RESIZE_COUNT * ((maxInstanceCount - 1) / RESIZE_COUNT + 1);
|
|
||||||
|
|
||||||
tempGlobalInstanceBuffer = new ComputeBuffer(nextCount, InstanceData.Stride, ComputeBufferType.Structured);
|
|
||||||
|
|
||||||
tempGlobalInstanceBuffer.name = $"ThoMagic global instance buffer";
|
|
||||||
Debug.Log($"Upsized globalInstanceBuffer {nextCount.ToString("N")} / {(nextCount * InstanceData.Stride / 1024 / 1024)}mb");
|
|
||||||
|
|
||||||
if (globalInstanceBuffer != null)
|
|
||||||
{
|
|
||||||
deleteOldBuffer = true;
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, resizeInstanceBufferShaderId, "tempGlobalInstances", tempGlobalInstanceBuffer);
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, resizeInstanceBufferShaderId, "globalInstances", globalInstanceBuffer);
|
|
||||||
commandBuffer.SetComputeIntParam(instancingShader, "_CountResize", globalInstanceBuffer.count);
|
|
||||||
commandBuffer.DispatchCompute(instancingShader, resizeInstanceBufferShaderId, Mathf.CeilToInt(globalInstanceBuffer.count / 64.0f), 1, 1);
|
|
||||||
|
|
||||||
/*instancingShader.SetBuffer(resizeInstanceBufferShaderId, "tempGlobalInstances", tempGlobalInstanceBuffer);
|
|
||||||
instancingShader.SetBuffer(resizeInstanceBufferShaderId, "globalInstances", globalInstanceBuffer);
|
|
||||||
instancingShader.SetInt("_Count", globalInstanceBuffer.count);
|
|
||||||
instancingShader.Dispatch(resizeInstanceBufferShaderId, Mathf.CeilToInt(globalInstanceBuffer.count / 64.0f), 1, 1);
|
|
||||||
|
|
||||||
globalInstanceBuffer.Dispose();
|
|
||||||
globalInstanceBuffer = tempGlobalInstanceBuffer;
|
|
||||||
tempGlobalInstanceBuffer = null;*/
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
globalInstanceBuffer = tempGlobalInstanceBuffer;
|
|
||||||
//tempGlobalInstanceBuffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tempGlobalInstanceBuffer = globalInstanceBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Upload
|
|
||||||
globalUpdateInstanceBuffer.SetData(uploadInstancesArray);
|
|
||||||
/*var updateTarget = globalUpdateInstanceBuffer.BeginWrite<InstanceData>(0, instanceUploadCount);
|
|
||||||
NativeArray<InstanceData>.Copy(uploadInstancesArray, updateTarget, instanceUploadCount);
|
|
||||||
globalUpdateInstanceBuffer.EndWrite<InstanceData>(instanceUploadCount);*/
|
|
||||||
|
|
||||||
/*instancingShader.SetBuffer(uploadInstancesShaderId, "globalUploadInstances", globalUpdateInstanceBuffer);
|
|
||||||
instancingShader.SetBuffer(uploadInstancesShaderId, "globalInstances", globalInstanceBuffer);
|
|
||||||
instancingShader.SetInt("_Count", instanceUploadCount);
|
|
||||||
instancingShader.Dispatch(uploadInstancesShaderId, Mathf.CeilToInt(instanceUploadCount / 64.0f), 1, 1);*/
|
|
||||||
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, uploadInstancesShaderId, "globalUploadInstances", globalUpdateInstanceBuffer);
|
|
||||||
commandBuffer.SetComputeBufferParam(instancingShader, uploadInstancesShaderId, "globalInstances", tempGlobalInstanceBuffer);
|
|
||||||
commandBuffer.SetComputeIntParam(instancingShader, "_CountUpload", instanceUploadCount);
|
|
||||||
commandBuffer.DispatchCompute(instancingShader, uploadInstancesShaderId, Mathf.CeilToInt(instanceUploadCount / 64.0f), 1, 1);
|
|
||||||
|
|
||||||
Graphics.ExecuteCommandBuffer(commandBuffer);
|
|
||||||
|
|
||||||
var fence = Graphics.CreateGraphicsFence(GraphicsFenceType.AsyncQueueSynchronisation, SynchronisationStageFlags.ComputeProcessing);
|
|
||||||
Graphics.WaitOnAsyncGraphicsFence(fence);
|
|
||||||
|
|
||||||
if(deleteOldBuffer)
|
|
||||||
{
|
|
||||||
globalInstanceBuffer.Dispose();
|
|
||||||
globalInstanceBuffer = tempGlobalInstanceBuffer;
|
|
||||||
tempGlobalInstanceBuffer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
instanceUploadCount = 0;
|
|
||||||
_instanceCountChanged = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Monitor.Exit(threadSecurity);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Instance handling
|
|
||||||
internal static int AddInstance(int objectId, Vector3 pos, Quaternion orientation, float scaleXZ, float scaleY)
|
|
||||||
{
|
|
||||||
if (isDisposed)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
Monitor.Enter(threadSecurity);
|
|
||||||
|
|
||||||
var myId = maxInstanceCount;
|
|
||||||
var objectData = GetObjectData(objectId);
|
|
||||||
objectData.count++;
|
|
||||||
|
|
||||||
//Take free slot in instance buffer
|
|
||||||
if (instanceBufferFreeSlot.Count > 0)
|
|
||||||
{
|
|
||||||
myId = instanceBufferFreeSlot.Dequeue();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
maxInstanceCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Directly upload if STREAMOUT_SIZE not reached, otherwise queue for upload
|
|
||||||
if (instanceUploadCount < STREAMOUT_SIZE && uploadInstancesArrayQueue.Count == 0)
|
|
||||||
uploadInstancesArray[instanceUploadCount++] = new InstanceData(InstanceData.Add, myId, objectData.prefabId, pos.x, pos.y, pos.z, orientation, scaleXZ, scaleY);
|
|
||||||
else
|
|
||||||
uploadInstancesArrayQueue.Enqueue(new InstanceData(InstanceData.Add, myId, objectData.prefabId, pos.x, pos.y, pos.z, orientation, scaleXZ, scaleY));
|
|
||||||
|
|
||||||
instanceCountChanged = true;
|
|
||||||
|
|
||||||
Monitor.Exit(threadSecurity);
|
|
||||||
|
|
||||||
return myId;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void RemoveInstance(int objectId, int instanceId)
|
|
||||||
{
|
|
||||||
if (isDisposed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Monitor.Enter(threadSecurity);
|
|
||||||
|
|
||||||
var objectData = GetObjectData(objectId);
|
|
||||||
objectData.count--;
|
|
||||||
|
|
||||||
//No need to reset instance data because the cullable index is removed, instance data will be reset on reuse.
|
|
||||||
/*if (instanceUploadCount < STREAMOUT_SIZE && uploadInstancesArrayQueue.Count == 0)
|
|
||||||
uploadInstancesArray[instanceUploadCount++] = new InstanceData(InstanceData.Delete, instanceId);
|
|
||||||
else
|
|
||||||
uploadInstancesArrayQueue.Enqueue(new InstanceData(InstanceData.Delete, instanceId));*/
|
|
||||||
|
|
||||||
externInstanceCountChanged = true;
|
|
||||||
//Remember free slot in instance buffer
|
|
||||||
instanceBufferFreeSlot.Enqueue(instanceId);
|
|
||||||
|
|
||||||
Monitor.Exit(threadSecurity);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Camera handling
|
|
||||||
public static IEnumerable<CameraRenderer> GetCameras()
|
|
||||||
{
|
|
||||||
return cameras.Values;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int RegisterCamera(Camera camera)
|
|
||||||
{
|
|
||||||
int num = camera != null ? camera.GetHashCode() : throw new ArgumentNullException(nameof(camera));
|
|
||||||
if (!cameras.ContainsKey(num))
|
|
||||||
{
|
|
||||||
CameraRenderer cameraRenderer = new CameraRenderer(camera);
|
|
||||||
cameraRenderer.SetFloatingOrigin(in _origin);
|
|
||||||
cameras.Add(num, cameraRenderer);
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CameraRenderer GetCamera(int cameraId)
|
|
||||||
{
|
|
||||||
CameraRenderer cameraRenderer;
|
|
||||||
return cameras.TryGetValue(cameraId, out cameraRenderer) ? cameraRenderer : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool RemoveCamera(int cameraId)
|
|
||||||
{
|
|
||||||
CameraRenderer cameraRenderer;
|
|
||||||
if (!cameras.TryGetValue(cameraId, out cameraRenderer))
|
|
||||||
return false;
|
|
||||||
cameraRenderer?.Dispose();
|
|
||||||
return cameras.Remove(cameraId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemoveDestroyedCameras()
|
|
||||||
{
|
|
||||||
_keysToRemove.Clear();
|
|
||||||
|
|
||||||
foreach (KeyValuePair<int, CameraRenderer> camera in cameras)
|
|
||||||
{
|
|
||||||
if (camera.Value.Camera == null)
|
|
||||||
_keysToRemove.Add(camera.Key);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (int id in _keysToRemove)
|
|
||||||
RemoveCamera(id);
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Object handling
|
|
||||||
public static int RegisterObject(GameObject gameObject, IInstanceRenderSettings settings, InstanceStreamer source, int ownerHash)
|
|
||||||
{
|
|
||||||
int num = gameObject != null ? /*gameObject.GetHashCode()*/ CalculateContentHash(gameObject) : throw new ArgumentNullException(nameof(gameObject));
|
|
||||||
|
|
||||||
if(!streamers.ContainsKey(ownerHash))
|
|
||||||
streamers.Add(ownerHash, source);
|
|
||||||
|
|
||||||
if (!objects.ContainsKey(num))
|
|
||||||
{
|
|
||||||
var obj = new ObjectData(gameObject, settings);
|
|
||||||
objects.Add(num, obj);
|
|
||||||
objectsList.Add(obj);
|
|
||||||
obj.contentHashCache = CalculateContentHash(num, false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
SetObjectSettings(gameObject, settings);
|
|
||||||
|
|
||||||
if (!objects[num].Owners.Contains(ownerHash))
|
|
||||||
objects[num].Owners.Add(ownerHash);
|
|
||||||
|
|
||||||
streamers[ownerHash].owners.Add(num);
|
|
||||||
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RemoveObject(int objectId, int ownerHash)
|
|
||||||
{
|
|
||||||
if (objects.TryGetValue(objectId, out var objectData))
|
|
||||||
{
|
|
||||||
objectData.Owners.Remove(ownerHash);
|
|
||||||
if (objectData.Owners.Count == 0)
|
|
||||||
{
|
|
||||||
objects.Remove(objectId);
|
|
||||||
objectsList.Remove(objectData);
|
|
||||||
prefabDataFreeSlot.Enqueue(objectData.prefabId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (streamers.TryGetValue(ownerHash, out var streamer))
|
|
||||||
{
|
|
||||||
streamer.owners.Remove(objectId);
|
|
||||||
|
|
||||||
if (streamer.owners.Count == 0)
|
|
||||||
streamers.Remove(ownerHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
rebuildPrefabs = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GameObject GetObject(int objectId)
|
|
||||||
{
|
|
||||||
return objects.TryGetValue(objectId, out var objectData) ? objectData.GameObject : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ObjectData GetObjectData(int objectId)
|
|
||||||
{
|
|
||||||
return objects[objectId];
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetObjectSettings(GameObject prefab, IInstanceRenderSettings instanceRenderSettings)
|
|
||||||
{
|
|
||||||
int hashCode = prefab.GetHashCode();
|
|
||||||
ObjectData objectData;
|
|
||||||
if (!objects.TryGetValue(hashCode, out objectData))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ((objectData.Settings == null && instanceRenderSettings != null)
|
|
||||||
|| (objectData.Settings != null && instanceRenderSettings == null)
|
|
||||||
|| instanceRenderSettings.Settings.GetHashCode() != objectData.Settings.Settings.GetHashCode())
|
|
||||||
{
|
|
||||||
objectData.Settings = instanceRenderSettings;
|
|
||||||
rebuildPrefabs = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int CalculateContentHash(GameObject gameObject)
|
|
||||||
{
|
|
||||||
int num = 13;
|
|
||||||
if (gameObject == null)
|
|
||||||
return num;
|
|
||||||
|
|
||||||
var lodGroup = gameObject.GetComponent<LODGroup>();
|
|
||||||
|
|
||||||
LOD[] lodArray;
|
|
||||||
|
|
||||||
if (lodGroup == null)
|
|
||||||
lodArray = new LOD[1]
|
|
||||||
{
|
|
||||||
new LOD(0.0001f, gameObject.GetComponentsInChildren<MeshRenderer>())
|
|
||||||
};
|
|
||||||
else
|
|
||||||
lodArray = lodGroup.GetLODs();
|
|
||||||
|
|
||||||
|
|
||||||
foreach (LOD loD in lodArray)
|
|
||||||
num = HashCode.Combine<int, int>(num, CalculateContentHash(loD));
|
|
||||||
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int CalculateContentHash(int objectId, bool reload = false)
|
|
||||||
{
|
|
||||||
var obj = objects[objectId];
|
|
||||||
|
|
||||||
int num = 13;
|
|
||||||
if (obj.GameObject == null)
|
|
||||||
return num;
|
|
||||||
|
|
||||||
if (reload)
|
|
||||||
{
|
|
||||||
var lodGroup = obj.GameObject.GetComponent<LODGroup>();
|
|
||||||
|
|
||||||
LOD[] lodArray;
|
|
||||||
|
|
||||||
if (lodGroup == null)
|
|
||||||
lodArray = new LOD[1]
|
|
||||||
{
|
|
||||||
new LOD(0.0001f, obj.GameObject.GetComponentsInChildren<MeshRenderer>())
|
|
||||||
};
|
|
||||||
else
|
|
||||||
lodArray = lodGroup.GetLODs();
|
|
||||||
|
|
||||||
obj.LODs = lodArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (LOD loD in obj.LODs)
|
|
||||||
num = HashCode.Combine<int, int>(num, CalculateContentHash(loD));
|
|
||||||
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int CalculateContentHash(LOD lod)
|
|
||||||
{
|
|
||||||
int num = 13;
|
|
||||||
if (lod.renderers != null)
|
|
||||||
{
|
|
||||||
foreach (var renderer in lod.renderers)
|
|
||||||
{
|
|
||||||
if (renderer == null)
|
|
||||||
{
|
|
||||||
num = HashCode.Combine<int, int>(num, 13);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MeshFilter component = renderer.GetComponent<MeshFilter>();
|
|
||||||
|
|
||||||
num = HashCode.Combine<int, int, int, int>(HashCode.Combine<int, int, int, int, int, int, int, int>(
|
|
||||||
num,
|
|
||||||
component == null || component.sharedMesh == null ? 13 : component.sharedMesh.GetHashCode(),
|
|
||||||
renderer.shadowCastingMode.GetHashCode(),
|
|
||||||
CalculateContentHash(renderer.sharedMaterials),
|
|
||||||
renderer.motionVectorGenerationMode.GetHashCode(),
|
|
||||||
renderer.receiveShadows.GetHashCode(),
|
|
||||||
renderer.rendererPriority.GetHashCode(),
|
|
||||||
renderer.renderingLayerMask.GetHashCode()),
|
|
||||||
renderer.gameObject.layer.GetHashCode(),
|
|
||||||
renderer.gameObject.transform.localPosition.GetHashCode(),
|
|
||||||
lod.screenRelativeTransitionHeight.GetHashCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int CalculateContentHash(Material[] materials)
|
|
||||||
{
|
|
||||||
int num = 13;
|
|
||||||
if (materials != null)
|
|
||||||
{
|
|
||||||
foreach (Material material in materials)
|
|
||||||
num = HashCode.Combine<int, int>(num,
|
|
||||||
material != null ? material.GetHashCode() : 13);
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool ContentHashChanged(int objectId)
|
|
||||||
{
|
|
||||||
if (!objects.ContainsKey(objectId))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return objects[objectId].contentHashCache != CalculateContentHash(objectId, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void Initialize()
|
|
||||||
{
|
|
||||||
isDisposed = false;
|
|
||||||
BuildBuffers();
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 76aaa879e050ed44ba26642123a3d6fe
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class RendererUtility
|
|
||||||
{
|
|
||||||
public static GameObject GetPrototypeToRender(DetailPrototype prototype) => prototype.prototype == null ? null :
|
|
||||||
prototype.prototype.transform.root.gameObject;
|
|
||||||
|
|
||||||
public static bool SupportsProceduralInstancing(GameObject gameObject)
|
|
||||||
{
|
|
||||||
foreach (var componentsInChild in gameObject.GetComponentsInChildren<MeshRenderer>())
|
|
||||||
{
|
|
||||||
foreach (var sharedMaterial in componentsInChild.sharedMaterials)
|
|
||||||
{
|
|
||||||
if (sharedMaterial != null && sharedMaterial.shader != null && !RendererUtility.SupportsProceduralInstancing(sharedMaterial))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool SupportsProceduralInstancing(Material material)
|
|
||||||
{
|
|
||||||
if (material == null)
|
|
||||||
throw new ArgumentNullException(nameof(material));
|
|
||||||
if (material.shader == null)
|
|
||||||
throw new ArgumentNullException("material.shader");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsSupportedByUnity(DetailPrototype prototype) => !prototype.usePrototypeMesh || prototype.prototype == null || prototype.prototype.GetComponent<LODGroup>() == null;
|
|
||||||
|
|
||||||
public static GameObject GetSupportedPlaceholder(DetailPrototype prototype) => IsSupportedByUnity(prototype) ? prototype.prototype : GetSupportedPlaceholder(prototype.prototype);
|
|
||||||
|
|
||||||
public static GameObject GetSupportedPlaceholder(GameObject prototype)
|
|
||||||
{
|
|
||||||
if (prototype == null)
|
|
||||||
return prototype;
|
|
||||||
LODGroup component = prototype.GetComponent<LODGroup>();
|
|
||||||
if (component == null)
|
|
||||||
return prototype;
|
|
||||||
foreach (LOD loD in component.GetLODs())
|
|
||||||
{
|
|
||||||
foreach (UnityEngine.Renderer renderer in loD.renderers)
|
|
||||||
{
|
|
||||||
if (renderer != null)
|
|
||||||
return renderer.gameObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 8948a91a0e0980f428ba7d5418d8dc6f
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 66cad6ae64c1463448771e8e1325a51c
|
|
||||||
folderAsset: yes
|
|
||||||
DefaultImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,438 +0,0 @@
|
||||||
{
|
|
||||||
"m_SGVersion": 3,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.GraphData",
|
|
||||||
"m_ObjectId": "4ea35b44b3fe4b25a7cb4e39a4b9b407",
|
|
||||||
"m_Properties": [],
|
|
||||||
"m_Keywords": [
|
|
||||||
{
|
|
||||||
"m_Id": "9ba4f485275c4fc9b2f1ce82a79369c4"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"m_Dropdowns": [],
|
|
||||||
"m_CategoryData": [
|
|
||||||
{
|
|
||||||
"m_Id": "480708220ad2491387a14ef84b7fe431"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"m_Nodes": [
|
|
||||||
{
|
|
||||||
"m_Id": "281b26d889224fc487a79fa8214bd0b1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"m_Id": "c5119037ba0c48fbba1bb84ead9a5adb"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"m_Id": "4e57f58cc3d24bf1b53cde5e846cbd31"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"m_Id": "304803996acc4ca999953697d05a2515"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"m_GroupDatas": [],
|
|
||||||
"m_StickyNoteDatas": [],
|
|
||||||
"m_Edges": [
|
|
||||||
{
|
|
||||||
"m_OutputSlot": {
|
|
||||||
"m_Node": {
|
|
||||||
"m_Id": "304803996acc4ca999953697d05a2515"
|
|
||||||
},
|
|
||||||
"m_SlotId": 1
|
|
||||||
},
|
|
||||||
"m_InputSlot": {
|
|
||||||
"m_Node": {
|
|
||||||
"m_Id": "281b26d889224fc487a79fa8214bd0b1"
|
|
||||||
},
|
|
||||||
"m_SlotId": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"m_OutputSlot": {
|
|
||||||
"m_Node": {
|
|
||||||
"m_Id": "4e57f58cc3d24bf1b53cde5e846cbd31"
|
|
||||||
},
|
|
||||||
"m_SlotId": 1
|
|
||||||
},
|
|
||||||
"m_InputSlot": {
|
|
||||||
"m_Node": {
|
|
||||||
"m_Id": "304803996acc4ca999953697d05a2515"
|
|
||||||
},
|
|
||||||
"m_SlotId": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"m_OutputSlot": {
|
|
||||||
"m_Node": {
|
|
||||||
"m_Id": "c5119037ba0c48fbba1bb84ead9a5adb"
|
|
||||||
},
|
|
||||||
"m_SlotId": 0
|
|
||||||
},
|
|
||||||
"m_InputSlot": {
|
|
||||||
"m_Node": {
|
|
||||||
"m_Id": "4e57f58cc3d24bf1b53cde5e846cbd31"
|
|
||||||
},
|
|
||||||
"m_SlotId": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"m_VertexContext": {
|
|
||||||
"m_Position": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0
|
|
||||||
},
|
|
||||||
"m_Blocks": []
|
|
||||||
},
|
|
||||||
"m_FragmentContext": {
|
|
||||||
"m_Position": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0
|
|
||||||
},
|
|
||||||
"m_Blocks": []
|
|
||||||
},
|
|
||||||
"m_PreviewData": {
|
|
||||||
"serializedMesh": {
|
|
||||||
"m_SerializedMesh": "{\"mesh\":{\"instanceID\":0}}",
|
|
||||||
"m_Guid": ""
|
|
||||||
},
|
|
||||||
"preventRotation": false
|
|
||||||
},
|
|
||||||
"m_Path": "Sub Graphs",
|
|
||||||
"m_GraphPrecision": 1,
|
|
||||||
"m_PreviewMode": 2,
|
|
||||||
"m_OutputNode": {
|
|
||||||
"m_Id": "281b26d889224fc487a79fa8214bd0b1"
|
|
||||||
},
|
|
||||||
"m_ActiveTargets": []
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 0,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot",
|
|
||||||
"m_ObjectId": "13e38f92c90c4e709dc150f29929c84a",
|
|
||||||
"m_Id": 0,
|
|
||||||
"m_DisplayName": "Out",
|
|
||||||
"m_SlotType": 1,
|
|
||||||
"m_Hidden": false,
|
|
||||||
"m_ShaderOutputName": "Out",
|
|
||||||
"m_StageCapability": 3,
|
|
||||||
"m_Value": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_DefaultValue": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_Labels": []
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 0,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.SubGraphOutputNode",
|
|
||||||
"m_ObjectId": "281b26d889224fc487a79fa8214bd0b1",
|
|
||||||
"m_Group": {
|
|
||||||
"m_Id": ""
|
|
||||||
},
|
|
||||||
"m_Name": "Output",
|
|
||||||
"m_DrawState": {
|
|
||||||
"m_Expanded": true,
|
|
||||||
"m_Position": {
|
|
||||||
"serializedVersion": "2",
|
|
||||||
"x": 25.00006866455078,
|
|
||||||
"y": -310.0,
|
|
||||||
"width": 96.99996185302735,
|
|
||||||
"height": 77.00001525878906
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"m_Slots": [
|
|
||||||
{
|
|
||||||
"m_Id": "aca287a71573483789d5dcb380daa40c"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"synonyms": [],
|
|
||||||
"m_Precision": 0,
|
|
||||||
"m_PreviewExpanded": true,
|
|
||||||
"m_DismissedVersion": 0,
|
|
||||||
"m_PreviewMode": 0,
|
|
||||||
"m_CustomColors": {
|
|
||||||
"m_SerializableColors": []
|
|
||||||
},
|
|
||||||
"IsFirstSlotValid": true
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 1,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.CustomFunctionNode",
|
|
||||||
"m_ObjectId": "304803996acc4ca999953697d05a2515",
|
|
||||||
"m_Group": {
|
|
||||||
"m_Id": ""
|
|
||||||
},
|
|
||||||
"m_Name": "IncludeThoMagicRenderer (Custom Function)",
|
|
||||||
"m_DrawState": {
|
|
||||||
"m_Expanded": true,
|
|
||||||
"m_Position": {
|
|
||||||
"serializedVersion": "2",
|
|
||||||
"x": -330.0,
|
|
||||||
"y": -310.0,
|
|
||||||
"width": 304.0,
|
|
||||||
"height": 94.0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"m_Slots": [
|
|
||||||
{
|
|
||||||
"m_Id": "59dd7ed4b8b14695ab4443b7b89ab9df"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"m_Id": "79b2dd99d8ea4726856c03ea82ede95b"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"synonyms": [
|
|
||||||
"code",
|
|
||||||
"HLSL"
|
|
||||||
],
|
|
||||||
"m_Precision": 0,
|
|
||||||
"m_PreviewExpanded": false,
|
|
||||||
"m_DismissedVersion": 0,
|
|
||||||
"m_PreviewMode": 0,
|
|
||||||
"m_CustomColors": {
|
|
||||||
"m_SerializableColors": []
|
|
||||||
},
|
|
||||||
"m_SourceType": 0,
|
|
||||||
"m_FunctionName": "IncludeThoMagicRenderer",
|
|
||||||
"m_FunctionSource": "678dff042fe9c004cb7522c2233af44d",
|
|
||||||
"m_FunctionBody": "Enter function body here..."
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 0,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot",
|
|
||||||
"m_ObjectId": "45a92477b6a7449c9c4d29eac6f7f4c1",
|
|
||||||
"m_Id": 0,
|
|
||||||
"m_DisplayName": "In",
|
|
||||||
"m_SlotType": 0,
|
|
||||||
"m_Hidden": false,
|
|
||||||
"m_ShaderOutputName": "In",
|
|
||||||
"m_StageCapability": 3,
|
|
||||||
"m_Value": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_DefaultValue": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_Labels": []
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 0,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.CategoryData",
|
|
||||||
"m_ObjectId": "480708220ad2491387a14ef84b7fe431",
|
|
||||||
"m_Name": "",
|
|
||||||
"m_ChildObjectList": [
|
|
||||||
{
|
|
||||||
"m_Id": "9ba4f485275c4fc9b2f1ce82a79369c4"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 1,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.CustomFunctionNode",
|
|
||||||
"m_ObjectId": "4e57f58cc3d24bf1b53cde5e846cbd31",
|
|
||||||
"m_Group": {
|
|
||||||
"m_Id": ""
|
|
||||||
},
|
|
||||||
"m_Name": "SetupThoMagicRenderer (Custom Function)",
|
|
||||||
"m_DrawState": {
|
|
||||||
"m_Expanded": true,
|
|
||||||
"m_Position": {
|
|
||||||
"serializedVersion": "2",
|
|
||||||
"x": -694.0,
|
|
||||||
"y": -310.0000305175781,
|
|
||||||
"width": 296.9999694824219,
|
|
||||||
"height": 94.00001525878906
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"m_Slots": [
|
|
||||||
{
|
|
||||||
"m_Id": "45a92477b6a7449c9c4d29eac6f7f4c1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"m_Id": "ca8752373fc04409ae302adfc1808024"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"synonyms": [
|
|
||||||
"code",
|
|
||||||
"HLSL"
|
|
||||||
],
|
|
||||||
"m_Precision": 0,
|
|
||||||
"m_PreviewExpanded": false,
|
|
||||||
"m_DismissedVersion": 0,
|
|
||||||
"m_PreviewMode": 0,
|
|
||||||
"m_CustomColors": {
|
|
||||||
"m_SerializableColors": []
|
|
||||||
},
|
|
||||||
"m_SourceType": 1,
|
|
||||||
"m_FunctionName": "SetupThoMagicRenderer",
|
|
||||||
"m_FunctionSource": "06018a56d442f704f82b130cdeaf74ef",
|
|
||||||
"m_FunctionBody": "Out = In;\n#pragma instancing_options procedural:SetupThoMagicRenderer\r\r\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 0,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot",
|
|
||||||
"m_ObjectId": "59dd7ed4b8b14695ab4443b7b89ab9df",
|
|
||||||
"m_Id": 0,
|
|
||||||
"m_DisplayName": "In",
|
|
||||||
"m_SlotType": 0,
|
|
||||||
"m_Hidden": false,
|
|
||||||
"m_ShaderOutputName": "In",
|
|
||||||
"m_StageCapability": 3,
|
|
||||||
"m_Value": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_DefaultValue": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_Labels": []
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 0,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot",
|
|
||||||
"m_ObjectId": "79b2dd99d8ea4726856c03ea82ede95b",
|
|
||||||
"m_Id": 1,
|
|
||||||
"m_DisplayName": "Out",
|
|
||||||
"m_SlotType": 1,
|
|
||||||
"m_Hidden": false,
|
|
||||||
"m_ShaderOutputName": "Out",
|
|
||||||
"m_StageCapability": 3,
|
|
||||||
"m_Value": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_DefaultValue": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_Labels": []
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 1,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.ShaderKeyword",
|
|
||||||
"m_ObjectId": "9ba4f485275c4fc9b2f1ce82a79369c4",
|
|
||||||
"m_Guid": {
|
|
||||||
"m_GuidSerialized": "019818f3-0f1b-49f3-8508-39d308ae3731"
|
|
||||||
},
|
|
||||||
"m_Name": "PROCEDURAL_INSTANCING_ON",
|
|
||||||
"m_DefaultRefNameVersion": 1,
|
|
||||||
"m_RefNameGeneratedByDisplayName": "PROCEDURAL_INSTANCING_ON",
|
|
||||||
"m_DefaultReferenceName": "_PROCEDURAL_INSTANCING_ON",
|
|
||||||
"m_OverrideReferenceName": "",
|
|
||||||
"m_GeneratePropertyBlock": true,
|
|
||||||
"m_UseCustomSlotLabel": false,
|
|
||||||
"m_CustomSlotLabel": "",
|
|
||||||
"m_DismissedVersion": 0,
|
|
||||||
"m_KeywordType": 0,
|
|
||||||
"m_KeywordDefinition": 0,
|
|
||||||
"m_KeywordScope": 1,
|
|
||||||
"m_KeywordStages": 63,
|
|
||||||
"m_Entries": [],
|
|
||||||
"m_Value": 1,
|
|
||||||
"m_IsEditable": true
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 0,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot",
|
|
||||||
"m_ObjectId": "aca287a71573483789d5dcb380daa40c",
|
|
||||||
"m_Id": 1,
|
|
||||||
"m_DisplayName": "Position",
|
|
||||||
"m_SlotType": 0,
|
|
||||||
"m_Hidden": false,
|
|
||||||
"m_ShaderOutputName": "Position",
|
|
||||||
"m_StageCapability": 3,
|
|
||||||
"m_Value": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_DefaultValue": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_Labels": []
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 1,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.PositionNode",
|
|
||||||
"m_ObjectId": "c5119037ba0c48fbba1bb84ead9a5adb",
|
|
||||||
"m_Group": {
|
|
||||||
"m_Id": ""
|
|
||||||
},
|
|
||||||
"m_Name": "Position",
|
|
||||||
"m_DrawState": {
|
|
||||||
"m_Expanded": true,
|
|
||||||
"m_Position": {
|
|
||||||
"serializedVersion": "2",
|
|
||||||
"x": -952.0,
|
|
||||||
"y": -310.0,
|
|
||||||
"width": 206.0,
|
|
||||||
"height": 131.00003051757813
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"m_Slots": [
|
|
||||||
{
|
|
||||||
"m_Id": "13e38f92c90c4e709dc150f29929c84a"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"synonyms": [
|
|
||||||
"location"
|
|
||||||
],
|
|
||||||
"m_Precision": 1,
|
|
||||||
"m_PreviewExpanded": false,
|
|
||||||
"m_DismissedVersion": 0,
|
|
||||||
"m_PreviewMode": 2,
|
|
||||||
"m_CustomColors": {
|
|
||||||
"m_SerializableColors": []
|
|
||||||
},
|
|
||||||
"m_Space": 0,
|
|
||||||
"m_PositionSource": 0
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
"m_SGVersion": 0,
|
|
||||||
"m_Type": "UnityEditor.ShaderGraph.Vector3MaterialSlot",
|
|
||||||
"m_ObjectId": "ca8752373fc04409ae302adfc1808024",
|
|
||||||
"m_Id": 1,
|
|
||||||
"m_DisplayName": "Out",
|
|
||||||
"m_SlotType": 1,
|
|
||||||
"m_Hidden": false,
|
|
||||||
"m_ShaderOutputName": "Out",
|
|
||||||
"m_StageCapability": 3,
|
|
||||||
"m_Value": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_DefaultValue": {
|
|
||||||
"x": 0.0,
|
|
||||||
"y": 0.0,
|
|
||||||
"z": 0.0
|
|
||||||
},
|
|
||||||
"m_Labels": []
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 9ac8f6239a1b8d94a9aa0ba4ece1f714
|
|
||||||
ScriptedImporter:
|
|
||||||
internalIDToNameTable: []
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
script: {fileID: 11500000, guid: 60072b568d64c40a485e0fc55012dc9f, type: 3}
|
|
||||||
|
|
@ -1,418 +0,0 @@
|
||||||
#pragma kernel Cull_32 GROUPS=32
|
|
||||||
#pragma kernel Cull_64 GROUPS=64
|
|
||||||
#pragma kernel Cull_128 GROUPS=128
|
|
||||||
#pragma kernel Cull_256 GROUPS=256
|
|
||||||
#pragma kernel Cull_512 GROUPS=512
|
|
||||||
#pragma kernel Cull_1024 GROUPS=1024
|
|
||||||
|
|
||||||
#pragma kernel Clear_32 GROUPS=32
|
|
||||||
#pragma kernel Clear_64 GROUPS=64
|
|
||||||
#pragma kernel Clear_128 GROUPS=128
|
|
||||||
#pragma kernel Clear_256 GROUPS=256
|
|
||||||
#pragma kernel Clear_512 GROUPS=512
|
|
||||||
#pragma kernel Clear_1024 GROUPS=1024
|
|
||||||
|
|
||||||
#pragma kernel Upload_32 GROUPS=32
|
|
||||||
#pragma kernel Upload_64 GROUPS=64
|
|
||||||
#pragma kernel Upload_128 GROUPS=128
|
|
||||||
#pragma kernel Upload_256 GROUPS=256
|
|
||||||
#pragma kernel Upload_512 GROUPS=512
|
|
||||||
#pragma kernel Upload_1024 GROUPS=1024
|
|
||||||
|
|
||||||
#pragma kernel Resize_32 GROUPS=32
|
|
||||||
#pragma kernel Resize_64 GROUPS=64
|
|
||||||
#pragma kernel Resize_128 GROUPS=128
|
|
||||||
#pragma kernel Resize_256 GROUPS=256
|
|
||||||
#pragma kernel Resize_512 GROUPS=512
|
|
||||||
#pragma kernel Resize_1024 GROUPS=1024
|
|
||||||
|
|
||||||
#include "UnityCG.cginc"
|
|
||||||
|
|
||||||
struct InstanceData
|
|
||||||
{
|
|
||||||
// position.xyz : position.xyz
|
|
||||||
// position.w : rotation.x
|
|
||||||
// scale.x : scale.xz
|
|
||||||
// scale.y : scale.y
|
|
||||||
// scale.zw : rotation.yz
|
|
||||||
float4 position;
|
|
||||||
float4 scale;
|
|
||||||
uint prefabId;
|
|
||||||
//Used to stream out new instances
|
|
||||||
int uploadAction;
|
|
||||||
int uploadId;
|
|
||||||
int age;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct PrefabData
|
|
||||||
{
|
|
||||||
uint batchIndex;
|
|
||||||
uint indexBufferStartOffset;
|
|
||||||
uint maxCount;
|
|
||||||
uint lodCount;
|
|
||||||
uint fadeLods;//Todo: Use Bitwise and save memory?
|
|
||||||
//x: density threshold, y: density range start, z: density range length, w: shadow distance
|
|
||||||
float4 densityInDistance;
|
|
||||||
float4 lodData[8];
|
|
||||||
uint2 indirectArgsIndexAndCountPerLod[8];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InstanceMeta
|
|
||||||
{
|
|
||||||
uint visibleLod;
|
|
||||||
uint fadeOutLod;
|
|
||||||
float fadeAnim;
|
|
||||||
|
|
||||||
// The scale of the instance can be adjusted
|
|
||||||
float Scale;
|
|
||||||
};
|
|
||||||
|
|
||||||
StructuredBuffer<InstanceData> globalUploadInstances;
|
|
||||||
RWStructuredBuffer<InstanceData> globalInstances;
|
|
||||||
RWStructuredBuffer<InstanceData> tempGlobalInstances;
|
|
||||||
StructuredBuffer<PrefabData> perCamPrefabs;
|
|
||||||
StructuredBuffer<uint> perCamCullableIndexesBuffer;
|
|
||||||
RWStructuredBuffer<InstanceMeta> perCamMeta;
|
|
||||||
RWStructuredBuffer<uint> perCamIndirectArgumentsBuffer;
|
|
||||||
RWStructuredBuffer<uint> perCamVisibleIndexesBuffer;
|
|
||||||
RWStructuredBuffer<uint> perCamShadowVisibleIndexesBuffer;
|
|
||||||
|
|
||||||
uint _CountClear;
|
|
||||||
uint _CountUpload;
|
|
||||||
uint _CountResize;
|
|
||||||
uint _CountCull;
|
|
||||||
float3 _CameraPosition;
|
|
||||||
float3 _ShadowDirection;
|
|
||||||
float4 _FrustumPlanes[6];
|
|
||||||
|
|
||||||
// Plane equation: {(a, b, c) = N, d = -dot(N, P)}.
|
|
||||||
// Returns the distance from the plane to the point 'p' along the normal.
|
|
||||||
// Positive -> in front (above), negative -> behind (below).
|
|
||||||
float DistanceFromPlane(float3 p, float4 plane)
|
|
||||||
{
|
|
||||||
return dot(float4(p, 1.0), plane);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns 'true' if the object is outside of the frustum.
|
|
||||||
// 'size' is the (negative) size of the object's bounds.
|
|
||||||
bool CullFrustum(float3 center, float size, float4 frustumPlanes[6], int numPlanes)
|
|
||||||
{
|
|
||||||
bool outside = false;
|
|
||||||
[unroll(6)]
|
|
||||||
for (int i = 0; i < numPlanes; i++)
|
|
||||||
outside = outside || DistanceFromPlane(center, frustumPlanes[i]) < size;
|
|
||||||
|
|
||||||
return outside;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns 'true' if the shadow of the object is outside of the frustum.
|
|
||||||
// 'size' is the (negative) size of the object's bounds.
|
|
||||||
bool CullShadowFrustum(float3 center, float size, float3 lightDirection, float4 frustumPlanes[6], int numPlanes)
|
|
||||||
{
|
|
||||||
bool outside = false;
|
|
||||||
[unroll(6)]
|
|
||||||
for (int i = 0; i < numPlanes; i++)
|
|
||||||
outside = outside || max(DistanceFromPlane(center, frustumPlanes[i]), DistanceFromPlane(center + lightDirection, frustumPlanes[i])) < size;
|
|
||||||
|
|
||||||
return outside;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculates the adjusted scale of the instance based on the "Density in Distance"
|
|
||||||
// setting. Instances get scaled down if the density is reduced.
|
|
||||||
float ReduceDensityScale(inout InstanceData instance, float4 densityInDistance, float distance)
|
|
||||||
{
|
|
||||||
float rangeStart = densityInDistance.y;
|
|
||||||
float rangeLength = densityInDistance.z;
|
|
||||||
|
|
||||||
// Calculate a dither pattern with range [0..1]
|
|
||||||
float dither = frac(dot(float3((instance.position.xz) * 16.0f, 0), uint3(2, 7, 23) / 17.0f));
|
|
||||||
|
|
||||||
float densityThreshold = 1 - densityInDistance.x;
|
|
||||||
float distanceNormalized = (distance - rangeStart) / (rangeLength * dither + 0.001f);
|
|
||||||
|
|
||||||
if(dither > densityInDistance.x && distanceNormalized > 0)
|
|
||||||
return 1 - distanceNormalized;
|
|
||||||
else
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClearArgumentsBuffer(uint3 id : SV_DispatchThreadID)
|
|
||||||
{
|
|
||||||
if (id.x >= _CountClear)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint indirectArgsCountOffset = id.x * 5 + 1;
|
|
||||||
perCamIndirectArgumentsBuffer[indirectArgsCountOffset] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Upload (uint3 id : SV_DispatchThreadID)
|
|
||||||
{
|
|
||||||
if (id.x >= _CountUpload)
|
|
||||||
return;
|
|
||||||
|
|
||||||
InstanceData instance = globalUploadInstances[id.x];
|
|
||||||
|
|
||||||
if (instance.uploadAction == 1)
|
|
||||||
{
|
|
||||||
globalInstances[instance.uploadId] = instance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Resize(uint3 id : SV_DispatchThreadID)
|
|
||||||
{
|
|
||||||
if (id.x >= _CountResize)
|
|
||||||
return;
|
|
||||||
|
|
||||||
tempGlobalInstances[id.x] = globalInstances[id.x];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Cull (uint3 id : SV_DispatchThreadID)
|
|
||||||
{
|
|
||||||
if(id.x >= _CountCull)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint instanceIndex = perCamCullableIndexesBuffer[id.x];
|
|
||||||
InstanceData instance = globalInstances[instanceIndex];
|
|
||||||
InstanceMeta meta = perCamMeta[instanceIndex];
|
|
||||||
PrefabData prefab = perCamPrefabs[instance.prefabId];
|
|
||||||
|
|
||||||
if (instance.age == 0)
|
|
||||||
{
|
|
||||||
meta = (InstanceMeta)0;
|
|
||||||
globalInstances[instanceIndex].age = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint lodCount = prefab.lodCount;
|
|
||||||
|
|
||||||
uint visibleLod = 0;
|
|
||||||
uint visibleShadowLod = 0;
|
|
||||||
uint batchOffset = 0;
|
|
||||||
uint indirectArgsCount = 0;
|
|
||||||
uint indirectArgsCountOffset = 0;
|
|
||||||
uint i = 0;
|
|
||||||
uint u = 0;
|
|
||||||
|
|
||||||
// Calculate active LOD
|
|
||||||
float dist = distance(instance.position.xyz, _CameraPosition.xyz);
|
|
||||||
|
|
||||||
[unroll(8)]
|
|
||||||
for(i=0; i < lodCount; i++)
|
|
||||||
{
|
|
||||||
float maxDist =
|
|
||||||
i < lodCount - 1 ? prefab.lodData[i].y * instance.scale.y : prefab.lodData[i].y;
|
|
||||||
|
|
||||||
if(dist >= prefab.lodData[i].x * instance.scale.y && dist < maxDist)
|
|
||||||
{
|
|
||||||
visibleLod = i + 1;
|
|
||||||
if (dist < prefab.densityInDistance.w)//prefab.densityInDistance.w = max shadow distance
|
|
||||||
visibleShadowLod = i + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reduce density
|
|
||||||
if(prefab.densityInDistance.x < 1)
|
|
||||||
{
|
|
||||||
meta.Scale = ReduceDensityScale(instance, prefab.densityInDistance, dist);
|
|
||||||
if(meta.Scale < 0.3)
|
|
||||||
{
|
|
||||||
visibleLod = 0;
|
|
||||||
visibleShadowLod = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
meta.Scale = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Frustum Culling
|
|
||||||
if(visibleLod > 0)
|
|
||||||
{
|
|
||||||
float size = -prefab.lodData[visibleLod - 1].z * length(instance.scale.xy);
|
|
||||||
const int planeCount = 5; // Do not test near/far planes
|
|
||||||
if (CullFrustum(instance.position.xyz, size, _FrustumPlanes, planeCount))
|
|
||||||
{
|
|
||||||
// Setting active LOD to 0 culls the instance. The LODs start from 1.
|
|
||||||
visibleLod = 0;
|
|
||||||
|
|
||||||
if (CullShadowFrustum(
|
|
||||||
instance.position.xyz,
|
|
||||||
size,
|
|
||||||
_ShadowDirection * 1000,
|
|
||||||
_FrustumPlanes,
|
|
||||||
planeCount))
|
|
||||||
{
|
|
||||||
visibleShadowLod = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
uint visibleCount = 0;
|
|
||||||
|
|
||||||
if (visibleLod > 0)
|
|
||||||
{
|
|
||||||
batchOffset = prefab.indexBufferStartOffset + ((visibleLod - 1) * prefab.maxCount);
|
|
||||||
indirectArgsCount = prefab.indirectArgsIndexAndCountPerLod[visibleLod - 1].y;
|
|
||||||
indirectArgsCountOffset = prefab.indirectArgsIndexAndCountPerLod[visibleLod - 1].x + 1;
|
|
||||||
|
|
||||||
InterlockedAdd(perCamIndirectArgumentsBuffer[indirectArgsCountOffset], 1, visibleCount);
|
|
||||||
|
|
||||||
for (i = 1, u = 5; i < indirectArgsCount; i++, u += 5)
|
|
||||||
{
|
|
||||||
InterlockedAdd(perCamIndirectArgumentsBuffer[indirectArgsCountOffset + u], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
perCamVisibleIndexesBuffer[batchOffset + visibleCount] = instanceIndex;
|
|
||||||
|
|
||||||
if (meta.visibleLod != visibleLod && meta.visibleLod > 0 && prefab.fadeLods > 0)
|
|
||||||
{
|
|
||||||
meta.fadeOutLod = meta.visibleLod;
|
|
||||||
meta.fadeAnim = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (visibleShadowLod > 0)
|
|
||||||
{
|
|
||||||
batchOffset = prefab.indexBufferStartOffset + ((visibleShadowLod - 1) * prefab.maxCount);
|
|
||||||
indirectArgsCount = prefab.indirectArgsIndexAndCountPerLod[visibleShadowLod - 1].y;
|
|
||||||
indirectArgsCountOffset = indirectArgsCount * 5 + prefab.indirectArgsIndexAndCountPerLod[visibleShadowLod - 1].x + 1;
|
|
||||||
|
|
||||||
InterlockedAdd(perCamIndirectArgumentsBuffer[indirectArgsCountOffset], 1, visibleCount);
|
|
||||||
|
|
||||||
for (i = 1, u = 5; i < indirectArgsCount; i++, u += 5)
|
|
||||||
{
|
|
||||||
InterlockedAdd(perCamIndirectArgumentsBuffer[indirectArgsCountOffset + u], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
perCamShadowVisibleIndexesBuffer[batchOffset + visibleCount] = instanceIndex;
|
|
||||||
|
|
||||||
if (meta.visibleLod != visibleShadowLod && meta.visibleLod > 0 && prefab.fadeLods > 0)
|
|
||||||
{
|
|
||||||
meta.fadeOutLod = meta.visibleLod;
|
|
||||||
meta.fadeAnim = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (meta.fadeOutLod == 0)
|
|
||||||
meta.fadeAnim = 0;
|
|
||||||
|
|
||||||
if (meta.fadeAnim == 0)
|
|
||||||
meta.fadeOutLod = 0;
|
|
||||||
|
|
||||||
//Add lod cross fade
|
|
||||||
if (meta.fadeAnim > 0 && meta.fadeOutLod > 0)
|
|
||||||
{
|
|
||||||
meta.fadeAnim = max(0, meta.fadeAnim - unity_DeltaTime.z);
|
|
||||||
|
|
||||||
//Normal
|
|
||||||
if (visibleLod > 0 && meta.fadeAnim > 0)
|
|
||||||
{
|
|
||||||
batchOffset = prefab.indexBufferStartOffset + ((meta.fadeOutLod - 1) * prefab.maxCount);
|
|
||||||
indirectArgsCount = prefab.indirectArgsIndexAndCountPerLod[meta.fadeOutLod - 1].y;
|
|
||||||
indirectArgsCountOffset = prefab.indirectArgsIndexAndCountPerLod[meta.fadeOutLod - 1].x + 1;
|
|
||||||
|
|
||||||
InterlockedAdd(perCamIndirectArgumentsBuffer[indirectArgsCountOffset], 1, visibleCount);
|
|
||||||
|
|
||||||
for (i = 1, u = 5; i < indirectArgsCount; i++, u += 5)
|
|
||||||
{
|
|
||||||
InterlockedAdd(perCamIndirectArgumentsBuffer[indirectArgsCountOffset + u], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
perCamVisibleIndexesBuffer[batchOffset + visibleCount] = instanceIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Shadow
|
|
||||||
if (visibleShadowLod > 0 && meta.fadeAnim > 0)
|
|
||||||
{
|
|
||||||
batchOffset = prefab.indexBufferStartOffset + ((meta.fadeOutLod - 1) * prefab.maxCount);
|
|
||||||
indirectArgsCount = prefab.indirectArgsIndexAndCountPerLod[meta.fadeOutLod - 1].y;
|
|
||||||
indirectArgsCountOffset = indirectArgsCount * 5 + prefab.indirectArgsIndexAndCountPerLod[meta.fadeOutLod - 1].x + 1;
|
|
||||||
|
|
||||||
InterlockedAdd(perCamIndirectArgumentsBuffer[indirectArgsCountOffset], 1, visibleCount);
|
|
||||||
|
|
||||||
for (i = 1, u = 5; i < indirectArgsCount; i++, u += 5)
|
|
||||||
{
|
|
||||||
InterlockedAdd(perCamIndirectArgumentsBuffer[indirectArgsCountOffset + u], 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
perCamShadowVisibleIndexesBuffer[batchOffset + visibleCount] = instanceIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
meta.visibleLod = max(visibleLod, visibleShadowLod);
|
|
||||||
perCamMeta[instanceIndex] = meta;
|
|
||||||
}
|
|
||||||
|
|
||||||
[numthreads(GROUPS,1,1)]
|
|
||||||
void Cull_32(uint3 id : SV_DispatchThreadID) { Cull(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS,1,1)]
|
|
||||||
void Cull_64(uint3 id : SV_DispatchThreadID) { Cull(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS,1,1)]
|
|
||||||
void Cull_128(uint3 id : SV_DispatchThreadID) { Cull(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS,1,1)]
|
|
||||||
void Cull_256(uint3 id : SV_DispatchThreadID) { Cull(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS,1,1)]
|
|
||||||
void Cull_512(uint3 id : SV_DispatchThreadID) { Cull(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS,1,1)]
|
|
||||||
void Cull_1024(uint3 id : SV_DispatchThreadID) { Cull(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Clear_32(uint3 id : SV_DispatchThreadID) { ClearArgumentsBuffer(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Clear_64(uint3 id : SV_DispatchThreadID) { ClearArgumentsBuffer(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Clear_128(uint3 id : SV_DispatchThreadID) { ClearArgumentsBuffer(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Clear_256(uint3 id : SV_DispatchThreadID) { ClearArgumentsBuffer(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Clear_512(uint3 id : SV_DispatchThreadID) { ClearArgumentsBuffer(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Clear_1024(uint3 id : SV_DispatchThreadID) { ClearArgumentsBuffer(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Upload_32(uint3 id : SV_DispatchThreadID) { Upload(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Upload_64(uint3 id : SV_DispatchThreadID) { Upload(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Upload_128(uint3 id : SV_DispatchThreadID) { Upload(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Upload_256(uint3 id : SV_DispatchThreadID) { Upload(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Upload_512(uint3 id : SV_DispatchThreadID) { Upload(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Upload_1024(uint3 id : SV_DispatchThreadID) { Upload(id); }
|
|
||||||
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Resize_32(uint3 id : SV_DispatchThreadID) { Resize(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Resize_64(uint3 id : SV_DispatchThreadID) { Resize(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Resize_128(uint3 id : SV_DispatchThreadID) { Resize(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Resize_256(uint3 id : SV_DispatchThreadID) { Resize(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Resize_512(uint3 id : SV_DispatchThreadID) { Resize(id); }
|
|
||||||
|
|
||||||
[numthreads(GROUPS, 1, 1)]
|
|
||||||
void Resize_1024(uint3 id : SV_DispatchThreadID) { Resize(id); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 4a840c1c229294d4996e80608a899881
|
|
||||||
ComputeShaderImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,136 +0,0 @@
|
||||||
#pragma multi_compile _LOD_FADE_CROSSFADE
|
|
||||||
|
|
||||||
#ifndef NODE_THOMAGIC_RENDERER_INCLUDED
|
|
||||||
#define NODE_THOMAGIC_RENDERER_INCLUDED
|
|
||||||
|
|
||||||
#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
|
|
||||||
|
|
||||||
|
|
||||||
#define UNITY_INDIRECT_DRAW_ARGS IndirectDrawIndexedArgs
|
|
||||||
#include "UnityIndirect.cginc"
|
|
||||||
|
|
||||||
#define Use_Macro_UNITY_MATRIX_M_instead_of_unity_ObjectToWorld unity_ObjectToWorld
|
|
||||||
#define Use_Macro_UNITY_MATRIX_I_M_instead_of_unity_WorldToObject unity_WorldToObject
|
|
||||||
|
|
||||||
#define LOD_FADE_CROSSFADE
|
|
||||||
|
|
||||||
struct InstanceData
|
|
||||||
{
|
|
||||||
// position.xyz : position.xyz
|
|
||||||
// position.w : rotation.x
|
|
||||||
// scale.x : scale.xz
|
|
||||||
// scale.y : scale.y
|
|
||||||
// scale.zw : rotation.yz
|
|
||||||
float4 position;
|
|
||||||
float4 scale;
|
|
||||||
uint prefabId;
|
|
||||||
int3 padding;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct InstanceMeta
|
|
||||||
{
|
|
||||||
uint visibleLod;
|
|
||||||
uint fadeOutLod;
|
|
||||||
float fadeAnim;
|
|
||||||
|
|
||||||
// The scale of the instance can be adjusted
|
|
||||||
float Scale;
|
|
||||||
};
|
|
||||||
|
|
||||||
StructuredBuffer<InstanceData> trInstances;
|
|
||||||
StructuredBuffer<uint> trPerCamVisibleIndexesBuffer;
|
|
||||||
StructuredBuffer<InstanceMeta> trPerCamMeta;
|
|
||||||
|
|
||||||
float4x4 trInstanceMatrix;
|
|
||||||
int trLodNr;
|
|
||||||
|
|
||||||
float4x4 TRS(float3 t, float4 r, float3 s)
|
|
||||||
{
|
|
||||||
float4x4 result = (float4x4)0;
|
|
||||||
result[0][0] = (1.0f - 2.0f * (r.y * r.y + r.z * r.z)) * s.x;
|
|
||||||
result[1][0] = (r.x * r.y + r.z * r.w) * s.x * 2.0f;
|
|
||||||
result[2][0] = (r.x * r.z - r.y * r.w) * s.x * 2.0f;
|
|
||||||
result[3][0] = 0.0f;
|
|
||||||
result[0][1] = (r.x * r.y - r.z * r.w) * s.y * 2.0f;
|
|
||||||
result[1][1] = (1.0f - 2.0f * (r.x * r.x + r.z * r.z)) * s.y;
|
|
||||||
result[2][1] = (r.y * r.z + r.x * r.w) * s.y * 2.0f;
|
|
||||||
result[3][1] = 0.0f;
|
|
||||||
result[0][2] = (r.x * r.z + r.y * r.w) * s.z * 2.0f;
|
|
||||||
result[1][2] = (r.y * r.z - r.x * r.w) * s.z * 2.0f;
|
|
||||||
result[2][2] = (1.0f - 2.0f * (r.x * r.x + r.y * r.y)) * s.z;
|
|
||||||
result[3][2] = 0.0f;
|
|
||||||
result[0][3] = t.x;
|
|
||||||
result[1][3] = t.y;
|
|
||||||
result[2][3] = t.z;
|
|
||||||
result[3][3] = 1.0f;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DecompressInstanceMatrix(inout float4x4 m, InstanceData instanceData, InstanceMeta meta)
|
|
||||||
{
|
|
||||||
float3 position = instanceData.position.xyz;
|
|
||||||
float4 rotation = float4(instanceData.position.w, instanceData.scale.zw, 0);
|
|
||||||
float3 scale = instanceData.scale.xyx * meta.Scale;
|
|
||||||
rotation.w = sqrt(1.0 - rotation.x * rotation.x - rotation.y * rotation.y - rotation.z * rotation.z);
|
|
||||||
m = TRS(position, rotation, scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
float4x4 inverse(float4x4 input)
|
|
||||||
{
|
|
||||||
#define minor(a,b,c) determinant(float3x3(input.a, input.b, input.c))
|
|
||||||
|
|
||||||
float4x4 cofactors = float4x4(
|
|
||||||
minor(_22_23_24, _32_33_34, _42_43_44),
|
|
||||||
-minor(_21_23_24, _31_33_34, _41_43_44),
|
|
||||||
minor(_21_22_24, _31_32_34, _41_42_44),
|
|
||||||
-minor(_21_22_23, _31_32_33, _41_42_43),
|
|
||||||
|
|
||||||
-minor(_12_13_14, _32_33_34, _42_43_44),
|
|
||||||
minor(_11_13_14, _31_33_34, _41_43_44),
|
|
||||||
-minor(_11_12_14, _31_32_34, _41_42_44),
|
|
||||||
minor(_11_12_13, _31_32_33, _41_42_43),
|
|
||||||
|
|
||||||
minor(_12_13_14, _22_23_24, _42_43_44),
|
|
||||||
-minor(_11_13_14, _21_23_24, _41_43_44),
|
|
||||||
minor(_11_12_14, _21_22_24, _41_42_44),
|
|
||||||
-minor(_11_12_13, _21_22_23, _41_42_43),
|
|
||||||
|
|
||||||
-minor(_12_13_14, _22_23_24, _32_33_34),
|
|
||||||
minor(_11_13_14, _21_23_24, _31_33_34),
|
|
||||||
-minor(_11_12_14, _21_22_24, _31_32_34),
|
|
||||||
minor(_11_12_13, _21_22_23, _31_32_33)
|
|
||||||
);
|
|
||||||
#undef minor
|
|
||||||
return transpose(cofactors) / determinant(input);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void SetupThoMagicRenderer()
|
|
||||||
{
|
|
||||||
#ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED
|
|
||||||
InitIndirectDrawArgs(0);
|
|
||||||
|
|
||||||
uint instanceID = GetIndirectInstanceID_Base(unity_InstanceID);
|
|
||||||
uint instanceIndex = trPerCamVisibleIndexesBuffer[instanceID];
|
|
||||||
InstanceMeta meta = trPerCamMeta[instanceIndex];
|
|
||||||
DecompressInstanceMatrix(unity_ObjectToWorld, trInstances[instanceIndex], meta);
|
|
||||||
unity_ObjectToWorld = mul(unity_ObjectToWorld, trInstanceMatrix);
|
|
||||||
unity_WorldToObject = inverse(unity_ObjectToWorld);
|
|
||||||
|
|
||||||
if (meta.visibleLod == trLodNr)
|
|
||||||
unity_LODFade.x = (1.0f - meta.fadeAnim);
|
|
||||||
else
|
|
||||||
unity_LODFade.x = -(1.0f - meta.fadeAnim);
|
|
||||||
|
|
||||||
// {% hd %}
|
|
||||||
// Instances are static so the previous matrix is the same as the current one.
|
|
||||||
// These matrices are required for correct motion vectors in HDRP.
|
|
||||||
#if SHADERPASS == SHADERPASS_MOTION_VECTORS && defined(SHADERPASS_CS_HLSL)
|
|
||||||
unity_MatrixPreviousM = unity_ObjectToWorld;
|
|
||||||
unity_MatrixPreviousMI = unity_WorldToObject;
|
|
||||||
#endif
|
|
||||||
// {% endhd %}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 7fe5ed120e85ccf499d429b359f89173
|
|
||||||
ShaderIncludeImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
#include "ThoMagicRenderer.cginc"
|
|
||||||
|
|
||||||
#ifndef THOMAGICRENDERERSETUP_INClUDED
|
|
||||||
#define THOMAGICRENDERERSETUP_INClUDED
|
|
||||||
void IncludeThoMagicRenderer_float(float3 In, out float3 Out)
|
|
||||||
{
|
|
||||||
Out = In;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,7 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 678dff042fe9c004cb7522c2233af44d
|
|
||||||
ShaderIncludeImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public struct SceneRenderSettings
|
|
||||||
{
|
|
||||||
public bool HasMainLight;
|
|
||||||
public bool HasMainLightShadows;
|
|
||||||
public Vector3 MainLightDirection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 8c1bc4c6b611d9d439d0f03feecc4e71
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,299 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
using UnityEditor;
|
|
||||||
#endif
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Rendering;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Adds a tree renderer and grass renderer to a terrain object.
|
|
||||||
/// </summary>
|
|
||||||
[AddComponentMenu("ThoMagic/Detail Renderer")]
|
|
||||||
[ExecuteAlways]
|
|
||||||
[DisallowMultipleComponent]
|
|
||||||
public class TerrainDetailRenderer : MonoBehaviour
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This event is called after this detail renderer is initialized.
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<TerrainDetailRenderer> Initialized;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The terrain object.
|
|
||||||
/// </summary>
|
|
||||||
public Terrain terrain;
|
|
||||||
/// <summary>
|
|
||||||
/// Cached terrain data.
|
|
||||||
/// </summary>
|
|
||||||
public TerrainData terrainData;
|
|
||||||
|
|
||||||
[NonSerialized]
|
|
||||||
private bool isInitialized = false;
|
|
||||||
|
|
||||||
[NonSerialized]
|
|
||||||
private TreeRenderer treeRenderer;
|
|
||||||
[NonSerialized]
|
|
||||||
private GrassRenderer grassRenderer;
|
|
||||||
|
|
||||||
[NonSerialized]
|
|
||||||
private Vector3 lastPosition;
|
|
||||||
|
|
||||||
[NonSerialized]
|
|
||||||
private Quaternion lastOrientation;
|
|
||||||
|
|
||||||
[NonSerialized]
|
|
||||||
private Vector3 lastScale;
|
|
||||||
|
|
||||||
|
|
||||||
[Tooltip("Should ThoMagic Renderer automatically refresh the terrain data if the terrain was modified at runtime? If disabled, use Refresh() from a custom script to refresh the terrain data.")]
|
|
||||||
[SerializeField]
|
|
||||||
private bool _autoRefreshTerrainAtRuntime = true;
|
|
||||||
|
|
||||||
[Tooltip("Should the default grass and tree rendering of Unity be enabled when ThoMagic Renderer is disabled?")]
|
|
||||||
[SerializeField]
|
|
||||||
private bool _enableUnityRendererWhenDisabled = true;
|
|
||||||
|
|
||||||
[Tooltip("Delay the initialization of ThoMagic Renderer until the first LateUpdate event")]
|
|
||||||
[SerializeField]
|
|
||||||
private bool _delayInitialize = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If true and the terrain is changed at runtime the renderers are updated automaticly.
|
|
||||||
/// If needed you can also refresh them manually by calling Refresh().
|
|
||||||
/// </summary>
|
|
||||||
public bool AutoRefreshTerrainAtRuntime
|
|
||||||
{
|
|
||||||
get => _autoRefreshTerrainAtRuntime;
|
|
||||||
set => _autoRefreshTerrainAtRuntime = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enable the default unity renderer for trees and grass if this component gets disabled?
|
|
||||||
/// </summary>
|
|
||||||
public bool EnableUnityRendererWhenDisabled
|
|
||||||
{
|
|
||||||
get => _enableUnityRendererWhenDisabled;
|
|
||||||
set => _enableUnityRendererWhenDisabled = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool HasValidTerrainData()
|
|
||||||
{
|
|
||||||
TerrainData terrainData = GetTerrainData();
|
|
||||||
return terrainData != null && terrainData.detailResolution > 0 && terrainData.alphamapResolution > 0 && terrainData.heightmapResolution > 0 && terrainData.size != Vector3.zero;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool CanInitialize() => isActiveAndEnabled && HasValidTerrainData();
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
//Load resources
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEnable()
|
|
||||||
{
|
|
||||||
if (_delayInitialize && Application.isPlaying || (isInitialized || !CanInitialize()))
|
|
||||||
return;
|
|
||||||
Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Start()
|
|
||||||
{
|
|
||||||
if (_delayInitialize && Application.isPlaying || (isInitialized || !CanInitialize()))
|
|
||||||
return;
|
|
||||||
Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDisable()
|
|
||||||
{
|
|
||||||
if (!isInitialized)
|
|
||||||
return;
|
|
||||||
Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
|
||||||
{
|
|
||||||
if (!isInitialized)
|
|
||||||
return;
|
|
||||||
Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Called by unity on terrain changes
|
|
||||||
public void OnTerrainChanged(TerrainChangedFlags flags)
|
|
||||||
{
|
|
||||||
if (!isInitialized || !AutoRefreshTerrainAtRuntime)
|
|
||||||
return;
|
|
||||||
Refresh(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to refresh the renderers.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="flags">The terrain changes</param>
|
|
||||||
public void Refresh(TerrainChangedFlags flags)
|
|
||||||
{
|
|
||||||
//if a detail object has to be replaced by a placeholder return immediatly, as a second OnTerrainChanged will be called
|
|
||||||
//cause the detail protoypes where changed on the terrain.
|
|
||||||
if(flags.HasFlag(TerrainChangedFlags.FlushEverythingImmediately) && ReplaceUnsupportedDetails() && AutoRefreshTerrainAtRuntime)
|
|
||||||
return;
|
|
||||||
|
|
||||||
treeRenderer?.OnTerrainChanged(flags);
|
|
||||||
grassRenderer?.OnTerrainChanged(flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Replace unsupported grass details with placeholders.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>True if a placeholder was created, otherwise false.</returns>
|
|
||||||
private bool ReplaceUnsupportedDetails()
|
|
||||||
{
|
|
||||||
bool flag = false;
|
|
||||||
DetailPrototype[] detailPrototypes = terrainData.detailPrototypes;
|
|
||||||
for (int index = 0; index < detailPrototypes.Length; ++index)
|
|
||||||
{
|
|
||||||
if (!RendererUtility.IsSupportedByUnity(detailPrototypes[index]))
|
|
||||||
{
|
|
||||||
GameObject supportedPlaceholder = RendererUtility.GetSupportedPlaceholder(detailPrototypes[index]);
|
|
||||||
if (supportedPlaceholder != detailPrototypes[index].prototype)
|
|
||||||
{
|
|
||||||
detailPrototypes[index].prototype = supportedPlaceholder;
|
|
||||||
flag = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (flag)
|
|
||||||
terrainData.detailPrototypes = detailPrototypes;
|
|
||||||
return flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DisableUnityRenderer()
|
|
||||||
{
|
|
||||||
if (terrain == null)
|
|
||||||
return;
|
|
||||||
terrain.drawTreesAndFoliage = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RestoreUnityRenderer()
|
|
||||||
{
|
|
||||||
if (terrain == null || !EnableUnityRendererWhenDisabled)
|
|
||||||
return;
|
|
||||||
terrain.drawTreesAndFoliage = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Initialize()
|
|
||||||
{
|
|
||||||
if (isInitialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
DisableUnityRenderer();
|
|
||||||
GetTerrainData();
|
|
||||||
ReplaceUnsupportedDetails();
|
|
||||||
|
|
||||||
if (treeRenderer == null)
|
|
||||||
treeRenderer = new TreeRenderer(terrain);
|
|
||||||
|
|
||||||
treeRenderer?.Load();
|
|
||||||
|
|
||||||
if (grassRenderer == null)
|
|
||||||
grassRenderer = new GrassRenderer(terrain);
|
|
||||||
|
|
||||||
grassRenderer?.Load();
|
|
||||||
|
|
||||||
if (Initialized != null)
|
|
||||||
Initialized(this, this);
|
|
||||||
|
|
||||||
isInitialized = true;
|
|
||||||
|
|
||||||
lastPosition = terrain.transform.position;
|
|
||||||
lastOrientation = terrain.transform.rotation;
|
|
||||||
lastScale = terrain.transform.localScale;
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
SceneView.lastActiveSceneView?.Repaint();
|
|
||||||
|
|
||||||
RenderPipelineManager.endContextRendering -= OnBeginContextRendering;
|
|
||||||
RenderPipelineManager.endContextRendering += OnBeginContextRendering;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
private void OnBeginContextRendering(
|
|
||||||
ScriptableRenderContext context,
|
|
||||||
List<Camera> cameras)
|
|
||||||
{
|
|
||||||
LateUpdate();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
private void LateUpdate()
|
|
||||||
{
|
|
||||||
if (!isInitialized && CanInitialize())
|
|
||||||
Initialize();
|
|
||||||
|
|
||||||
if (!isInitialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
//TODO: We are currently not supporting a floating offset,
|
|
||||||
//so if the terrain moves we refresh/rebuild everything,
|
|
||||||
//which is expenisve. If possible we should switch to a
|
|
||||||
//floating offset.
|
|
||||||
if (terrain.transform.position != lastPosition ||
|
|
||||||
terrain.transform.localScale != lastScale ||
|
|
||||||
terrain.transform.rotation != lastOrientation)
|
|
||||||
{
|
|
||||||
lastPosition = terrain.transform.position;
|
|
||||||
lastOrientation = terrain.transform.rotation;
|
|
||||||
lastScale = terrain.transform.localScale;
|
|
||||||
Refresh(TerrainChangedFlags.FlushEverythingImmediately);
|
|
||||||
}
|
|
||||||
|
|
||||||
treeRenderer?.LateUpdate();
|
|
||||||
grassRenderer?.LateUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Destroy()
|
|
||||||
{
|
|
||||||
if (!isInitialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
treeRenderer?.Destroy();
|
|
||||||
treeRenderer = null;
|
|
||||||
grassRenderer?.Destroy();
|
|
||||||
grassRenderer = null;
|
|
||||||
|
|
||||||
RestoreUnityRenderer();
|
|
||||||
isInitialized = false;
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
RenderPipelineManager.endContextRendering -= OnBeginContextRendering;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private TerrainData GetTerrainData()
|
|
||||||
{
|
|
||||||
if (terrain == null)
|
|
||||||
terrain = GetComponent<Terrain>();
|
|
||||||
terrainData = terrain != null ? terrain.terrainData : null;
|
|
||||||
return terrainData;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Bounds CalculateBounds(GameObject obj)
|
|
||||||
{
|
|
||||||
var meshRenderer = obj.GetComponentsInChildren<MeshRenderer>();
|
|
||||||
|
|
||||||
Bounds b = new Bounds();
|
|
||||||
if (meshRenderer.Length > 0)
|
|
||||||
{
|
|
||||||
b = new Bounds(meshRenderer[0].bounds.center, meshRenderer[0].bounds.size);
|
|
||||||
for (int r = 1; r < meshRenderer.Length; r++)
|
|
||||||
{
|
|
||||||
b.Encapsulate(meshRenderer[r].bounds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 533ab6e6ebf6be745ae7f02dc1922e05
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
[AddComponentMenu("ThoMagic/Renderer Object Settings")]
|
|
||||||
[DisallowMultipleComponent]
|
|
||||||
[ExecuteAlways]
|
|
||||||
public class ThoMagicRendererObjectSettings : MonoBehaviour, IInstanceRenderSettings
|
|
||||||
{
|
|
||||||
[SerializeField]
|
|
||||||
private bool _render = true;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideRendering;
|
|
||||||
[Min(0.0f)]
|
|
||||||
[SerializeField]
|
|
||||||
private float _renderDistance = 150;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideRenderDistance;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _renderShadows = true;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideRenderShadows;
|
|
||||||
[Min(0.0f)]
|
|
||||||
[SerializeField]
|
|
||||||
private float _shadowDistance = 80;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideShadowDistance;
|
|
||||||
[Range(0.01f, 1f)]
|
|
||||||
[SerializeField]
|
|
||||||
private float _densityInDistance = 0.125f;
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideDensityInDistance;
|
|
||||||
[SerializeField]
|
|
||||||
private Vector2 _densityInDistanceFalloff = new Vector2(0.08f, 0.0075f);
|
|
||||||
[SerializeField]
|
|
||||||
private bool _overrideDensityInDistanceFalloff;
|
|
||||||
private ReflectionProbe _reflectionProbe;
|
|
||||||
|
|
||||||
public InstanceRenderSettings Settings => new InstanceRenderSettings()
|
|
||||||
{
|
|
||||||
Supported = true,
|
|
||||||
Render = !this._overrideRendering || this._render,
|
|
||||||
RenderDistance = this._overrideRenderDistance ? this._renderDistance : -1f,
|
|
||||||
ShadowDistance = this._overrideShadowDistance ? this._shadowDistance : -1f,
|
|
||||||
Shadows = !this._overrideRenderShadows || this._renderShadows,
|
|
||||||
DensityInDistance = this._overrideDensityInDistance ? this._densityInDistance : 1f,
|
|
||||||
DensityInDistanceFalloff = this._overrideDensityInDistanceFalloff ? this._densityInDistanceFalloff : Vector2.zero
|
|
||||||
};
|
|
||||||
|
|
||||||
public bool? Render
|
|
||||||
{
|
|
||||||
get => !this._overrideRendering ? new bool?() : new bool?(this._render);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
this._overrideRendering = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
this._render = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float? RenderDistance
|
|
||||||
{
|
|
||||||
get => !this._overrideRenderDistance ? new float?() : new float?(this._renderDistance);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
this._overrideRenderDistance = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
this._renderDistance = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool? RenderShadows
|
|
||||||
{
|
|
||||||
get => !this._overrideRenderShadows ? new bool?() : new bool?(this._renderShadows);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
this._overrideRenderShadows = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
this._renderShadows = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float? ShadowDistance
|
|
||||||
{
|
|
||||||
get => !this._overrideShadowDistance ? new float?() : new float?(this._shadowDistance);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
this._overrideShadowDistance = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
this._shadowDistance = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float? DensityInDistance
|
|
||||||
{
|
|
||||||
get => !this._overrideDensityInDistance ? new float?() : new float?(this._densityInDistance);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
this._overrideDensityInDistance = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
this._densityInDistance = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Vector2? DensityInDistanceFalloff
|
|
||||||
{
|
|
||||||
get => !this._overrideDensityInDistanceFalloff ? new Vector2?() : new Vector2?(this._densityInDistanceFalloff);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
this._overrideDensityInDistanceFalloff = value.HasValue;
|
|
||||||
if (!value.HasValue)
|
|
||||||
return;
|
|
||||||
this._densityInDistanceFalloff = value.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEnable()
|
|
||||||
{
|
|
||||||
_reflectionProbe = GetComponent<ReflectionProbe>();
|
|
||||||
if (_reflectionProbe == null)
|
|
||||||
return;
|
|
||||||
CameraRenderer.ReflectionProbeSettings = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDisable()
|
|
||||||
{
|
|
||||||
if (_reflectionProbe == null || CameraRenderer.ReflectionProbeSettings as ThoMagicRendererObjectSettings != this)
|
|
||||||
return;
|
|
||||||
CameraRenderer.ReflectionProbeSettings = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnValidate()
|
|
||||||
{
|
|
||||||
if (_reflectionProbe == null || !Application.isEditor)
|
|
||||||
return;
|
|
||||||
this._reflectionProbe.RenderProbe();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: c2a37bd5b745b59448b6bc6c1ff5f09c
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
using UnityEditor;
|
|
||||||
#endif
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.Rendering;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
[ExecuteAlways]
|
|
||||||
[DisallowMultipleComponent]
|
|
||||||
public class TileObjectRenderer : MonoBehaviour
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// This event is called after this detail renderer is initialized.
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<TileObjectRenderer> Initialized;
|
|
||||||
|
|
||||||
[NonSerialized]
|
|
||||||
private bool isInitialized = false;
|
|
||||||
|
|
||||||
|
|
||||||
[Tooltip("Delay the initialization of ThoMagic Renderer until the first LateUpdate event")]
|
|
||||||
[SerializeField]
|
|
||||||
private bool _delayInitialize = true;
|
|
||||||
|
|
||||||
private bool CanInitialize() => isActiveAndEnabled;
|
|
||||||
|
|
||||||
[NonSerialized]
|
|
||||||
private TileObjectStreamer tileObjectStreamer;
|
|
||||||
|
|
||||||
private void Awake()
|
|
||||||
{
|
|
||||||
//Load resources
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnEnable()
|
|
||||||
{
|
|
||||||
if (_delayInitialize && Application.isPlaying || (isInitialized || !CanInitialize()))
|
|
||||||
return;
|
|
||||||
Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Start()
|
|
||||||
{
|
|
||||||
if (_delayInitialize && Application.isPlaying || (isInitialized || !CanInitialize()))
|
|
||||||
return;
|
|
||||||
Initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDisable()
|
|
||||||
{
|
|
||||||
if (!isInitialized)
|
|
||||||
return;
|
|
||||||
Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDestroy()
|
|
||||||
{
|
|
||||||
if (!isInitialized)
|
|
||||||
return;
|
|
||||||
Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Initialize()
|
|
||||||
{
|
|
||||||
if (isInitialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (tileObjectStreamer == null)
|
|
||||||
tileObjectStreamer = new TileObjectStreamer(gameObject);
|
|
||||||
|
|
||||||
tileObjectStreamer?.Load();
|
|
||||||
|
|
||||||
if (Initialized != null)
|
|
||||||
Initialized(this, this);
|
|
||||||
|
|
||||||
isInitialized = true;
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
SceneView.lastActiveSceneView?.Repaint();
|
|
||||||
|
|
||||||
RenderPipelineManager.endContextRendering -= OnBeginContextRendering;
|
|
||||||
RenderPipelineManager.endContextRendering += OnBeginContextRendering;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
private void OnBeginContextRendering(
|
|
||||||
ScriptableRenderContext context,
|
|
||||||
List<Camera> cameras)
|
|
||||||
{
|
|
||||||
//LateUpdate();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
private void LateUpdate()
|
|
||||||
{
|
|
||||||
if (!isInitialized && CanInitialize())
|
|
||||||
Initialize();
|
|
||||||
|
|
||||||
if (!isInitialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
tileObjectStreamer?.LateUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Destroy()
|
|
||||||
{
|
|
||||||
if (!isInitialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
tileObjectStreamer?.Destroy();
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
tileObjectStreamer = null;
|
|
||||||
|
|
||||||
isInitialized = false;
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
RenderPipelineManager.endContextRendering -= OnBeginContextRendering;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTransformChildrenChanged()
|
|
||||||
{
|
|
||||||
tileObjectStreamer?.MarkDirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 365a0b5496e0c71438e4b4a0b232aca6
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,246 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class TileObjectStreamer : InstanceStreamer
|
|
||||||
{
|
|
||||||
private readonly GameObject objectContainer;
|
|
||||||
private Bounds worldBounds;
|
|
||||||
private float maxDistance;
|
|
||||||
|
|
||||||
private Dictionary<int, int> registeredPrefabs = new Dictionary<int, int>();
|
|
||||||
private Dictionary<int, GameObject> instanceToGameObject = new Dictionary<int, GameObject>();
|
|
||||||
|
|
||||||
private bool isDirty = true;
|
|
||||||
|
|
||||||
public TileObjectStreamer(GameObject objectContainer)
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
this.objectContainer = objectContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Recycle()
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
|
|
||||||
int ownerHash = GetHashCode();
|
|
||||||
foreach (var item in registeredPrefabs)
|
|
||||||
{
|
|
||||||
RendererPool.RemoveObject(item.Value, ownerHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var item in instanceToGameObject)
|
|
||||||
{
|
|
||||||
var mrs = item.Value.gameObject.GetComponentsInChildren<MeshRenderer>();
|
|
||||||
foreach(var mr in mrs)
|
|
||||||
mr.enabled = true;
|
|
||||||
|
|
||||||
var lod = item.Value.gameObject.GetComponent<LODGroup>();
|
|
||||||
if (lod != null)
|
|
||||||
lod.enabled = true;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
registeredPrefabs.Clear();
|
|
||||||
instanceToGameObject.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Load()
|
|
||||||
{
|
|
||||||
isDirty = false;
|
|
||||||
|
|
||||||
Recycle();
|
|
||||||
|
|
||||||
worldBounds.SetMinMax(Vector3.zero, Vector3.zero);
|
|
||||||
|
|
||||||
registeredPrefabs.Clear();
|
|
||||||
instanceToGameObject.Clear();
|
|
||||||
|
|
||||||
int ownerHash = GetHashCode();
|
|
||||||
|
|
||||||
foreach (Transform child in objectContainer.transform)
|
|
||||||
{
|
|
||||||
var hash = CalculateHash(child.gameObject);
|
|
||||||
|
|
||||||
if(hash != 0)
|
|
||||||
{
|
|
||||||
int objectId;
|
|
||||||
|
|
||||||
if (registeredPrefabs.ContainsKey(hash))
|
|
||||||
{
|
|
||||||
objectId = registeredPrefabs[hash];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var settings = GetSettingsOrDefault(child.gameObject);
|
|
||||||
objectId = RendererPool.RegisterObject(child.gameObject, settings, this, ownerHash);
|
|
||||||
registeredPrefabs.Add(hash, objectId);
|
|
||||||
if (settings.Settings.RenderDistance == 0)
|
|
||||||
maxDistance = float.MaxValue;
|
|
||||||
|
|
||||||
maxDistance = Mathf.Max(maxDistance, settings.Settings.RenderDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
var mrs = child.gameObject.GetComponentsInChildren<MeshRenderer>();
|
|
||||||
Bounds bounds = new Bounds();
|
|
||||||
bool first = true;
|
|
||||||
foreach (var mr in mrs)
|
|
||||||
{
|
|
||||||
if (first)
|
|
||||||
{
|
|
||||||
bounds = mr.bounds;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
bounds.Encapsulate(mr.bounds);
|
|
||||||
}
|
|
||||||
|
|
||||||
mr.enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var lod = child.gameObject.GetComponent<LODGroup>();
|
|
||||||
if(lod != null)
|
|
||||||
lod.enabled = false;
|
|
||||||
|
|
||||||
var instanceId = AddInstance(objectId, new Vector3(child.position.x, child.position.y, child.position.z), child.rotation, child.lossyScale.x, child.lossyScale.z);
|
|
||||||
|
|
||||||
instanceToGameObject.Add(instanceId, child.gameObject);
|
|
||||||
if (instanceToGameObject.Count == 1)
|
|
||||||
worldBounds = bounds;
|
|
||||||
else
|
|
||||||
worldBounds.Encapsulate(bounds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private IInstanceRenderSettings GetSettingsOrDefault(GameObject gameObject)
|
|
||||||
{
|
|
||||||
IInstanceRenderSettings instanceRenderSettings;
|
|
||||||
return gameObject.TryGetComponent(out instanceRenderSettings) ? instanceRenderSettings : new DefaultRenderSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CalculateHash(GameObject gameObject)
|
|
||||||
{
|
|
||||||
int num = 13;
|
|
||||||
|
|
||||||
var lodGroup = gameObject.GetComponent<LODGroup>();
|
|
||||||
|
|
||||||
LOD[] lodArray;
|
|
||||||
|
|
||||||
if (lodGroup == null)
|
|
||||||
{
|
|
||||||
var mr = gameObject.GetComponentsInChildren<MeshRenderer>();
|
|
||||||
|
|
||||||
if (mr == null)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
lodArray = new LOD[1]
|
|
||||||
{
|
|
||||||
new LOD(0.0001f, mr)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
lodArray = lodGroup.GetLODs();
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var loD in lodArray)
|
|
||||||
num = HashCode.Combine<int, int>(num, CalcualteContentHash(loD));
|
|
||||||
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CalcualteContentHash(LOD lod)
|
|
||||||
{
|
|
||||||
int num = 13;
|
|
||||||
if (lod.renderers != null)
|
|
||||||
{
|
|
||||||
foreach (var renderer in lod.renderers)
|
|
||||||
{
|
|
||||||
if (renderer == null)
|
|
||||||
{
|
|
||||||
num = HashCode.Combine<int, int>(num, 13);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MeshFilter component = renderer.GetComponent<MeshFilter>();
|
|
||||||
|
|
||||||
num = HashCode.Combine<int, int, int>(HashCode.Combine<int, int, int, int, int, int, int, int>(
|
|
||||||
num,
|
|
||||||
component == null || component.sharedMesh == null ? 13 : component.sharedMesh.GetHashCode(),
|
|
||||||
renderer.shadowCastingMode.GetHashCode(),
|
|
||||||
CalculateContentHash(renderer.sharedMaterials),
|
|
||||||
renderer.motionVectorGenerationMode.GetHashCode(),
|
|
||||||
renderer.receiveShadows.GetHashCode(),
|
|
||||||
renderer.rendererPriority.GetHashCode(),
|
|
||||||
renderer.renderingLayerMask.GetHashCode()),
|
|
||||||
renderer.gameObject.layer.GetHashCode(),
|
|
||||||
lod.screenRelativeTransitionHeight.GetHashCode());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int CalculateContentHash(Material[] materials)
|
|
||||||
{
|
|
||||||
int num = 13;
|
|
||||||
if (materials != null)
|
|
||||||
{
|
|
||||||
foreach (Material material in materials)
|
|
||||||
num = HashCode.Combine<int, int>(num,
|
|
||||||
material != null ? material.GetHashCode() : 13);
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool IsInRange(Camera referenceCamera, Plane[] planes)
|
|
||||||
{
|
|
||||||
var eyePos = referenceCamera.transform.position;
|
|
||||||
|
|
||||||
if ((eyePos - worldBounds.ClosestPoint(eyePos)).magnitude <= Mathf.Min(referenceCamera.farClipPlane, maxDistance))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode()
|
|
||||||
{
|
|
||||||
return streamerInstanceId.GetHashCode();//HashCode.Combine(streamerInstanceId, objectInstanceIds.Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Destroy()
|
|
||||||
{
|
|
||||||
Recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LateUpdate()
|
|
||||||
{
|
|
||||||
if (isDirty)
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void MarkDirty()
|
|
||||||
{
|
|
||||||
isDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DefaultRenderSettings : IInstanceRenderSettings
|
|
||||||
{
|
|
||||||
public InstanceRenderSettings Settings => new InstanceRenderSettings()
|
|
||||||
{
|
|
||||||
Render = true,
|
|
||||||
DensityInDistance = 1f,
|
|
||||||
DensityInDistanceFalloff = Vector2.zero,
|
|
||||||
RenderDistance = 0.0f,
|
|
||||||
ShadowDistance = 0.0f,
|
|
||||||
Shadows = true,
|
|
||||||
Supported = true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 14d63a559cc9211479181fed32a2a5c0
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
272
TreeRenderer.cs
272
TreeRenderer.cs
|
|
@ -1,272 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class TreeRenderer : TreeStreamer
|
|
||||||
{
|
|
||||||
private Terrain terrain;
|
|
||||||
private TerrainData terrainData;
|
|
||||||
private List<int> treePrototypesRegistered;
|
|
||||||
private Dictionary<int, TreeInfo> treeToInstances = new Dictionary<int, TreeInfo>();
|
|
||||||
private HashSet<int> touchedTrees = new HashSet<int>();
|
|
||||||
private bool isDirty;
|
|
||||||
private float maxTreeDistance = 0;
|
|
||||||
|
|
||||||
class TreeInfo
|
|
||||||
{
|
|
||||||
internal int instanceId;
|
|
||||||
internal int objectId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TreeRenderer(Terrain terrain)
|
|
||||||
: base(terrain)
|
|
||||||
{
|
|
||||||
this.terrain = terrain;
|
|
||||||
terrainData = terrain.terrainData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LateUpdate()
|
|
||||||
{
|
|
||||||
if (isDirty)
|
|
||||||
{
|
|
||||||
isDirty = false;
|
|
||||||
ApplyChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Application.isEditor && !Application.isPlaying)
|
|
||||||
RebuildChangedPrototypes();
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTreeTerrainInstanceId(TreeInstance tree)
|
|
||||||
{
|
|
||||||
return HashCode.Combine(tree.position.x, tree.position.y, tree.position.z, tree.rotation, tree.widthScale, tree.heightScale, tree.prototypeIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ApplyChanges()
|
|
||||||
{
|
|
||||||
Vector3 size = terrainData.size;
|
|
||||||
Vector3 offset = terrain.transform.position;
|
|
||||||
|
|
||||||
var treeInstances = terrainData.treeInstances;
|
|
||||||
touchedTrees.Clear();
|
|
||||||
foreach (var treeInstance in treeInstances)
|
|
||||||
{
|
|
||||||
if (treeInstance.widthScale == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var treeTerrainInstanceId = GetTreeTerrainInstanceId(treeInstance);
|
|
||||||
if (!treeToInstances.ContainsKey(treeTerrainInstanceId))
|
|
||||||
{
|
|
||||||
var treeId = treePrototypesRegistered[treeInstance.prototypeIndex];
|
|
||||||
var prefab = RendererPool.GetObject(treeId);
|
|
||||||
var quaternion = Quaternion.Euler(0.0f, 57.2957801818848f * treeInstance.rotation, 0.0f);
|
|
||||||
var instanceId = AddInstance(treeId, new Vector3(treeInstance.position.x * size.x,
|
|
||||||
treeInstance.position.y * size.y,
|
|
||||||
treeInstance.position.z * size.z) + offset, quaternion,
|
|
||||||
treeInstance.widthScale * prefab.transform.localScale.x,
|
|
||||||
treeInstance.heightScale * prefab.transform.localScale.y
|
|
||||||
);
|
|
||||||
treeToInstances[treeTerrainInstanceId] = new TreeInfo { instanceId = instanceId, objectId = treeId };
|
|
||||||
touchedTrees.Add(treeTerrainInstanceId);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
touchedTrees.Add(treeTerrainInstanceId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach(var treeTerrainInstanceId in treeToInstances.Keys.ToList())
|
|
||||||
{
|
|
||||||
if(!touchedTrees.Contains(treeTerrainInstanceId))
|
|
||||||
{
|
|
||||||
RemoveInstance(treeToInstances[treeTerrainInstanceId].objectId, treeToInstances[treeTerrainInstanceId].instanceId);
|
|
||||||
treeToInstances.Remove(treeTerrainInstanceId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Build(maxTreeDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Load()
|
|
||||||
{
|
|
||||||
maxTreeDistance = 0;
|
|
||||||
|
|
||||||
Vector3 size = terrainData.size;
|
|
||||||
Vector3 offset = terrain.transform.position;
|
|
||||||
|
|
||||||
Recycle();
|
|
||||||
|
|
||||||
int ownerHash = GetHashCode();
|
|
||||||
|
|
||||||
if (treePrototypesRegistered == null)
|
|
||||||
treePrototypesRegistered = new List<int>();
|
|
||||||
|
|
||||||
var treePrototypes = terrainData.treePrototypes;
|
|
||||||
if(treePrototypes.Length > 0)
|
|
||||||
{
|
|
||||||
foreach(var treePrototype in treePrototypes)
|
|
||||||
{
|
|
||||||
if(treePrototype.prefab != null)
|
|
||||||
{
|
|
||||||
var settings = GetSettingsOrDefault(treePrototype.prefab);
|
|
||||||
treePrototypesRegistered.Add(RendererPool.RegisterObject(treePrototype.prefab, settings, this, ownerHash));
|
|
||||||
|
|
||||||
if (settings.Settings.RenderDistance == 0)
|
|
||||||
maxTreeDistance = float.MaxValue;
|
|
||||||
|
|
||||||
if(settings.Settings.RenderDistance > maxTreeDistance)
|
|
||||||
maxTreeDistance = settings.Settings.RenderDistance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var treeInstances = terrainData.treeInstances;
|
|
||||||
foreach (var treeInstance in treeInstances)
|
|
||||||
{
|
|
||||||
var treeTerrainInstanceId = GetTreeTerrainInstanceId(treeInstance);
|
|
||||||
var treeId = treePrototypesRegistered[treeInstance.prototypeIndex];
|
|
||||||
var prefab = RendererPool.GetObject(treeId);
|
|
||||||
var quaternion = Quaternion.Euler(0.0f, 57.2957801818848f * treeInstance.rotation, 0.0f);
|
|
||||||
var instanceId = AddInstance(treeId, new Vector3(treeInstance.position.x * size.x,
|
|
||||||
treeInstance.position.y * size.y,
|
|
||||||
treeInstance.position.z * size.z) + offset, quaternion,
|
|
||||||
treeInstance.widthScale * prefab.transform.localScale.x,
|
|
||||||
treeInstance.heightScale * prefab.transform.localScale.y);
|
|
||||||
treeToInstances[treeTerrainInstanceId] = new TreeInfo { instanceId = instanceId, objectId = treeId };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Build(maxTreeDistance);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Recycle()
|
|
||||||
{
|
|
||||||
if (treePrototypesRegistered == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
treeToInstances.Clear();
|
|
||||||
|
|
||||||
Clear();
|
|
||||||
|
|
||||||
var ownerHash = GetHashCode();
|
|
||||||
|
|
||||||
for (int index = 0; index < treePrototypesRegistered.Count; ++index)
|
|
||||||
{
|
|
||||||
var objectId = treePrototypesRegistered[index];
|
|
||||||
RendererPool.RemoveObject(objectId, ownerHash);
|
|
||||||
}
|
|
||||||
|
|
||||||
treePrototypesRegistered.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RebuildChangedPrototypes()
|
|
||||||
{
|
|
||||||
TreePrototype[] treePrototypes = terrainData.treePrototypes;
|
|
||||||
if (treePrototypes.Length != treePrototypesRegistered.Count)
|
|
||||||
{
|
|
||||||
Load();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int ownerHash = GetHashCode();
|
|
||||||
maxTreeDistance = 0;
|
|
||||||
for (int index = 0; index < treePrototypesRegistered.Count; ++index)
|
|
||||||
{
|
|
||||||
int treeId = treePrototypesRegistered[index];
|
|
||||||
|
|
||||||
GameObject prefab = treePrototypes[index].prefab;
|
|
||||||
GameObject gameObject = RendererPool.GetObject(treeId);
|
|
||||||
if (prefab != gameObject)
|
|
||||||
{
|
|
||||||
RendererPool.RemoveObject(treeId, ownerHash);
|
|
||||||
if (prefab != null)
|
|
||||||
{
|
|
||||||
var settings = GetSettingsOrDefault(prefab);
|
|
||||||
|
|
||||||
if (settings.Settings.RenderDistance == 0)
|
|
||||||
maxTreeDistance = float.MaxValue;
|
|
||||||
|
|
||||||
if (settings.Settings.RenderDistance > maxTreeDistance)
|
|
||||||
maxTreeDistance = settings.Settings.RenderDistance;
|
|
||||||
|
|
||||||
treePrototypesRegistered[index] = RendererPool.RegisterObject(prefab, settings, this, ownerHash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (RendererPool.ContentHashChanged(treeId))
|
|
||||||
{
|
|
||||||
RendererPool.RemoveObject(treeId, ownerHash);
|
|
||||||
if (prefab != null)
|
|
||||||
{
|
|
||||||
var settings = GetSettingsOrDefault(prefab);
|
|
||||||
|
|
||||||
if (settings.Settings.RenderDistance == 0)
|
|
||||||
maxTreeDistance = float.MaxValue;
|
|
||||||
|
|
||||||
if (settings.Settings.RenderDistance > maxTreeDistance)
|
|
||||||
maxTreeDistance = settings.Settings.RenderDistance;
|
|
||||||
|
|
||||||
treePrototypesRegistered[index] = RendererPool.RegisterObject(prefab, settings, this, ownerHash);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (prefab != null)
|
|
||||||
{
|
|
||||||
var settings = GetSettingsOrDefault(prefab);
|
|
||||||
|
|
||||||
if (settings.Settings.RenderDistance == 0)
|
|
||||||
maxTreeDistance = float.MaxValue;
|
|
||||||
|
|
||||||
if (settings.Settings.RenderDistance > maxTreeDistance)
|
|
||||||
maxTreeDistance = settings.Settings.RenderDistance;
|
|
||||||
|
|
||||||
RendererPool.SetObjectSettings(prefab, settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Destroy()
|
|
||||||
{
|
|
||||||
Recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnTerrainChanged(TerrainChangedFlags flags)
|
|
||||||
{
|
|
||||||
if(flags.HasFlag(TerrainChangedFlags.TreeInstances))
|
|
||||||
isDirty = true;
|
|
||||||
else if (flags.HasFlag(TerrainChangedFlags.DelayedHeightmapUpdate))
|
|
||||||
isDirty = true;
|
|
||||||
else if (flags.HasFlag(TerrainChangedFlags.Heightmap))
|
|
||||||
isDirty = true;
|
|
||||||
else if (flags.HasFlag(TerrainChangedFlags.HeightmapResolution))
|
|
||||||
isDirty = true;
|
|
||||||
else if (flags.HasFlag(TerrainChangedFlags.Holes))
|
|
||||||
isDirty = true;
|
|
||||||
else if (flags.HasFlag(TerrainChangedFlags.DelayedHolesUpdate))
|
|
||||||
isDirty = true;
|
|
||||||
else if (flags.HasFlag(TerrainChangedFlags.FlushEverythingImmediately))
|
|
||||||
isDirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private IInstanceRenderSettings GetSettingsOrDefault(GameObject gameObject)
|
|
||||||
{
|
|
||||||
IInstanceRenderSettings instanceRenderSettings;
|
|
||||||
return gameObject.TryGetComponent(out instanceRenderSettings) ? instanceRenderSettings : new DefaultRenderSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DefaultRenderSettings : IInstanceRenderSettings
|
|
||||||
{
|
|
||||||
public InstanceRenderSettings Settings => new InstanceRenderSettings()
|
|
||||||
{
|
|
||||||
Render = true,
|
|
||||||
DensityInDistance = 1f,
|
|
||||||
DensityInDistanceFalloff = Vector2.zero,
|
|
||||||
RenderDistance = 0.0f,
|
|
||||||
ShadowDistance = 0.0f,
|
|
||||||
Shadows = true,
|
|
||||||
Supported = true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: e0f78ad04f9bf164e81068b0dcffd13b
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace Assets.ThoMagic.Renderer
|
|
||||||
{
|
|
||||||
public class TreeStreamer : InstanceStreamer
|
|
||||||
{
|
|
||||||
private readonly Terrain _terrain;
|
|
||||||
private Bounds worldBounds;
|
|
||||||
private float maxTreeDistance;
|
|
||||||
|
|
||||||
public TreeStreamer(Terrain terrain)
|
|
||||||
: base()
|
|
||||||
=> _terrain = terrain;
|
|
||||||
|
|
||||||
public void Build(float maxDistance)
|
|
||||||
{
|
|
||||||
maxTreeDistance = maxDistance;
|
|
||||||
|
|
||||||
Vector3 position = _terrain.GetPosition();
|
|
||||||
worldBounds = new Bounds(_terrain.terrainData.bounds.center + position, _terrain.terrainData.bounds.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool IsInRange(Camera referenceCamera, Plane[] planes)
|
|
||||||
{
|
|
||||||
if (!_terrain.editorRenderFlags.HasFlag(TerrainRenderFlags.Trees))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var eyePos = referenceCamera.transform.position;
|
|
||||||
|
|
||||||
if ((eyePos - worldBounds.ClosestPoint(eyePos)).magnitude <= Mathf.Min(referenceCamera.farClipPlane, maxTreeDistance))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
fileFormatVersion: 2
|
|
||||||
guid: 049083d3196ea1b429c2057a188c2bf2
|
|
||||||
MonoImporter:
|
|
||||||
externalObjects: {}
|
|
||||||
serializedVersion: 2
|
|
||||||
defaultReferences: []
|
|
||||||
executionOrder: 0
|
|
||||||
icon: {instanceID: 0}
|
|
||||||
userData:
|
|
||||||
assetBundleName:
|
|
||||||
assetBundleVariant:
|
|
||||||
Loading…
Reference in New Issue