1
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
public static class GBillboardUtilities
|
||||
{
|
||||
private static Dictionary<BillboardAsset, Mesh> billboardMeshes;
|
||||
private static Dictionary<BillboardAsset, Mesh> BillboardMeshes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (billboardMeshes == null)
|
||||
{
|
||||
billboardMeshes = new Dictionary<BillboardAsset, Mesh>();
|
||||
}
|
||||
return billboardMeshes;
|
||||
}
|
||||
}
|
||||
|
||||
public static Mesh GetMesh(BillboardAsset billboard)
|
||||
{
|
||||
if (!BillboardMeshes.ContainsKey(billboard))
|
||||
{
|
||||
BillboardMeshes.Add(billboard, null);
|
||||
}
|
||||
if (BillboardMeshes[billboard] == null)
|
||||
{
|
||||
Mesh m = CreateMesh(billboard);
|
||||
BillboardMeshes[billboard] = m;
|
||||
}
|
||||
return BillboardMeshes[billboard];
|
||||
}
|
||||
|
||||
private static Mesh CreateMesh(BillboardAsset billboard)
|
||||
{
|
||||
Mesh m = new Mesh();
|
||||
Vector2[] uvs = billboard.GetVertices();
|
||||
Vector3[] vertices = new Vector3[billboard.vertexCount];
|
||||
for (int i = 0; i < vertices.Length; ++i)
|
||||
{
|
||||
vertices[i] = new Vector3(
|
||||
(uvs[i].x - 0.5f) * billboard.width,
|
||||
uvs[i].y * billboard.height + billboard.bottom,
|
||||
0);
|
||||
}
|
||||
ushort[] tris = billboard.GetIndices();
|
||||
int[] trisInt = new int[tris.Length];
|
||||
for (int i = 0; i < trisInt.Length; ++i)
|
||||
{
|
||||
trisInt[i] = tris[i];
|
||||
}
|
||||
|
||||
m.vertices = vertices;
|
||||
m.uv = uvs;
|
||||
m.triangles = trisInt;
|
||||
m.name = billboard.name;
|
||||
return m;
|
||||
}
|
||||
|
||||
public static void CleanUp()
|
||||
{
|
||||
foreach (Mesh m in BillboardMeshes.Values)
|
||||
{
|
||||
GUtilities.DestroyObject(m);
|
||||
}
|
||||
BillboardMeshes.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3bf1b19b259c514e84f8bf8edf83cbc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,67 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using Unity.Burst;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
#if GRIFFIN_BURST
|
||||
[BurstCompile(CompileSynchronously = true)]
|
||||
#endif
|
||||
public struct GBuildInstancedBatchJob : IJob
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeArray<GGrassInstance> instances;
|
||||
|
||||
[WriteOnly]
|
||||
public NativeArray<Vector3Int> batchMetadata; //x: prototypeIndex, y: startIndex, z: length
|
||||
|
||||
public int maxLength;
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
int startIndex = 0;
|
||||
int prototypeIndex = int.MinValue;
|
||||
Vector3Int metadata = new Vector3Int(instances[0].prototypeIndex, startIndex, 0);
|
||||
int batchCount = 0;
|
||||
|
||||
int length = instances.Length;
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
prototypeIndex = instances[i].prototypeIndex;
|
||||
if (prototypeIndex != metadata.x)
|
||||
{
|
||||
metadata.z = i - metadata.y;
|
||||
batchCount += 1;
|
||||
batchMetadata[batchCount] = metadata;
|
||||
|
||||
metadata.x = prototypeIndex;
|
||||
metadata.y = i;
|
||||
metadata.z = 0;
|
||||
}
|
||||
else if (i - metadata.y + 1 > maxLength)
|
||||
{
|
||||
metadata.z = maxLength;
|
||||
batchCount += 1;
|
||||
batchMetadata[batchCount] = metadata;
|
||||
|
||||
metadata.y = i;
|
||||
metadata.z = 0;
|
||||
}
|
||||
else if (i == length - 1)
|
||||
{
|
||||
metadata.z = i - metadata.y + 1;
|
||||
batchCount += 1;
|
||||
batchMetadata[batchCount] = metadata;
|
||||
}
|
||||
}
|
||||
|
||||
metadata.Set(-1, -1, batchCount);
|
||||
batchMetadata[0] = metadata;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: acea7ee9e78d0af4e9c7e5fbfa9dce69
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,62 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Burst;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
#if GRIFFIN_BURST
|
||||
[BurstCompile(CompileSynchronously = true)]
|
||||
#endif
|
||||
public struct GCalculateGrassTransformJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeArray<GGrassInstance> instances;
|
||||
[ReadOnly]
|
||||
public NativeArray<float> prototypePivotOffset;
|
||||
[ReadOnly]
|
||||
public NativeArray<Vector3> prototypeSize;
|
||||
|
||||
[WriteOnly]
|
||||
public NativeArray<Matrix4x4> transforms;
|
||||
|
||||
public Vector3 terrainSize;
|
||||
public Vector3 terrainPos;
|
||||
|
||||
public void Execute(int i)
|
||||
{
|
||||
GGrassInstance grass = instances[i];
|
||||
|
||||
float pivotOffset;
|
||||
Vector3 size;
|
||||
if (grass.prototypeIndex < 0 || grass.prototypeIndex >= prototypePivotOffset.Length)
|
||||
{
|
||||
pivotOffset = 0;
|
||||
size = Vector3.one;
|
||||
}
|
||||
else
|
||||
{
|
||||
pivotOffset = prototypePivotOffset[grass.prototypeIndex];
|
||||
size = prototypeSize[grass.prototypeIndex];
|
||||
}
|
||||
|
||||
Vector3 worldPos = new Vector3(
|
||||
grass.position.x * terrainSize.x + terrainPos.x,
|
||||
grass.position.y * terrainSize.y + terrainPos.y + pivotOffset,
|
||||
grass.position.z * terrainSize.z + terrainPos.z);
|
||||
|
||||
Vector3 worldScale = new Vector3(
|
||||
grass.scale.x * size.x,
|
||||
grass.scale.y * size.y,
|
||||
grass.scale.z * size.z);
|
||||
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(worldPos, grass.rotation, worldScale);
|
||||
|
||||
transforms[i] = matrix;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1110bbb1c37eb2e4dbf8e5bfda3400be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,15 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
public struct GCellCullingParams
|
||||
{
|
||||
public Vector3 boundCenter;
|
||||
public Vector3 boundSize;
|
||||
public int instanceCount;
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb0decc1cc19c2f48a3b8528dbea10d3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,138 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Jobs;
|
||||
using Unity.Collections;
|
||||
using Unity.Burst;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
#if GRIFFIN_BURST
|
||||
[BurstCompile(CompileSynchronously = true)]
|
||||
#endif
|
||||
public struct GCullAndCalculateTreeTransformJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeArray<GTreeInstance> instances;
|
||||
[ReadOnly]
|
||||
public NativeArray<float> prototypePivotOffset;
|
||||
[ReadOnly]
|
||||
public NativeArray<Quaternion> prototypeBaseRotation;
|
||||
[ReadOnly]
|
||||
public NativeArray<Vector3> prototypeBaseScale;
|
||||
[ReadOnly]
|
||||
public NativeArray<BoundingSphere> prototypeBounds;
|
||||
[ReadOnly]
|
||||
public NativeArray<bool> prototypeWillDoFrustumTest;
|
||||
[ReadOnly]
|
||||
public NativeArray<Plane> frustum;
|
||||
|
||||
public NativeArray<int> prototypeIndices;
|
||||
[WriteOnly]
|
||||
public NativeArray<Matrix4x4> transforms;
|
||||
[WriteOnly]
|
||||
public NativeArray<byte> cullResult;
|
||||
|
||||
public Vector3 cullBoxMin;
|
||||
public Vector3 cullBoxMax;
|
||||
public byte flagCulled;
|
||||
public byte flagVisible;
|
||||
public byte flagBillboard;
|
||||
|
||||
public Vector3 terrainPos;
|
||||
public Vector3 terrainSize;
|
||||
|
||||
public Vector3 cameraPos;
|
||||
public float treeDistance;
|
||||
public float billboardStart;
|
||||
public float cullVolumeBias;
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
GTreeInstance tree = instances[index];
|
||||
if (tree.prototypeIndex < 0 || tree.prototypeIndex >= prototypePivotOffset.Length)
|
||||
{
|
||||
cullResult[index] = flagCulled;
|
||||
return;
|
||||
}
|
||||
|
||||
if (tree.position.x < cullBoxMin.x || tree.position.x > cullBoxMax.x ||
|
||||
tree.position.y < cullBoxMin.y || tree.position.y > cullBoxMax.y ||
|
||||
tree.position.z < cullBoxMin.z || tree.position.z > cullBoxMax.z)
|
||||
{
|
||||
cullResult[index] = flagCulled;
|
||||
return;
|
||||
}
|
||||
|
||||
float pivotOffset = prototypePivotOffset[tree.prototypeIndex];
|
||||
Vector3 worldPos = new Vector3(
|
||||
tree.position.x * terrainSize.x + terrainPos.x,
|
||||
tree.position.y * terrainSize.y + terrainPos.y + pivotOffset,
|
||||
tree.position.z * terrainSize.z + terrainPos.z);
|
||||
|
||||
float sqrDistance = Vector3.SqrMagnitude(worldPos - cameraPos);
|
||||
float sqrTreeDistance = treeDistance * treeDistance;
|
||||
if (sqrDistance > sqrTreeDistance)
|
||||
{
|
||||
cullResult[index] = flagCulled;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 baseScale = prototypeBaseScale[tree.prototypeIndex];
|
||||
Vector3 worldScale = new Vector3(
|
||||
tree.scale.x * baseScale.x,
|
||||
tree.scale.y * baseScale.y,
|
||||
tree.scale.z * baseScale.z);
|
||||
|
||||
bool testFrustum = prototypeWillDoFrustumTest[tree.prototypeIndex];
|
||||
if (testFrustum)
|
||||
{
|
||||
BoundingSphere b = prototypeBounds[tree.prototypeIndex];
|
||||
b.position = worldPos;
|
||||
b.radius *= Mathf.Max(worldScale.x, Mathf.Max(worldScale.y, worldScale.z));
|
||||
b.radius += cullVolumeBias;
|
||||
if (!DoFrustumTest(frustum, b))
|
||||
{
|
||||
cullResult[index] = flagCulled;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float sqrBillboardStart = billboardStart * billboardStart;
|
||||
if (sqrDistance >= sqrBillboardStart)
|
||||
{
|
||||
cullResult[index] = flagBillboard;
|
||||
}
|
||||
else
|
||||
{
|
||||
cullResult[index] = flagVisible;
|
||||
}
|
||||
|
||||
if (prototypeIndices[index] < 0)
|
||||
{
|
||||
Quaternion baseRotation = prototypeBaseRotation[tree.prototypeIndex];
|
||||
Quaternion worldRotation = tree.rotation * baseRotation;
|
||||
|
||||
Matrix4x4 matrix = Matrix4x4.TRS(worldPos, worldRotation, worldScale);
|
||||
transforms[index] = matrix;
|
||||
prototypeIndices[index] = tree.prototypeIndex;
|
||||
}
|
||||
}
|
||||
|
||||
private bool DoFrustumTest(NativeArray<Plane> frustum, BoundingSphere bounds)
|
||||
{
|
||||
float d = 0;
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
d = frustum[i].GetDistanceToPoint(bounds.position);
|
||||
if (d < -bounds.radius)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fddf2b3749fae7244ba21feba47fc595
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,64 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Jobs;
|
||||
using Unity.Burst;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
#if GRIFFIN_BURST
|
||||
[BurstCompile(CompileSynchronously = true)]
|
||||
#endif
|
||||
public struct GCullCellJob : IJobParallelFor
|
||||
{
|
||||
[ReadOnly]
|
||||
public NativeArray<GCellCullingParams> cullParams;
|
||||
[ReadOnly]
|
||||
public NativeArray<Plane> frustum;
|
||||
|
||||
public Matrix4x4 normalizeToWorldMatrix;
|
||||
public int visibleValue;
|
||||
public int culledValue;
|
||||
|
||||
[WriteOnly]
|
||||
public NativeArray<int> cullResults;
|
||||
|
||||
public void Execute(int index)
|
||||
{
|
||||
GCellCullingParams param = cullParams[index];
|
||||
if (param.instanceCount == 0)
|
||||
{
|
||||
cullResults[index] = culledValue;
|
||||
return;
|
||||
}
|
||||
BoundingSphere b = new BoundingSphere();
|
||||
b.position = normalizeToWorldMatrix.MultiplyPoint(param.boundCenter);
|
||||
b.radius = normalizeToWorldMatrix.MultiplyVector(param.boundSize).x;
|
||||
if (!DoFrustumTest(frustum, b))
|
||||
{
|
||||
cullResults[index] = culledValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
cullResults[index] = visibleValue;
|
||||
}
|
||||
}
|
||||
|
||||
private bool DoFrustumTest(NativeArray<Plane> frustum, BoundingSphere bounds)
|
||||
{
|
||||
float d = 0;
|
||||
for (int i = 0; i < 6; ++i)
|
||||
{
|
||||
d = frustum[i].GetDistanceToPoint(bounds.position);
|
||||
if (d < -bounds.radius)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 253a86558e40f194781e257e714032d0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,23 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
public static class GFrustumUtilities
|
||||
{
|
||||
private static Vector3[] corners = new Vector3[4];
|
||||
|
||||
public static void Calculate(Camera cam, Plane[] planes, float zFar)
|
||||
{
|
||||
GeometryUtility.CalculateFrustumPlanes(cam, planes);
|
||||
cam.CalculateFrustumCorners(GCommon.UnitRect, zFar, Camera.MonoOrStereoscopicEye.Mono, corners);
|
||||
planes[5].Set3Points(
|
||||
cam.transform.TransformPoint(corners[0]),
|
||||
cam.transform.TransformPoint(corners[1]),
|
||||
cam.transform.TransformPoint(corners[2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b88f92cc59acf6948bfe150a5369d579
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,94 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
public static class GGrassMaterialProvider
|
||||
{
|
||||
public const string FADE_KW = "FADE";
|
||||
|
||||
public static Material GetMaterial(bool isInteractiveGrassEnabled, bool isBillboardEnabled)
|
||||
{
|
||||
Material mat;
|
||||
if (isInteractiveGrassEnabled)
|
||||
{
|
||||
mat = GetInteractiveGrassMaterial();
|
||||
}
|
||||
else
|
||||
{
|
||||
mat = GetNonInteractiveGrassMaterial(isBillboardEnabled);
|
||||
}
|
||||
if (mat != null)
|
||||
{
|
||||
mat.EnableKeyword(FADE_KW);
|
||||
}
|
||||
return mat;
|
||||
}
|
||||
|
||||
private static Material GetInteractiveGrassMaterial()
|
||||
{
|
||||
GRenderPipelineType pipeline = GCommon.CurrentRenderPipeline;
|
||||
if (pipeline == GRenderPipelineType.Builtin)
|
||||
{
|
||||
return GRuntimeSettings.Instance.foliageRendering.grassInteractiveMaterial;
|
||||
}
|
||||
else if (pipeline == GRenderPipelineType.Universal)
|
||||
{
|
||||
return GRuntimeSettings.Instance.foliageRendering.urpGrassInteractiveMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Material GetNonInteractiveGrassMaterial(bool isBillboardEnabled)
|
||||
{
|
||||
if (isBillboardEnabled)
|
||||
{
|
||||
return GetNonInteractiveBillboardGrassMaterial();
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetNonInteractiveNonBillboardGrassMaterial();
|
||||
}
|
||||
}
|
||||
|
||||
private static Material GetNonInteractiveNonBillboardGrassMaterial()
|
||||
{
|
||||
GRenderPipelineType pipeline = GCommon.CurrentRenderPipeline;
|
||||
if (pipeline == GRenderPipelineType.Builtin)
|
||||
{
|
||||
return GRuntimeSettings.Instance.foliageRendering.grassMaterial;
|
||||
}
|
||||
else if (pipeline == GRenderPipelineType.Universal)
|
||||
{
|
||||
return GRuntimeSettings.Instance.foliageRendering.urpGrassMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Material GetNonInteractiveBillboardGrassMaterial()
|
||||
{
|
||||
GRenderPipelineType pipeline = GCommon.CurrentRenderPipeline;
|
||||
if (pipeline == GRenderPipelineType.Builtin)
|
||||
{
|
||||
return GRuntimeSettings.Instance.foliageRendering.grassBillboardMaterial;
|
||||
}
|
||||
else if (pipeline == GRenderPipelineType.Universal)
|
||||
{
|
||||
return GRuntimeSettings.Instance.foliageRendering.urpGrassBillboardMaterial;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f2be55c77b2e0947a2096f0d2369ef7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,14 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
internal class GGrassPatchData
|
||||
{
|
||||
internal GInstancedBatch[] instancedBatches;
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d8875b6227e50924cbf6acfb4716d1ca
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,32 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
internal class GGrassPatchNativeData : IDisposable
|
||||
{
|
||||
internal NativeArray<GGrassInstance> instances;
|
||||
internal NativeArray<Matrix4x4> trs;
|
||||
internal NativeArray<Vector3Int> metadata;
|
||||
|
||||
public GGrassPatchNativeData(List<GGrassInstance> grasses)
|
||||
{
|
||||
grasses.Sort((g0, g1) => { return g0.prototypeIndex.CompareTo(g1.prototypeIndex); });
|
||||
instances = new NativeArray<GGrassInstance>(grasses.ToArray(), Allocator.TempJob);
|
||||
trs = new NativeArray<Matrix4x4>(grasses.Count, Allocator.TempJob);
|
||||
metadata = new NativeArray<Vector3Int>(grasses.Count + 1, Allocator.TempJob);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GNativeArrayUtilities.Dispose(instances);
|
||||
GNativeArrayUtilities.Dispose(trs);
|
||||
GNativeArrayUtilities.Dispose(metadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7e4e48d2e2be864bb222e58505db2da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,533 @@
|
||||
#if GRIFFIN
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Unity.Collections;
|
||||
using Unity.Jobs;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
public class GGrassRenderer
|
||||
{
|
||||
public delegate void ConfiguringMaterialHandler(GStylizedTerrain terrain, int prototypeIndex, MaterialPropertyBlock propertyBlock);
|
||||
public static event ConfiguringMaterialHandler ConfiguringMaterial;
|
||||
|
||||
private GStylizedTerrain terrain;
|
||||
|
||||
private Camera camera;
|
||||
private Plane[] frustum;
|
||||
private int[] cellCullResults;
|
||||
private int[] cellCulledFrameCount;
|
||||
private float[] cellSqrDistanceToCam;
|
||||
private Bounds[] cellWorldBounds;
|
||||
private List<int> visibleCells;
|
||||
private List<int> cellToProcess;
|
||||
|
||||
private const int CULLED = 0;
|
||||
private const int VISIBLE = 1;
|
||||
private const int FRAME_COUNT_TO_UNLOAD_CELL = 100;
|
||||
|
||||
private Matrix4x4 normalizedToLocalMatrix;
|
||||
private Matrix4x4 localToWorldMatrix;
|
||||
private Matrix4x4 normalizedToWorldMatrix;
|
||||
|
||||
private float grassDistance;
|
||||
private Vector3 terrainPosition;
|
||||
private Vector3 terrainSize;
|
||||
private float cullBias;
|
||||
|
||||
private GGrassPatch[] cells;
|
||||
private GGrassPatchData[] cellsData;
|
||||
private GGrassPatchNativeData[] cellsNativeData;
|
||||
private List<GGrassPrototype> prototypes;
|
||||
private MaterialPropertyBlock[] propertyBlocks;
|
||||
private IGGrassMaterialConfigurator materialConfigurator;
|
||||
|
||||
private Mesh[] baseMeshes;
|
||||
private Material[] materials;
|
||||
|
||||
private const int BATCH_MAX_INSTANCE_COUNT = 1023;
|
||||
|
||||
private bool willIgnoreCellLimit;
|
||||
|
||||
public GGrassRenderer(GStylizedTerrain terrain)
|
||||
{
|
||||
this.terrain = terrain;
|
||||
RecalculateCellBounds();
|
||||
}
|
||||
|
||||
private void RecalculateCellBounds()
|
||||
{
|
||||
GGrassPatch[] cells = terrain.TerrainData.Foliage.GrassPatches;
|
||||
for (int i = 0; i < cells.Length; ++i)
|
||||
{
|
||||
cells[i].RecalculateBounds();
|
||||
}
|
||||
}
|
||||
|
||||
public void Render(Camera cam)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (GRuntimeSettings.Instance.isEditingGeometry)
|
||||
return;
|
||||
if (!CheckSystemInstancingAvailable())
|
||||
return;
|
||||
InitFrame(cam);
|
||||
if (CullTerrain())
|
||||
return;
|
||||
CullCells();
|
||||
CalculateAndCacheTransforms();
|
||||
BuildBatches();
|
||||
ConfigureMaterial();
|
||||
Submit();
|
||||
}
|
||||
catch (GSkipFrameException)
|
||||
{ }
|
||||
|
||||
CleanUpFrame();
|
||||
}
|
||||
|
||||
private bool CheckSystemInstancingAvailable()
|
||||
{
|
||||
return SystemInfo.supportsInstancing;
|
||||
}
|
||||
|
||||
private void InitFrame(Camera cam)
|
||||
{
|
||||
if (cullBias != GRuntimeSettings.Instance.renderingDefault.grassCullBias)
|
||||
{
|
||||
ClearAllCells();
|
||||
}
|
||||
|
||||
cullBias = GRuntimeSettings.Instance.renderingDefault.grassCullBias;
|
||||
terrainPosition = terrain.transform.position;
|
||||
terrainSize = terrain.TerrainData.Geometry.Size;
|
||||
grassDistance = terrain.TerrainData.Rendering.GrassDistance;
|
||||
cells = terrain.TerrainData.Foliage.GrassPatches;
|
||||
willIgnoreCellLimit =
|
||||
cellsData == null ||
|
||||
GRuntimeSettings.Instance.isEditingFoliage;
|
||||
|
||||
if (cellsData == null || cellsData.Length != cells.Length)
|
||||
{
|
||||
cellsData = new GGrassPatchData[cells.Length];
|
||||
}
|
||||
for (int i = 0; i < cellsData.Length; ++i)
|
||||
{
|
||||
if (cellsData[i] == null)
|
||||
{
|
||||
cellsData[i] = new GGrassPatchData();
|
||||
}
|
||||
}
|
||||
|
||||
if (cellsNativeData == null || cellsNativeData.Length != cells.Length)
|
||||
{
|
||||
cellsNativeData = new GGrassPatchNativeData[cells.Length];
|
||||
}
|
||||
|
||||
if (visibleCells == null)
|
||||
{
|
||||
visibleCells = new List<int>();
|
||||
}
|
||||
if (cellToProcess == null)
|
||||
{
|
||||
cellToProcess = new List<int>();
|
||||
}
|
||||
|
||||
if (terrain.TerrainData.Foliage.Grasses != null)
|
||||
{
|
||||
prototypes = terrain.TerrainData.Foliage.Grasses.Prototypes;
|
||||
if (propertyBlocks == null || propertyBlocks.Length != prototypes.Count)
|
||||
{
|
||||
propertyBlocks = new MaterialPropertyBlock[prototypes.Count];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
prototypes = new List<GGrassPrototype>();
|
||||
}
|
||||
|
||||
if (materialConfigurator == null)
|
||||
{
|
||||
materialConfigurator = System.Activator.CreateInstance<GSimpleGrassMaterialConfigurator>();
|
||||
}
|
||||
|
||||
normalizedToLocalMatrix = Matrix4x4.Scale(terrainSize);
|
||||
localToWorldMatrix = terrain.transform.localToWorldMatrix;
|
||||
normalizedToWorldMatrix = localToWorldMatrix * normalizedToLocalMatrix;
|
||||
|
||||
camera = cam;
|
||||
if (frustum == null)
|
||||
{
|
||||
frustum = new Plane[6];
|
||||
}
|
||||
GFrustumUtilities.Calculate(camera, frustum, grassDistance);
|
||||
|
||||
if (cellCullResults == null || cellCullResults.Length != cells.Length)
|
||||
{
|
||||
cellCullResults = new int[cells.Length];
|
||||
}
|
||||
|
||||
if (cellCulledFrameCount == null || cellCulledFrameCount.Length != cells.Length)
|
||||
{
|
||||
cellCulledFrameCount = new int[cells.Length];
|
||||
}
|
||||
|
||||
if (cellSqrDistanceToCam == null || cellSqrDistanceToCam.Length != cells.Length)
|
||||
{
|
||||
cellSqrDistanceToCam = new float[cells.Length];
|
||||
}
|
||||
|
||||
if (cellWorldBounds == null || cellWorldBounds.Length != cells.Length)
|
||||
{
|
||||
cellWorldBounds = new Bounds[cells.Length];
|
||||
for (int i = 0; i < cells.Length; ++i)
|
||||
{
|
||||
cellWorldBounds[i] = new Bounds()
|
||||
{
|
||||
center = normalizedToWorldMatrix.MultiplyPoint(cells[i].bounds.center),
|
||||
size = normalizedToWorldMatrix.MultiplyVector(cells[i].bounds.size) + Vector3.one * cullBias
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
GUtilities.EnsureArrayLength(ref baseMeshes, prototypes.Count);
|
||||
GUtilities.EnsureArrayLength(ref materials, prototypes.Count);
|
||||
for (int i = 0; i < prototypes.Count; ++i)
|
||||
{
|
||||
baseMeshes[i] = prototypes[i].GetBaseMesh();
|
||||
materials[i] =
|
||||
prototypes[i].Shape == GGrassShape.DetailObject ?
|
||||
prototypes[i].DetailMaterial :
|
||||
GGrassMaterialProvider.GetMaterial(terrain.TerrainData.Foliage.EnableInteractiveGrass, prototypes[i].IsBillboard);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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.GrassDistance > 0;
|
||||
if (!nonZeroDistanceTest)
|
||||
return true;
|
||||
|
||||
bool frustumTest = GeometryUtility.TestPlanesAABB(frustum, terrain.Bounds);
|
||||
if (!frustumTest)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void CullCells()
|
||||
{
|
||||
visibleCells.Clear();
|
||||
cellToProcess.Clear();
|
||||
|
||||
Vector3 camWorldPos = camera.transform.position;
|
||||
for (int i = 0; i < cells.Length; ++i)
|
||||
{
|
||||
if (cells[i].InstanceCount == 0)
|
||||
{
|
||||
cellCullResults[i] = CULLED;
|
||||
}
|
||||
else
|
||||
{
|
||||
int cullResult = GeometryUtility.TestPlanesAABB(frustum, cellWorldBounds[i]) ? VISIBLE : CULLED;
|
||||
cellCullResults[i] = cullResult;
|
||||
if (cullResult == VISIBLE)
|
||||
{
|
||||
visibleCells.Add(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (cellCullResults[i] == CULLED)
|
||||
{
|
||||
cellCulledFrameCount[i] += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
cellCulledFrameCount[i] = 0;
|
||||
}
|
||||
cellSqrDistanceToCam[i] = Vector3.SqrMagnitude(cellWorldBounds[i].center - camWorldPos);
|
||||
}
|
||||
|
||||
for (int i = 0; i < visibleCells.Count; ++i)
|
||||
{
|
||||
int cellIndex = visibleCells[i];
|
||||
if (cellsData[cellIndex].instancedBatches == null)
|
||||
{
|
||||
cellToProcess.Add(cellIndex);
|
||||
if (cellToProcess.Count == GRuntimeSettings.Instance.renderingDefault.grassCellToProcessPerFrame &&
|
||||
!willIgnoreCellLimit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateAndCacheTransforms()
|
||||
{
|
||||
if (cellToProcess.Count == 0)
|
||||
return;
|
||||
bool willSkipFrame = false;
|
||||
|
||||
try
|
||||
{
|
||||
NativeArray<float> prototypePivotOffset = new NativeArray<float>(prototypes.Count, Allocator.TempJob);
|
||||
NativeArray<Vector3> prototypeSize = new NativeArray<Vector3>(prototypes.Count, Allocator.TempJob);
|
||||
for (int i = 0; i < prototypes.Count; ++i)
|
||||
{
|
||||
prototypePivotOffset[i] = prototypes[i].pivotOffset;
|
||||
prototypeSize[i] = prototypes[i].size;
|
||||
}
|
||||
|
||||
JobHandle[] handles = new JobHandle[cellToProcess.Count];
|
||||
for (int i = 0; i < cellToProcess.Count; ++i)
|
||||
{
|
||||
int cellIndex = cellToProcess[i];
|
||||
GGrassPatch cell = cells[cellIndex];
|
||||
GGrassPatchNativeData nativeData = new GGrassPatchNativeData(cell.Instances);
|
||||
cellsNativeData[cellIndex] = nativeData;
|
||||
|
||||
GCalculateGrassTransformJob job = new GCalculateGrassTransformJob()
|
||||
{
|
||||
instances = nativeData.instances,
|
||||
transforms = nativeData.trs,
|
||||
prototypePivotOffset = prototypePivotOffset,
|
||||
prototypeSize = prototypeSize,
|
||||
terrainSize = terrainSize,
|
||||
terrainPos = terrainPosition
|
||||
};
|
||||
//handles[i] = job.Schedule();
|
||||
handles[i] = job.Schedule(nativeData.instances.Length, 100);
|
||||
}
|
||||
|
||||
GJobUtilities.CompleteAll(handles);
|
||||
|
||||
prototypePivotOffset.Dispose();
|
||||
prototypeSize.Dispose();
|
||||
}
|
||||
catch (System.InvalidOperationException)
|
||||
{
|
||||
willSkipFrame = true;
|
||||
}
|
||||
catch (System.Exception e)
|
||||
{
|
||||
Debug.LogException(e);
|
||||
}
|
||||
|
||||
if (willSkipFrame)
|
||||
{
|
||||
throw new GSkipFrameException();
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildBatches()
|
||||
{
|
||||
JobHandle[] handles = new JobHandle[cellToProcess.Count];
|
||||
for (int i = 0; i < cellToProcess.Count; ++i)
|
||||
{
|
||||
int cellIndex = cellToProcess[i];
|
||||
GGrassPatchNativeData nativeData = cellsNativeData[cellIndex];
|
||||
|
||||
//GGrassPatch cell = cellToProcess[i];
|
||||
GBuildInstancedBatchJob job = new GBuildInstancedBatchJob()
|
||||
{
|
||||
instances = nativeData.instances,
|
||||
batchMetadata = nativeData.metadata,
|
||||
maxLength = BATCH_MAX_INSTANCE_COUNT
|
||||
};
|
||||
handles[i] = job.Schedule();
|
||||
}
|
||||
|
||||
GJobUtilities.CompleteAll(handles);
|
||||
|
||||
for (int i = 0; i < cellToProcess.Count; ++i)
|
||||
{
|
||||
int cellIndex = cellToProcess[i];
|
||||
CreateInstancedBatches(cellIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateInstancedBatches(int cellIndex)
|
||||
{
|
||||
GGrassPatchNativeData nativeData = cellsNativeData[cellIndex];
|
||||
|
||||
NativeArray<Matrix4x4> trs = nativeData.trs;
|
||||
NativeArray<Vector3Int> metadata = nativeData.metadata;
|
||||
|
||||
GInstancedBatch[] batches = new GInstancedBatch[metadata[0].z];
|
||||
for (int i = 0; i < batches.Length; ++i)
|
||||
{
|
||||
int prototypeIndex = metadata[i + 1].x;
|
||||
int startIndex = metadata[i + 1].y;
|
||||
int length = metadata[i + 1].z;
|
||||
int indexLimit = startIndex + length;
|
||||
|
||||
GInstancedBatch batch = new GInstancedBatch(BATCH_MAX_INSTANCE_COUNT);
|
||||
batch.prototypeIndex = prototypeIndex;
|
||||
for (int j = startIndex; j < indexLimit; ++j)
|
||||
{
|
||||
batch.AddTransform(trs[j]);
|
||||
}
|
||||
|
||||
batches[i] = batch;
|
||||
}
|
||||
|
||||
cellsData[cellIndex].instancedBatches = batches;
|
||||
}
|
||||
|
||||
private void ConfigureMaterial()
|
||||
{
|
||||
for (int i = 0; i < prototypes.Count; ++i)
|
||||
{
|
||||
if (propertyBlocks[i] == null)
|
||||
propertyBlocks[i] = new MaterialPropertyBlock();
|
||||
propertyBlocks[i].Clear();
|
||||
materialConfigurator.Configure(terrain, i, propertyBlocks[i]);
|
||||
if (ConfiguringMaterial != null)
|
||||
{
|
||||
ConfiguringMaterial.Invoke(terrain, i, propertyBlocks[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Submit()
|
||||
{
|
||||
for (int i = 0; i < visibleCells.Count; ++i)
|
||||
{
|
||||
int cellIndex = visibleCells[i];
|
||||
Submit(cellIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private void Submit(int cellIndex)
|
||||
{
|
||||
GGrassPatchData data = cellsData[cellIndex];
|
||||
if (data == null)
|
||||
return;
|
||||
GInstancedBatch[] batches = data.instancedBatches;
|
||||
if (batches == null)
|
||||
return;
|
||||
for (int i = 0; i < batches.Length; ++i)
|
||||
{
|
||||
GInstancedBatch b = batches[i];
|
||||
if (b.prototypeIndex >= prototypes.Count)
|
||||
continue;
|
||||
|
||||
GGrassPrototype proto = prototypes[b.prototypeIndex];
|
||||
MaterialPropertyBlock propertyBlock = propertyBlocks[b.prototypeIndex];
|
||||
|
||||
Mesh baseMesh = baseMeshes[b.prototypeIndex];
|
||||
if (baseMesh == null)
|
||||
continue;
|
||||
|
||||
Material material = materials[b.prototypeIndex];
|
||||
if (material == null || !material.enableInstancing)
|
||||
continue;
|
||||
|
||||
Graphics.DrawMeshInstanced(
|
||||
baseMesh,
|
||||
0,
|
||||
material,
|
||||
b.transforms,
|
||||
b.instanceCount,
|
||||
propertyBlock,
|
||||
proto.shadowCastingMode,
|
||||
proto.receiveShadow,
|
||||
proto.layer,
|
||||
camera,
|
||||
LightProbeUsage.BlendProbes);
|
||||
}
|
||||
}
|
||||
|
||||
private void CleanUpFrame()
|
||||
{
|
||||
if (cellsNativeData != null)
|
||||
{
|
||||
for (int i = 0; i < cellsNativeData.Length; ++i)
|
||||
{
|
||||
if (cellsNativeData[i] != null)
|
||||
{
|
||||
cellsNativeData[i].Dispose();
|
||||
cellsNativeData[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UnloadInactiveCells();
|
||||
}
|
||||
|
||||
private void UnloadInactiveCells()
|
||||
{
|
||||
float sqrRenderDistance = grassDistance * grassDistance;
|
||||
for (int i = 0; i < cells.Length; ++i)
|
||||
{
|
||||
if (cellCulledFrameCount[i] >= FRAME_COUNT_TO_UNLOAD_CELL &&
|
||||
cellSqrDistanceToCam[i] >= sqrRenderDistance)
|
||||
{
|
||||
ClearCellData(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void ClearCellData(int index)
|
||||
{
|
||||
if (cellsData != null)
|
||||
{
|
||||
cellsData[index] = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void CalculateCellWorldBounds(int index)
|
||||
{
|
||||
if (cellWorldBounds != null)
|
||||
{
|
||||
cellWorldBounds[index] = new Bounds()
|
||||
{
|
||||
center = normalizedToWorldMatrix.MultiplyPoint(cells[index].bounds.center),
|
||||
size = normalizedToWorldMatrix.MultiplyVector(cells[index].bounds.size)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal void ClearAllCells()
|
||||
{
|
||||
cells = null;
|
||||
cellsData = null;
|
||||
cellWorldBounds = null;
|
||||
RecalculateCellBounds();
|
||||
}
|
||||
|
||||
internal void CleanUp()
|
||||
{
|
||||
ClearAllCells();
|
||||
}
|
||||
|
||||
internal void OnCellChanged(int index)
|
||||
{
|
||||
ClearCellData(index);
|
||||
CalculateCellWorldBounds(index);
|
||||
}
|
||||
|
||||
internal void OnPatchGridSizeChanged()
|
||||
{
|
||||
ClearAllCells();
|
||||
}
|
||||
|
||||
internal void OnPrototypeGroupChanged()
|
||||
{
|
||||
ClearAllCells();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c1c78d363aaf404d97038e7df1c7572
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,33 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
internal class GInstancedBatch
|
||||
{
|
||||
internal Matrix4x4[] transforms;
|
||||
internal int instanceCount;
|
||||
internal int prototypeIndex;
|
||||
|
||||
public GInstancedBatch(int maxInstanceCount)
|
||||
{
|
||||
transforms = new Matrix4x4[maxInstanceCount];
|
||||
instanceCount = 0;
|
||||
}
|
||||
|
||||
public void AddTransform(Matrix4x4 m)
|
||||
{
|
||||
transforms[instanceCount] = m;
|
||||
instanceCount += 1;
|
||||
}
|
||||
|
||||
public void ClearTransforms()
|
||||
{
|
||||
instanceCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f83b55574e3ce1469bf74b72430553c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,20 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Jobs;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
public static class GJobUtilities
|
||||
{
|
||||
public static void CompleteAll(JobHandle[] handles)
|
||||
{
|
||||
for (int i = 0; i < handles.Length; ++i)
|
||||
{
|
||||
handles[i].Complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 21642d52c4f639d4dbc61de384710337
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,19 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
public class GRenderingVisualization
|
||||
{
|
||||
public Vector3 boxMin;
|
||||
public Vector3 boxMax;
|
||||
public Color terrainBoundsColor;
|
||||
|
||||
public Matrix4x4[] trs;
|
||||
public byte[] cullResult;
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75e7203b548498b479e1dc40802db128
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,52 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Pinwheel.Griffin;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
public class GSimpleGrassMaterialConfigurator : IGGrassMaterialConfigurator
|
||||
{
|
||||
private static readonly int COLOR = Shader.PropertyToID("_Color");
|
||||
private static readonly int MAIN_TEX = Shader.PropertyToID("_MainTex");
|
||||
private static readonly int NOISE_TEX = Shader.PropertyToID("_NoiseTex");
|
||||
private static readonly int WIND = Shader.PropertyToID("_Wind");
|
||||
private static readonly int BEND_FACTOR = Shader.PropertyToID("_BendFactor");
|
||||
private static readonly int VECTOR_FIELD = Shader.PropertyToID("_VectorField");
|
||||
private static readonly int WORLD_TO_NORMALIZED = Shader.PropertyToID("_WorldToNormalized");
|
||||
private static readonly int FADE_MIN_DISTANCE = Shader.PropertyToID("_FadeMinDistance");
|
||||
private static readonly int FADE_MAX_DISTANCE = Shader.PropertyToID("_FadeMaxDistance");
|
||||
|
||||
public void Configure(GStylizedTerrain terrain, int prototypeIndex, MaterialPropertyBlock propertyBlock)
|
||||
{
|
||||
GGrassPrototype proto = terrain.TerrainData.Foliage.Grasses.Prototypes[prototypeIndex];
|
||||
|
||||
propertyBlock.SetTexture(NOISE_TEX, GRuntimeSettings.Instance.foliageRendering.windNoiseTexture);
|
||||
|
||||
IEnumerator<GWindZone> windZone = GWindZone.ActiveWindZones.GetEnumerator();
|
||||
if (windZone.MoveNext())
|
||||
{
|
||||
GWindZone w = windZone.Current;
|
||||
propertyBlock.SetVector(WIND, w.GetWindParams());
|
||||
}
|
||||
|
||||
propertyBlock.SetColor(COLOR, proto.Color);
|
||||
if (proto.Shape != GGrassShape.DetailObject && proto.Texture != null)
|
||||
propertyBlock.SetTexture(MAIN_TEX, proto.Texture);
|
||||
propertyBlock.SetFloat(BEND_FACTOR, proto.BendFactor);
|
||||
|
||||
if (terrain.TerrainData.Foliage.EnableInteractiveGrass)
|
||||
{
|
||||
propertyBlock.SetTexture(VECTOR_FIELD, terrain.GetGrassVectorFieldRenderTexture());
|
||||
propertyBlock.SetMatrix(WORLD_TO_NORMALIZED, terrain.GetWorldToNormalizedMatrix());
|
||||
}
|
||||
|
||||
float fadeMaxDistance = terrain.TerrainData.Rendering.GrassDistance;
|
||||
float fadeMinDistance = Mathf.Clamp(terrain.TerrainData.Rendering.GrassFadeStart, 0f, 0.99f) * fadeMaxDistance;
|
||||
propertyBlock.SetFloat(FADE_MIN_DISTANCE, fadeMinDistance);
|
||||
propertyBlock.SetFloat(FADE_MAX_DISTANCE, fadeMaxDistance);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 47710649461da2949a2bfb30d05e70a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,13 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
public class GSkipFrameException : Exception
|
||||
{
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 222c87f944ae70540a26b7ae6cb0e20d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,35 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Unity.Collections;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
internal class GTreeNativeData : IDisposable
|
||||
{
|
||||
internal NativeArray<GTreeInstance> instances;
|
||||
internal NativeArray<int> prototypeIndices;
|
||||
internal NativeArray<Matrix4x4> trs;
|
||||
internal NativeArray<byte> cullResults;
|
||||
|
||||
public GTreeNativeData(List<GTreeInstance> trees)
|
||||
{
|
||||
instances = new NativeArray<GTreeInstance>(trees.ToArray(), Allocator.Persistent);
|
||||
prototypeIndices = new NativeArray<int>(trees.Count, Allocator.Persistent);
|
||||
GUtilities.Fill(prototypeIndices, -1);
|
||||
trs = new NativeArray<Matrix4x4>(trees.Count, Allocator.Persistent);
|
||||
cullResults = new NativeArray<byte>(trees.Count, Allocator.Persistent);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GNativeArrayUtilities.Dispose(instances);
|
||||
GNativeArrayUtilities.Dispose(prototypeIndices);
|
||||
GNativeArrayUtilities.Dispose(trs);
|
||||
GNativeArrayUtilities.Dispose(cullResults);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 044a58944776b0a42b889fdca06ca71d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,629 @@
|
||||
#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
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e103efa80d697e4bb7dadbb56db1bb2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
@@ -0,0 +1,13 @@
|
||||
#if GRIFFIN
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Pinwheel.Griffin.Rendering
|
||||
{
|
||||
public interface IGGrassMaterialConfigurator
|
||||
{
|
||||
void Configure(GStylizedTerrain terrain, int prototypeIndex, MaterialPropertyBlock propertyBlock);
|
||||
}
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cb9c323a4aad2d4469e14d941215b1ce
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user