diff --git a/Runtime/VectorTile.cs b/Runtime/VectorTile.cs index b28dae785c9d688fb4fce27d115ebff655bef7a3..35f49bc137f77a3f0ae08ccb64491857dc9b209c 100644 --- a/Runtime/VectorTile.cs +++ b/Runtime/VectorTile.cs @@ -67,6 +67,7 @@ namespace Wander private bool valid; private bool finished; private bool triangulated; + private bool layersIdentified; private Task parseTileTask; private List<VectorTileLayer> layers; private List<List<TriangulatedPolygon>> polygonLayers; @@ -178,94 +179,68 @@ namespace Wander polygonLayers[i][j].OptimizeForIsPointInTriangle(); } - // Calls callback(x, y, channel) for each raster position (256 channels) where each value represents a single channel. - // If no polygon was hit, 255 is called. - // If polygon was hit, but no feature was matched, 254 is called. - // Returns a list of failed to match pixels. This can be due to geometry not exactly matching or a layer not being found. - public List<Vector3Int> RenderToTextureSingle( - int width, int height, - List<string> matchingAttribKeys, - List<List<string>> layerNamesList, - Func<int, int, byte, bool> callback ) + // Identify feature by specifying a (unique) index based on some criteria. This can very per + // vector tile provider. + public void IdentifyLayers( Func<VectorTileLayer, List<KeyValuePair<string, object>>, int> selectionCallback ) { Debug.Assert( triangulated, "First call Triangulate." ); - Debug.Assert( layerNamesList.Count < 254, "254 and 255 are reserved for no hit or no match." ); - // First match layers to layer names. - bool cancel = false; - List<Vector3Int> failedPixels = new List<Vector3Int>(); - for (int l = 0;l < layers.Count && !cancel;l++) + for (int l = 0;l < layers.Count;l++) { var layer = layers[l]; - for (int f = 0;f < layer.VectorTileFeatures.Count && !cancel;f++) + for (int f = 0;f < layer.VectorTileFeatures.Count;f++) { var feature = layer.VectorTileFeatures[f]; feature.SelectedLayerIdx = 254; - if (feature.GeometryType != Tile.GeomType.Polygon) - continue; - - if (feature.Attributes == null) - continue; - - // Find matching layer. - bool matchingLayerFound = false; - for (int a = 0;a < feature.Attributes.Count && !matchingLayerFound;a++) + int uniqueId = selectionCallback( layer, feature.Attributes ); + if (uniqueId > -1) { - for (int m = 0;m < matchingAttribKeys.Count && !matchingLayerFound;m++) + if ( uniqueId == 8 ) { - if (feature.Attributes[a].Key != matchingAttribKeys[m]) - continue; - - string function = feature.Attributes[a].Value as string; - for (int layerIdx = 0;layerIdx < layerNamesList.Count && !matchingLayerFound;layerIdx++) - { - for (int layerNameIdx = 0;layerNameIdx < layerNamesList[layerIdx].Count;layerNameIdx++) - { - if (function.Contains( layerNamesList[layerIdx][layerNameIdx] )) - { - feature.SelectedLayerIdx = layerIdx; - matchingLayerFound = true; - break; - } - } - } - } - } - - // try Layer.name - if (!matchingLayerFound) - { - for (int layerIdx = 0;layerIdx < layerNamesList.Count && !matchingLayerFound;layerIdx++) - { - for (int layerNameIdx = 0;layerNameIdx < layerNamesList[layerIdx].Count;layerNameIdx++) - { - if (layer.Name.Contains( layerNamesList[layerIdx][layerNameIdx] )) - { - feature.SelectedLayerIdx = layerIdx; - matchingLayerFound = true; - break; - } - } + int jt = 0; } + feature.SelectedLayerIdx = uniqueId; + break; // Done with this feature. } } } - Debug.Assert( width == height, "Must be square images." ); + layersIdentified = true; + } - // For each layer, for each pixel, now check triangle intersections. - for (int l = 0;l < layers.Count && !cancel;l++) + // Calls callback(x, y, channel) for each raster position (256 channels) where each value represents a single channel. + // If no polygon was hit, 255 is called. + // If polygon was hit, but no feature was matched, 254 is called. + // Returns a list of failed to match pixels. This can be due to geometry not exactly matching or a layer not being found. + // Also out puts a remap of layer indices. For instance for a specific tile, only layerIdx 1, 6 and 3 are used. + // Then this 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 List<Vector3Int> failedPixels, + bool? cancelToken) + { + Debug.Assert( triangulated, "First call Triangulate." ); + Debug.Assert( layersIdentified, "Identify layers first." ); + Debug.Assert( resolution > 1, "Must be at least 2." ); + + byte [] texture = new byte[resolution*resolution]; + failedPixels = new List<Vector3Int>(); + remappedLayerIndices = new Dictionary<byte, byte>(); + byte cachedPixel = 0; + + // For each layer, for each pixel, check triangle intersections. + for (int l = 0;l < layers.Count && !cancelToken.Value;l++) { var layer = layers[l]; - float fx = (float)layer.Extent / width; + float fx = (float)layer.Extent / resolution; TriangulatedPolygon cachedPoly = default; int cachedVtxIdx = -1; int cachedTriIdx = -1; - byte cachedPixel = 0; - for (int y = 0;y < height && !cancel;y++) + for (int y = 0;y < resolution && !cancelToken.Value;y++) { - for (int x = 0;x < width && !cancel;x++) + for (int x = 0; x < resolution; x++) { Vector2 p = new Vector2(fx*x+0.5f*fx, fx*y+0.5f*fx); bool hit = false; @@ -283,7 +258,7 @@ namespace Wander hit = GeomUtil.PointIsInsideTriangle2( p, vertices[cachedVtxIdx], vertices[cachedVtxIdx+1], vertices[cachedVtxIdx+2], denoms[cachedTriIdx] ); if (hit) { - cancel = callback( x, y, cachedPixel ); // If SelectedLayerIdx did not match, it was set to 254. + texture[(resolution - y -1)*resolution+x] = cachedPixel; continue; // Pass from cache, continue to next pixel. } } @@ -317,7 +292,11 @@ namespace Wander cachedTriIdx = triIdx; cachedVtxIdx = vIdx; cachedPixel = (byte)feature.SelectedLayerIdx; - cancel = callback( x, y, cachedPixel ); + if ( cachedPixel == 8 ) + { + int jt = 0; + } + texture[(resolution - y -1)*resolution+x] = cachedPixel; break; } } @@ -327,13 +306,49 @@ namespace Wander if ( !hit ) { + // texture[(resolution - y -1)*resolution+x] = 255; failedPixels.Add( new Vector3Int( x, y, 255 ) ); } } } } - return failedPixels; + // 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. + cachedPixel = 255; + int remappedIndexCounter = -1; + byte remappedPixel = 255; + int addr = 0; + for (int y = 0;y < resolution && !cancelToken.Value;y++) + { + for (int x = 0;x < resolution;x++) + { + byte pixel = texture[addr]; + if (pixel == 8 ) + { + int jt = 0; + } + if ( pixel != cachedPixel && pixel != 254 /*used for not matched layer*/ ) + { + if (!remappedLayerIndices.TryGetValue( pixel, out remappedPixel )) + { + if (remappedIndexCounter < 253) // 254 is reserved for no layer match, 255 is ray hit with triangle. + { + remappedIndexCounter++; + remappedPixel = (byte)remappedIndexCounter; + remappedLayerIndices.Add( pixel, remappedPixel ); + } + else remappedPixel = 0; + } + cachedPixel = pixel; + } + texture[addr] = remappedPixel; + addr++; + } + } + + Debug.Assert(remappedIndexCounter <= 254, "Exceeded layer count." ); + return texture; } }