diff --git a/Runtime/VectorTile.cs b/Runtime/VectorTile.cs index c9785379072fc7c6d1fd3a05ec34fe4fd04166b0..19230f3403042b3a0bf73c3e0d1978388bcb408c 100644 --- a/Runtime/VectorTile.cs +++ b/Runtime/VectorTile.cs @@ -7,33 +7,33 @@ using System.Threading.Tasks; using UnityEngine; using UnityEngine.Networking; -namespace Wander -{ +namespace Wander +{ internal class QuadTreeNode { internal Vector2 min, max; internal QuadTreeNode[] children; internal List<(int, int, int)> triangles; // Layer, Feature, Triangle index. internal int depth; - } - - // Always multiple of 3. Each 3 form a triangle. + } + + // Always multiple of 3. Each 3 form a triangle. public class TriangulatedPolygon { - public List<Vector2> vertices; + public List<Vector2> vertices; public List<Vector2> mins; public List<Vector2> maxs; public List<float> denoms; public void OptimizeForIsPointInTriangle() { - if ( vertices == null ) return; // Non poly layers have this. + if (vertices == null) return; // Non poly layers have this. mins = new List<Vector2>( vertices.Count / 3 ); maxs = new List<Vector2>( vertices.Count / 3 ); denoms = new List<float>( vertices.Count / 3 ); - for ( int i = 0; i < vertices.Count; i += 3 ) + for (int i = 0;i < vertices.Count;i += 3) { float minx = float.MaxValue; float miny = float.MaxValue; @@ -61,8 +61,8 @@ namespace Wander denoms.Add( denominator ); } } - } - + } + public class VectorTile { public bool DownloadStarted => started; @@ -84,7 +84,7 @@ namespace Wander public void StartDownload() { - UnityEngine.Debug.Assert(!started); + UnityEngine.Debug.Assert( !started ); request.SendWebRequest(); started = true; } @@ -122,17 +122,17 @@ namespace Wander // Number of failed polys to triangulate are returned. public int Triangulate() { - if (triangulated ) + if (triangulated) return 0; int numFailedPolys = 0; polygonLayers = new List<List<TriangulatedPolygon>>(); List<double> vertices = new List<double>(); List<int> holeIndices = new List<int>(); - for ( int i =0; i < layers.Count; i++ ) + for (int i = 0;i < layers.Count;i++) { var features = layers[i].VectorTileFeatures; var polygons = new List<TriangulatedPolygon>(); - for (int j = 0; j< features.Count; j++) + for (int j = 0;j< features.Count;j++) { polygons.Add( new TriangulatedPolygon() ); // Add empty to ensure list(layer) of lists(features) match. if (features[j].GeometryType != Tile.GeomType.Polygon) @@ -160,7 +160,7 @@ namespace Wander var indices = EarcutNet.Earcut.Tessellate( vertices, holeIndices ); TriangulatedPolygon poly = new TriangulatedPolygon(); poly.vertices = new List<Vector2>( indices.Count ); - for (int k = 0;k < indices.Count; k++) + for (int k = 0;k < indices.Count;k++) { double x = vertices[indices[k]*2]; double y = vertices[indices[k]*2+1]; @@ -273,7 +273,7 @@ namespace Wander } UnityEngine.Debug.Log( "Optimize for raytracing took " + sw.ElapsedMilliseconds ); } - catch ( Exception e ) + catch (Exception e) { UnityEngine.Debug.LogException( e ); } @@ -304,15 +304,15 @@ namespace Wander layersIdentified = true; } - // Generates a raster for each position (pixel) (256 channels) where each value represents a single channel. - // If no polygon was hit, 255 is encoded. - // If polygon was hit, but no feature was matched, 254 is encoded. + // Generates a raster for each position (pixel). 2 pixels are pushed in 1 (each 16 bit). + // If no polygon was hit, 15 is encoded (reserved). + // If polygon was hit, but no feature was matched, also 15 is encoded. // Returns a list of failed to match pixels. This can be due to geometry not exactly matching or a layer not being found. // Also outputs a remap of layer indices. For instance for a specific tile, only layerIdx 1, 6 and 3 are used. // Then it applies a remapping of 1->0, 6->1, 3->2. So that in the shader only 3 textures are needed starting from 0, 1.. etc. public byte[] RenderToTextureSingle( int resolution, - out Dictionary<byte, byte> remappedLayerIndices, + out Dictionary<byte, byte> remappedLayerIndices, out List<Vector3Int> failedPixels, ref bool cancelToken ) { @@ -323,7 +323,7 @@ namespace Wander UnityEngine.Debug.Assert( layersIdentified, "Identify layers first." ); UnityEngine.Debug.Assert( resolution > 1, "Must be at least 2." ); - byte [] texture = new byte[resolution*resolution]; + byte [] texture = new byte[resolution*resolution/2]; failedPixels = new List<Vector3Int>(); remappedLayerIndices = new Dictionary<byte, byte>(); float oneOverRes = 1.0f / resolution; @@ -336,7 +336,7 @@ namespace Wander // For each pixel. for (int y = 0;y < resolution && !cancelToken;y++) { - for (int x = 0;x < resolution && !cancelToken ;x++) + for (int x = 0;x < resolution && !cancelToken;x++) { bool hit = false; Vector2 p = new Vector2(fx*x+0.5f, fx*y+0.5f); @@ -351,16 +351,16 @@ namespace Wander continue; } - if ( node.children != null ) + if (node.children != null) { - for ( int c = 0; c < node.children.Length; c++ ) + for (int c = 0;c < node.children.Length;c++) { stack.Add( node.children[c] ); } } else if (node.triangles != null) // Is leaf { - for( int t = 0; t < node.triangles.Count; t++ ) + for (int t = 0;t < node.triangles.Count;t++) { int ti = (cachedTriIdx + t) % node.triangles.Count; // Continue searching where we left off int l = node.triangles[ti].Item1; @@ -375,9 +375,13 @@ namespace Wander hit = GeomUtil.PointIsInsideTriangle2( p, vertices[t2*3], vertices[t2*3+1], vertices[t2*3+2], denoms[t2] ); if (hit) { - cachedTriIdx = ti; + cachedTriIdx = ti; var layerIdx = (byte)layers[l].VectorTileFeatures[f].SelectedLayerIdx; - texture[(resolution - y -1)*resolution+x] = layerIdx; + //var addr2 = y*resolution+x; + var addr2 = (resolution - y -1)*resolution+x; + if (layerIdx > 15) layerIdx = 15; // max layers 15 (15 is reserved, nothing was hit). + var shift = (addr2&1) << 2; + texture[addr2>>1] |= (byte)(layerIdx << shift); break; } } @@ -390,8 +394,10 @@ namespace Wander stack.Clear(); if (!hit) { - texture[(resolution - y -1)*resolution+x] = 255; - failedPixels.Add( new Vector3Int( x, y, 255 ) ); + var addr2 = (resolution - y -1)*resolution+x; + var shift = (addr2&1) << 2; + texture[addr2>>1] |= (byte)(15 << shift); + failedPixels.Add( new Vector3Int( x, y, 15 ) ); } else { @@ -402,47 +408,61 @@ namespace Wander // Determine number of different layers. For instance, if only layer 3, 8, 14 and 15 are used, we select // a material that only uses 4 textures and put 3 -> Albedo_0 -> 8 to Albedo_1, etc. in the shader. - byte cachedPixel = 255; + byte cachedPixel = 15; int remappedIndexCounter = -1; - byte remappedPixel = 255; - int addr = 0; + byte remappedPixel = 15; + uint addr = 0; + byte pixel = 0; for (int y = 0;y < resolution && !cancelToken;y++) { - for (int x = 0;x < resolution;x++) + for (int x = 0;x < resolution && !cancelToken;x++) { - byte pixel = texture[addr]; - if (pixel < 254) + if ((x&1) == 0) + pixel = texture[addr>>1]; + byte shift = (byte)((addr&1)); + byte pixel2 = (byte)((pixel >> (shift<<2)) & 15); + if (pixel2 != 15) { - if (pixel != cachedPixel) + if (pixel2 != cachedPixel) { - if (!remappedLayerIndices.TryGetValue( pixel, out remappedPixel )) + if (!remappedLayerIndices.TryGetValue( pixel2, out remappedPixel )) { - if (remappedIndexCounter < 253) // 254 is reserved for no layer match, 255 is ray hit with triangle. + if (remappedIndexCounter < 13) // 15 is reserved for not found, so 0-14 are valid. { remappedIndexCounter++; remappedPixel = (byte)remappedIndexCounter; - remappedLayerIndices.Add( pixel, remappedPixel ); + remappedLayerIndices.Add( pixel2, remappedPixel ); } - else remappedPixel = 0; + else remappedPixel = 15; // Reserved for invalid. } - cachedPixel = pixel; + cachedPixel = pixel2; } - texture[addr] = remappedPixel; - } // leave unfound or unhit pixels on their original value. + UnityEngine.Debug.Assert( remappedPixel < 15 ); + } + else + { + remappedPixel = 15; + cachedPixel = 15; + } + // Data in texture is not zero anymore, so must check if is even, then just set, otherwise OR-add it. + if (shift==0) + texture[addr>>1] = remappedPixel; + else + texture[addr>>1] |= (byte)(remappedPixel << 4); addr++; } } - UnityEngine.Debug.Assert(remappedIndexCounter <= 254, "Exceeded layer count." ); + UnityEngine.Debug.Assert( remappedIndexCounter <= 15, "Exceeded layer count." ); // Invalid pixel is not added to map, so max must be 15, not 16. UnityEngine.Debug.Log( "Render to texture took " + sw.ElapsedMilliseconds ); return texture; } - } - - public static class VectorTileLoader + } + + public static class VectorTileLoader { - public static VectorTile LoadFromUrl( string url, bool autoStart=true ) + public static VectorTile LoadFromUrl( string url, bool autoStart = true ) { VectorTile tile = new VectorTile(); tile.request = UnityWebRequest.Get( url ); @@ -452,5 +472,5 @@ namespace Wander } return tile; } - } + } } \ No newline at end of file