626 lines
23 KiB
C#
626 lines
23 KiB
C#
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
|
|
{
|
|
/// <summary>Sets all the elements in the selected area of the array to the specified value.</summary>
|
|
/// <param name="dmns">The area of the array to perform the operations.</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Sets all the elements with values within the range limits on the channel selected.</summary>
|
|
/// <param name="range">Array of values to check against the range limits.</param>
|
|
/// <param name="channel">The channel to set the values to.</param>
|
|
/// <param name="dmns">The area of the array to perform the operations.</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Clamps all the values to within the set range.</summary>
|
|
/// <param name="dmns">The area of the array to perform the operations.</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Rotates the array CW or CCW.</summary>
|
|
/// <param name="CW">CW = 90°, CCW = 270°</param>
|
|
/// <param name="dmns">The area of the array to perform the operations.</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Flips the values of the array.</summary>
|
|
/// <param name="dmns">The area of the array to perform the operations.</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Normalises the values of the array between 2 floats.</summary>
|
|
/// <param name="normaliseLow">Min value of the array.</param>
|
|
/// <param name="normaliseHigh">Max value of the array.</param>
|
|
/// <param name="dmns">The area of the array to perform the operations.</param>
|
|
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;
|
|
}
|
|
|
|
/// <summary>Offsets the values of the array by the specified value.</summary>
|
|
/// <param name="dmns">The area of the array to perform the operations.</param>
|
|
/// <param name="clampOffset">Prevent array values from overflowing.</param>
|
|
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<short> 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;
|
|
}
|
|
}
|
|
} |