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