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 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 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; } } } }