Files
Fishing2/Packages/com.jbooth.microverse/Scripts/PreviewRenderer.cs
2025-06-09 23:23:13 +08:00

362 lines
17 KiB
C#

using UnityEngine;
using UnityEngine.TerrainTools;
using System.Collections.Generic;
namespace JBooth.MicroVerseCore
{
public class PreviewRenderer
{
static Material brushPreviewMat;
static Material noiseMat;
static Material filterSetMat;
private static float kNormalizedHeightScale => PaintContext.kNormalizedHeightScale;
public static Noise noisePreview = null;
public static void DrawNoisePreview()
{
if (noisePreview == null)
return;
if (MicroVerse.instance == null)
return;
if (!noiseMat) noiseMat = new Material(Shader.Find("Hidden/MicroVerse/PreviewNoiseWorld"));
foreach (var terrain in MicroVerse.instance.terrains)
{
if (terrain != null)
{
System.Collections.Generic.List<string> keywords = new System.Collections.Generic.List<string>();
int vertexCount = SetupDrawing(terrain, noiseMat);
noiseMat.SetVector("_TerrainSize", new Vector2(terrain.terrainData.size.x, terrain.terrainData.size.z));
noiseMat.SetTexture("_NoiseTexture", noisePreview.texture);
noiseMat.SetTextureScale("_NoiseTexture", noisePreview.GetTextureScale());
noiseMat.SetTextureOffset("_NoiseTexture", noisePreview.GetTextureOffset());
noisePreview.EnableKeyword(noiseMat, "_", keywords);
noiseMat.SetVector("_Param", noisePreview.GetParamVector());
noiseMat.SetVector("_Param2", noisePreview.GetParam2Vector());
noiseMat.SetFloat("_NoiseChannel", (int)noisePreview.channel);
noiseMat.SetColor("_Color", MicroVerse.instance.options.colors.noisePreviewColor);
noiseMat.SetVector("_Remap", new Vector2(-noisePreview.displayGamma, 1 + noisePreview.displayGamma));
noiseMat.shaderKeywords = keywords.ToArray();
Graphics.DrawProceduralNow(MeshTopology.Triangles, vertexCount);
}
}
}
public enum FilterSetType
{
Height,
Slope,
Angle,
Curvature,
Flow,
Texture
}
public static FilterSet.Filter filter = null;
public static FilterSet filterSet = null; // needed for texture filtering
public static FilterSetType filterSetType = FilterSetType.Height;
public static void DrawFilterSetPreview()
{
if (filter == null && filterSetType != FilterSetType.Texture)
return;
if (filterSetType == FilterSetType.Texture && filterSet == null)
return;
if (MicroVerse.instance == null)
return;
if (!filterSetMat) filterSetMat = new Material(Shader.Find("Hidden/MicroVerse/PreviewFilterWorld"));
foreach (var terrain in MicroVerse.instance.terrains)
{
if (terrain == null)
continue;
var realHeight = terrain.terrainData.heightmapScale.y * 2;
if (filter != null)
{
filterSetMat.SetVector("_HeightRange", filter.range / realHeight);
filterSetMat.SetVector("_HeightSmoothness", filter.smoothness / realHeight);
filterSetMat.SetVector("_SlopeRange", filter.range * Mathf.Deg2Rad);
filterSetMat.SetVector("_SlopeSmoothness", filter.smoothness * Mathf.Deg2Rad);
filterSetMat.SetVector("_AngleRange", filter.range * Mathf.Deg2Rad);
filterSetMat.SetVector("_AngleSmoothness", filter.smoothness * Mathf.Deg2Rad);
filterSetMat.SetVector("_CurvatureRange", filter.range);
filterSetMat.SetVector("_CurvatureSmoothness", filter.smoothness);
filterSetMat.SetVector("_FlowRange", filter.range);
filterSetMat.SetVector("_FlowSmoothness", filter.smoothness);
filterSetMat.SetColor("_Color", MicroVerse.instance.options.colors.filterPreviewColor);
}
filterSetMat.SetTexture("_Normalmap", terrain.normalmapTexture);
filterSetMat.DisableKeyword("_HEIGHTFILTER");
filterSetMat.DisableKeyword("_SLOPEFILTER");
filterSetMat.DisableKeyword("_ANGLEFILTER");
filterSetMat.DisableKeyword("_CURVATUREFILTER");
filterSetMat.DisableKeyword("_FLOWFILTER");
filterSetMat.DisableKeyword("_TEXTUREFILTER");
filterSetMat.DisableKeyword("_USECURVE");
if (filter != null && filter.filterType == FilterSet.Filter.FilterType.Curve)
{
filterSetMat.EnableKeyword("_USECURVE");
filterSetMat.SetTexture("_Curve", filter.curveTexture);
}
RenderTexture tempCurvature = null;
RenderTexture tempFlow = null;
switch (filterSetType)
{
case FilterSetType.Height:
{
filterSetMat.EnableKeyword("_HEIGHTFILTER");
break;
}
case FilterSetType.Slope:
{
filterSetMat.EnableKeyword("_SLOPEFILTER");
break;
}
case FilterSetType.Angle:
{
filterSetMat.EnableKeyword("_ANGLEFILTER");
break;
}
case FilterSetType.Curvature:
{
filterSetMat.EnableKeyword("_CURVATUREFILTER");
filterSetMat.SetFloat("_MipBias", filter.mipBias);
Dictionary<Terrain, RenderTexture> d = new Dictionary<Terrain, RenderTexture>();
d.Add(terrain, terrain.normalmapTexture);
if (terrain.leftNeighbor != null)
d.Add(terrain.leftNeighbor, terrain.leftNeighbor.normalmapTexture);
if (terrain.rightNeighbor != null)
d.Add(terrain.rightNeighbor, terrain.rightNeighbor.normalmapTexture);
if (terrain.topNeighbor != null)
d.Add(terrain.topNeighbor, terrain.topNeighbor.normalmapTexture);
if (terrain.bottomNeighbor != null)
d.Add(terrain.bottomNeighbor, terrain.bottomNeighbor.normalmapTexture);
var active = RenderTexture.active; // must restore this!
tempCurvature = MapGen.GenerateCurvatureMap(terrain, d, terrain.terrainData.alphamapWidth, terrain.terrainData.alphamapHeight);
tempCurvature.name = "Temp::CurvaturePreview";
RenderTexture.active = active;
filterSetMat.SetTexture("_Curvemap", tempCurvature);
break;
}
case FilterSetType.Flow:
{
filterSetMat.EnableKeyword("_FLOWFILTER");
Dictionary<Terrain, RenderTexture> d = new Dictionary<Terrain, RenderTexture>();
d.Add(terrain, terrain.terrainData.heightmapTexture);
var active = RenderTexture.active; // must restore this!
tempFlow = MapGen.GenerateFlowMap(terrain, d);
tempFlow.name = "Temp::FlowPreview";
RenderTexture.active = active;
filterSetMat.SetTexture("_Flowmap", tempFlow);
break;
}
case FilterSetType.Texture:
{
if (filterSet != null)
{
filterSetMat.EnableKeyword("_TEXTUREFILTER");
filterSetMat.SetVectorArray("_TextureLayerWeights", filterSet.GetTextureWeights(terrain.terrainData.terrainLayers));
filterSetMat.SetTexture("_Control0", Texture2D.blackTexture);
filterSetMat.SetTexture("_Control1", Texture2D.blackTexture);
filterSetMat.SetTexture("_Control2", Texture2D.blackTexture);
filterSetMat.SetTexture("_Control3", Texture2D.blackTexture);
filterSetMat.SetTexture("_Control4", Texture2D.blackTexture);
filterSetMat.SetTexture("_Control5", Texture2D.blackTexture);
filterSetMat.SetTexture("_Control6", Texture2D.blackTexture);
filterSetMat.SetTexture("_Control7", Texture2D.blackTexture);
var splats = terrain.terrainData.alphamapTextures;
for (int i = 0; i < terrain.terrainData.alphamapTextureCount; ++i)
{
filterSetMat.SetTexture("_Control" + i, splats[i]);
}
}
break;
}
}
int vertexCount = SetupDrawing(terrain, filterSetMat);
Graphics.DrawProceduralNow(MeshTopology.Triangles, vertexCount);
if (tempCurvature != null)
{
RenderTexture.ReleaseTemporary(tempCurvature);
}
if (tempFlow != null)
{
RenderTexture.ReleaseTemporary(tempFlow);
}
}
}
public static void DrawStampPreview(IModifier mod, Terrain[] terrains, Transform transform, FalloffFilter filter,
Color color, Texture2D colorTex = null)
{
if (MicroVerse.instance.options.colors.drawStampPreviews == false)
{
DrawNoisePreview();
DrawFilterSetPreview();
return;
}
foreach (var terrain in terrains)
{
if (terrain == null)
continue;
var terrainBounds = terrain.terrainData.bounds;
terrainBounds.center = terrain.transform.position;
terrainBounds.center += new Vector3(terrainBounds.size.x * 0.5f, 0, terrainBounds.size.z * 0.5f);
if (terrainBounds.Intersects(mod.GetBounds()))
{
if (filter.filterType == FalloffFilter.FilterType.Range)
{
Draw(terrain, transform, filter.falloffRange, color, colorTex);
}
else if (filter.filterType == FalloffFilter.FilterType.Texture)
{
Draw(terrain, transform, filter.texture, color, colorTex, (int)filter.textureChannel);
}
}
}
DrawNoisePreview();
DrawFilterSetPreview();
}
public static void Draw(Terrain terrain, Texture2D tex)
{
if (!terrain) return;
if (!brushPreviewMat) brushPreviewMat = new Material(Shader.Find("Hidden/MicroVerse/PreviewStamp"));
int vertexCount = SetupDrawing(terrain, brushPreviewMat);
brushPreviewMat.DisableKeyword("_USEFALLOFFTEXTURE");
brushPreviewMat.EnableKeyword("_NOFALLOFF");
brushPreviewMat.SetTexture("_ColorTex", tex);
brushPreviewMat.SetColor("_Color", Color.white);
brushPreviewMat.SetTexture("_MainTex", Texture2D.whiteTexture);
Graphics.DrawProceduralNow(MeshTopology.Triangles, vertexCount);
}
public static void Draw(Terrain terrain, Transform transform, Texture2D tex,
Color color, Texture2D colorTex = null, int channel = 0)
{
if (!terrain) return;
if (!brushPreviewMat) brushPreviewMat = new Material(Shader.Find("Hidden/MicroVerse/PreviewStamp"));
if (color.a < 0.05) return;
int vertexCount = SetupDrawing(terrain, transform, brushPreviewMat);
brushPreviewMat.DisableKeyword("_USEFALLOFFTEXTURE");
brushPreviewMat.SetTexture("_ColorTex", colorTex);
brushPreviewMat.SetColor("_Color", color);
brushPreviewMat.EnableKeyword("_USEFALLOFFTEXTURE");
brushPreviewMat.SetTexture("_MainTex", tex);
brushPreviewMat.SetFloat("_FalloffChannel", channel);
Graphics.DrawProceduralNow(MeshTopology.Triangles, vertexCount);
}
public static void Draw(Terrain terrain, Transform transform, Vector2 falloffRange,
Color color, Texture2D colorTex = null, int falloffChannel = 0)
{
if (!terrain) return;
if (!brushPreviewMat) brushPreviewMat = new Material(Shader.Find("Hidden/MicroVerse/PreviewStamp"));
if (color.a < 0.01f) return;
int vertexCount = SetupDrawing(terrain, transform, brushPreviewMat);
brushPreviewMat.DisableKeyword("_USEFALLOFFTEXTURE");
brushPreviewMat.SetTexture("_ColorTex", colorTex);
brushPreviewMat.SetColor("_Color", color);
brushPreviewMat.SetVector("_Falloff", falloffRange);
brushPreviewMat.SetFloat("_FalloffChannel", falloffChannel);
Graphics.DrawProceduralNow(MeshTopology.Triangles, vertexCount);
}
// whole terrain, no brush
static int SetupDrawing(Terrain terrain, Material mat)
{
Texture heightmapTexture = terrain.terrainData.heightmapTexture;
RectInt pixelRect = new RectInt(0, 0, heightmapTexture.width, heightmapTexture.height);
Vector2 pixelSize = new Vector2(terrain.terrainData.size.x / heightmapTexture.width, terrain.terrainData.size.z / heightmapTexture.height);
int quadsX = pixelRect.width + 1;
int quadsY = pixelRect.height + 1;
int vertexCount = quadsX * quadsY * (2 * 3); // two triangles (2 * 3 vertices) per quad
const int kMaxVerts = 256*256;
int vertSkip = 1;
while (vertexCount >= kMaxVerts / 2) // in practice we want to stay well below 16 million verts, for perf sanity
{
quadsX = (quadsX + 1) / 2;
quadsY = (quadsY + 1) / 2;
vertexCount = quadsX * quadsY * (2 * 3);
vertSkip *= 2;
}
// this is used to tessellate the quad mesh (from within the vertex shader)
mat.SetVector("_QuadRez", new Vector4(quadsX, quadsY, vertexCount, vertSkip));
// paint context pixels to heightmap uv: uv = (pixels + 0.5) / width
float invWidth = 1.0f / heightmapTexture.width;
float invHeight = 1.0f / heightmapTexture.height;
mat.SetVector("_HeightmapUV_PCPixelsX", new Vector4(invWidth, 0.0f, 0.0f, 0.0f));
mat.SetVector("_HeightmapUV_PCPixelsY", new Vector4(0.0f, invHeight, 0.0f, 0.0f));
mat.SetVector("_HeightmapUV_Offset", new Vector4(0.5f * invWidth, 0.5f * invHeight, 0.0f, 0.0f));
mat.SetTexture("_Heightmap", heightmapTexture);
float scaleX = pixelSize.x;
float scaleY = (terrain.terrainData.heightmapScale.y) / kNormalizedHeightScale;
float scaleZ = pixelSize.y;
mat.SetVector("_ObjectPos_PCPixelsX", new Vector4(scaleX, 0.0f, 0.0f, 0.0f));
mat.SetVector("_ObjectPos_HeightMapSample", new Vector4(0.0f, scaleY, 0.0f, 0.0f));
mat.SetVector("_ObjectPos_PCPixelsY", new Vector4(0.0f, 0.0f, scaleZ, 0.0f));
//Note slightly offset, so raise up so it doesn't clip through terrain
mat.SetVector("_ObjectPos_Offset", new Vector4((pixelRect.xMin * scaleX), 1.0f, (pixelRect.yMin * scaleZ) + (pixelSize.y * 0.0f), 1.0f));
BrushTransform brushXform = TerrainPaintUtility.CalculateBrushTransform(terrain, new Vector2(0.5f, 0.5f), terrain.terrainData.size.x, 0.0f);
// paint context origin in terrain space
// (note this is the UV space origin and size, not the mesh origin & size)
float pcOriginX = pixelRect.xMin * pixelSize.x;
float pcOriginZ = pixelRect.yMin * pixelSize.y;
float pcSizeX = pixelSize.x;
float pcSizeZ = pixelSize.y;
Vector2 scaleU = pcSizeX * brushXform.targetX;
Vector2 scaleV = pcSizeZ * brushXform.targetY;
Vector2 offset = brushXform.targetOrigin + pcOriginX * brushXform.targetX + pcOriginZ * brushXform.targetY;
mat.SetVector("_BrushUV_PCPixelsX", new Vector4(scaleU.x, scaleU.y, 0.0f, 0.0f));
mat.SetVector("_BrushUV_PCPixelsY", new Vector4(scaleV.x, scaleV.y, 0.0f, 0.0f));
mat.SetVector("_BrushUV_Offset", new Vector4(offset.x, offset.y, 0.0f, 1.0f));
mat.SetVector("_TerrainObjectToWorldOffset", terrain.GetPosition());
mat.SetPass(0);
return vertexCount;
}
// with stamp
static int SetupDrawing(Terrain terrain, Transform stampTransform, Material mat)
{
mat.SetMatrix("_Transform", TerrainUtil.ComputeStampMatrix(terrain, stampTransform)); ;
return SetupDrawing(terrain, mat);
}
}
}