Files
Fishing2/Packages/com.jbooth.microsplat.streams/Scripts/StreamManager.cs
2025-06-04 09:09:39 +08:00

419 lines
13 KiB
C#

//////////////////////////////////////////////////////
// MicroSplat
// Copyright (c) Jason Booth
//////////////////////////////////////////////////////1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace JBooth.MicroSplat
{
#if __MICROSPLAT__
public class StreamManager : MonoBehaviour
{
MicroSplatObject msObject;
bool onBuffer0 = true;
// Thank you unity, for your continued dedication to making things incompatible. Custom Render textures work on Standard pipeline,
// but don't. So I have to detect the SRP and use different systems for SRP/Non-SRP code.
public class UpdateBuffer
{
public virtual void Init(int w, int h) { }
public virtual void Disable() { }
public virtual void BlitA() { }
public virtual void BlitB() { }
public virtual RenderTexture GetCurrent() { return null; }
public Material updateMat;
public int width;
public int height;
}
public class SRPBuffers : UpdateBuffer
{
public CustomRenderTexture buffer0;
public CustomRenderTexture buffer1;
public CustomRenderTexture currentBuffer;
public override void BlitA()
{
updateMat.SetTexture("_MainTex", buffer0);
buffer1.Update();
currentBuffer = buffer1;
}
public override void BlitB()
{
updateMat.SetTexture("_MainTex", buffer1);
buffer0.Update();
currentBuffer = buffer0;
}
public override RenderTexture GetCurrent() { return currentBuffer; }
public override void Init(int w, int h)
{
width = w;
height = h;
updateMat = new Material(Shader.Find("Hidden/MicroSplat/StreamUpdateSRP"));
buffer0 = new CustomRenderTexture(w, h, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
buffer1 = new CustomRenderTexture(w, h, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
buffer0.initializationMode = CustomRenderTextureUpdateMode.OnDemand;
buffer1.initializationMode = CustomRenderTextureUpdateMode.OnDemand;
buffer0.updateMode = CustomRenderTextureUpdateMode.OnDemand;
buffer1.updateMode = CustomRenderTextureUpdateMode.OnDemand;
buffer0.initializationSource = CustomRenderTextureInitializationSource.TextureAndColor;
buffer1.initializationSource = CustomRenderTextureInitializationSource.TextureAndColor;
buffer0.initializationTexture = Texture2D.blackTexture;
buffer1.initializationTexture = Texture2D.blackTexture;
buffer0.depth = 0;
buffer1.depth = 0;
buffer0.material = updateMat;
buffer1.material = updateMat;
buffer0.Create();
buffer1.Create();
buffer0.Initialize();
buffer1.Initialize();
}
public override void Disable()
{
buffer0.Release();
buffer1.Release();
DestroyImmediate(buffer0);
DestroyImmediate(buffer1);
DestroyImmediate(updateMat);
buffer0 = null;
buffer1 = null;
updateMat = null;
}
}
public class StandardBuffers : UpdateBuffer
{
public RenderTexture buffer0;
public RenderTexture buffer1;
public RenderTexture currentBuffer;
public override void BlitA()
{
Graphics.Blit(buffer0, buffer1, updateMat);
currentBuffer = buffer1;
}
public override void BlitB()
{
Graphics.Blit(buffer1, buffer0, updateMat);
currentBuffer = buffer0;
}
public override RenderTexture GetCurrent() { return currentBuffer; }
public override void Init(int w, int h)
{
width = w;
height = h;
updateMat = new Material(Shader.Find("Hidden/MicroSplat/StreamUpdate"));
buffer0 = new RenderTexture(w, h, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
buffer1 = new RenderTexture(w, h, 0, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Linear);
Graphics.Blit(Texture2D.blackTexture, buffer0);
Graphics.Blit(Texture2D.blackTexture, buffer1);
}
public override void Disable()
{
buffer0.Release();
buffer1.Release();
DestroyImmediate(buffer0);
DestroyImmediate(buffer1);
DestroyImmediate(updateMat);
buffer0 = null;
buffer1 = null;
updateMat = null;
}
}
public UpdateBuffer updateBuffer;
Vector4[] spawnBuffer = new Vector4[64];
Vector4[] colliderBuffer = new Vector4[64];
// props
public Vector2 evaporation = new Vector2(0.01f, 0.01f);
public Vector2 strength = new Vector2(1.0f, 1.0f);
public Vector2 speed = new Vector2(1, 1);
public Vector2 resistance = new Vector2(0.1f, 0.1f);
public float wetnessEvaporation = 0.01f;
public float burnEvaporation = 0.01f;
List<StreamEmitter> emitters = new List<StreamEmitter>(16);
List<StreamCollider> colliders = new List<StreamCollider>(16);
static Vector2 WorldToTerrain(MicroSplatObject ter, Vector3 point, int width, int height)
{
Bounds b = ter.GetBounds();
point = ter.transform.worldToLocalMatrix.MultiplyPoint(point);
float x = (point.x / b.size.x) * width;
float z = (point.z / b.size.z) * height;
return new Vector2(x, z);
}
public void Register(StreamEmitter e)
{
emitters.Add(e);
}
public void Unregister(StreamEmitter e)
{
emitters.Remove(e);
}
public void Register(StreamCollider e)
{
colliders.Add(e);
}
public void Unregister(StreamCollider e)
{
colliders.Remove(e);
}
void Awake()
{
msObject = GetComponent<MicroSplatObject>();
}
void OnEnable()
{
var terrainDesc = msObject.GetTerrainDescriptor();
if (terrainDesc.heightMap == null)
{
Debug.LogError("Terrain doesn't have height descriptor");
return;
}
if (msObject.keywordSO == null)
{
Debug.LogError("Terrain does not have keywords");
return;
}
bool found = false;
foreach (var k in msObject.keywordSO.keywords)
{
if (k.StartsWith("_MSRENDERLOOP_UNITYURP") || k.StartsWith("_MSRENDERLOOP_UNITYHDRP"))
{
found = true;
break;
}
}
if (found)
{
updateBuffer = new SRPBuffers();
}
else
{
updateBuffer = new StandardBuffers();
}
updateBuffer.Init(terrainDesc.heightMap.width, terrainDesc.heightMap.height);
}
void OnDisable()
{
onBuffer0 = false;
updateBuffer.Disable();
updateBuffer = null;
}
double timeSinceWetnessEvap = 0;
double timeSinceBurnEvap = 0;
double timeSinceEvapX = 0;
double timeSinceEvapY = 0;
Vector2 evapAmount = new Vector2(0, 0);
void Update()
{
if (msObject == null)
{
return;
}
var terrainDesc = msObject.GetTerrainDescriptor();
if (terrainDesc.heightMap == null)
{
return;
}
int emitterCount = emitters.Count;
if (emitterCount > 64)
{
emitterCount = 64;
}
int colliderCount = colliders.Count;
if (colliderCount > 64)
{
colliderCount = 64;
}
int usedEmitters = 0;
for (int i = 0; i < emitters.Count; ++i)
{
var e = emitters[i];
if (e == null)
continue;
Vector2 ter = WorldToTerrain(msObject, e.transform.position, updateBuffer.width, updateBuffer.height);
if (ter.x >= 0 && ter.x < updateBuffer.width && ter.y >= 0 && ter.y < updateBuffer.height)
{
Vector3 pos = e.transform.position + Vector3.left * e.transform.lossyScale.x;
Vector2 endPoint = WorldToTerrain(msObject, pos, updateBuffer.width, updateBuffer.height);
float d = Vector2.Distance(ter, endPoint);
if (d < 1)
d = 1;
d *= e.strength;
Vector4 data = new Vector4(ter.x, ter.y, 0, 0);
if (e.emitterType == StreamEmitter.EmitterType.Water)
{
data.z = d;
}
else
{
data.w = d;
}
spawnBuffer[usedEmitters] = data;
usedEmitters++;
}
}
int usedColliders = 0;
for (int i = 0; i < colliders.Count; ++i)
{
var c = colliders[i];
Vector2 ter = WorldToTerrain(msObject, c.transform.position, updateBuffer.width, updateBuffer.height);
if (ter.x >= 0 && ter.x < updateBuffer.width && ter.y >= 0 && ter.y < updateBuffer.height)
{
Vector3 pos = c.transform.position + Vector3.left * c.transform.lossyScale.x;
Vector2 endPoint = WorldToTerrain(msObject, pos, updateBuffer.width, updateBuffer.height);
float d = Vector2.Distance(ter, endPoint);
Vector4 data = new Vector4(ter.x, ter.y, 0, 0);
if (c.colliderType != StreamCollider.ColliderType.Lava)
{
data.z = d;
}
if (c.colliderType != StreamCollider.ColliderType.Water)
{
data.w = d;
}
colliderBuffer[usedColliders] = data;
usedColliders++;
}
}
updateBuffer.updateMat.SetVectorArray("_Positions", spawnBuffer);
updateBuffer.updateMat.SetVectorArray("_Colliders", colliderBuffer);
updateBuffer.updateMat.SetInt("_PositionsCount", usedEmitters);
updateBuffer.updateMat.SetInt("_CollidersCount", usedColliders);
updateBuffer.updateMat.SetVector("_SpawnStrength", strength);
updateBuffer.updateMat.SetTexture("_TerrainHeight", terrainDesc.heightMap);
updateBuffer.updateMat.SetVector("_TerrainHeightmapScale", terrainDesc.heightMapScale);
updateBuffer.updateMat.SetFloat("_DeltaTime", Time.smoothDeltaTime);
updateBuffer.updateMat.SetVector("_Speed", speed * Time.timeScale);
updateBuffer.updateMat.SetVector("_Resistance", resistance);
if (onBuffer0)
{
if (evaporation.x > 0)
{
float evapDelay = (1.0f / evaporation.x) / 255.0f;
if (timeSinceEvapX > evapDelay)
{
timeSinceEvapX = 0;
evapAmount.x = 0.004f;
}
else
{
evapAmount.x = 0;
}
}
if (evaporation.y > 0)
{
float evapDelay = (1.0f / evaporation.y) / 255.0f;
if (timeSinceEvapY > evapDelay)
{
timeSinceEvapY = 0;
evapAmount.y = 0.004f;
}
else
{
evapAmount.y = 0;
}
}
updateBuffer.updateMat.SetVector("_Evaporation", evapAmount);
if (wetnessEvaporation > 0)
{
float wetnessDelay = (1.0f / wetnessEvaporation) / 255.0f;
if (timeSinceWetnessEvap > wetnessDelay)
{
updateBuffer.updateMat.SetFloat("_WetnessEvaporation", 0.004f);
timeSinceWetnessEvap = 0;
}
else
{
updateBuffer.updateMat.SetFloat("_WetnessEvaporation", 0);
}
}
if (burnEvaporation > 0)
{
float burnDelay = (1.0f * burnEvaporation) / 255.0f;
if (timeSinceBurnEvap > burnDelay)
{
updateBuffer.updateMat.SetFloat("_BurnEvaporation", 0.004f);
timeSinceBurnEvap = 0;
}
else
{
updateBuffer.updateMat.SetFloat("_BurnEvaporation", 0);
}
}
updateBuffer.BlitA();
}
else
{
// only spawn, evaporate on first pass
updateBuffer.updateMat.SetInt("_PositionsCount", 0);
updateBuffer.updateMat.SetVector("_Evaporation", Vector2.zero);
updateBuffer.updateMat.SetFloat("_WetnessEvaporation", 0);
updateBuffer.updateMat.SetFloat("_BurnEvaporation", 0);
updateBuffer.BlitB();
}
onBuffer0 = !onBuffer0;
float dt = Time.deltaTime;
timeSinceEvapX += dt;
timeSinceEvapY += dt;
timeSinceWetnessEvap += dt;
timeSinceBurnEvap += dt;
msObject.matInstance.SetTexture("_DynamicStreamControl", updateBuffer.GetCurrent());
}
}
#endif
}