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