Files
2025-06-09 23:23:13 +08:00

824 lines
32 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Unity.Collections;
using Unity.Jobs;
using Unity.Burst;
using Unity.Mathematics;
using UnityEngine.Rendering;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.Profiling;
namespace JBooth.MicroVerseCore
{
public class TreeUtil
{
static ComputeShader occlusionShader = null;
static int _Result = Shader.PropertyToID("_Result");
static int _Positions = Shader.PropertyToID("_Positions");
static int _Result_Width = Shader.PropertyToID("_Result_Width");
static int _Result_Height = Shader.PropertyToID("_Result_Height");
public static void ApplyOcclusion(RenderTexture positions, OcclusionData od, bool others, bool selfSDF)
{
if (others == false && selfSDF == false)
return;
Profiler.BeginSample("Apply Tree Occlusion");
if (occlusionShader == null)
{
occlusionShader = (ComputeShader)Resources.Load("MicroVersePositionToOcclusionMask");
}
occlusionShader.DisableKeyword("_R8");
int kernelHandle = occlusionShader.FindKernel("CSMain");
occlusionShader.SetTexture(kernelHandle, _Result, od.terrainMask);
occlusionShader.SetTexture(kernelHandle, _Positions, positions);
occlusionShader.SetInt(_Result_Width, od.terrainMask.width);
occlusionShader.SetInt(_Result_Height, od.terrainMask.height);
if (others)
{
occlusionShader.Dispatch(kernelHandle, Mathf.CeilToInt(positions.width/512), positions.height, 1);
}
if (others || selfSDF)
{
occlusionShader.EnableKeyword("_R8");
if (od.currentTreeMask == null)
{
var desc = od.terrainMask.descriptor;
desc.colorFormat = RenderTextureFormat.R8;
od.currentTreeMask = RenderTexture.GetTemporary(desc);
RenderTexture.active = od.currentTreeMask;
GL.Clear(false, true, Color.clear);
od.currentTreeMask.name = "Occlusion::CurrentTreeMask";
}
RenderTexture.active = od.currentTreeMask;
GL.Clear(false, true, Color.clear);
occlusionShader.SetTexture(kernelHandle, _Result, od.currentTreeMask);
occlusionShader.Dispatch(kernelHandle, Mathf.CeilToInt(positions.width / 256.0f), positions.height, 1);
}
Profiler.EndSample();
}
}
[BurstCompile]
public struct UnpackTreeInstanceJob : IJob
{
public NativeArray<int> count;
[WriteOnly] public NativeArray<TreeInstance> trees;
[ReadOnly] public NativeArray<half4> placementData;
[ReadOnly] public NativeArray<half4> randomData;
[ReadOnly] public NativeArray<int> treeIndexes;
public void Execute()
{
for (int i = 0; i < placementData.Length; ++i)
{
half4 pd = placementData[i];
if (pd.w > 0)
{
var tree = new TreeInstance();
half4 rd = randomData[i];
tree.position = new Vector3(pd.x, pd.y * 2, pd.z);
tree.color = Color.white;
tree.lightmapColor = Color.white;
tree.prototypeIndex = treeIndexes[(int)rd.x % treeIndexes.Length];
tree.heightScale = rd.y;
tree.widthScale = rd.z;
tree.rotation = rd.w;
trees[count[0]] = tree;
count[0] = count[0] + 1;
}
}
}
}
public class TreeJobHolder
{
public UnpackTreeInstanceJob job;
public JobHandle handle;
public NativeArray<half4> placementData;
public NativeArray<half4> randomData;
private RenderTexture filteredInstances;
private RenderTexture randomResults;
private AsyncGPUReadbackRequest gpuRequestPlacement;
private AsyncGPUReadbackRequest gpuRequestRandoms;
private NativeArray<int> treeIndexes;
public bool IsDone()
{
if (MicroVerse.noAsyncReadback)
{
handle.Complete();
}
return (gpuRequestPlacement.done && gpuRequestRandoms.done && handle.IsCompleted);
}
public bool canceled { get; set; }
public void Cleanup()
{
handle.Complete();
if (placementData.IsCreated)
placementData.Dispose();
if (randomData.IsCreated)
randomData.Dispose();
if (treeIndexes.IsCreated)
treeIndexes.Dispose();
if (job.count.IsCreated)
job.count.Dispose();
if (job.trees.IsCreated)
job.trees.Dispose();
}
public void Dispose()
{
Cleanup();
}
void LaunchJob()
{
job = new UnpackTreeInstanceJob()
{
placementData = placementData,
randomData = randomData,
count = new NativeArray<int>(1, Allocator.TempJob),
trees = new NativeArray<TreeInstance>(placementData.Length, Allocator.TempJob),
treeIndexes = treeIndexes,
};
handle = job.Schedule();
}
private void OnAsyncCompletePositions(AsyncGPUReadbackRequest obj)
{
RenderTexture.active = null;
if (filteredInstances != null)
{
RenderTexture.active = null;
Object.DestroyImmediate(filteredInstances);
}
filteredInstances = null;
if (randomResults == null)
{
if (canceled)
{
if (placementData.IsCreated)
placementData.Dispose();
if (randomData.IsCreated)
randomData.Dispose();
if (treeIndexes.IsCreated)
treeIndexes.Dispose();
}
else
{
LaunchJob();
}
}
}
private void OnAsyncCompleteRandoms(AsyncGPUReadbackRequest obj)
{
RenderTexture.active = null;
if (randomResults != null)
Object.DestroyImmediate(randomResults);
randomResults = null;
if (filteredInstances == null)
{
if (canceled)
{
if (placementData.IsCreated)
placementData.Dispose();
if (randomData.IsCreated)
randomData.Dispose();
if (treeIndexes.IsCreated)
treeIndexes.Dispose();
}
else
{
LaunchJob();
}
}
}
public void AddJob(RenderTexture filteredInstances, RenderTexture randomResults, NativeArray<int> treeIndexes)
{
this.treeIndexes = treeIndexes;
this.filteredInstances = filteredInstances;
this.randomResults = randomResults;
if (MicroVerse.noAsyncReadback)
{
Texture2D place = new Texture2D(filteredInstances.width, filteredInstances.height, TextureFormat.RGBAHalf, false, true);
Texture2D random = new Texture2D(filteredInstances.width, filteredInstances.height, TextureFormat.RGBAHalf, false, true);
RenderTexture.active = filteredInstances;
place.ReadPixels(new Rect(0, 0, place.width, place.height), 0, 0);
place.Apply();
placementData = place.GetRawTextureData<half4>();
RenderTexture.active = randomResults;
random.ReadPixels(new Rect(0, 0, randomResults.width, randomResults.height), 0, 0);
random.Apply();
randomData = random.GetRawTextureData<half4>();
LaunchJob();
Object.DestroyImmediate(place);
Object.DestroyImmediate(random);
}
else
{
this.placementData = new NativeArray<half4>(filteredInstances.width * filteredInstances.height, Allocator.Persistent);
this.randomData = new NativeArray<half4>(filteredInstances.width * filteredInstances.height, Allocator.Persistent);
this.gpuRequestPlacement = AsyncGPUReadback.RequestIntoNativeArray<half4>(ref placementData, filteredInstances, 0, OnAsyncCompletePositions);
this.gpuRequestRandoms = AsyncGPUReadback.RequestIntoNativeArray<half4>(ref randomData, randomResults, 0, OnAsyncCompleteRandoms);
}
}
}
[ExecuteAlways]
public class TreeStamp : Stamp, ITreeModifier, ITextureModifier
{
public override FilterSet GetFilterSet()
{
return filterSet;
}
// pos.xyz, weight
// index, scale.xy, rot
[System.Serializable]
public struct Randomization
{
public float weight;
public Vector2 scaleHeightRange;
public Vector2 scaleWidthRange;
public float sink;
public float scaleMultiplierAtBoundaries;
public Vector2 weightRange;
public int flags;
public bool lockScaleWidthHeight { get { return (flags & (1 << 1)) != 0; } set { if (value) flags |= 1 << 1; else flags &= ~(1 << 1); } }
public bool randomRotation { get { return (flags & (1 << 2)) == 0; } set { if (!value) flags |= 1 << 2; else flags &= ~(1 << 2); } }
public bool densityByWeight { get { return (flags & (1 << 3)) == 0; } set { if (!value) flags |= 1 << 3; else flags &= ~(1 << 3); } }
public bool disabled { get { return (flags & (1 << 4)) == 0; } set { if (!value) flags |= 1 << 4; else flags &= ~(1 << 4); } }
public bool mapHeightFilterToScale { get { return (flags & (1 << 5)) != 0; } set { if (value) flags |= 1 << 5; else flags &= ~(1 << 5); } }
public bool mapWeightToScale { get { return (flags & (1 << 6)) != 0; } set { if (value) flags |= 1 << 6; else flags &= ~(1 << 6); } }
public bool randomScale { get { return (flags & (1 << 7)) == 0; } set { if (!value) flags |= 1 << 7; else flags &= ~(1 << 7); } }
}
public int version = 0;
static Texture2D randomTexture;
// these arrays must be kept in sync. Would like to contain them as one,
// but one needs pointers to objects and the other needs to be a struct
// for jobs.
public List<TreePrototypeSerializable> prototypes = new List<TreePrototypeSerializable>();
public List<Randomization> randomizations = new List<Randomization>();
public uint seed = 0;
public Texture2D poissonDisk;
[Range(0,2)]
public float poissonDiskStrength = 1;
[Range(0.1f, 20)]
public float density = 1;
[Tooltip("Write into occlusion system so other things won't spawn on top of us")]
public bool occludeOthers = true;
[Tooltip("Read occlusion system so we won't spawn where we're not supposed to")]
public bool occludedByOthers = true;
public float minDistanceFromTree = 0;
public float maxDistanceFromTree = 0;
public float minDistanceFromObject = 0;
public float maxDistanceFromObject = 0;
public float minDistanceFromParent = 0;
public float maxDistanceFromParent = 0;
public bool sdfClamp;
[Tooltip("Minimum height to place tree - this lets you spawn objects on water, for instance")]
public float minHeight = -99999;
[Tooltip("Allows to to raise or lower the terrain around tree objects")]
[Range(-3, 3)]
public float heightModAmount = 0;
[Tooltip("Controls the width of the height adjustment")]
[Range(0.1f, 20)]
public float heightModWidth = 5;
[Tooltip("Texture to apply")]
public TerrainLayer layer;
[Tooltip("Weight of texture to apply")]
[Range(0,1)]
public float layerWeight = 0;
[Tooltip("Controls the width of the texturing")]
[Range(0.1f, 20)]
public float layerWidth = 5;
[Tooltip("Applies the slope filter from the stamp to the height/texture mods, so they don't go out over cliffs")]
public bool applyFilteringToTextureMod = false;
public FilterSet filterSet = new FilterSet();
Vector4[] textureLayerWeights;
Material material;
RenderBuffer[] _mrt;
public override void OnEnable()
{
base.OnEnable();
Revision();
}
void Revision()
{
if (version == 0)
{
version = 1;
for (int i = 0; i < randomizations.Count; ++i)
{
var rand = randomizations[i];
rand.disabled = false;
randomizations[i] = rand;
}
}
}
public bool NeedCurvatureMap() { return filterSet.NeedCurvatureMap(); }
public bool NeedFlowMap() { return filterSet.NeedFlowMap(); }
public override Bounds GetBounds()
{
FalloffOverride fo = GetComponentInParent<FalloffOverride>();
var foType = filterSet.falloffFilter.filterType;
var foFilter = filterSet.falloffFilter;
if (fo != null && fo.enabled)
{
foType = fo.filter.filterType;
foFilter = fo.filter;
}
#if __MICROVERSE_SPLINES__
if (foType == FalloffFilter.FilterType.SplineArea && foFilter.splineArea != null)
{
return foFilter.splineArea.GetBounds();
}
#endif
if (foType == FalloffFilter.FilterType.Global && foFilter.paintArea != null && foFilter.paintArea.clampOutsideOfBounds)
{
return foFilter.paintArea.GetBounds();
}
if (foType == FalloffFilter.FilterType.Global)
return new Bounds(Vector3.zero, new Vector3(99999, 999999, 99999));
else
{
return TerrainUtil.GetBounds(transform);
}
}
public bool OccludesOthers()
{
return occludeOthers;
}
public bool UsesOtherTreeSDF() { return minDistanceFromTree > 0 || maxDistanceFromTree > 0; }
public bool UsesOtherObjectSDF() { return minDistanceFromObject > 0 || maxDistanceFromObject > 0; }
// Do I need an sdf for myself
public bool NeedSDF()
{
return heightModAmount > 0 || (layer != null && layerWeight > 0);
}
// do I need my parent to generate an SDF is parented
public bool NeedParentSDF()
{
return minDistanceFromParent > 0 || maxDistanceFromParent > 0;
}
// Do I need to generate an SDF for subspawners
public bool NeedToGenerateSDFForChilden()
{
var me = GetComponent<ISpawner>();
var subs = GetComponentsInChildren<ISpawner>(false);
foreach (var s in subs)
{
if (s != me && s.NeedParentSDF()) return true;
}
return false;
}
Dictionary<Terrain, RenderTexture> sdfs = new Dictionary<Terrain, RenderTexture>();
public void SetSDF(Terrain t, RenderTexture rt)
{
if (sdfs.ContainsKey(t))
{
Debug.LogError("Stamp " + this.name + " already generated sdf for " + t.name);
}
sdfs[t] = rt;
}
public RenderTexture GetSDF(Terrain t)
{
if (sdfs.ContainsKey(t))
return sdfs[t];
return null;
}
ComputeBuffer randomizationBuffer;
static Shader treeStampShader = null;
public void Initialize()
{
if(prototypes.Count == 0)
{
return;
}
if (treeStampShader == null)
{
treeStampShader = Shader.Find("Hidden/MicroVerse/VegetationFilter");
}
prototypeMappings.Clear();
if (material == null)
{
material = new Material(treeStampShader);
}
if (_mrt == null)
_mrt = new RenderBuffer[2];
if (randomTexture == null)
{
randomTexture = new Texture2D(64, 64, TextureFormat.RGBAHalf, false, true);
randomTexture.filterMode = FilterMode.Point;
var data = randomTexture.GetRawTextureData<half4>();
Unity.Mathematics.Random rand = new Unity.Mathematics.Random(31);
rand.InitState(31);
for (int i = 0; i < data.Length; ++i)
{
data[i] = (half4)rand.NextFloat4(0, 1);
}
randomTexture.Apply(false, false);
}
NativeArray<Randomization> randoms = new NativeArray<Randomization>(randomizations.Count, Allocator.Temp);
randoms.CopyFrom(randomizations.ToArray());
randomizationBuffer = new ComputeBuffer(randomizations.Count, UnsafeUtility.SizeOf<Randomization>());
randomizationBuffer.SetData(randoms);
randoms.Dispose();
material.SetTexture(_RandomTex, randomTexture);
material.SetTexture(_Disc, poissonDisk);
material.SetFloat(_DiscStrength, poissonDiskStrength);
material.SetFloat(_MinHeight, minHeight);
material.SetFloat(_NumTreeIndexes, prototypes.Count);
material.SetFloat(_HeightOffset, heightModAmount);
material.SetFloat(_ModWidth, Mathf.Max(layerWeight, heightModWidth));
material.SetFloat(_Seed, seed);
material.SetBuffer(_Randomizations, randomizationBuffer);
if (poissonDisk != null)
{
poissonDisk.wrapMode = TextureWrapMode.Repeat;
poissonDisk.filterMode = FilterMode.Point;
}
if (prototypes != null && prototypes.Count != 0 && (prototypeIndexes == null || prototypeIndexes.Length != prototypes.Count))
prototypeIndexes = new int[prototypes.Count];
keywordBuilder.ClearInitial();
filterSet.PrepareMaterial(this.transform, material, keywordBuilder.initialKeywords);
}
public void InqTreePrototypes(List<TreePrototypeSerializable> trees)
{
trees.AddRange(prototypes);
}
public bool NeedTreeClear() { return false; }
public void ApplyTreeClear(TreeData td) { }
public bool NeedDetailClear() { return false; }
public void ApplyDetailClear(DetailData td) { }
int[] prototypeIndexes;
Dictionary<Terrain, RenderTexture> posWeightRTs = new Dictionary<Terrain, RenderTexture>();
Dictionary<Terrain, RenderTexture> randomsRTs = new Dictionary<Terrain, RenderTexture>();
static int _RandomTex = Shader.PropertyToID("_RandomTex");
static int _Disc = Shader.PropertyToID("_Disc");
static int _DiscStrength = Shader.PropertyToID("_DiscStrength");
static int _Density = Shader.PropertyToID("_Density");
static int _InstanceCount = Shader.PropertyToID("_InstanceCount");
static int _Heightmap = Shader.PropertyToID("_Heightmap");
static int _Normalmap = Shader.PropertyToID("_Normalmap");
static int _Curvemap = Shader.PropertyToID("_Curvemap");
static int _Flowmap = Shader.PropertyToID("_Flowmap");
static int _ClearLayer = Shader.PropertyToID("_ClearLayer");
static int _ClearMask = Shader.PropertyToID("_ClearMask");
static int _MinHeight = Shader.PropertyToID("_MinHeight");
static int _NumTreeIndexes = Shader.PropertyToID("_NumTreeIndexes");
static int _TotalWeights = Shader.PropertyToID("_TotalWeights");
static int _HeightOffset = Shader.PropertyToID("_HeightOffset");
static int _PlacementMask = Shader.PropertyToID("_PlacementMask");
static int _TerrainPixelCount = Shader.PropertyToID("_TerrainPixelCount");
static int _ModWidth = Shader.PropertyToID("_ModWidth");
static int _IndexMap = Shader.PropertyToID("_IndexMap");
static int _WeightMap = Shader.PropertyToID("_WeightMap");
static int _Seed = Shader.PropertyToID("_Seed");
static int _TextureLayerWeights = Shader.PropertyToID("_TextureLayerWeights");
static int _Randomizations = Shader.PropertyToID("_Randomizations");
static int _YCount = Shader.PropertyToID("_YCount");
Dictionary<Terrain, int[]> prototypeMappings = new Dictionary<Terrain, int[]>();
public void ApplyTreeStamp(TreeData td, Dictionary<Terrain, List<TreeJobHolder>> jobs, OcclusionData od)
{
if (poissonDisk == null)
return;
if (prototypes.Count == 0)
return;
Profiler.BeginSample("Apply Tree Stamp");
textureLayerWeights = filterSet.GetTextureWeights(od.terrain.terrainData.terrainLayers);
float totalWeight = 0;
for (int i = 0; i < prototypes.Count; ++i)
{
prototypeIndexes[i] = VegetationUtilities.FindTreeIndex(od.terrain, prototypes[i]);
totalWeight += randomizations[i].weight + 1;
}
prototypeMappings.Add(od.terrain, prototypeIndexes);
keywordBuilder.Clear();
material.SetFloat(_ClearLayer, td.layerIndex);
material.SetTexture(_ClearMask, td.treeClearMap);
material.SetTexture(_Heightmap, td.heightMap);
material.SetTexture(_Normalmap, td.normalMap);
material.SetTexture(_Curvemap, td.curveMap);
material.SetTexture(_Flowmap, td.flowMap);
material.SetVectorArray(_TextureLayerWeights, textureLayerWeights);
material.SetFloat(_TotalWeights, totalWeight);
if (occludedByOthers)
{
material.SetTexture(_PlacementMask, od.terrainMask);
}
else
{
material.SetTexture(_PlacementMask, null);
}
float ratio = td.heightMap.width / td.terrain.terrainData.size.x;
FilterSet.PrepareSDFFilter(keywordBuilder, material, transform, od, ratio, sdfClamp,
minDistanceFromTree, maxDistanceFromTree,
minDistanceFromObject, maxDistanceFromObject,
minDistanceFromParent, maxDistanceFromParent);
material.SetInt(_TerrainPixelCount, td.heightMap.width);
material.SetTexture(_IndexMap, td.dataCache.indexMaps[td.terrain]);
material.SetTexture(_WeightMap, td.dataCache.weightMaps[td.terrain]);
keywordBuilder.Add("_RECONSTRUCTNORMAL");
float densityScale = GetTerrainScalingFactor(td.terrain);
filterSet.PrepareTransform(this.transform, td.terrain, material, keywordBuilder.keywords, densityScale);
keywordBuilder.Assign(material);
int instanceCount = Mathf.RoundToInt(512 * density * density * densityScale * densityScale);
material.SetFloat(_InstanceCount, instanceCount);
float ny = (float)instanceCount / 512.0f;
int yCount = Mathf.FloorToInt(instanceCount / 512);
if (ny != Mathf.FloorToInt(ny))
yCount += 1;
material.SetFloat(_YCount, yCount);
var posWeightRT = new RenderTexture(512, yCount, 0,
RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
posWeightRT.name = "TreeStamp::PositonWeightRT";
var randomsRT = new RenderTexture(512, yCount, 0,
RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear);
posWeightRTs[td.terrain] = posWeightRT;
randomsRTs[td.terrain] = randomsRT;
_mrt[0] = posWeightRT.colorBuffer;
_mrt[1] = randomsRT.colorBuffer;
Graphics.SetRenderTarget(_mrt, posWeightRT.depthBuffer);
Graphics.Blit(poissonDisk, material, 0);
TreeUtil.ApplyOcclusion(posWeightRT, od, occludeOthers, heightModAmount > 0 || layer != null && layerWeight > 0);
Profiler.EndSample();
}
static int _RealHeight = Shader.PropertyToID("_RealHeight");
static int _TreeSDF = Shader.PropertyToID("_TreeSDF");
static int _Amount = Shader.PropertyToID("_Amount");
static int _Width = Shader.PropertyToID("_Width");
static int _Index = Shader.PropertyToID("_Index");
Material heightModMat = null;
Material splatModMat = null;
public void ProcessTreeStamp(TreeData vd, Dictionary<Terrain, List<TreeJobHolder>> jobs, OcclusionData od)
{
if (poissonDisk == null)
return;
if (prototypes.Count == 0)
return;
Profiler.BeginSample("Process Tree Stamp");
Profiler.BeginSample("Height/Texture Mods");
var posWeightRT = posWeightRTs[vd.terrain];
var randomsRT = randomsRTs[vd.terrain];
if (heightModAmount != 0)
{
if (heightModMat == null)
{
heightModMat = new Material(Shader.Find("Hidden/MicroVerse/TreeHeightMod"));
}
var nhm = RenderTexture.GetTemporary(vd.heightMap.descriptor);
heightModMat.SetFloat(_RealHeight, od.RealHeight);
heightModMat.SetTexture(_TreeSDF, od.currentTreeSDF);
heightModMat.SetFloat(_Amount, heightModAmount);
heightModMat.SetTexture(_PlacementMask, od.terrainMask);
float ratio = vd.heightMap.width / vd.terrain.terrainData.size.x;
heightModMat.SetFloat(_Width, heightModWidth * ratio);
Graphics.Blit(vd.heightMap, nhm, heightModMat);
Graphics.Blit(nhm, vd.heightMap);
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(nhm);
}
if ((layer != null && layerWeight > 0))
{
if (splatModMat == null)
{
splatModMat = new Material(Shader.Find("Hidden/MicroVerse/TreeSplatMod"));
}
if (applyFilteringToTextureMod)
{
var kb = new KeywordBuilder();
kb.Add("_RECONSTRUCTNORMAL");
kb.Add("_APPLYFILTER");
filterSet.PrepareTransform(this.transform, od.terrain, splatModMat, kb.keywords);
filterSet.PrepareMaterial(this.transform, splatModMat, kb.initialKeywords);
splatModMat.SetTexture(_Heightmap, vd.heightMap);
splatModMat.SetTexture(_Normalmap, vd.normalMap);
splatModMat.SetTexture(_Curvemap, vd.curveMap);
splatModMat.SetTexture(_Flowmap, vd.flowMap);
splatModMat.SetVectorArray(_TextureLayerWeights, textureLayerWeights);
kb.Assign(splatModMat);
}
var indexMap = vd.dataCache.indexMaps[vd.terrain];
var weightMap = vd.dataCache.weightMaps[vd.terrain];
var nim = RenderTexture.GetTemporary(indexMap.descriptor);
var nwm = RenderTexture.GetTemporary(weightMap.descriptor);
splatModMat.SetTexture(_TreeSDF, od.currentTreeSDF);
splatModMat.SetTexture(_IndexMap, indexMap);
splatModMat.SetTexture(_WeightMap, weightMap);
splatModMat.SetTexture(_PlacementMask, od.terrainMask);
splatModMat.SetFloat(_Amount, layerWeight);
float ratio = indexMap.width / vd.terrain.terrainData.size.x;
splatModMat.SetFloat(_Width, layerWidth * ratio);
int splatIndex = TerrainUtil.FindTextureChannelIndex(vd.terrain, layer);
splatModMat.SetFloat(_Index, splatIndex);
RenderBuffer[] _mrt = new RenderBuffer[2];
_mrt[0] = nim.colorBuffer;
_mrt[1] = nwm.colorBuffer;
Graphics.SetRenderTarget(_mrt, nim.depthBuffer);
Graphics.Blit(null, splatModMat);
// copy back, this sucks!
Graphics.Blit(nim, indexMap);
Graphics.Blit(nwm, weightMap);
RenderTexture.active = null;
RenderTexture.ReleaseTemporary(nim);
RenderTexture.ReleaseTemporary(nwm);
}
UnityEngine.Profiling.Profiler.EndSample();
#if UNITY_EDITOR && __MICROVERSE_MASKS__
if (MicroVerse.instance.bufferCaptureTarget != null)
{
if (MicroVerse.instance.bufferCaptureTarget.IsOutputFlagSet(BufferCaptureTarget.BufferCapture.TreeStampOcclusionMask))
{
var nm = od.currentTreeMask;
if (nm != null)
MicroVerse.instance.bufferCaptureTarget.SaveRenderData(od.terrain, BufferCaptureTarget.BufferCapture.TreeStampOcclusionMask, nm, this.name);
}
if (MicroVerse.instance.bufferCaptureTarget.IsOutputFlagSet(BufferCaptureTarget.BufferCapture.TreeStampSDF))
{
var nm = od.currentTreeSDF;
if (nm != null)
MicroVerse.instance.bufferCaptureTarget.SaveRenderData(od.terrain, BufferCaptureTarget.BufferCapture.TreeStampSDF, nm, this.name);
}
}
#endif
Profiler.BeginSample("Setup Jobs");
// setup job to unpack
var jholder = new TreeJobHolder();
NativeArray<int> indexes = new NativeArray<int>(prototypeMappings[od.terrain].Length, Allocator.Persistent);
indexes.CopyFrom(prototypeMappings[od.terrain]);
jholder.AddJob(posWeightRT, randomsRT, indexes);
if (jobs.ContainsKey(vd.terrain))
{
jobs[vd.terrain].Add(jholder);
}
else
{
jobs.Add(vd.terrain, new List<TreeJobHolder>() { jholder });
}
Profiler.EndSample();
Profiler.EndSample();
}
protected override void OnDestroy()
{
if (material != null) DestroyImmediate(material);
if (heightModMat != null) DestroyImmediate(heightModMat);
if (splatModMat != null) DestroyImmediate(splatModMat);
base.OnDestroy();
}
public void Dispose()
{
RenderTexture.active = null;
foreach (var k in sdfs.Values)
{
if (k != null)
{
RenderTexture.ReleaseTemporary(k);
}
}
if(randomizationBuffer != null)
{
randomizationBuffer.Dispose();
}
sdfs.Clear();
}
void OnDrawGizmosSelected()
{
if (filterSet.falloffFilter.filterType != FalloffFilter.FilterType.Global &&
filterSet.falloffFilter.filterType != FalloffFilter.FilterType.SplineArea)
{
if (MicroVerse.instance != null)
{
Gizmos.color = MicroVerse.instance.options.colors.treeStampColor;
Gizmos.matrix = transform.localToWorldMatrix;
Gizmos.DrawWireCube(new Vector3(0, 0.5f, 0), Vector3.one);
}
}
}
public bool ApplyTextureStamp(RenderTexture indexSrc, RenderTexture indexDest, RenderTexture weightSrc, RenderTexture weightDest, TextureData splatmapData, OcclusionData od)
{
return false;
}
public void InqTerrainLayers(Terrain terrain, List<TerrainLayer> prototypes)
{
if (layer != null)
{
prototypes.Add(layer);
}
}
}
}