using System; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Collections.Generic; using UnityEngine; using static AreaManager; namespace RustMapEditor.Maths { public static class Array { /// Sets all the elements in the selected area of the array to the specified value. /// The area of the array to perform the operations. public static float[,] SetValues(float[,] array, float value, Area dmns = null) { if (dmns == null) dmns = AreaManager.ActiveArea; Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) array[i, j] = value; }); return array; } public static float[,,] SetValues(float[,,] array, int channel, Area dmns = null) { if (dmns == null) dmns = AreaManager.ActiveArea; int channelLength = array.GetLength(2); Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) { for (int k = 0; k < channelLength; k++) array[i, j, k] = 0; array[i, j, channel] = 1; } }); return array; } public static bool[,] SetValues(bool[,] array, bool value, Area dmns = null) { if (dmns == null) dmns = new Area(AreaManager.ActiveArea.x0, AreaManager.ActiveArea.x1 * 2, AreaManager.ActiveArea.z0, AreaManager.ActiveArea.z1 * 2); Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) array[i, j] = value; }); return array; } /// Sets all the elements with values within the range limits on the channel selected. /// Array of values to check against the range limits. /// The channel to set the values to. /// The area of the array to perform the operations. public static float[,,] SetRange(float[,,] array, float[,] range, int channel, float rangeLow, float rangeHigh, Area dmns = null) { if (dmns == null) dmns = AreaManager.ActiveArea; int channelCount = array.GetLength(2); Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) { if (range[i, j] >= rangeLow && range[i, j] <= rangeHigh) { for (int k = 0; k < channelCount; k++) array[i, j, k] = 0; array[i, j, channel] = 1; } } }); return array; } public static float[,,] SetRangeBlend(float[,,] array, float[,] range, int channel, float rangeLow, float rangeHigh, float rangeBlendLow, float rangeBlendHigh, Area dmns = null) { if (dmns == null) dmns = AreaManager.ActiveArea; int channelLength = array.GetLength(2); for (int i = dmns.x0; i < dmns.x1; i++) { Parallel.For(dmns.z0, dmns.z1, j => { float[] normalised = new float[channelLength]; if (range[i, j] >= rangeLow && range[i, j] <= rangeHigh) { for (int k = 0; k < channelLength; k++) array[i, j, k] = 0; array[i, j, channel] = 1; } else if (range[i, j] >= rangeBlendLow && range[i, j] < rangeLow) { float normalisedRange = range[i, j] - rangeBlendLow; float newRange = rangeLow - rangeBlendLow; float rangeBlend = normalisedRange / newRange; // Holds data about the texture weight between the blend ranges. for (int k = 0; k < channelLength; k++) // Gets the weights of the textures in the pos. { if (k == channel) array[i, j, channel] = rangeBlend; else array[i, j, k] = array[i, j, k] * Mathf.Clamp01(1f - rangeBlend); normalised[k] = array[i, j, k]; } float normalisedWeights = normalised.Sum(); for (int k = 0; k < channelLength; k++) { normalised[k] /= normalisedWeights; array[i, j, k] = normalised[k]; } } else if (range[i, j] > rangeHigh && range[i, j] <= rangeBlendHigh) { float normalisedRange = range[i, j] - rangeHigh; float newRange = rangeBlendHigh - rangeHigh; float rangeBlend = normalisedRange / newRange; // Holds data about the texture weight between the blend ranges. float rangeBlendInverted = 1 - rangeBlend; for (int k = 0; k < channelLength; k++) // Gets the weights of the textures in the pos. { if (k == channel) array[i, j, channel] = rangeBlendInverted; else array[i, j, k] = array[i, j, k] * Mathf.Clamp01(1f - rangeBlendInverted); normalised[k] = array[i, j, k]; } float normalisedWeights = normalised.Sum(); for (int k = 0; k < channelLength; k++) { normalised[k] /= normalisedWeights; array[i, j, k] = normalised[k]; } } }); } return array; } public static bool[,] SetRange(bool[,] array, float[,] range, bool value, float rangeLow, float rangeHigh, Area dmns = null) { if (dmns == null) dmns = new Area(AreaManager.ActiveArea.x0, AreaManager.ActiveArea.x1 * 2, AreaManager.ActiveArea.z0, AreaManager.ActiveArea.z1 * 2); Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) { if (range[i * 2, j * 2] > rangeLow && range[i * 2, j * 2] < rangeHigh) array[i, j] = value; } }); return array; } public static float[,,] SetRiver(float[,,] array, float[,] landHeights, float[,] waterHeights, bool aboveTerrain, int channel, Area dmns = null) { if (dmns == null) dmns = AreaManager.ActiveArea; int channelLength = array.GetLength(2); if (aboveTerrain) { Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) { if (waterHeights[i, j] > 500 && waterHeights[i, j] > landHeights[i, j]) { for (int k = 0; k < channelLength; k++) array[i, j, k] = 0; array[i, j, channel] = 1; } } }); } else { Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) { if (waterHeights[i, j] > 500) { for (int k = 0; k < channelLength; k++) array[i, j, k] = 0; array[i, j, channel] = 1; } } }); } return array; } public static bool[,] SetRiver(bool[,] array, float[,] landHeights, float[,] waterHeights, bool aboveTerrain, bool value, Area dmns = null) { if (dmns == null) dmns = new Area(AreaManager.ActiveArea.x0, AreaManager.ActiveArea.x1 * 2, AreaManager.ActiveArea.z0, AreaManager.ActiveArea.z1 * 2); if (aboveTerrain) { Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) { if (waterHeights[i, j] > 500 && waterHeights[i, j] > landHeights[i, j]) array[i, j] = value; } }); } else { Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) { if (waterHeights[i, j] > 500) array[i, j] = value; } }); } return array; } public static bool[,] CheckConditions(float[,] array, bool[,] conditionsMet, float minValue, float maxValue) { int arrayLength = array.GetLength(0); Parallel.For(0, arrayLength, i => { for (int j = 0; j < arrayLength; j++) { if (array[i, j] < minValue) conditionsMet[i, j] = true; else if (array[i, j] > maxValue) conditionsMet[i, j] = true; } }); return conditionsMet; } public static bool[,] CheckConditions(float[,,] array, bool[,] conditionsMet, int layer, float weight) { int arrayLength = array.GetLength(0); int channelLength = array.GetLength(2); Parallel.For(0, arrayLength, i => { for (int j = 0; j < arrayLength; j++) { if (array[i, j, layer] < weight) conditionsMet[i, j] = true; } }); return conditionsMet; } public static bool[,] CheckConditions(bool[,] array, bool[,] conditionsMet, bool value) { int arrayLength = array.GetLength(0); Parallel.For(0, arrayLength, i => { for (int j = 0; j < arrayLength; j++) { if (array[i, j] != value) conditionsMet[i, j] = true; } }); return conditionsMet; } /// Clamps all the values to within the set range. /// The area of the array to perform the operations. public static float[,] ClampValues(float[,] array, float minValue, float maxValue, Area dmns = null) { if (dmns == null) dmns = AreaManager.ActiveArea; Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) array[i, j] = Mathf.Clamp(array[i, j], minValue, maxValue); }); return array; } /// Rotates the array CW or CCW. /// CW = 90°, CCW = 270° /// The area of the array to perform the operations. public static float[,] Rotate(float[,] array, bool CW, Area dmns = null) { if (dmns == null) dmns = AreaManager.ActiveArea; float[,] newArray = new float[array.GetLength(0), array.GetLength(1)]; if (CW) { Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) newArray[i, j] = array[j, dmns.x1 - i - 1]; }); } else { Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) newArray[i, j] = array[dmns.z1 - j - 1, i]; }); } return newArray; } public static float[,,] Rotate(float[,,] array, bool CW, Area dmns = null) { if (dmns == null) dmns = AreaManager.ActiveArea; int channelLength = array.GetLength(2); float[,,] newArray = new float[array.GetLength(0), array.GetLength(1), array.GetLength(2)]; if (CW) { Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) { for (int k = 0; k < channelLength; k++) newArray[i, j, k] = array[j, dmns.x1 - i - 1, k]; } }); } else { Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) { for (int k = 0; k < channelLength; k++) newArray[i, j, k] = array[dmns.z1 - j - 1, i, k]; } }); } return newArray; } public static bool[,] Rotate(bool[,] array, bool CW, Area dmns = null) { if (dmns == null) dmns = new Area(AreaManager.ActiveArea.x0, AreaManager.ActiveArea.x1 * 2, AreaManager.ActiveArea.z0, AreaManager.ActiveArea.z1 * 2); bool[,] newArray = new bool[array.GetLength(0), array.GetLength(1)]; if (CW) { Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) newArray[i, j] = array[j, dmns.x1 - i - 1]; }); } else { Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) newArray[i, j] = array[dmns.z1 - j - 1, i]; }); } return newArray; } /// Flips the values of the array. /// The area of the array to perform the operations. public static float[,] Invert(float[,] array, Area dmns = null) { if (dmns == null) dmns = AreaManager.ActiveArea; Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) array[i, j] = 1 - array[i, j]; }); return array; } public static float[,,] Invert(float[,,] array, Area dmns = null) { if (dmns == null) dmns = AreaManager.ActiveArea; int channelLength = array.GetLength(2); Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) { for (int k = 0; k < channelLength; k++) array[i, j, k] = 1 - array[i, j, k]; } }); return array; } public static bool[,] Invert(bool[,] array, Area dmns = null) { if (dmns == null) dmns = new Area(AreaManager.ActiveArea.x0, AreaManager.ActiveArea.x1 * 2, AreaManager.ActiveArea.z0, AreaManager.ActiveArea.z1 * 2); Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) array[i, j] = !array[i, j]; }); return array; } /// Normalises the values of the array between 2 floats. /// Min value of the array. /// Max value of the array. /// The area of the array to perform the operations. public static float[,] Normalise(float[,] array, float normaliseLow, float normaliseHigh, Area dmns = null) { if (dmns == null) dmns = AreaManager.ActiveArea; float highestPoint = 0f, lowestPoint = 1f, heightRange = 0f, normalisedHeightRange = 0f; Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) { if (array[i, j] < lowestPoint) lowestPoint = array[i, j]; else if (array[i, j] > highestPoint) highestPoint = array[i, j]; } }); heightRange = highestPoint - lowestPoint; normalisedHeightRange = normaliseHigh - normaliseLow; Parallel.For(dmns.x0, dmns.x1, i => { for (int j = dmns.z0; j < dmns.z1; j++) array[i, j] = normaliseLow + ((array[i, j] - lowestPoint) / heightRange) * normalisedHeightRange; }); return array; } /// Offsets the values of the array by the specified value. /// The area of the array to perform the operations. /// Prevent array values from overflowing. public static float[,] Offset(float[,] array, float offset, bool clampOffset, Area dmns = null) { float[,] tempArray = array; CancellationTokenSource source = new CancellationTokenSource(); ParallelOptions options = new ParallelOptions() { CancellationToken = source.Token}; try { if (dmns == null) dmns = AreaManager.ActiveArea; Parallel.For(dmns.x0, dmns.x1, options, i => { for (int j = dmns.z0; j < dmns.z1; j++) { if (clampOffset == true) { if ((array[i, j] + offset > 1f || array[i, j] + offset < 0f)) source.Cancel(); else tempArray[i, j] += offset; } else tempArray[i, j] += offset; } }); } catch (OperationCanceledException) { return array; } return tempArray; } // resize heightmap to splat map public static float[,] HeightToSplat(float[,] array) { int arrayLength = TerrainManager.SplatMapRes; float ratio = (1f*array.GetLength(0)-1) / (arrayLength*1f); Debug.Log(ratio + " " + arrayLength); float[,] arrayOut = new float[arrayLength, arrayLength]; if (array == null) { Debug.Log("null array received"); return arrayOut; } else { //Debug.LogError(arrayLength + " " + array.GetLength(0)); Parallel.For(0, arrayLength, i => { for (int j = 0; j < arrayLength; j++) { int indexI = (int)(i * ratio); int indexJ = (int)(j * ratio); if (indexI < array.GetLength(0) && indexJ < array.GetLength(1)) { arrayOut[i, j] = Mathf.Abs(array[indexI, indexJ]); } } }); return arrayOut; } } // representing heightmap data as texture map for landmaps public static float[,,] HeightToSplat(float[,] array, bool[,] spawn, int arrayLength) { int ratio = (array.GetLength(0)-1) / arrayLength; float[,,] arrayOut = new float[arrayLength, arrayLength, 3]; int ratioX = (array.GetLength(0) - 1) / (arrayLength - 1); int ratioY = (array.GetLength(1) - 1) / (arrayLength - 1); //Debug.LogError(arrayLength + " " + array.GetLength(0)); Parallel.For(0, arrayLength, i => { for (int j = 0; j < arrayLength; j++) { int indexI = i * ratioX; int indexJ = j * ratioY; if (indexI < array.GetLength(0) && indexJ < array.GetLength(1)) { if (!spawn[indexI, indexJ]) { arrayOut[i, j, 0] = 1f - array[indexI, indexJ]; arrayOut[i, j, 1] = array[indexI, indexJ]; arrayOut[i, j, 2] = 0f; } else { arrayOut[i, j, 0] = 0f; arrayOut[i, j, 1] = 0f; arrayOut[i, j, 2] = 20f; } } } }); return arrayOut; } public static float[,] ShortMapToFloatArray(TerrainMap terrainMap) { float[,] array = new float[terrainMap.res, terrainMap.res]; int arrayLength = array.GetLength(0); Parallel.For(0, arrayLength, i => { for (int j = 0; j < arrayLength; j++) array[i, j] = BitUtility.Short2Float(terrainMap[i, j]); }); return array; } public static byte[] FloatArrayToByteArray(float[,] array) { short[] shortArray = new short[array.GetLength(0) * array.GetLength(1)]; int arrayLength = array.GetLength(0); Parallel.For(0, arrayLength, i => { for (int j = 0; j < arrayLength; j++) shortArray[(i * arrayLength) + j] = BitUtility.Float2Short(array[i, j]); }); byte[] byteArray = new byte[shortArray.Length * 2]; Buffer.BlockCopy(shortArray, 0, byteArray, 0, byteArray.Length); return byteArray; } public static float[,,] NormaliseMulti(float[,,] array, int texturesAmount) { int length = (int)Math.Sqrt(array.Length / texturesAmount); int arrayLength = array.GetLength(0); int channelLength = array.GetLength(2); Parallel.For(0, arrayLength, i => { float[] splatWeights = new float[channelLength]; for (int j = 0; j < arrayLength; j++) { for (int k = 0; k < channelLength; k++) splatWeights[k] = array[i, j, k]; float normalisedWeights = splatWeights.Sum(); for (int k = 0; k < channelLength; k++) { splatWeights[k] /= normalisedWeights; array[i, j, k] = splatWeights[k]; } } }); return array; } } }