diff --git a/Runtime/Mapbox/BaseModule/Data/DataFetchers/TerrainData.cs b/Runtime/Mapbox/BaseModule/Data/DataFetchers/TerrainData.cs index e97dec27d..8438974d4 100644 --- a/Runtime/Mapbox/BaseModule/Data/DataFetchers/TerrainData.cs +++ b/Runtime/Mapbox/BaseModule/Data/DataFetchers/TerrainData.cs @@ -61,10 +61,29 @@ private float ReadElevation(float x, float y, Vector4 terrainTextureScaleOffset) } else { - return ElevationValues[(int) yy * width + (int) xx]; + // Bilinear interpolation + int x1 = (int) xx; + int y1 = (int) yy; + int x2 = x1 + 1; + int y2 = y1 + 1; + if (x2 >= width) + { + x2 = x1; + } + if (y2 >= width) + { + y2 = y1; + } + float x2y1 = ElevationValues[y1 * width + x2]; + float x1y2 = ElevationValues[y2 * width + x1]; + float x2y2 = ElevationValues[y2 * width + x2]; + float x1y1 = ElevationValues[y1 * width + x1]; + float x1y = Mathf.Lerp(x1y1, x2y1, xx - x1); + float x2y = Mathf.Lerp(x1y2, x2y2, xx - x1); + return Mathf.Lerp(x1y, x2y, yy - y1); } } } -} \ No newline at end of file +} diff --git a/Runtime/Mapbox/BaseModule/Data/Platform/Cache/TypeMemoryCache.cs b/Runtime/Mapbox/BaseModule/Data/Platform/Cache/TypeMemoryCache.cs index 20eb0c928..8f687e722 100644 --- a/Runtime/Mapbox/BaseModule/Data/Platform/Cache/TypeMemoryCache.cs +++ b/Runtime/Mapbox/BaseModule/Data/Platform/Cache/TypeMemoryCache.cs @@ -32,6 +32,11 @@ public TypeMemoryCache(int cacheSize = 100) public void Add(T data) { + if (_cacheHash == null) + { + Debug.LogWarning("Function called after TypeMemoryCache destroyed"); + return; + } if (_cacheHash.TryGetValue(data.TileId, out var node)) { _cache.Remove(node); @@ -78,6 +83,11 @@ private void DropItem(LinkedListNode node) public bool Exists(CanonicalTileId tileId) { + if (_cacheHash == null) + { + Debug.LogWarning("Function called after TypeMemoryCache destroyed"); + return false; + } return _cacheHash.ContainsKey(tileId) || _fallbackDatas.ContainsKey(tileId); //return _datas.ContainsKey(tileId) || _fallbackDatas.ContainsKey(tileId); } @@ -85,6 +95,12 @@ public bool Exists(CanonicalTileId tileId) public bool Get(CanonicalTileId tileId, out T outData) { outData = null; + if (_cacheHash == null) + { + Debug.LogWarning("Function called after TypeMemoryCache destroyed"); + return false; + } + if (_cacheHash.TryGetValue(tileId, out var linkedNode)) { outData = linkedNode.Value; @@ -108,11 +124,21 @@ public bool Get(CanonicalTileId tileId, out T outData) public IEnumerable GetAllDatas() { + if (_cacheHash == null) + { + Debug.LogWarning("Function called after TypeMemoryCache destroyed"); + return new List(); + } return _cacheHash.Values.Select(x => x.Value); } public void Remove(CanonicalTileId tileId) { + if (_cacheHash == null) + { + Debug.LogWarning("Function called after TypeMemoryCache destroyed"); + return; + } if (_cacheHash.TryGetValue(tileId, out var linkedListNode)) { DropItem(linkedListNode); @@ -127,6 +153,11 @@ public void RetainTiles(HashSet retainedTiles) public void MarkFallback(CanonicalTileId dataTileId) { + if (_cacheHash == null) + { + Debug.LogWarning("Function called after TypeMemoryCache destroyed"); + return; + } if (_cacheHash.TryGetValue(dataTileId, out var data)) { _cacheHash.Remove(dataTileId); @@ -160,4 +191,4 @@ public interface ITypeCache { void OnDestroy(); } -} \ No newline at end of file +} diff --git a/Runtime/Mapbox/BaseModule/Data/TerrainStrategies/ElevatedTerrainStrategy.cs b/Runtime/Mapbox/BaseModule/Data/TerrainStrategies/ElevatedTerrainStrategy.cs index be1a50d58..98130f802 100644 --- a/Runtime/Mapbox/BaseModule/Data/TerrainStrategies/ElevatedTerrainStrategy.cs +++ b/Runtime/Mapbox/BaseModule/Data/TerrainStrategies/ElevatedTerrainStrategy.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Mapbox.BaseModule.Data.Tiles; using Mapbox.BaseModule.Map; using Mapbox.BaseModule.Unity; using Mapbox.BaseModule.Utilities; @@ -85,6 +86,7 @@ public override void RegisterTile(UnityMapTile tile, bool createElevatedMesh) sharedMesh.SetTriangles(triangle, i); } sharedMesh.uv = newMesh.Uvs; + tile.TerrainContainer.FixMeshBounds(!createElevatedMesh); } // if (_elevationOptions.sideWallOptions.isActive) @@ -289,6 +291,112 @@ private MeshDataArray CreateBaseMeshSkirts(float size, int sideVertexCount) //mesh.Triangles.Add(_newTriangleList.ToArray()); return mesh; } + + + /// + /// Checkes all neighbours of the given tile and stitches the edges to achieve a smooth mesh surface. + /// + /// UnityMapTile of the tile being processed. + /// All tiles, including this tile, used to find neighbors + /// + public void FixStitches(UnityMapTile tile, Dictionary allTiles) + { + UnwrappedTileId tileId = tile.UnwrappedTileId; + int sampleCount = _useTileSkirts + ? _elevationOptions.modificationOptions.sampleCount + 3 + : _elevationOptions.modificationOptions.sampleCount + 1; + + Vector3[] targetVerts; + Vector3[] verts = tile.MeshFilter.sharedMesh.vertices; + var meshVertCount = verts.Length; + if (allTiles.ContainsKey(tileId.North) && allTiles[tileId.North].TerrainContainer.State == TileContainerState.Final) + { + targetVerts = allTiles[tileId.North].MeshFilter.sharedMesh.vertices; + + for (int i = 0; i < sampleCount; i++) + { + int source_i = meshVertCount - sampleCount + i; + int target_i = i; + verts[source_i] = new Vector3(verts[source_i].x, targetVerts[target_i].y, verts[source_i].z); + } + allTiles[tileId.North].MeshFilter.sharedMesh.vertices = targetVerts; + } + + if (allTiles.ContainsKey(tileId.South) && allTiles[tileId.South].TerrainContainer.State == TileContainerState.Final) + { + targetVerts = allTiles[tileId.South].MeshFilter.sharedMesh.vertices; + + for (int i = 0; i < sampleCount; i++) + { + int source_i = i; + int target_i = meshVertCount - sampleCount + i; + verts[source_i] = new Vector3(verts[source_i].x, targetVerts[target_i].y, verts[source_i].z); + } + } + + if (allTiles.ContainsKey(tileId.West) && allTiles[tileId.West].TerrainContainer.State == TileContainerState.Final) + { + targetVerts = allTiles[tileId.West].MeshFilter.sharedMesh.vertices; + + for (int i = 0; i < sampleCount; i++) + { + int target_i = i * sampleCount + sampleCount - 1; + int source_i = i * sampleCount; + verts[source_i] = new Vector3(verts[source_i].x, targetVerts[target_i].y, verts[source_i].z); + } + } + + if (allTiles.ContainsKey(tileId.East) && allTiles[tileId.East].TerrainContainer.State == TileContainerState.Final) + { + targetVerts = allTiles[tileId.East].MeshFilter.sharedMesh.vertices; + + for (int i = 0; i < sampleCount; i++) + { + int target_i = i * sampleCount; + int source_i = i * sampleCount + sampleCount - 1; + verts[source_i] = new Vector3(verts[source_i].x, targetVerts[target_i].y, verts[source_i].z); + } + } + + if (allTiles.ContainsKey(tileId.NorthWest) && allTiles[tileId.NorthWest].TerrainContainer.State == TileContainerState.Final) + { + targetVerts = allTiles[tileId.NorthWest].MeshFilter.sharedMesh.vertices; + + int source_i = meshVertCount - sampleCount; + int target_i = sampleCount - 1; + verts[source_i] = new Vector3(verts[source_i].x, targetVerts[target_i].y, verts[source_i].z); + } + + if (allTiles.ContainsKey(tileId.NorthEast) && allTiles[tileId.NorthEast].TerrainContainer.State == TileContainerState.Final) + { + targetVerts = allTiles[tileId.NorthEast].MeshFilter.sharedMesh.vertices; + + int source_i = meshVertCount - 1; + int target_i = 0; + verts[source_i] = new Vector3(verts[source_i].x, targetVerts[target_i].y, verts[source_i].z); + } + + if (allTiles.ContainsKey(tileId.SouthWest) && allTiles[tileId.SouthWest].TerrainContainer.State == TileContainerState.Final) + { + targetVerts = allTiles[tileId.SouthWest].MeshFilter.sharedMesh.vertices; + + int source_i = 0; + int target_i = meshVertCount - 1; + verts[source_i] = new Vector3(verts[source_i].x, targetVerts[target_i].y, verts[source_i].z); + } + + if (allTiles.ContainsKey(tileId.SouthEast) && allTiles[tileId.SouthEast].TerrainContainer.State == TileContainerState.Final) + { + targetVerts = allTiles[tileId.SouthEast].MeshFilter.sharedMesh.vertices; + int source_i = sampleCount - 1; + int target_i = meshVertCount - sampleCount; + verts[source_i] = new Vector3(verts[source_i].x, targetVerts[target_i].y, verts[source_i].z); + } + + tile.MeshFilter.sharedMesh.vertices = verts; + tile.MeshFilter.sharedMesh.RecalculateNormals(); + } + #endregion } } diff --git a/Runtime/Mapbox/BaseModule/Unity/UnityTileTerrainContainer.cs b/Runtime/Mapbox/BaseModule/Unity/UnityTileTerrainContainer.cs index f103ba777..2a55e02d7 100644 --- a/Runtime/Mapbox/BaseModule/Unity/UnityTileTerrainContainer.cs +++ b/Runtime/Mapbox/BaseModule/Unity/UnityTileTerrainContainer.cs @@ -1,5 +1,4 @@ using System; -using Mapbox.BaseModule.Data.DataFetchers; using Mapbox.BaseModule.Data.Tiles; using UnityEngine; using TerrainData = Mapbox.BaseModule.Data.DataFetchers.TerrainData; @@ -43,6 +42,43 @@ public void SetTerrainData(TerrainData terrainData, bool useShaderElevation, Til TerrainData.ElevationValuesUpdated += OnElevationValuesUpdated; _unityMapTile.Material.SetFloat(_elevationMultiplierFieldNameID, useShaderElevation ? 1 : 0); + FixMeshBounds(useShaderElevation); + } + + public void FixMeshBounds(bool useShaderElevation) + { + Mesh mesh = _unityMapTile.MeshFilter.mesh; + if (mesh != null && mesh.vertexCount != 0) + { + mesh.RecalculateBounds(); + if (useShaderElevation) + { + mesh.bounds = GetBoundsAdjustedForElevation(); + } + } + } + + private Bounds GetBoundsAdjustedForElevation() + { + Mesh mesh = _unityMapTile.MeshFilter.mesh; + if (TerrainData == null || TerrainData.ElevationValues == null) + { + return mesh.bounds; + } + + float maxY = float.MinValue; + float minY = float.MaxValue; + foreach (float t in TerrainData.ElevationValues) + { + float elevationScaled = t * _unityMapTile.TileScale; + maxY = Mathf.Max(maxY, elevationScaled); + minY = Mathf.Min(minY, elevationScaled); + } + Vector3 center = mesh.bounds.center; + center.y = (maxY + minY) / 2; + Vector3 size = mesh.bounds.size; + size.y = maxY - minY; + return new Bounds(center, size); } public void OnTerrainUpdated() @@ -87,24 +123,7 @@ public float QueryHeightData(float x, float y) { if (TerrainData != null && TerrainData.ElevationValues.Length > 0) { - var width = (int)Mathf.Sqrt(TerrainData.ElevationValues.Length); - var sectionWidth = width * _terrainTextureScaleOffset.x - 1; - var padding = width * new Vector2(_terrainTextureScaleOffset.z, _terrainTextureScaleOffset.w); - - var xx = padding.x + (x * sectionWidth); - var yy = padding.y + (y * sectionWidth); - - var index = (int) yy * width - + (int) xx; - if (TerrainData.ElevationValues.Length <= index) - { - return 0; - } - else - { - return TerrainData.ElevationValues[(int) yy * width + (int) xx]; - } - + return TerrainData.QueryHeightData(_unityMapTile.CanonicalTileId, x, y); } return 0; } @@ -130,4 +149,4 @@ public enum TileContainerState Temporary, Final } -} \ No newline at end of file +} diff --git a/Runtime/Mapbox/GeocodingApi/Geocoder.cs b/Runtime/Mapbox/GeocodingApi/Geocoder.cs index 4eb728e1c..5d614b145 100644 --- a/Runtime/Mapbox/GeocodingApi/Geocoder.cs +++ b/Runtime/Mapbox/GeocodingApi/Geocoder.cs @@ -43,6 +43,11 @@ public IAsyncRequest Geocode(GeocodeResource geocode, Action { + if (response.Data == null) + { + callback(null); + return; + } var str = Encoding.UTF8.GetString(response.Data); var data = Deserialize(str); diff --git a/Runtime/Mapbox/ImageModule/Terrain/TerrainLayerModule.cs b/Runtime/Mapbox/ImageModule/Terrain/TerrainLayerModule.cs index 5defbb211..896a08fff 100644 --- a/Runtime/Mapbox/ImageModule/Terrain/TerrainLayerModule.cs +++ b/Runtime/Mapbox/ImageModule/Terrain/TerrainLayerModule.cs @@ -21,12 +21,12 @@ public class TerrainLayerModule : ITerrainLayerModule private TerrainStrategy _terrainStrategy; //Terrain module doesn't support cpu elevation now after TileCreator changes - public TerrainLayerModule(Source source, TerrainLayerModuleSettings settings) : base() + public TerrainLayerModule(Source source, TerrainLayerModuleSettings settings, TerrainStrategy terrainStrategy) : base() { _settings = settings; _retainedTerrainTiles = new HashSet(); _rasterSource = source; - _terrainStrategy = new ElevatedTerrainStrategy(); + _terrainStrategy = terrainStrategy; } public virtual IEnumerator Initialize() diff --git a/Runtime/Mapbox/ImageModule/Unity/TerrainLayerModuleScript.cs b/Runtime/Mapbox/ImageModule/Unity/TerrainLayerModuleScript.cs index a5d8ef6d1..e84c3e0e8 100644 --- a/Runtime/Mapbox/ImageModule/Unity/TerrainLayerModuleScript.cs +++ b/Runtime/Mapbox/ImageModule/Unity/TerrainLayerModuleScript.cs @@ -43,7 +43,8 @@ public override ILayerModule ConstructModule(MapService service, IMapInformation var module = new TerrainLayerModule( service.GetTerrainRasterSource(Settings.DataSettings), - Settings); + Settings, + new ElevatedTerrainStrategy()); mapInformation.QueryElevation = module.QueryElevation; ModuleImplementation = module; diff --git a/Runtime/Mapbox/VectorModule/VectorLayerVisualizer.cs b/Runtime/Mapbox/VectorModule/VectorLayerVisualizer.cs index aee393414..9c3bbb457 100644 --- a/Runtime/Mapbox/VectorModule/VectorLayerVisualizer.cs +++ b/Runtime/Mapbox/VectorModule/VectorLayerVisualizer.cs @@ -219,6 +219,7 @@ protected List GameObjectModifications(CanonicalTileId canonicalTile if(!_results.ContainsKey(canonicalTileId)) _results.Add(canonicalTileId, new List()); _results[canonicalTileId].Add(entity); + entity.Mesh.RecalculateBounds(); OnVectorMeshCreated(entity.GameObject); } } @@ -312,4 +313,4 @@ protected MeshData CombineMeshData(HashSet meshDataList) public Action OnVectorMeshCreated = list => { }; public Action OnVectorMeshDestroyed = go => { }; } -} \ No newline at end of file +}