Files
2026-02-21 16:45:37 +08:00

1016 lines
23 KiB
C#

using System;
using UnityEngine;
namespace ErosionBrushPlugin
{
[Serializable]
public class Matrix
{
public class Stacker
{
public CoordRect smallRect;
public CoordRect bigRect;
public bool preserveDetail = true;
private Matrix downscaled;
private Matrix upscaled;
private Matrix difference;
private bool isDownscaled;
public Matrix matrix
{
get
{
if (isDownscaled)
{
return downscaled;
}
return upscaled;
}
}
public Stacker(CoordRect smallRect, CoordRect bigRect)
{
this.smallRect = smallRect;
this.bigRect = bigRect;
isDownscaled = false;
if (bigRect == smallRect)
{
upscaled = (downscaled = new Matrix(bigRect));
return;
}
downscaled = new Matrix(smallRect);
upscaled = new Matrix(bigRect);
difference = new Matrix(bigRect);
}
public void ToSmall()
{
if (!(bigRect == smallRect))
{
Matrix obj = upscaled;
CoordRect newRect = smallRect;
Matrix result = downscaled;
downscaled = obj.Resize(newRect, 1f, result);
if (preserveDetail)
{
Matrix obj2 = downscaled;
newRect = bigRect;
result = difference;
difference = obj2.Resize(newRect, 1f, result);
difference.Blur();
difference.InvSubtract(upscaled);
}
isDownscaled = true;
}
}
public void ToBig()
{
if (!(bigRect == smallRect))
{
Matrix obj = downscaled;
CoordRect newRect = bigRect;
Matrix result = upscaled;
upscaled = obj.Resize(newRect, 1f, result);
upscaled.Blur();
if (preserveDetail)
{
upscaled.Add(difference);
}
isDownscaled = false;
}
}
}
public float[] array;
public CoordRect rect;
public int pos;
public float this[int x, int z]
{
get
{
return array[(z - rect.offset.z) * rect.size.x + x - rect.offset.x];
}
set
{
array[(z - rect.offset.z) * rect.size.x + x - rect.offset.x] = value;
}
}
public float this[Coord c]
{
get
{
return array[(c.z - rect.offset.z) * rect.size.x + c.x - rect.offset.x];
}
set
{
array[(c.z - rect.offset.z) * rect.size.x + c.x - rect.offset.x] = value;
}
}
public float this[Vector3 pos]
{
get
{
return array[((int)pos.z - rect.offset.z) * rect.size.x + (int)pos.x - rect.offset.x];
}
set
{
array[((int)pos.z - rect.offset.z) * rect.size.x + (int)pos.x - rect.offset.x] = value;
}
}
public float nextX
{
get
{
return array[pos + 1];
}
set
{
array[pos + 1] = value;
}
}
public float prevX
{
get
{
return array[pos - 1];
}
set
{
array[pos - 1] = value;
}
}
public float nextZ
{
get
{
return array[pos + rect.size.x];
}
set
{
array[pos + rect.size.x] = value;
}
}
public float prevZ
{
get
{
return array[pos - rect.size.x];
}
set
{
array[pos - rect.size.x] = value;
}
}
public float nextXnextZ
{
get
{
return array[pos + rect.size.x + 1];
}
set
{
array[pos + rect.size.x + 1] = value;
}
}
public float prevXnextZ
{
get
{
return array[pos + rect.size.x - 1];
}
set
{
array[pos + rect.size.x - 1] = value;
}
}
public float nextXprevZ
{
get
{
return array[pos - rect.size.x + 1];
}
set
{
array[pos - rect.size.x + 1] = value;
}
}
public float prevXprevZ
{
get
{
return array[pos - rect.size.x - 1];
}
set
{
array[pos - rect.size.x - 1] = value;
}
}
public Matrix(CoordRect rect)
{
this.rect = rect;
array = new float[rect.size.x * rect.size.z];
}
public Matrix(Coord offset, Coord size)
{
rect = new CoordRect(offset, size);
array = new float[rect.size.x * rect.size.z];
}
public void ChangeRect(CoordRect newRect)
{
rect.offset.x = newRect.offset.x;
rect.offset.z = newRect.offset.z;
if (rect.size.x != newRect.size.x || rect.size.z != newRect.size.z)
{
rect = newRect;
array = new float[rect.size.x * rect.size.z];
}
}
public Matrix Clone(Matrix result = null)
{
if (result == null)
{
result = new Matrix(rect);
}
result.rect = rect;
result.pos = pos;
if (result.array.Length != array.Length)
{
result.array = new float[array.Length];
}
for (int i = 0; i < array.Length; i++)
{
result.array[i] = array[i];
}
return result;
}
public void SetPos(int x, int z)
{
pos = (z - rect.offset.z) * rect.size.x + x - rect.offset.x;
}
public void SetPos(int x, int z, int s)
{
pos = (z - rect.offset.z) * rect.size.x + x - rect.offset.x + s * rect.size.x * rect.size.z;
}
public void MoveX()
{
pos++;
}
public void MoveZ()
{
pos += rect.size.x;
}
public void MovePrevX()
{
pos--;
}
public void MovePrevZ()
{
pos -= rect.size.x;
}
public float[,] ReadHeighmap(TerrainData data, float height = 1f)
{
CoordRect centerRect = CoordRect.Intersect(rect, new CoordRect(0, 0, data.heightmapResolution, data.heightmapResolution));
float[,] heights = data.GetHeights(centerRect.offset.x, centerRect.offset.z, centerRect.size.x, centerRect.size.z);
Coord min = centerRect.Min;
Coord max = centerRect.Max;
for (int i = min.x; i < max.x; i++)
{
for (int j = min.z; j < max.z; j++)
{
this[i, j] = heights[j - min.z, i - min.x] * height;
}
}
RemoveBorders(centerRect);
return heights;
}
public void WriteHeightmap(TerrainData data, float[,] array = null, float brushFallof = 0.5f)
{
CoordRect coordRect = CoordRect.Intersect(rect, new CoordRect(0, 0, data.heightmapResolution, data.heightmapResolution));
if (array == null || array.Length != coordRect.size.x * coordRect.size.z)
{
array = new float[coordRect.size.z, coordRect.size.x];
}
Coord min = coordRect.Min;
Coord max = coordRect.Max;
for (int i = min.x; i < max.x; i++)
{
for (int j = min.z; j < max.z; j++)
{
float num = Fallof(i, j, brushFallof);
if (!Mathf.Approximately(num, 0f))
{
array[j - min.z, i - min.x] = this[i, j] * num + array[j - min.z, i - min.x] * (1f - num);
}
}
}
data.SetHeights(coordRect.offset.x, coordRect.offset.z, array);
}
public float[,,] ReadSplatmap(TerrainData data, int channel, float[,,] array = null)
{
CoordRect centerRect = CoordRect.Intersect(rect, new CoordRect(0, 0, data.alphamapResolution, data.alphamapResolution));
if (array == null)
{
array = data.GetAlphamaps(centerRect.offset.x, centerRect.offset.z, centerRect.size.x, centerRect.size.z);
}
Coord min = centerRect.Min;
Coord max = centerRect.Max;
for (int i = min.x; i < max.x; i++)
{
for (int j = min.z; j < max.z; j++)
{
this[i, j] = array[j - min.z, i - min.x, channel];
}
}
RemoveBorders(centerRect);
return array;
}
public static void AddSplatmaps(TerrainData data, Matrix[] matrices, int[] channels, float[] opacity, float[,,] array = null, float brushFallof = 0.5f)
{
int alphamapLayers = data.alphamapLayers;
bool[] array2 = new bool[alphamapLayers];
for (int i = 0; i < channels.Length; i++)
{
array2[channels[i]] = true;
}
float[] array3 = new float[alphamapLayers];
CoordRect c = new CoordRect(size: new Coord(data.alphamapResolution, data.alphamapResolution), offset: new Coord(0, 0));
CoordRect coordRect = CoordRect.Intersect(c, matrices[0].rect);
if (array == null)
{
array = data.GetAlphamaps(coordRect.offset.x, coordRect.offset.z, coordRect.size.x, coordRect.size.z);
}
Coord min = coordRect.Min;
Coord max = coordRect.Max;
for (int j = min.x; j < max.x; j++)
{
for (int k = min.z; k < max.z; k++)
{
float num = matrices[0].Fallof(j, k, brushFallof);
if (Mathf.Approximately(num, 0f))
{
continue;
}
for (int l = 0; l < alphamapLayers; l++)
{
array3[l] = array[k - min.z, j - min.x, l];
}
for (int m = 0; m < matrices.Length; m++)
{
matrices[m][j, k] = Mathf.Max(0f, matrices[m][j, k] - array3[channels[m]]);
}
for (int n = 0; n < matrices.Length; n++)
{
matrices[n][j, k] *= num * opacity[n];
}
float num2 = 0f;
for (int num3 = 0; num3 < matrices.Length; num3++)
{
num2 += matrices[num3][j, k];
}
if (num2 > 1f)
{
for (int num4 = 0; num4 < matrices.Length; num4++)
{
matrices[num4][j, k] /= num2;
}
num2 = 1f;
}
float num5 = 1f - num2;
for (int num6 = 0; num6 < alphamapLayers; num6++)
{
array3[num6] *= num5;
}
for (int num7 = 0; num7 < matrices.Length; num7++)
{
array3[channels[num7]] += matrices[num7][j, k];
}
for (int num8 = 0; num8 < alphamapLayers; num8++)
{
array[k - min.z, j - min.x, num8] = array3[num8];
}
}
}
data.SetAlphamaps(coordRect.offset.x, coordRect.offset.z, array);
}
public void ToTexture(Texture2D texture = null, Color[] colors = null, float rangeMin = 0f, float rangeMax = 1f, bool resizeTexture = false)
{
if (texture == null)
{
texture = new Texture2D(rect.size.x, rect.size.z);
}
if (resizeTexture)
{
texture.Resize(rect.size.x, rect.size.z);
}
CoordRect c = new CoordRect(size: new Coord(texture.width, texture.height), offset: new Coord(0, 0));
CoordRect coordRect = CoordRect.Intersect(c, rect);
if (colors == null || colors.Length != coordRect.size.x * coordRect.size.z)
{
colors = new Color[coordRect.size.x * coordRect.size.z];
}
Coord min = coordRect.Min;
Coord max = coordRect.Max;
for (int i = min.x; i < max.x; i++)
{
for (int j = min.z; j < max.z; j++)
{
float num = this[i, j];
num -= rangeMin;
num /= rangeMax - rangeMin;
float num2 = num * 256f;
int num3 = (int)num2;
float num4 = num2 - (float)num3;
float num5 = (float)num3 / 256f;
float num6 = (float)(num3 + 1) / 256f;
int num7 = i - min.x;
int num8 = j - min.z;
colors[num8 * (max.x - min.x) + num7] = new Color(num5, (!(num4 > 0.333f)) ? num5 : num6, (!(num4 > 0.666f)) ? num5 : num6);
}
}
texture.SetPixels(coordRect.offset.x, coordRect.offset.z, coordRect.size.x, coordRect.size.z, colors);
texture.Apply();
}
public void FromTexture(Texture2D texture, bool fillBorders = false)
{
CoordRect c = new CoordRect(size: new Coord(texture.width, texture.height), offset: new Coord(0, 0));
CoordRect centerRect = CoordRect.Intersect(c, rect);
Color[] pixels = texture.GetPixels(centerRect.offset.x, centerRect.offset.z, centerRect.size.x, centerRect.size.z);
Coord min = centerRect.Min;
Coord max = centerRect.Max;
for (int i = min.x; i < max.x; i++)
{
for (int j = min.z; j < max.z; j++)
{
int num = i - min.x;
int num2 = j - min.z;
Color color = pixels[num2 * (max.x - min.x) + num];
this[i, j] = (color.r + color.g + color.b) / 3f;
}
}
if (fillBorders)
{
RemoveBorders(centerRect);
}
}
public Texture2D SimpleToTexture(Texture2D texture = null, Color[] colors = null, float rangeMin = 0f, float rangeMax = 1f, string savePath = null)
{
if (texture == null)
{
texture = new Texture2D(rect.size.x, rect.size.z);
}
if (texture.width != rect.size.x || texture.height != rect.size.z)
{
texture.Resize(rect.size.x, rect.size.z);
}
if (colors == null || colors.Length != rect.size.x * rect.size.z)
{
colors = new Color[rect.size.x * rect.size.z];
}
for (int i = 0; i < array.Length; i++)
{
float num = array[i];
num -= rangeMin;
num /= rangeMax - rangeMin;
colors[i] = new Color(num, num, num);
}
texture.SetPixels(colors);
texture.Apply();
return texture;
}
public void SimpleFromTexture(Texture2D texture)
{
ChangeRect(new CoordRect(rect.offset.x, rect.offset.z, texture.width, texture.height));
Color[] pixels = texture.GetPixels();
for (int i = 0; i < array.Length; i++)
{
Color color = pixels[i];
array[i] = (color.r + color.g + color.b) / 3f;
}
}
public void RemoveBorders()
{
Coord min = rect.Min;
Coord coord = rect.Max - 1;
for (int i = min.x; i <= coord.x; i++)
{
SetPos(i, min.z);
array[pos] = nextZ;
}
for (int j = min.x; j <= coord.x; j++)
{
SetPos(j, coord.z);
array[pos] = prevZ;
}
for (int k = min.z; k <= coord.z; k++)
{
SetPos(min.x, k);
array[pos] = nextX;
}
for (int l = min.z; l <= coord.z; l++)
{
SetPos(coord.x, l);
array[pos] = prevX;
}
}
public void RemoveBorders(int borderMinX, int borderMinZ, int borderMaxX, int borderMaxZ)
{
Coord min = rect.Min;
Coord max = rect.Max;
if (borderMinZ != 0)
{
for (int i = min.x; i < max.x; i++)
{
float value = this[i, min.z + borderMinZ];
for (int j = min.z; j < min.z + borderMinZ; j++)
{
this[i, j] = value;
}
}
}
if (borderMaxZ != 0)
{
for (int k = min.x; k < max.x; k++)
{
float value2 = this[k, max.z - borderMaxZ];
for (int l = max.z - borderMaxZ; l < max.z; l++)
{
this[k, l] = value2;
}
}
}
if (borderMinX != 0)
{
for (int m = min.z; m < max.z; m++)
{
float value3 = this[min.x + borderMinX, m];
for (int n = min.x; n < min.x + borderMinX; n++)
{
this[n, m] = value3;
}
}
}
if (borderMaxX == 0)
{
return;
}
for (int num = min.z; num < max.z; num++)
{
float value4 = this[max.x - borderMaxX, num];
for (int num2 = max.x - borderMaxX; num2 < max.x; num2++)
{
this[num2, num] = value4;
}
}
}
public void RemoveBorders(CoordRect centerRect)
{
RemoveBorders(Mathf.Max(0, centerRect.offset.x - rect.offset.x), Mathf.Max(0, centerRect.offset.z - rect.offset.z), Mathf.Max(0, rect.Max.x - centerRect.Max.x + 1), Mathf.Max(0, rect.Max.z - centerRect.Max.z + 1));
}
public Matrix Resize(CoordRect newRect, float smoothness = 1f, Matrix result = null)
{
int num = newRect.size.x / rect.size.x;
int num2 = rect.size.x / newRect.size.x;
if (num > 1 && !newRect.Divisible(num))
{
Debug.LogError(string.Concat("Matrix rect ", rect, " could not be upscaled to ", newRect, " with factor ", num));
}
if (num2 > 1 && !rect.Divisible(num2))
{
Debug.LogError(string.Concat("Matrix rect ", rect, " could not be downscaled to ", newRect, " with factor ", num2));
}
if (num > 1)
{
result = Upscale(num, result);
}
if (num2 > 1)
{
result = Downscale(num2, smoothness, result);
}
if (num <= 1 && num2 <= 1)
{
return Clone(result);
}
return result;
}
public Matrix Upscale(int factor, Matrix result = null)
{
if (result == null)
{
result = new Matrix(rect * factor);
}
result.ChangeRect(rect * factor);
if (factor == 1)
{
return Clone(result);
}
Coord min = rect.Min;
Coord coord = rect.Max - 1;
float num = 1f / (float)factor;
for (int i = min.x; i < coord.x; i++)
{
for (int j = min.z; j < coord.z; j++)
{
float a = this[i, j];
float a2 = this[i + 1, j];
float b = this[i, j + 1];
float b2 = this[i + 1, j + 1];
for (int k = 0; k < factor; k++)
{
for (int l = 0; l < factor; l++)
{
float t = (float)k * num;
float t2 = (float)l * num;
float a3 = Mathf.Lerp(a, b, t2);
float b3 = Mathf.Lerp(a2, b2, t2);
result[i * factor + k, j * factor + l] = Mathf.Lerp(a3, b3, t);
}
}
}
}
result.RemoveBorders(0, 0, factor + 1, factor + 1);
return result;
}
public Matrix Downscale(int factor = 2, float smoothness = 1f, Matrix result = null)
{
if (!rect.Divisible(factor))
{
Debug.LogError(string.Concat("Matrix rect ", rect, " could not be downscaled with factor ", factor));
}
if (result == null)
{
result = new Matrix(rect / factor);
}
result.ChangeRect(rect / factor);
if (factor == 1)
{
return Clone(result);
}
Coord min = rect.Min;
Coord min2 = result.rect.Min;
Coord max = result.rect.Max;
if (smoothness < 0.0001f)
{
for (int i = min2.x; i < max.x; i++)
{
for (int j = min2.z; j < max.z; j++)
{
int x = (i - min2.x) * factor + min.x;
int z = (j - min2.z) * factor + min.z;
result[i, j] = this[x, z];
}
}
}
else
{
for (int k = min2.x; k < max.x; k++)
{
for (int l = min2.z; l < max.z; l++)
{
int num = (k - min2.x) * factor + min.x;
int num2 = (l - min2.z) * factor + min.z;
float num3 = 0f;
for (int m = num; m < num + factor; m++)
{
for (int n = num2; n < num2 + factor; n++)
{
num3 += this[m, n];
}
}
result[k, l] = num3 / (float)(factor * factor) * smoothness + this[num, num2] * (1f - smoothness);
}
}
}
return result;
}
public void Cavity(float strength)
{
Func<float, float, float, float> blurFn = delegate(float prev, float curr, float next)
{
float num2 = curr - (next + prev) / 2f;
return num2 * num2 * (float)((num2 > 0f) ? 1 : (-1)) * strength * 1000f;
};
Matrix matrix = new Matrix(rect);
matrix.Blur(blurFn, 1f, 1, this, true, false);
Matrix matrix2 = new Matrix(rect);
matrix2.Blur(blurFn, 1f, 1, this, false);
for (int num = 0; num < array.Length; num++)
{
array[num] = matrix.array[num] + matrix2.array[num];
}
}
public void OverBlur(int iterations = 20)
{
Matrix matrix = Clone();
for (int i = 1; i <= iterations; i++)
{
switch (i)
{
case 1:
case 2:
matrix.Blur();
break;
case 3:
matrix.Blur();
matrix.Blur();
break;
default:
{
int step = i - 2;
matrix.Blur(null, 0.666f, step);
break;
}
}
for (int j = 0; j < array.Length; j++)
{
float num = matrix.array[j] * (float)i;
float num2 = array[j];
array[j] = num2 + num + num2 * num;
}
}
}
public void Blur(Func<float, float, float, float> blurFn = null, float intensity = 0.666f, int step = 1, Matrix reference = null, bool horizontal = true, bool vertical = true)
{
Coord min = rect.Min;
Coord max = rect.Max;
if (reference == null)
{
reference = this;
}
int num = max.x - 1;
int num2 = max.z - 1;
if (horizontal)
{
for (int i = min.z; i <= num2; i++)
{
float num3 = reference[min.x, i];
float num4 = num3;
float num5 = num3;
float num6 = num3;
float num7 = num3;
for (int j = min.x + step; j <= num; j += step)
{
num6 = ((blurFn != null) ? blurFn(num5, num4, num3) : ((num5 + num3) / 2f));
num6 = num4 * (1f - intensity) + num6 * intensity;
num5 = num4;
num4 = num3;
try
{
num3 = reference[j + step * 2, i];
}
catch
{
num3 = reference[num, i];
}
if (step == 1)
{
this[j, i] = num6;
}
else
{
for (int k = 0; k < step; k++)
{
float num8 = 1f * (float)k / (float)step;
this[j - step + k, i] = num6 * num8 + num7 * (1f - num8);
}
}
num7 = num6;
}
}
}
if (!vertical)
{
return;
}
for (int l = min.x; l <= num; l++)
{
float num9 = reference[l, min.z];
float num10 = num9;
float num11 = num9;
float num12 = num9;
float num13 = num9;
for (int m = min.z + step; m <= num2; m += step)
{
num12 = ((blurFn != null) ? blurFn(num11, num10, num9) : ((num11 + num9) / 2f));
num12 = num10 * (1f - intensity) + num12 * intensity;
num11 = num10;
num10 = num9;
try
{
num9 = reference[l, m + step * 2];
}
catch
{
num9 = reference[l, num2];
}
if (step == 1)
{
this[l, m] = num12;
}
else
{
for (int n = 0; n < step; n++)
{
float num14 = 1f * (float)n / (float)step;
this[l, m - step + n] = num12 * num14 + num13 * (1f - num14);
}
}
num13 = num12;
}
}
}
public float GetOnTerrain(Vector2 worldPos, Terrain terrain)
{
float num = (worldPos.x - terrain.transform.position.x) / terrain.terrainData.size.x;
float num2 = (worldPos.y - terrain.transform.position.z) / terrain.terrainData.size.z;
int value = Mathf.RoundToInt(num * (float)rect.size.x + (float)rect.offset.x);
int value2 = Mathf.RoundToInt(num2 * (float)rect.size.z + (float)rect.offset.z);
value = Mathf.Clamp(value, rect.Min.x + 1, rect.Max.x - 1);
value2 = Mathf.Clamp(value2, rect.Min.z + 1, rect.Max.z - 1);
return this[value, value2];
}
public static void BlendLayers(Matrix[] matrices, float[] opacity)
{
Coord min = matrices[0].rect.Min;
Coord max = matrices[0].rect.Max;
for (int i = min.x; i < max.x; i++)
{
for (int j = min.z; j < max.z; j++)
{
float num = 0f;
for (int num2 = matrices.Length - 1; num2 >= 0; num2--)
{
float num3 = matrices[num2][i, j];
float num4 = Mathf.Clamp01(num + num3 - 1f);
matrices[num2][i, j] = num3 - num4;
num += num3 - num4;
}
}
}
}
public static void NormalizeLayers(Matrix[] matrices, float[] opacity)
{
Coord min = matrices[0].rect.Min;
Coord max = matrices[0].rect.Max;
for (int i = min.x; i < max.x; i++)
{
for (int j = min.z; j < max.z; j++)
{
float num = 0f;
for (int k = 0; k < matrices.Length; k++)
{
num += matrices[k][i, j];
}
if (num > 1f)
{
for (int l = 0; l < matrices.Length; l++)
{
matrices[l][i, j] /= num;
}
}
}
}
}
public float Fallof(int x, int z, float fallof)
{
if (fallof < 0f)
{
return 1f;
}
float num = (float)rect.size.x / 2f - 1f;
float num2 = ((float)x - ((float)rect.offset.x + num)) / num;
float num3 = (float)rect.size.z / 2f - 1f;
float num4 = ((float)z - ((float)rect.offset.z + num3)) / num3;
float num5 = Mathf.Sqrt(num2 * num2 + num4 * num4);
float num6 = Mathf.Clamp01((1f - num5) / (1f - fallof));
return 3f * num6 * num6 - 2f * num6 * num6 * num6;
}
public void Add(Matrix add)
{
for (int i = 0; i < array.Length; i++)
{
array[i] += add.array[i];
}
}
public void Add(float add)
{
for (int i = 0; i < array.Length; i++)
{
array[i] += add;
}
}
public void Subtract(Matrix m)
{
for (int i = 0; i < array.Length; i++)
{
array[i] -= m.array[i];
}
}
public void InvSubtract(Matrix m)
{
for (int i = 0; i < array.Length; i++)
{
array[i] = m.array[i] - array[i];
}
}
public void ClampSubtract(Matrix m)
{
for (int i = 0; i < array.Length; i++)
{
array[i] = Mathf.Clamp01(array[i] - m.array[i]);
}
}
public void Multiply(Matrix m)
{
for (int i = 0; i < array.Length; i++)
{
array[i] *= m.array[i];
}
}
public void Multiply(float m)
{
for (int i = 0; i < array.Length; i++)
{
array[i] *= m;
}
}
public bool CheckRange(float min, float max)
{
for (int i = 0; i < array.Length; i++)
{
if (array[i] < min || array[i] > max)
{
return false;
}
}
return true;
}
public void Clear()
{
for (int i = 0; i < array.Length; i++)
{
array[i] = 0f;
}
}
}
}