using System.Text; using GaiaCommon1; using UnityEngine; namespace Gaia { public class GaiaWorldManager { private Bounds m_worldBoundsWU; private Vector3 m_worldBoundsWUMin; private Vector3 m_worldBoundsWUMax; private Vector3 m_worldBoundsWUSize; private Bounds m_worldBoundsTU; private Vector3 m_worldBoundsTUMin; private Vector3 m_worldBoundsTUMax; private Vector3 m_worldBoundsTUSize; private Bounds m_worldBoundsNU; private Vector3 m_worldBoundsNUMin; private Vector3 m_worldBoundsNUMax; private Vector3 m_WUtoTU = Vector3.one; private Vector3 m_TUtoWU = Vector3.one; private Vector3 m_TUtoNU = Vector3.one; private Vector3 m_NUtoTU = Vector3.one; private Vector3 m_WUtoNU = Vector3.one; private Vector3 m_NUtoWU = Vector3.one; private Vector3 m_NUZeroOffset = Vector3.zero; private Vector3 m_TUZeroOffset = Vector3.zero; private ulong m_boundsCheckErrors; private Terrain[,] m_physicalTerrainArray; private UnityHeightMap[,] m_heightMapTerrainArray; private int m_tileCount; public int TileCount => m_tileCount; public Terrain[,] PhysicalTerrainArray { get { return m_physicalTerrainArray; } set { m_physicalTerrainArray = value; } } public UnityHeightMap[,] HeightMapTerrainArray { get { return m_heightMapTerrainArray; } set { m_heightMapTerrainArray = value; } } public Bounds WorldBoundsWU => m_worldBoundsWU; public Bounds WorldBoundsTU => m_worldBoundsTU; public Bounds WorldBoundsNU => m_worldBoundsNU; public Vector3 WUtoTUConversionFactor => m_WUtoTU; public Vector3 WUtoNUConversionFactor => m_WUtoNU; public ulong BoundsCheckErrors { get { return m_boundsCheckErrors; } set { m_boundsCheckErrors = value; } } public GaiaWorldManager() { } public GaiaWorldManager(Terrain[] terrains) { Terrain terrain = null; m_worldBoundsWU = default(Bounds); m_worldBoundsTU = default(Bounds); m_worldBoundsNU = default(Bounds); string text = IsValidWorld(terrains); if (!string.IsNullOrEmpty(text)) { Debug.LogError("GaiaWorldManager(terrains) ERROR" + text); return; } for (int i = 0; i < terrains.Length; i++) { terrain = terrains[i]; Bounds bounds = new Bounds(terrain.transform.position, terrain.terrainData.size); bounds.center += bounds.extents; if (i == 0) { m_worldBoundsWU = new Bounds(bounds.center, bounds.size); } else { m_worldBoundsWU.Encapsulate(bounds); } Bounds bounds2 = default(Bounds); m_WUtoTU = new Vector3((float)terrain.terrainData.heightmapResolution / terrain.terrainData.size.x, (float)(terrain.terrainData.heightmapResolution - 1) / Mathf.Max(terrain.terrainData.size.x, terrain.terrainData.size.z) * terrain.terrainData.size.y / terrain.terrainData.size.y, (float)terrain.terrainData.heightmapResolution / terrain.terrainData.size.z); m_TUtoWU = new Vector3(1f / m_WUtoTU.x, 1f / m_WUtoTU.y, 1f / m_WUtoTU.z); bounds2.center = Vector3.Scale(bounds.center, m_WUtoTU); bounds2.size = Vector3.Scale(bounds.size, m_WUtoTU); if (i == 0) { m_worldBoundsTU = new Bounds(bounds2.center, bounds2.size); } else { m_worldBoundsTU.Encapsulate(bounds2); } } if (terrain != null) { m_TUtoNU = new Vector3(1f / m_worldBoundsTU.size.x, 1f / m_worldBoundsTU.size.y, 1f / m_worldBoundsTU.size.z); m_NUtoTU = m_worldBoundsTU.size; m_WUtoNU = Vector3.Scale(m_WUtoTU, m_TUtoNU); m_NUtoWU = m_worldBoundsWU.size; } m_worldBoundsNU.center = Vector3.Scale(m_worldBoundsTU.center, m_TUtoNU); m_worldBoundsNU.size = Vector3.Scale(m_worldBoundsTU.size, m_TUtoNU); m_NUZeroOffset = Vector3.zero - m_worldBoundsNU.min; m_TUZeroOffset = Vector3.zero - m_worldBoundsTU.min; m_tileCount = (int)(m_worldBoundsNU.size.x * m_worldBoundsNU.size.z); m_physicalTerrainArray = new Terrain[(int)m_worldBoundsNU.size.x, (int)m_worldBoundsNU.size.z]; m_heightMapTerrainArray = new UnityHeightMap[(int)m_worldBoundsNU.size.x, (int)m_worldBoundsNU.size.z]; for (int j = 0; j < terrains.Length; j++) { terrain = terrains[j]; Vector3 vector = WUtoPTI(terrain.transform.position); m_physicalTerrainArray[(int)vector.x, (int)vector.z] = terrain; } m_worldBoundsWUMax = m_worldBoundsWU.max; m_worldBoundsWUMin = m_worldBoundsWU.min; m_worldBoundsWUSize = m_worldBoundsWU.size; m_worldBoundsTUMax = m_worldBoundsTU.max; m_worldBoundsTUMin = m_worldBoundsTU.min; m_worldBoundsTUSize = m_worldBoundsTU.size; m_worldBoundsNUMax = m_worldBoundsNU.max; m_worldBoundsNUMin = m_worldBoundsNU.min; } public string IsValidWorld(Terrain[] terrains) { Terrain terrain = null; Terrain terrain2 = null; StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < terrains.Length; i++) { terrain2 = terrains[i]; if (terrain == null) { terrain = terrain2; } if (terrain2.terrainData.size.x != terrain2.terrainData.size.z) { stringBuilder.Append($"\nTerrain {terrain2.name} is not a square {terrain2.terrainData.size.x} {terrain2.terrainData.size.z}"); } if (terrain2.terrainData.size != terrain.terrainData.size) { stringBuilder.Append($"\nTerrain {terrain2.name} - {terrain.name} size does not match {terrain2.terrainData.size} {terrain.terrainData.size}"); } if (terrain2.terrainData.heightmapResolution != terrain.terrainData.heightmapResolution) { stringBuilder.Append($"\nTerrain {terrain2.name} - {terrain.name} heightmapResolution does not match {terrain2.terrainData.heightmapResolution} {terrain.terrainData.heightmapResolution}"); } if (terrain2.terrainData.alphamapResolution != terrain.terrainData.alphamapResolution) { stringBuilder.Append($"\nTerrain {terrain2.name} - {terrain.name} alphamapResolution does not match {terrain2.terrainData.alphamapResolution} {terrain.terrainData.alphamapResolution}"); } if (terrain2.terrainData.baseMapResolution != terrain.terrainData.baseMapResolution) { stringBuilder.Append($"\nTerrain {terrain2.name} - {terrain.name} baseMapResolution does not match {terrain2.terrainData.baseMapResolution} {terrain.terrainData.baseMapResolution}"); } if (terrain2.terrainData.detailResolution != terrain.terrainData.detailResolution) { stringBuilder.Append($"\nTerrain {terrain2.name} - {terrain.name} detailResolution does not match {terrain2.terrainData.detailResolution} {terrain.terrainData.detailResolution}"); } if (terrain2.terrainData.alphamapLayers != terrain.terrainData.alphamapLayers) { stringBuilder.Append($"\nTerrain {terrain2.name} - {terrain.name} alphamapLayers does not match {terrain2.terrainData.alphamapLayers} {terrain.terrainData.alphamapLayers}"); } if (terrain2.terrainData.detailPrototypes.Length != terrain.terrainData.detailPrototypes.Length) { stringBuilder.Append($"\nTerrain {terrain2.name} - {terrain.name} detailPrototypes.Length does not match {terrain2.terrainData.detailPrototypes.Length} {terrain.terrainData.detailPrototypes.Length}"); } if (GaiaSplatPrototype.GetGaiaSplatPrototypes(terrain2).Length != GaiaSplatPrototype.GetGaiaSplatPrototypes(terrain).Length) { stringBuilder.Append($"\nTerrain {terrain2.name} - {terrain.name} splatPrototypes.Length does not match {GaiaSplatPrototype.GetGaiaSplatPrototypes(terrain2).Length} {GaiaSplatPrototype.GetGaiaSplatPrototypes(terrain).Length}"); } if (terrain2.terrainData.treePrototypes.Length != terrain.terrainData.treePrototypes.Length) { stringBuilder.Append($"\nTerrain {terrain2.name} - {terrain.name} treePrototypes.Length does not match {terrain2.terrainData.treePrototypes.Length} {terrain.terrainData.treePrototypes.Length}"); } } return stringBuilder.ToString(); } private Terrain GetTerrainWU(Vector3 positionWU) { if (!InBoundsWU(positionWU)) { m_boundsCheckErrors++; return null; } Vector3 vector = WUtoPTI(positionWU); return m_physicalTerrainArray[(int)vector.x, (int)vector.z]; } private Terrain GetTerrainTU(Vector3 positionTU) { if (!InBoundsTU(positionTU)) { m_boundsCheckErrors++; return null; } TUtoPTI(ref positionTU); return m_physicalTerrainArray[(int)positionTU.x, (int)positionTU.z]; } private Terrain GetTerrainNU(Vector3 positionNU) { if (!InBoundsNU(positionNU)) { m_boundsCheckErrors++; return null; } NUtoPTI(ref positionNU); return m_physicalTerrainArray[(int)positionNU.x, (int)positionNU.z]; } private UnityHeightMap GetHeightMapWU(Vector3 positionWU) { if (!InBoundsWU(positionWU)) { m_boundsCheckErrors++; return null; } Vector3 vector = WUtoPTI(positionWU); UnityHeightMap unityHeightMap = m_heightMapTerrainArray[(int)vector.x, (int)vector.z]; if (unityHeightMap == null) { Terrain terrainWU = GetTerrainWU(positionWU); if (terrainWU != null) { unityHeightMap = (m_heightMapTerrainArray[(int)vector.x, (int)vector.z] = new UnityHeightMap(terrainWU)); } } return unityHeightMap; } private UnityHeightMap GetHeightMapTU(Vector3 positionTU) { if (!InBoundsTU(positionTU)) { m_boundsCheckErrors++; return null; } TUtoPTI(ref positionTU); UnityHeightMap unityHeightMap = m_heightMapTerrainArray[(int)positionTU.x, (int)positionTU.z]; if (unityHeightMap == null) { Terrain terrainTU = GetTerrainTU(positionTU); if (terrainTU != null) { unityHeightMap = (m_heightMapTerrainArray[(int)positionTU.x, (int)positionTU.z] = new UnityHeightMap(terrainTU)); } } return unityHeightMap; } private UnityHeightMap GetHeightMapNU(Vector3 positionNU) { if (!InBoundsNU(positionNU)) { m_boundsCheckErrors++; return null; } NUtoPTI(ref positionNU); UnityHeightMap unityHeightMap = m_heightMapTerrainArray[(int)positionNU.x, (int)positionNU.z]; if (unityHeightMap == null) { Terrain terrainNU = GetTerrainNU(positionNU); if (terrainNU != null) { unityHeightMap = (m_heightMapTerrainArray[(int)positionNU.x, (int)positionNU.z] = new UnityHeightMap(terrainNU)); } } return unityHeightMap; } public void LoadFromWorld() { for (int i = 0; i < m_heightMapTerrainArray.GetLength(0); i++) { for (int j = 0; j < m_heightMapTerrainArray.GetLength(1); j++) { UnityHeightMap unityHeightMap = m_heightMapTerrainArray[i, j]; if (unityHeightMap == null) { Terrain terrain = m_physicalTerrainArray[i, j]; if (terrain != null) { m_heightMapTerrainArray[i, j] = new UnityHeightMap(terrain); } } else { unityHeightMap.LoadFromTerrain(m_physicalTerrainArray[i, j]); } } } } public void SaveToWorld(bool forceWrite = false) { for (int i = 0; i < m_heightMapTerrainArray.GetLength(0); i++) { for (int j = 0; j < m_heightMapTerrainArray.GetLength(1); j++) { UnityHeightMap unityHeightMap = m_heightMapTerrainArray[i, j]; if (unityHeightMap == null) { continue; } if (!forceWrite) { if (unityHeightMap.IsDirty()) { unityHeightMap.SaveToTerrain(m_physicalTerrainArray[i, j]); } } else { unityHeightMap.SaveToTerrain(m_physicalTerrainArray[i, j]); } } } } public void SetHeightWU(float heightWU) { float height = Mathf.Clamp01(heightWU / m_worldBoundsWUSize.y); for (int i = 0; i < m_heightMapTerrainArray.GetLength(0); i++) { for (int j = 0; j < m_heightMapTerrainArray.GetLength(1); j++) { m_heightMapTerrainArray[i, j].SetHeight(height); } } } public void SetHeightWU(Vector3 positionWU, float height) { UnityHeightMap heightMapWU = GetHeightMapWU(positionWU); if (heightMapWU != null) { positionWU = WUtoPTO(positionWU); heightMapWU[(int)positionWU.x, (int)positionWU.z] = height; } else { m_boundsCheckErrors++; } } public float GetHeightWU(Vector3 positionWU) { UnityHeightMap heightMapWU = GetHeightMapWU(positionWU); if (heightMapWU != null) { positionWU = WUtoPTO(positionWU); return heightMapWU[(int)positionWU.x, (int)positionWU.z]; } return float.MinValue; } public float GetHeightInterpolatedWU(Vector3 positionWU) { UnityHeightMap heightMapWU = GetHeightMapWU(positionWU); if (heightMapWU != null) { positionWU = WUtoPTO(positionWU); return heightMapWU[positionWU.x, positionWU.z]; } return float.MinValue; } public void SetHeightTU(Vector3 positionTU, float height) { UnityHeightMap heightMapTU = GetHeightMapTU(positionTU); if (heightMapTU != null) { TUtoPTO(ref positionTU); heightMapTU[(int)positionTU.x, (int)positionTU.z] = height; } else { m_boundsCheckErrors++; } } public float GetHeightTU(Vector3 positionTU) { UnityHeightMap heightMapTU = GetHeightMapTU(positionTU); if (heightMapTU != null) { TUtoPTO(ref positionTU); return heightMapTU[(int)positionTU.x, (int)positionTU.z]; } return float.MinValue; } public float GetHeightInterpolatedTU(Vector3 positionTU) { UnityHeightMap heightMapTU = GetHeightMapTU(positionTU); if (heightMapTU != null) { TUtoPTO(ref positionTU); return heightMapTU[positionTU.x, positionTU.z]; } return float.MinValue; } public void FlattenWorld() { for (int i = 0; i < m_heightMapTerrainArray.GetLength(0); i++) { for (int j = 0; j < m_heightMapTerrainArray.GetLength(1); j++) { UnityHeightMap unityHeightMap = m_heightMapTerrainArray[i, j]; if (unityHeightMap == null) { Terrain terrain = m_physicalTerrainArray[i, j]; if (terrain != null) { unityHeightMap = (m_heightMapTerrainArray[i, j] = new UnityHeightMap(terrain)); } } if (unityHeightMap != null) { unityHeightMap.SetHeight(0f); unityHeightMap.SaveToTerrain(m_physicalTerrainArray[i, j]); } } } } public void SmoothWorld() { for (int i = 0; i < m_heightMapTerrainArray.GetLength(0); i++) { for (int j = 0; j < m_heightMapTerrainArray.GetLength(1); j++) { UnityHeightMap unityHeightMap = m_heightMapTerrainArray[i, j]; if (unityHeightMap == null) { Terrain terrain = m_physicalTerrainArray[i, j]; if (terrain != null) { unityHeightMap = (m_heightMapTerrainArray[i, j] = new UnityHeightMap(terrain)); } } if (unityHeightMap != null) { unityHeightMap.Smooth(1); unityHeightMap.SaveToTerrain(m_physicalTerrainArray[i, j]); } } } } public void ExportWorldAsPng(string path) { Vector3 center = m_worldBoundsTU.center; HeightMap heightMap = new HeightMap((int)m_worldBoundsTUSize.z, (int)m_worldBoundsTUSize.x); int num = 0; for (int i = (int)m_worldBoundsTUMin.x; i < (int)m_worldBoundsTUMax.x; i++) { center.x = i; int num2 = 0; for (int j = (int)m_worldBoundsTUMin.z; j < (int)m_worldBoundsTUMax.z; j++) { center.z = j; heightMap[num2, num] = GetHeightTU(center); num2++; } num++; } GaiaUtils.CompressToSingleChannelFileImage(heightMap.Heights(), path, TextureFormat.RGBA32, exportPNG: true, exportJPG: false); } public void ExportSplatmapAsPng(string path, int textureIdx) { Terrain activeTerrain = Terrain.activeTerrain; if (activeTerrain == null) { Debug.LogError("No active terrain, unable to export splatmaps"); return; } int alphamapWidth = activeTerrain.terrainData.alphamapWidth; int alphamapHeight = activeTerrain.terrainData.alphamapHeight; int alphamapLayers = activeTerrain.terrainData.alphamapLayers; if (textureIdx < alphamapLayers) { HeightMap heightMap = new HeightMap(activeTerrain.terrainData.GetAlphamaps(0, 0, alphamapWidth, alphamapHeight), textureIdx); heightMap.Flip(); GaiaUtils.CompressToSingleChannelFileImage(heightMap.Heights(), path, TextureFormat.RGBA32, exportPNG: true, exportJPG: false); } else { GaiaUtils.CompressToMultiChannelFileImage(activeTerrain.terrainData.GetAlphamaps(0, 0, alphamapWidth, alphamapHeight), path, TextureFormat.RGBA32, exportPNG: true, exportJPG: false); } } public void ExportGrassmapAsPng(string path) { Terrain activeTerrain = Terrain.activeTerrain; if (activeTerrain == null) { Debug.LogError("No active terrain, unable to export grassmaps"); return; } int detailWidth = activeTerrain.terrainData.detailWidth; int detailHeight = activeTerrain.terrainData.detailHeight; int num = activeTerrain.terrainData.detailPrototypes.Length; float[,,] array = new float[detailWidth, detailHeight, num]; for (int i = 0; i < activeTerrain.terrainData.detailPrototypes.Length; i++) { int[,] detailLayer = activeTerrain.terrainData.GetDetailLayer(0, 0, activeTerrain.terrainData.detailWidth, activeTerrain.terrainData.detailHeight, i); for (int j = 0; j < detailWidth; j++) { for (int k = 0; k < detailHeight; k++) { array[j, k, i] = (float)detailLayer[j, k] / 16f; } } for (int l = 0; l < detailWidth; l++) { for (int m = 0; m < detailHeight; m++) { array[m, l, i] = array[l, m, i]; } } } GaiaUtils.CompressToMultiChannelFileImage(array, path, TextureFormat.RGBA32, exportPNG: true, exportJPG: false); } public void ExportNormalmapAsPng(string path) { for (int i = 0; i < m_physicalTerrainArray.GetLength(0); i++) { for (int j = 0; j < m_physicalTerrainArray.GetLength(1); j++) { } } } public void ExportNormalmapAsPng1(string path) { Terrain terrain = null; int num = 0; int num2 = 0; float[,,] array = null; for (int i = 0; i < m_physicalTerrainArray.GetLength(0); i++) { for (int j = 0; j < m_physicalTerrainArray.GetLength(1); j++) { terrain = m_physicalTerrainArray[i, j]; if (!(terrain != null)) { continue; } num = terrain.terrainData.heightmapResolution; num2 = terrain.terrainData.heightmapResolution; array = new float[num, num2, 4]; for (int k = 0; k < num; k++) { for (int l = 0; l < num2; l++) { Vector3 interpolatedNormal = terrain.terrainData.GetInterpolatedNormal((float)k / (float)num, (float)l / (float)num2); array[k, l, 0] = interpolatedNormal.x * 0.5f + 0.5f; array[k, l, 1] = interpolatedNormal.y * 0.5f + 0.5f; array[k, l, 2] = interpolatedNormal.z * 0.5f + 0.5f; } } GaiaUtils.CompressToMultiChannelFileImage(array, path + "_" + i + "_" + j, TextureFormat.RGBA32, exportPNG: true, exportJPG: false); } } } public void ExportWaterflowMapAsPng(int iterations, string path) { for (int i = 0; i < m_physicalTerrainArray.GetLength(0); i++) { for (int j = 0; j < m_physicalTerrainArray.GetLength(1); j++) { GaiaUtils.CompressToSingleChannelFileImage(m_heightMapTerrainArray[i, j].FlowMap(iterations).Normalise().Heights(), path + "_" + i + "_" + j + ".png", TextureFormat.RGBA32, exportPNG: true, exportJPG: false); } } } public void ExportShorelineMask(string path, float shoreHeightWU, float shoreWidthWU) { Vector3 center = m_worldBoundsTU.center; float shoreHeightNU = shoreHeightWU / m_worldBoundsWUSize.y; Vector3 vector = WUtoTU(new Vector3(shoreWidthWU, shoreWidthWU, shoreWidthWU)); HeightMap heightMap = new HeightMap((int)m_worldBoundsTUSize.z, (int)m_worldBoundsTUSize.x); float num = 0f; for (float num2 = m_worldBoundsTUMin.x; num2 < m_worldBoundsTUMax.x; num2 += 1f) { center.x = num2; float num3 = 0f; for (float num4 = m_worldBoundsTUMin.z; num4 < m_worldBoundsTUMax.z; num4 += 1f) { center.z = num4; MakeMask(center, shoreHeightNU, vector.x, heightMap); num3 += 1f; } num += 1f; } heightMap.Flip(); GaiaUtils.CompressToSingleChannelFileImage(heightMap.Heights(), path, TextureFormat.RGBA32, exportPNG: true, exportJPG: false); } private void MakeMask(Vector3 positionTU, float shoreHeightNU, float maskSizeTU, HeightMap waterMask) { float num = positionTU.x - maskSizeTU; float num2 = positionTU.x + maskSizeTU; float num3 = positionTU.z - maskSizeTU; float num4 = positionTU.z + maskSizeTU; Vector3 center = m_worldBoundsTU.center; for (float num5 = num; num5 < num2; num5 += 1f) { center.x = num5; for (float num6 = num3; num6 < num4; num6 += 1f) { center.z = num6; if (!InBoundsTU(center) || !(GetHeightTU(center) <= shoreHeightNU)) { continue; } float num7 = GaiaCommon1.Utils.Math_Distance(num5, num6, positionTU.x, positionTU.z) / maskSizeTU; if (num7 <= 1f) { num7 = 1f - num7; int x = (int)(num5 + m_TUZeroOffset.x); int z = (int)(num6 + m_TUZeroOffset.z); if (num7 > waterMask[x, z]) { waterMask[x, z] = num7; } } } } } public bool InBoundsWU(Vector3 positionWU) { if (positionWU.x >= m_worldBoundsWUMin.x && positionWU.z >= m_worldBoundsWUMin.z && positionWU.x < m_worldBoundsWUMax.x && positionWU.z < m_worldBoundsWUMax.z) { return true; } return false; } public bool InBoundsTU(Vector3 positionTU) { if (positionTU.x >= m_worldBoundsTUMin.x && positionTU.z >= m_worldBoundsTUMin.z && positionTU.x < m_worldBoundsTUMax.x && positionTU.z < m_worldBoundsTUMax.z) { return true; } return false; } public bool InBoundsNU(Vector3 positionNU) { if (positionNU.x >= m_worldBoundsNUMin.x && positionNU.z >= m_worldBoundsNUMin.z && positionNU.x < m_worldBoundsNUMax.x && positionNU.z < m_worldBoundsNUMax.z) { return true; } return false; } public Vector3 WUtoTU(Vector3 positionWU) { return Vector3.Scale(positionWU, m_WUtoTU); } public Vector3 WUtoNU(Vector3 positionWU) { return Vector3.Scale(positionWU, m_WUtoNU); } public Vector3 WUtoPTI(Vector3 positionWU) { positionWU = WUtoNU(positionWU); NUtoPTI(ref positionWU); return positionWU; } public Vector3 WUtoPTO(Vector3 positionWU) { positionWU = WUtoTU(positionWU); TUtoPTO(ref positionWU); return positionWU; } public Vector3 TUtoWU(Vector3 positionTU) { return Vector3.Scale(positionTU, m_TUtoWU); } public Vector3 TUtoNU(Vector3 positionTU) { return Vector3.Scale(positionTU, m_TUtoNU); } public void TUtoPTI(ref Vector3 positionTU) { positionTU.x = (float)(int)(positionTU.x + m_NUZeroOffset.x) * m_TUtoNU.x; positionTU.y = (float)(int)(positionTU.y + m_NUZeroOffset.y) * m_TUtoNU.y; positionTU.z = (float)(int)(positionTU.z + m_NUZeroOffset.z) * m_TUtoNU.z; } public void TUtoPTO(ref Vector3 positionTU) { positionTU.x = (positionTU.x + m_TUZeroOffset.x) % m_worldBoundsTUSize.x; positionTU.y = (positionTU.y + m_TUZeroOffset.y) % m_worldBoundsTUSize.y; positionTU.z = (positionTU.z + m_TUZeroOffset.z) % m_worldBoundsTUSize.z; } public Vector3 NUtoWU(Vector3 positionNU) { return Vector3.Scale(positionNU, m_NUtoWU); } public Vector3 NUtoTU(Vector3 positionNU) { return Vector3.Scale(positionNU, m_NUtoTU); } public void NUtoPTI(ref Vector3 positionNU) { positionNU.x = Mathf.Floor(positionNU.x + m_NUZeroOffset.x); positionNU.y = Mathf.Floor(positionNU.y + m_NUZeroOffset.y); positionNU.z = Mathf.Floor(positionNU.z + m_NUZeroOffset.z); } public void NUtoPTO(ref Vector3 positionNU) { positionNU.x = (positionNU.x + m_NUZeroOffset.x) % 1f * m_worldBoundsTUSize.x; positionNU.y = (positionNU.y + m_NUZeroOffset.y) % 1f * m_worldBoundsTUSize.y; positionNU.z = (positionNU.z + m_NUZeroOffset.z) % 1f * m_worldBoundsTUSize.z; } public Vector3 Ceil(Vector3 source) { return new Vector3(Mathf.Ceil(source.x), Mathf.Ceil(source.y), Mathf.Ceil(source.z)); } public Vector3 Floor(Vector3 source) { return new Vector3(Mathf.Floor(source.x), Mathf.Floor(source.y), Mathf.Floor(source.z)); } public void Test() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("GaiaWorldManagerTest\n"); stringBuilder.Append($"World Bounds WU : Min {m_worldBoundsWU.min}, Centre {m_worldBoundsWU.center}, Max {m_worldBoundsWU.max}, Size {m_worldBoundsWU.size}\n"); stringBuilder.Append($"World Bounds TU : Min {m_worldBoundsTU.min}, Centre {m_worldBoundsTU.center}, Max {m_worldBoundsTU.max}, Size {m_worldBoundsTU.size}\n"); stringBuilder.Append($"World Bounds NU : Min {m_worldBoundsNU.min}, Centre {m_worldBoundsNU.center}, Max {m_worldBoundsNU.max}, Size {m_worldBoundsNU.size}\n"); stringBuilder.Append("\nBounds Tests:"); Vector3 vector = new Vector3(m_worldBoundsWU.min.x - 1f, m_worldBoundsWU.min.y, m_worldBoundsWU.min.z); stringBuilder.Append($"\nMAX - InBoundsWU({vector}) = {InBoundsWU(vector)}\n"); vector = new Vector3(m_worldBoundsTU.min.x - 1f, m_worldBoundsTU.min.y, m_worldBoundsTU.min.z); stringBuilder.Append($"\nMAX - InBoundsTU({vector}) = {InBoundsTU(vector)}\n"); vector = new Vector3(m_worldBoundsNU.min.x - 0.1f, m_worldBoundsNU.min.y, m_worldBoundsNU.min.z); stringBuilder.Append($"\nMAX - InBoundsNU({vector}) = {InBoundsNU(vector)}\n"); stringBuilder.Append("\nPosition Conversion Tests (MAX):"); vector = new Vector3(m_worldBoundsWU.min.x - 1f, m_worldBoundsWU.center.y, m_worldBoundsWU.max.z + 1f); stringBuilder.Append($"\nInBoundsWU({vector}) = {InBoundsWU(vector)}\n"); stringBuilder.Append($"WUtoTU({vector}) = {WUtoTU(vector).x:0.000}, {WUtoTU(vector).z:0.000}\n"); stringBuilder.Append($"WUtoNU({vector}) = {WUtoNU(vector).x:0.000}, {WUtoNU(vector).z:0.000}\n"); stringBuilder.Append($"WUtoPTI({vector}) = {WUtoPTI(vector).x}, {WUtoPTI(vector).z}\n"); stringBuilder.Append($"WUtoPTO({vector}) = {WUtoPTO(vector).x}, {WUtoPTO(vector).z}\n"); stringBuilder.Append("\nPosition Conversion Tests (MIN, CENTRE, MAX):"); vector = new Vector3(m_worldBoundsWU.min.x, m_worldBoundsWU.center.y, m_worldBoundsWU.max.z); stringBuilder.Append($"\nInBoundsWU({vector}) = {InBoundsWU(vector)}\n"); stringBuilder.Append($"WUtoTU({vector}) = {WUtoTU(vector).x:0.000}, {WUtoTU(vector).z:0.000}\n"); stringBuilder.Append($"WUtoNU({vector}) = {WUtoNU(vector).x:0.000}, {WUtoNU(vector).z:0.000}\n"); stringBuilder.Append($"WUtoPTI({vector}) = {WUtoPTI(vector).x}, {WUtoPTI(vector).z}\n"); stringBuilder.Append($"WUtoPTO({vector}) = {WUtoPTO(vector).x}, {WUtoPTO(vector).z}\n"); vector = WUtoTU(vector); stringBuilder.Append($"\nTUtoWU({vector}) = {TUtoWU(vector)}\n"); stringBuilder.Append($"TUtoNU({vector}) = {TUtoNU(vector)}\n"); vector = TUtoNU(vector); stringBuilder.Append($"\nNUtoWU({vector}) = {NUtoWU(vector)}\n"); stringBuilder.Append($"NUtoTU({vector}) = {NUtoTU(vector)}\n"); stringBuilder.Append("\nTerrain Tests:"); FlattenWorld(); m_boundsCheckErrors = 0uL; TestBlobWU(m_worldBoundsWU.min, 100, 0.25f); TestBlobTU(m_worldBoundsTU.center, 100, 0.5f); TestBlobWU(m_worldBoundsWU.max, 100, 1f); SaveToWorld(); stringBuilder.Append($"Bounds check errors : {m_boundsCheckErrors}"); Debug.Log(stringBuilder.ToString()); } public void TestBlobWU(Vector3 positionWU, int widthWU, float height) { Vector3 vector = WUtoTU(new Vector3(widthWU, widthWU, widthWU)); Vector3 vector2 = WUtoTU(positionWU); for (int i = (int)(vector2.x - vector.x); i < (int)(vector2.x + vector.x); i++) { for (int j = (int)(vector2.z - vector.z); j < (int)(vector2.z + vector.z); j++) { Vector3 positionTU = new Vector3(i, m_worldBoundsTU.center.y, j); SetHeightTU(positionTU, height); } } } public void TestBlobTU(Vector3 positionTU, int widthWU, float height) { Vector3 vector = WUtoTU(new Vector3(widthWU, widthWU, widthWU)); for (int i = (int)(positionTU.x - vector.x); i < (int)(positionTU.x + vector.x); i++) { for (int j = (int)(positionTU.z - vector.z); j < (int)(positionTU.z + vector.z); j++) { Vector3 positionTU2 = new Vector3(i, m_worldBoundsTU.center.y, j); SetHeightTU(positionTU2, height); } } } } }