630 lines
24 KiB
C#
630 lines
24 KiB
C#
|
#if GRIFFIN
|
||
|
using UnityEngine;
|
||
|
using System.Collections;
|
||
|
using System.Collections.Generic;
|
||
|
using Unity.Collections;
|
||
|
using Unity.Jobs;
|
||
|
using UnityEngine.Rendering;
|
||
|
|
||
|
namespace Pinwheel.Griffin.Rendering
|
||
|
{
|
||
|
public class GTreeRenderer
|
||
|
{
|
||
|
public struct PrototypeCache
|
||
|
{
|
||
|
public bool validation;
|
||
|
public bool canDrawInstanced;
|
||
|
public bool canDrawBillboardInstanced;
|
||
|
public int subMeshCount;
|
||
|
public Vector4[] billboardImageTexcoords;
|
||
|
public Mesh billboardMesh; //global cached, disposed in GRuntimeSettings
|
||
|
}
|
||
|
|
||
|
public static Dictionary<GStylizedTerrain, GRenderingVisualization> vis;
|
||
|
|
||
|
private GStylizedTerrain terrain;
|
||
|
private GFoliage foliage;
|
||
|
|
||
|
private Camera camera;
|
||
|
private Plane[] frustum;
|
||
|
private Vector3[] nearFrustumCorners;
|
||
|
private Vector3[] farFrustumCorners;
|
||
|
private Vector3 cullBoxMin;
|
||
|
private Vector3 cullBoxMax;
|
||
|
|
||
|
private const byte CULLED = 0;
|
||
|
private const byte VISIBLE = 1;
|
||
|
private const byte BILLBOARD = 2;
|
||
|
|
||
|
private Matrix4x4 normalizedToLocalMatrix;
|
||
|
private Matrix4x4 localToWorldMatrix;
|
||
|
private Matrix4x4 normalizedToWorldMatrix;
|
||
|
|
||
|
private float treeDistance;
|
||
|
private float billboardStart;
|
||
|
private float cullVolumeBias;
|
||
|
private Vector3 terrainPosition;
|
||
|
private Vector3 terrainSize;
|
||
|
|
||
|
private List<GTreePrototype> prototypes;
|
||
|
private PrototypeCache[] prototypeCache;
|
||
|
|
||
|
private bool enableInstancing;
|
||
|
private Matrix4x4[] batchContainer;
|
||
|
private int batchInstanceCount;
|
||
|
private Matrix4x4[] billboardBatchContainer;
|
||
|
private int billboardBatchInstanceCount;
|
||
|
|
||
|
private int[] instancePrototypeIndices;
|
||
|
private Matrix4x4[] instanceTransforms;
|
||
|
private byte[] instanceCullResults;
|
||
|
|
||
|
private const int BATCH_MAX_INSTANCE_COUNT = 500;
|
||
|
private bool isWarningLogged;
|
||
|
|
||
|
private const string BILLBOARD_IMAGE_TEXCOORDS = "_ImageTexcoords";
|
||
|
private const string BILLBOARD_IMAGE_COUNT = "_ImageCount";
|
||
|
|
||
|
private GTreeNativeData nativeData;
|
||
|
private NativeArray<Plane> frustumPlanes;
|
||
|
private NativeArray<float> prototypePivotOffset;
|
||
|
private NativeArray<Quaternion> prototypeBaseRotation;
|
||
|
private NativeArray<Vector3> prototypeBaseScale;
|
||
|
private NativeArray<BoundingSphere> prototypeBounds;
|
||
|
private NativeArray<bool> prototypeWillDoFrustumTest;
|
||
|
|
||
|
public GTreeRenderer(GStylizedTerrain terrain)
|
||
|
{
|
||
|
this.terrain = terrain;
|
||
|
}
|
||
|
|
||
|
public void Render(Camera cam)
|
||
|
{
|
||
|
try
|
||
|
{
|
||
|
if (GRuntimeSettings.Instance.isEditingGeometry)
|
||
|
return;
|
||
|
|
||
|
InitFrame(cam);
|
||
|
if (CullTerrain())
|
||
|
return;
|
||
|
CalculateQuickInstancesCullBox();
|
||
|
CreateCommonJobData();
|
||
|
CullAndCalculateTreeTransform();
|
||
|
CopyInstanceNativeData();
|
||
|
if (enableInstancing)
|
||
|
{
|
||
|
DrawInstanced();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Draw();
|
||
|
}
|
||
|
}
|
||
|
catch (GSkipFrameException) { }
|
||
|
|
||
|
CleanUpFrame();
|
||
|
}
|
||
|
|
||
|
private void InitFrame(Camera cam)
|
||
|
{
|
||
|
foliage = terrain.TerrainData.Foliage;
|
||
|
|
||
|
terrainPosition = terrain.transform.position;
|
||
|
terrainSize = terrain.TerrainData.Geometry.Size;
|
||
|
treeDistance = terrain.TerrainData.Rendering.TreeDistance;
|
||
|
billboardStart = terrain.TerrainData.Rendering.BillboardStart;
|
||
|
cullVolumeBias = GRuntimeSettings.Instance.renderingDefault.treeCullBias;
|
||
|
|
||
|
if (terrain.TerrainData.Foliage.Trees != null)
|
||
|
{
|
||
|
prototypes = terrain.TerrainData.Foliage.Trees.Prototypes;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
prototypes = new List<GTreePrototype>();
|
||
|
}
|
||
|
|
||
|
if (prototypeCache == null || prototypeCache.Length!=prototypes.Count)
|
||
|
{
|
||
|
prototypeCache = new PrototypeCache[prototypes.Count];
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < prototypes.Count; ++i)
|
||
|
{
|
||
|
GTreePrototype p = prototypes[i];
|
||
|
PrototypeCache cache = prototypeCache[i];
|
||
|
|
||
|
bool valid = prototypes[i].IsValid;
|
||
|
cache.validation = valid;
|
||
|
if (valid)
|
||
|
{
|
||
|
cache.subMeshCount = p.sharedMesh.subMeshCount;
|
||
|
cache.canDrawInstanced = IsInstancingEnabledForAllMaterials(p);
|
||
|
cache.canDrawBillboardInstanced =
|
||
|
p.billboard != null &&
|
||
|
p.billboard.material != null &&
|
||
|
p.billboard.material.enableInstancing;
|
||
|
}
|
||
|
if (p.billboard != null)
|
||
|
{
|
||
|
cache.billboardMesh = GBillboardUtilities.GetMesh(p.billboard);
|
||
|
}
|
||
|
if (p.billboard != null && p.billboard.material != null)
|
||
|
{
|
||
|
if (cache.billboardImageTexcoords == null ||
|
||
|
cache.billboardImageTexcoords.Length != p.billboard.imageCount)
|
||
|
{
|
||
|
cache.billboardImageTexcoords = p.billboard.GetImageTexCoords();
|
||
|
}
|
||
|
Material mat = p.billboard.material;
|
||
|
mat.SetVectorArray(BILLBOARD_IMAGE_TEXCOORDS, cache.billboardImageTexcoords);
|
||
|
mat.SetInt(BILLBOARD_IMAGE_COUNT, p.billboard.imageCount);
|
||
|
}
|
||
|
|
||
|
prototypeCache[i] = cache;
|
||
|
}
|
||
|
|
||
|
enableInstancing = terrain.TerrainData.Rendering.EnableInstancing && SystemInfo.supportsInstancing;
|
||
|
|
||
|
normalizedToLocalMatrix = Matrix4x4.Scale(terrainSize);
|
||
|
localToWorldMatrix = terrain.transform.localToWorldMatrix;
|
||
|
normalizedToWorldMatrix = localToWorldMatrix * normalizedToLocalMatrix;
|
||
|
|
||
|
camera = cam;
|
||
|
if (frustum == null)
|
||
|
{
|
||
|
frustum = new Plane[6];
|
||
|
}
|
||
|
GFrustumUtilities.Calculate(camera, frustum, treeDistance);
|
||
|
if (nearFrustumCorners == null)
|
||
|
{
|
||
|
nearFrustumCorners = new Vector3[4];
|
||
|
}
|
||
|
if (farFrustumCorners == null)
|
||
|
{
|
||
|
farFrustumCorners = new Vector3[4];
|
||
|
}
|
||
|
|
||
|
if (batchContainer == null)
|
||
|
{
|
||
|
batchContainer = new Matrix4x4[BATCH_MAX_INSTANCE_COUNT];
|
||
|
}
|
||
|
if (billboardBatchContainer == null)
|
||
|
{
|
||
|
billboardBatchContainer = new Matrix4x4[BATCH_MAX_INSTANCE_COUNT];
|
||
|
}
|
||
|
|
||
|
if (!isWarningLogged)
|
||
|
{
|
||
|
for (int i = 0; i < prototypes.Count; ++i)
|
||
|
{
|
||
|
if (!prototypes[i].IsValid)
|
||
|
{
|
||
|
string msg = string.Format(
|
||
|
"Tree prototye {0}: " +
|
||
|
"The prototype is not valid, make sure you've assigned a prefab with correct mesh and materials setup.",
|
||
|
i);
|
||
|
Debug.LogWarning(msg);
|
||
|
}
|
||
|
if (enableInstancing && prototypes[i].IsValid)
|
||
|
{
|
||
|
if (!IsInstancingEnabledForAllMaterials(prototypes[i]))
|
||
|
{
|
||
|
string msg = string.Format(
|
||
|
"Tree prototype {0} ({1}): " +
|
||
|
"Instancing need to be enabled for all materials for the renderer to work at its best. " +
|
||
|
"Otherwise it will fallback to non-instanced for this prototype.",
|
||
|
i, prototypes[i].Prefab.name);
|
||
|
Debug.LogWarning(msg);
|
||
|
}
|
||
|
if (prototypes[i].billboard != null &&
|
||
|
prototypes[i].billboard.material != null &&
|
||
|
prototypes[i].billboard.material.enableInstancing == false)
|
||
|
{
|
||
|
string msg = string.Format(
|
||
|
"Tree prototype {0} ({1}): " +
|
||
|
"Instancing need to be enabled for billboard material for the renderer to work at its best. " +
|
||
|
"Otherwise it will fallback to non-instanced for this prototype when render billboards.",
|
||
|
i, prototypes[i].Prefab.name);
|
||
|
Debug.LogWarning(msg);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
isWarningLogged = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
///
|
||
|
/// </summary>
|
||
|
/// <returns>True if the terrain is culled</returns>
|
||
|
private bool CullTerrain()
|
||
|
{
|
||
|
bool prototypeCountTest = prototypes.Count > 0;
|
||
|
if (!prototypeCountTest)
|
||
|
return true;
|
||
|
|
||
|
bool nonZeroDistanceTest = terrain.TerrainData.Rendering.TreeDistance > 0;
|
||
|
if (!nonZeroDistanceTest)
|
||
|
return true;
|
||
|
|
||
|
bool instanceCountTest = terrain.TerrainData.Foliage.TreeInstances.Count > 0;
|
||
|
if (!instanceCountTest)
|
||
|
return true;
|
||
|
|
||
|
bool frustumTest = GeometryUtility.TestPlanesAABB(frustum, terrain.Bounds);
|
||
|
if (!frustumTest)
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
private void CalculateQuickInstancesCullBox()
|
||
|
{
|
||
|
camera.CalculateFrustumCorners(GCommon.UnitRect, camera.nearClipPlane, Camera.MonoOrStereoscopicEye.Mono, nearFrustumCorners);
|
||
|
camera.CalculateFrustumCorners(GCommon.UnitRect, treeDistance, Camera.MonoOrStereoscopicEye.Mono, farFrustumCorners);
|
||
|
|
||
|
for (int i = 0; i < 4; ++i)
|
||
|
{
|
||
|
nearFrustumCorners[i] = camera.transform.TransformPoint(nearFrustumCorners[i]);
|
||
|
farFrustumCorners[i] = camera.transform.TransformPoint(farFrustumCorners[i]);
|
||
|
}
|
||
|
|
||
|
cullBoxMin = Vector3.zero;
|
||
|
cullBoxMax = Vector3.zero;
|
||
|
|
||
|
cullBoxMin.x = Mathf.Min(
|
||
|
nearFrustumCorners[0].x, nearFrustumCorners[1].x, nearFrustumCorners[2].x, nearFrustumCorners[3].x,
|
||
|
farFrustumCorners[0].x, farFrustumCorners[1].x, farFrustumCorners[2].x, farFrustumCorners[3].x);
|
||
|
cullBoxMin.y = Mathf.Min(
|
||
|
nearFrustumCorners[0].y, nearFrustumCorners[1].y, nearFrustumCorners[2].y, nearFrustumCorners[3].y,
|
||
|
farFrustumCorners[0].y, farFrustumCorners[1].y, farFrustumCorners[2].y, farFrustumCorners[3].y);
|
||
|
cullBoxMin.z = Mathf.Min(
|
||
|
nearFrustumCorners[0].z, nearFrustumCorners[1].z, nearFrustumCorners[2].z, nearFrustumCorners[3].z,
|
||
|
farFrustumCorners[0].z, farFrustumCorners[1].z, farFrustumCorners[2].z, farFrustumCorners[3].z);
|
||
|
|
||
|
cullBoxMax.x = Mathf.Max(
|
||
|
nearFrustumCorners[0].x, nearFrustumCorners[1].x, nearFrustumCorners[2].x, nearFrustumCorners[3].x,
|
||
|
farFrustumCorners[0].x, farFrustumCorners[1].x, farFrustumCorners[2].x, farFrustumCorners[3].x);
|
||
|
cullBoxMax.y = Mathf.Max(
|
||
|
nearFrustumCorners[0].y, nearFrustumCorners[1].y, nearFrustumCorners[2].y, nearFrustumCorners[3].y,
|
||
|
farFrustumCorners[0].y, farFrustumCorners[1].y, farFrustumCorners[2].y, farFrustumCorners[3].y);
|
||
|
cullBoxMax.z = Mathf.Max(
|
||
|
nearFrustumCorners[0].z, nearFrustumCorners[1].z, nearFrustumCorners[2].z, nearFrustumCorners[3].z,
|
||
|
farFrustumCorners[0].z, farFrustumCorners[1].z, farFrustumCorners[2].z, farFrustumCorners[3].z);
|
||
|
|
||
|
cullBoxMin -= Vector3.one * cullVolumeBias;
|
||
|
cullBoxMax += Vector3.one * cullVolumeBias;
|
||
|
|
||
|
cullBoxMin = terrain.WorldPointToNormalized(cullBoxMin);
|
||
|
cullBoxMax = terrain.WorldPointToNormalized(cullBoxMax);
|
||
|
}
|
||
|
|
||
|
private void CreateCommonJobData()
|
||
|
{
|
||
|
frustumPlanes = new NativeArray<Plane>(frustum, Allocator.TempJob);
|
||
|
prototypePivotOffset = new NativeArray<float>(prototypes.Count, Allocator.TempJob);
|
||
|
prototypeBaseRotation = new NativeArray<Quaternion>(prototypes.Count, Allocator.TempJob);
|
||
|
prototypeBaseScale = new NativeArray<Vector3>(prototypes.Count, Allocator.TempJob);
|
||
|
prototypeBounds = new NativeArray<BoundingSphere>(prototypes.Count, Allocator.TempJob);
|
||
|
prototypeWillDoFrustumTest = new NativeArray<bool>(prototypes.Count, Allocator.TempJob);
|
||
|
}
|
||
|
|
||
|
private void CullAndCalculateTreeTransform()
|
||
|
{
|
||
|
if (nativeData == null)
|
||
|
{
|
||
|
nativeData = new GTreeNativeData(terrain.TerrainData.Foliage.TreeInstances);
|
||
|
}
|
||
|
|
||
|
bool willSkipFrame = false;
|
||
|
|
||
|
|
||
|
try
|
||
|
{
|
||
|
for (int i = 0; i < prototypes.Count; ++i)
|
||
|
{
|
||
|
prototypePivotOffset[i] = prototypes[i].PivotOffset;
|
||
|
prototypeBaseRotation[i] = prototypes[i].BaseRotation;
|
||
|
prototypeBaseScale[i] = prototypes[i].BaseScale;
|
||
|
prototypeBounds[i] = prototypes[i].GetBoundingSphere();
|
||
|
prototypeWillDoFrustumTest[i] = IsInstancingEnabledForAllMaterials(prototypes[i]);
|
||
|
}
|
||
|
|
||
|
GCullAndCalculateTreeTransformJob job = new GCullAndCalculateTreeTransformJob()
|
||
|
{
|
||
|
instances = nativeData.instances,
|
||
|
prototypeIndices = nativeData.prototypeIndices,
|
||
|
transforms = nativeData.trs,
|
||
|
prototypePivotOffset = prototypePivotOffset,
|
||
|
prototypeBaseRotation = prototypeBaseRotation,
|
||
|
prototypeBaseScale = prototypeBaseScale,
|
||
|
cullResult = nativeData.cullResults,
|
||
|
cullBoxMin = cullBoxMin,
|
||
|
cullBoxMax = cullBoxMax,
|
||
|
flagCulled = CULLED,
|
||
|
flagVisible = VISIBLE,
|
||
|
flagBillboard = BILLBOARD,
|
||
|
terrainPos = terrainPosition,
|
||
|
terrainSize = terrainSize,
|
||
|
cameraPos = camera.transform.position,
|
||
|
treeDistance = treeDistance,
|
||
|
billboardStart = billboardStart,
|
||
|
cullVolumeBias = cullVolumeBias,
|
||
|
prototypeBounds = prototypeBounds,
|
||
|
prototypeWillDoFrustumTest = prototypeWillDoFrustumTest,
|
||
|
frustum = frustumPlanes
|
||
|
};
|
||
|
|
||
|
JobHandle handle = job.Schedule(nativeData.instances.Length, 100);
|
||
|
handle.Complete();
|
||
|
}
|
||
|
catch (System.InvalidOperationException)
|
||
|
{
|
||
|
foliage.TreeAllChanged();
|
||
|
willSkipFrame = true;
|
||
|
}
|
||
|
catch (System.Exception e)
|
||
|
{
|
||
|
Debug.LogException(e);
|
||
|
}
|
||
|
|
||
|
prototypePivotOffset.Dispose();
|
||
|
prototypeBaseRotation.Dispose();
|
||
|
prototypeBaseScale.Dispose();
|
||
|
prototypeBounds.Dispose();
|
||
|
prototypeWillDoFrustumTest.Dispose();
|
||
|
frustumPlanes.Dispose();
|
||
|
|
||
|
if (willSkipFrame)
|
||
|
{
|
||
|
throw new GSkipFrameException();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private bool IsInstancingEnabledForAllMaterials(GTreePrototype p)
|
||
|
{
|
||
|
if (!enableInstancing)
|
||
|
return false;
|
||
|
if (p.SharedMaterials == null || p.SharedMaterials.Length == 0)
|
||
|
return false;
|
||
|
for (int i = 0; i < p.SharedMaterials.Length; ++i)
|
||
|
{
|
||
|
if (!p.SharedMaterials[i].enableInstancing)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private void CopyInstanceNativeData()
|
||
|
{
|
||
|
if (instancePrototypeIndices == null || instancePrototypeIndices.Length != nativeData.prototypeIndices.Length)
|
||
|
{
|
||
|
instancePrototypeIndices = new int[nativeData.prototypeIndices.Length];
|
||
|
}
|
||
|
if (instanceTransforms == null || instanceTransforms.Length != nativeData.trs.Length)
|
||
|
{
|
||
|
instanceTransforms = new Matrix4x4[nativeData.trs.Length];
|
||
|
}
|
||
|
if (instanceCullResults == null || instanceCullResults.Length != nativeData.cullResults.Length)
|
||
|
{
|
||
|
instanceCullResults = new byte[nativeData.cullResults.Length];
|
||
|
}
|
||
|
|
||
|
nativeData.prototypeIndices.CopyTo(instancePrototypeIndices);
|
||
|
nativeData.trs.CopyTo(instanceTransforms);
|
||
|
nativeData.cullResults.CopyTo(instanceCullResults);
|
||
|
}
|
||
|
|
||
|
private void Draw()
|
||
|
{
|
||
|
GTreePrototype proto;
|
||
|
Mesh mesh;
|
||
|
Material[] materials;
|
||
|
Material billboardMaterial;
|
||
|
int protoIndex = 0;
|
||
|
int submeshCount = 0;
|
||
|
int drawCount = 0;
|
||
|
Matrix4x4 trs;
|
||
|
|
||
|
int count = instancePrototypeIndices.Length;
|
||
|
for (int i = 0; i < count; ++i)
|
||
|
{
|
||
|
if (instanceCullResults[i] == CULLED)
|
||
|
continue;
|
||
|
|
||
|
protoIndex = instancePrototypeIndices[i];
|
||
|
if (prototypeCache[protoIndex].validation == false)
|
||
|
continue;
|
||
|
|
||
|
proto = prototypes[protoIndex];
|
||
|
trs = instanceTransforms[i];
|
||
|
|
||
|
if (instanceCullResults[i] == VISIBLE)
|
||
|
{
|
||
|
mesh = proto.sharedMesh;
|
||
|
materials = proto.sharedMaterials;
|
||
|
submeshCount = prototypeCache[protoIndex].subMeshCount;
|
||
|
drawCount = Mathf.Min(materials.Length, submeshCount);
|
||
|
for (int d = 0; d < drawCount; ++d)
|
||
|
{
|
||
|
Graphics.DrawMesh(
|
||
|
mesh,
|
||
|
trs,
|
||
|
materials[d],
|
||
|
proto.layer,
|
||
|
camera,
|
||
|
d,
|
||
|
null,
|
||
|
proto.shadowCastingMode,
|
||
|
proto.receiveShadow,
|
||
|
null,
|
||
|
LightProbeUsage.BlendProbes,
|
||
|
null);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (proto.billboard == null)
|
||
|
continue;
|
||
|
mesh = prototypeCache[protoIndex].billboardMesh;
|
||
|
billboardMaterial = proto.billboard.material;
|
||
|
Graphics.DrawMesh(
|
||
|
mesh,
|
||
|
trs,
|
||
|
billboardMaterial,
|
||
|
proto.layer,
|
||
|
camera,
|
||
|
0,
|
||
|
null,
|
||
|
proto.billboardShadowCastingMode,
|
||
|
proto.BillboardReceiveShadow,
|
||
|
null,
|
||
|
LightProbeUsage.BlendProbes,
|
||
|
null);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DrawInstanced()
|
||
|
{
|
||
|
for (int i = 0; i < prototypes.Count; ++i)
|
||
|
{
|
||
|
if (prototypeCache[i].validation == false)
|
||
|
continue;
|
||
|
DrawInstanced(i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void DrawInstanced(int prototypeIndex)
|
||
|
{
|
||
|
GTreePrototype proto = prototypes[prototypeIndex];
|
||
|
Mesh mesh = proto.sharedMesh;
|
||
|
Material[] materials = proto.sharedMaterials;
|
||
|
int submeshCount = prototypeCache[prototypeIndex].subMeshCount;
|
||
|
int drawCount = Mathf.Min(submeshCount, materials.Length);
|
||
|
|
||
|
Mesh billboardMesh = null;
|
||
|
Material billboardMaterial = null;
|
||
|
BillboardAsset billboard = proto.billboard;
|
||
|
if (billboard != null)
|
||
|
{
|
||
|
billboardMesh = GBillboardUtilities.GetMesh(billboard);
|
||
|
billboardMaterial = billboard.material;
|
||
|
}
|
||
|
|
||
|
bool canDrawInstanced = prototypeCache[prototypeIndex].canDrawInstanced;
|
||
|
bool canDrawBillboardInstanced = prototypeCache[prototypeIndex].canDrawBillboardInstanced;
|
||
|
|
||
|
batchInstanceCount = 0;
|
||
|
billboardBatchInstanceCount = 0;
|
||
|
int count = instancePrototypeIndices.Length;
|
||
|
for (int i = 0; i <= count; ++i)
|
||
|
{
|
||
|
if (i == count || batchInstanceCount == BATCH_MAX_INSTANCE_COUNT)
|
||
|
{
|
||
|
if (canDrawInstanced)
|
||
|
{
|
||
|
for (int d = 0; d < drawCount; ++d)
|
||
|
{
|
||
|
Graphics.DrawMeshInstanced(
|
||
|
mesh, d, materials[d],
|
||
|
batchContainer, batchInstanceCount, null,
|
||
|
proto.shadowCastingMode, proto.receiveShadow, proto.layer,
|
||
|
camera, LightProbeUsage.BlendProbes);
|
||
|
}
|
||
|
batchInstanceCount = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (i == count || billboardBatchInstanceCount == BATCH_MAX_INSTANCE_COUNT)
|
||
|
{
|
||
|
if (billboard != null && canDrawBillboardInstanced)
|
||
|
{
|
||
|
Graphics.DrawMeshInstanced(
|
||
|
billboardMesh, 0, billboardMaterial,
|
||
|
billboardBatchContainer, billboardBatchInstanceCount, null,
|
||
|
proto.billboardShadowCastingMode, proto.BillboardReceiveShadow, proto.layer,
|
||
|
camera, LightProbeUsage.BlendProbes);
|
||
|
billboardBatchInstanceCount = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (i == count)
|
||
|
break;
|
||
|
|
||
|
if (instanceCullResults[i] == CULLED)
|
||
|
continue;
|
||
|
if (instancePrototypeIndices[i] != prototypeIndex)
|
||
|
continue;
|
||
|
|
||
|
if (instanceCullResults[i] == VISIBLE)
|
||
|
{
|
||
|
if (canDrawInstanced)
|
||
|
{
|
||
|
batchContainer[batchInstanceCount] = instanceTransforms[i];
|
||
|
batchInstanceCount += 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (int d = 0; d < drawCount; ++d)
|
||
|
{
|
||
|
Graphics.DrawMesh(
|
||
|
mesh, instanceTransforms[i], materials[d],
|
||
|
proto.layer, camera, d, null,
|
||
|
proto.shadowCastingMode, proto.receiveShadow,
|
||
|
null, LightProbeUsage.BlendProbes, null);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (instanceCullResults[i] == BILLBOARD && billboard != null)
|
||
|
{
|
||
|
if (canDrawBillboardInstanced)
|
||
|
{
|
||
|
billboardBatchContainer[billboardBatchInstanceCount] = instanceTransforms[i];
|
||
|
billboardBatchInstanceCount += 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Graphics.DrawMesh(
|
||
|
billboardMesh, instanceTransforms[i], billboardMaterial,
|
||
|
proto.layer, camera, 0, null,
|
||
|
proto.billboardShadowCastingMode, proto.BillboardReceiveShadow,
|
||
|
null, LightProbeUsage.BlendProbes, null);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private void CleanUpFrame()
|
||
|
{
|
||
|
GNativeArrayUtilities.Dispose(frustumPlanes);
|
||
|
GNativeArrayUtilities.Dispose(prototypePivotOffset);
|
||
|
GNativeArrayUtilities.Dispose(prototypeBaseRotation);
|
||
|
GNativeArrayUtilities.Dispose(prototypeBaseScale);
|
||
|
GNativeArrayUtilities.Dispose(prototypeBounds);
|
||
|
GNativeArrayUtilities.Dispose(prototypeWillDoFrustumTest);
|
||
|
}
|
||
|
|
||
|
internal void ResetTrees()
|
||
|
{
|
||
|
if (nativeData != null)
|
||
|
{
|
||
|
nativeData.Dispose();
|
||
|
nativeData = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
internal void CleanUp()
|
||
|
{
|
||
|
ResetTrees();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|