Files
Fishing2/Packages/com.jbooth.microverse.objects/Scripts/SpawnProcessor_Object.cs
2025-06-04 00:00:18 +08:00

328 lines
12 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Linq;
using UnityEngine.Profiling;
using Unity.Collections;
using Unity.Mathematics;
namespace JBooth.MicroVerseCore
{
public partial class SpawnProcessor
{
void RenderObjectClearLayers(Terrain[] terrains, MicroVerse.DataCache dataCache)
{
bool needsObjectClearMap = false;
foreach (var m in spawners)
{
IObjectModifier tm = m as IObjectModifier;
if (tm != null)
{
needsObjectClearMap |= tm.NeedObjectClear();
}
}
// generate clear maps for everything
foreach (var terrain in terrains)
{
var terrainBounds = TerrainUtil.ComputeTerrainBounds(terrain);
var indexMap = dataCache.indexMaps[terrain];
var weightMap = dataCache.weightMaps[terrain];
var heightmapGen = dataCache.heightMaps[terrain];
var normalGen = dataCache.normalMaps[terrain];
var curvatureGen = dataCache.curvatureMaps[terrain];
var flowGen = dataCache.flowMaps[terrain];
RenderTexture clearMap = null;
if (needsObjectClearMap)
{
int size = terrain.terrainData.alphamapResolution;
clearMap = RenderTexture.GetTemporary(size, size, 0, RenderTextureFormat.RG16, RenderTextureReadWrite.Linear);
RenderTexture.active = clearMap;
GL.Clear(false, true, Color.clear);
RenderTexture.active = null;
}
ObjectData td = new ObjectData(terrain, heightmapGen, normalGen, curvatureGen, flowGen, indexMap, weightMap, clearMap);
dataCache.objectDatas[terrain] = td;
if (needsObjectClearMap)
{
foreach (var s in spawners)
{
IObjectModifier objectModifier = s as IObjectModifier;
if (objectModifier != null)
{
if (terrainBounds.Intersects(objectModifier.GetBounds()))
{
objectModifier.ApplyObjectClear(td);
}
}
}
}
}
// reset layers
foreach (var terrain in terrains)
{
if (dataCache.objectDatas.ContainsKey(terrain))
{
var td = dataCache.objectDatas[terrain];
td.layerIndex = -1;
}
}
}
void RenderObjectStamp(Terrain[] terrains, IObjectModifier objectModifier, MicroVerse.DataCache dataCache, bool allSDF, bool enableSDF)
{
foreach (var terrain in terrains)
{
var terrainBounds = TerrainUtil.ComputeTerrainBounds(terrain);
if (terrainBounds.Intersects(objectModifier.GetBounds()))
{
var occlusionData = dataCache.occlusionDatas[terrain];
ObjectData td = dataCache.objectDatas[terrain];
objectModifier.ApplyObjectStamp(td, objectJobHolders, occlusionData);
}
}
bool needToGenerateForChildren = objectModifier.NeedToGenerateSDFForChilden();
if (allSDF || objectModifier.NeedSDF() || needToGenerateForChildren || (objectModifier.OccludesOthers() && enableSDF))
{
bool sdfOthers = objectModifier.OccludesOthers() && enableSDF;
foreach (var terrain in terrains)
{
var terrainBounds = TerrainUtil.ComputeTerrainBounds(terrain);
if (terrainBounds.Intersects(objectModifier.GetBounds()))
{
var od = dataCache.occlusionDatas[terrain];
od?.RenderObjectSDF(terrain, dataCache.occlusionDatas, sdfOthers || allSDF);
if (needToGenerateForChildren && od != null && od.currentObjectSDF != null)
{
RenderTexture rt = RenderTexture.GetTemporary(od.currentObjectSDF.descriptor);
Graphics.Blit(od.currentObjectSDF, rt);
objectModifier.SetSDF(terrain, rt);
}
}
}
}
foreach (var terrain in terrains)
{
var terrainBounds = TerrainUtil.ComputeTerrainBounds(terrain);
var occlusionData = dataCache.occlusionDatas[terrain];
if (dataCache.objectDatas.ContainsKey(terrain))
{
var td = dataCache.objectDatas[terrain];
if (terrainBounds.Intersects(objectModifier.GetBounds()))
{
objectModifier.ProcessObjectStamp(td, objectJobHolders, occlusionData);
}
}
}
}
void CancelObjectJobs(MicroVerse.DataCache dataCache)
{
foreach (var lst in objectJobHolders.Values)
{
foreach (var h in lst)
{
h.canceled = true;
}
}
if (dataCache != null)
{
foreach (var td in dataCache.objectDatas.Values)
{
if (td.clearMap != null)
{
RenderTexture.ReleaseTemporary(td.clearMap);
td.clearMap = null;
}
}
}
}
Dictionary<Terrain, List<ObjectJobHolder>> objectJobHolders = new Dictionary<Terrain, List<ObjectJobHolder>>();
// object pooling is only used during regeneration, and then the pools are
// cleared so that any extras are removed
[System.Serializable]
public class Pool
{
public GameObject prefab;
public Stack<GameObject> instances = new Stack<GameObject>();
}
public static List<Pool> pools = new List<Pool>();
public static GameObject Spawn(Terrain t, GameObject go, Transform parent, bool asPrefab = false)
{
#if UNITY_EDITOR
foreach (var pool in pools)
{
if (pool.prefab == go)
{
if (pool.instances.Count > 0)
{
var i = pool.instances.Pop();
if (i.transform.parent != parent)
{
i.transform.parent = parent;
}
return i;
}
}
}
GameObject ret = null;
if (asPrefab)
ret = UnityEditor.PrefabUtility.InstantiatePrefab(go, parent) as GameObject;
else
ret = Object.Instantiate(go, parent);
#else
GameObject ret = GameObject.Instantiate(go, parent);
#endif
return ret;
}
public static void Despawn(GameObject instance)
{
#if UNITY_EDITOR
instance.hideFlags = HideFlags.DontSaveInEditor;
instance.transform.parent = null;
GameObject go = UnityEditor.PrefabUtility.GetCorrespondingObjectFromOriginalSource(instance);
foreach (var pool in pools)
{
if (pool.prefab == go)
{
pool.instances.Push(instance);
return;
}
}
Pool p = new Pool();
p.prefab = go;
p.instances.Push(instance);
pools.Add(p);
#else
GameObject.DestroyImmediate(instance);
#endif
}
void ClearPools()
{
foreach (var p in pools)
{
foreach (var i in p.instances)
{
GameObject.DestroyImmediate(i);
}
p.instances.Clear();
}
pools.Clear();
}
List<Terrain> finishedObjects = new List<Terrain>();
public void ApplyObjects()
{
var noAsync = MicroVerse.noAsyncReadback;
Profiler.BeginSample("Apply Objects");
finishedObjects.Clear();
int count = 0;
foreach (var terrain in objectJobHolders.Keys)
{
var lst = objectJobHolders[terrain];
for (int i = 0; i < lst.Count; ++i)
{
var h = lst[i];
if (h.IsDone())
{
if (h.canceled)
{
h.Dispose();
lst.RemoveAt(i);
i--;
}
else
{
Vector3 terrainSize = terrain.terrainData.size;
Vector3 terrainPos = terrain.transform.position;
for (int j = h.unpackIndex; j < h.positionWeightData.Length; ++j)
{
half4 positionWeight = h.positionWeightData[j];
if (positionWeight.w <= 0)
{
continue;
}
half4 rotationData = h.rotationData[j];
half4 scaleData = h.scaleIndexData[j];
if (scaleData.w < 0 || scaleData.w >= h.stamp.prototypes.Count)
{
Debug.LogError("Index out of range " + rotationData.w + " of " + h.stamp.prototypes.Count);
continue;
}
var prefab = h.stamp.prototypes[(int)scaleData.w];
var position = new Vector3(positionWeight.x * terrainSize.x, positionWeight.y * terrainSize.y, positionWeight.z * terrainSize.z) + terrain.transform.position;
var rotation = new Quaternion(rotationData.x, rotationData.y, rotationData.z, rotationData.w);
var scale = new Vector3(scaleData.x, scaleData.y, scaleData.z);
GameObject go = Spawn(terrain, prefab, h.stamp.FindParentObject(terrain), h.stamp.spawnAsPrefab);
go.transform.SetPositionAndRotation(position, rotation);
go.transform.localScale = scale;
if (h.stamp.hideInHierarchy)
go.hideFlags = HideFlags.HideInHierarchy;
else
go.hideFlags = HideFlags.None;
h.stamp.spawnedInstances.Add(go);
count++;
if (count > 1500 && !noAsync)
{
h.unpackIndex = j;
Profiler.EndSample();
return;
}
}
h.Dispose();
lst.RemoveAt(i);
i--;
}
}
}
if (lst.Count == 0)
{
finishedObjects.Add(terrain);
}
}
foreach (var f in finishedObjects)
{
objectJobHolders.Remove(f);
}
finishedObjects.Clear();
if (objectJobHolders.Count == 0)
{
ClearPools();
}
Profiler.EndSample();
}
}
}