From 4db425fc36ef2e2eaa480e24ee4472a7b19c864c Mon Sep 17 00:00:00 2001 From: bartjuhhh <bart.knuiman@wur.nl> Date: Wed, 8 Nov 2023 17:56:28 +0100 Subject: [PATCH] Can now rasterize instead of raytrace -> slightly faster. --- Runtime/VectorTile.cs | 196 +++++++++++++++++++++++++++++++++++------- 1 file changed, 165 insertions(+), 31 deletions(-) diff --git a/Runtime/VectorTile.cs b/Runtime/VectorTile.cs index 8ac514e..37c640b 100644 --- a/Runtime/VectorTile.cs +++ b/Runtime/VectorTile.cs @@ -1,8 +1,10 @@ +using GluonGui.WorkspaceWindow.Views.WorkspaceExplorer.Configuration; using Mapbox.Vector.Tile; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using UnityEngine; using UnityEngine.Networking; @@ -25,13 +27,17 @@ namespace Wander public List<Vector2> maxs; public List<float> denoms; - public void OptimizeForIsPointInTriangle() + public void OptimizeForIsPointInTriangle(bool calcDenoms) { 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 ); + + if (calcDenoms) + { + denoms = new List<float>( vertices.Count / 3 ); + } for (int i = 0;i < vertices.Count;i += 3) { @@ -55,10 +61,13 @@ namespace Wander if (vertices[i+2].y > maxy) maxy = vertices[i+2].y; maxs.Add( new Vector2( maxx, maxy ) ); - // float denominator = ((vertex2.y - vertex3.y) * (vertex1.x - vertex3.x) + (vertex3.x - vertex2.x) * (vertex1.y - vertex3.y)); - float denominator = ((vertices[i+1].y - vertices[i+2].y) * (vertices[i].x - vertices[i+2].x) + (vertices[i+2].x - vertices[i+1].x) * (vertices[i].y - vertices[i+2].y)); - denominator = 1.0f / denominator; - denoms.Add( denominator ); + if (calcDenoms) + { + // float denominator = ((vertex2.y - vertex3.y) * (vertex1.x - vertex3.x) + (vertex3.x - vertex2.x) * (vertex1.y - vertex3.y)); + float denominator = ((vertices[i+1].y - vertices[i+2].y) * (vertices[i].x - vertices[i+2].x) + (vertices[i+2].x - vertices[i+1].x) * (vertices[i].y - vertices[i+2].y)); + denominator = 1.0f / denominator; + denoms.Add( denominator ); + } } } } @@ -184,19 +193,44 @@ namespace Wander return numFailedPolys; } - public void OptimizeForPointIsInsideTriangle() + bool DoesPolygonLayerQualify( int layerIdx, int featureIdx ) + { + var layer = layers[layerIdx]; + var feature = layer.VectorTileFeatures[featureIdx]; + + if (feature.GeometryType != Tile.GeomType.Polygon) + return false; + + var polygons = polygonLayers[layerIdx][featureIdx]; + if (polygons.vertices.Count == 0) + return false; + + if (feature.SelectedLayerIdx == 254) + return false; + + if (feature.RelativeHeight != 0) + return false; + + return true; + } + + public void OptimizeForPointIsInsideTriangle(bool calcDenoms) { + for (int i = 0;i < polygonLayers.Count;i++) + { + for (int j = 0;j < polygonLayers[i].Count;j++) + { + polygonLayers[i][j].OptimizeForIsPointInTriangle( true ); + } + } + } + + public void GenerateQuadTreeForRaytrace() + { try { Stopwatch sw = new Stopwatch(); sw.Restart(); - for (int i = 0;i < polygonLayers.Count;i++) - { - for (int j = 0;j < polygonLayers[i].Count;j++) - { - polygonLayers[i][j].OptimizeForIsPointInTriangle(); - } - } // Generate quadtree List<QuadTreeNode> stack = new List<QuadTreeNode>(); @@ -209,19 +243,7 @@ namespace Wander var layer = layers[l]; for (int f = 0;f < layer.VectorTileFeatures.Count;f++) { - var feature = layer.VectorTileFeatures[f]; - - if (feature.GeometryType != Tile.GeomType.Polygon) - continue; - - var polygons = polygonLayers[l][f]; - if (polygons.vertices.Count == 0) - continue; - - if (feature.SelectedLayerIdx == 254) - continue; - - if (feature.RelativeHeight != 0) + if (!DoesPolygonLayerQualify( l, f )) continue; var poly = polygonLayers[l][f]; @@ -283,6 +305,27 @@ namespace Wander } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + float sign( Vector2 p1, Vector2 p2, Vector2 p3 ) + { + return (p1.x - p3.x) * (p2.y - p3.y) - (p2.x - p3.x) * (p1.y - p3.y); + } + + public bool PointInTriangle( Vector2 pt, Vector2 v1, Vector2 v2, Vector2 v3 ) + { + float d1, d2, d3; + bool has_neg, has_pos; + + d1 = sign( pt, v1, v2 ); + d2 = sign( pt, v2, v3 ); + d3 = sign( pt, v3, v1 ); + + has_neg = (d1 < 0) || (d2 < 0) || (d3 < 0); + has_pos = (d1 > 0) || (d2 > 0) || (d3 > 0); + + return !(has_neg && has_pos); + } + // 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, @@ -312,7 +355,98 @@ namespace Wander // 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. - public byte[] RenderToTextureSingle( + public byte[] RasterizeToTexture( + int resolution, + out List<Vector3Int> failedPixels, + ref bool cancelToken ) + { + Stopwatch sw = new Stopwatch(); + sw.Restart(); + + failedPixels = new List<Vector3Int>(); + + UnityEngine.Debug.Assert( 4096 % resolution == 0 ); + UnityEngine.Debug.Assert( triangulated, "First call Triangulate." ); + UnityEngine.Debug.Assert( layersIdentified, "Identify layers first." ); + UnityEngine.Debug.Assert( resolution > 1, "Must be at least 2." ); + + byte [] texture = new byte[resolution*resolution/2]; + int d = 4096 / resolution; + int shift = -1; + while (d!=0) + { + shift++; + d>>=1; + } + + for (int l = 0; l < polygonLayers.Count;l++) + { + var layer = layers[l]; + for (int f = 0;f < layer.VectorTileFeatures.Count;f++) + { + if (DoesPolygonLayerQualify( l, f )) + { + var poly = polygonLayers[l][f]; + var vertices = poly.vertices; + var layerIdx = (byte)layers[l].VectorTileFeatures[f].SelectedLayerIdx; + for (int i = 0, t = 0;i < vertices.Count;i += 3, t++) + { + var min = poly.mins[t]; + var max = poly.maxs[t]; + int x1 = Mathf.FloorToInt( min.x ); + int y1 = Mathf.FloorToInt( min.y ); + int x2 = Mathf.FloorToInt( max.x ); + int y2 = Mathf.FloorToInt( max.y ); + + x1 >>= shift; + y1 >>= shift; + x2 >>= shift; + y2 >>= shift; + + if (x1 < 0) x1 = 0; + if (x1 >= resolution) x1 = resolution-1; + + if (x2 < 0) x2 = 0; + if (x2 >= resolution) x2 = resolution-1; + + if (y1 < 0) y1 = 0; + if (y1 >= resolution) y1 = resolution-1; + + if (y2 < 0) y2 = 0; + if (y2 >= resolution) y2 = resolution-1; + + for (int y = y1;y <= y2;y++) // texture space + { + for (int x = x1;x <= x2 && !cancelToken; x++) + { + int xk2 = x<<shift; // vector tile space + int yk2 = y<<shift; + + if (PointInTriangle( new Vector2( xk2, yk2 ), poly.vertices[i], poly.vertices[i+1], poly.vertices[i+2] )) + { + var addr2 = (resolution - y -1)*resolution+x; + //var addr2 = (resolution* y)+x; + if (layerIdx > 15) + layerIdx = 15; // max layers 15 (15 is reserved, nothing was hit). + var shift2 = (addr2&1) << 2; + texture[addr2>>1] |= (byte)(layerIdx << shift2); + } + } + } + } + } + } + } + + UnityEngine.Debug.Log( "Rasterize to texture took " + sw.ElapsedMilliseconds ); + return texture; + } + + // 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. + public byte[] RaytraceToTexture( int resolution, out List<Vector3Int> failedPixels, ref bool cancelToken ) @@ -377,9 +511,9 @@ namespace Wander { cachedTriIdx = ti; var layerIdx = (byte)layers[l].VectorTileFeatures[f].SelectedLayerIdx; - //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). + 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; @@ -407,7 +541,7 @@ namespace Wander } - // UnityEngine.Debug.Log( "Render to texture took " + sw.ElapsedMilliseconds ); + // UnityEngine.Debug.Log( "Raytrace texture took " + sw.ElapsedMilliseconds ); return texture; } -- GitLab