Skip to content
Snippets Groups Projects
Commit e2e641ed authored by Knuiman, Bart's avatar Knuiman, Bart
Browse files

Correctly working version with big optimization (QuadTree).

There is one strange not optimization working (caching). So it is left, see: TODO
parent 0b29970a
No related branches found
No related tags found
No related merge requests found
using Codice.CM.Client.Differences;
using Mapbox.Vector.Tile; using Mapbox.Vector.Tile;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
...@@ -11,6 +13,14 @@ using static Wander.Easing; ...@@ -11,6 +13,14 @@ using static Wander.Easing;
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 class TriangulatedPolygon
{ {
...@@ -30,7 +40,7 @@ namespace Wander ...@@ -30,7 +40,7 @@ namespace Wander
for ( int i = 0; i < vertices.Count; i += 3 ) for ( int i = 0; i < vertices.Count; i += 3 )
{ {
float minx = float.MaxValue; float minx = float.MaxValue;
float miny = float.MaxValue; float miny = float.MaxValue;
if (vertices[i+0].x < minx) minx = vertices[i].x; if (vertices[i+0].x < minx) minx = vertices[i].x;
if (vertices[i+0].y < miny) miny = vertices[i].y; if (vertices[i+0].y < miny) miny = vertices[i].y;
if (vertices[i+1].x < minx) minx = vertices[i+1].x; if (vertices[i+1].x < minx) minx = vertices[i+1].x;
...@@ -72,12 +82,13 @@ namespace Wander ...@@ -72,12 +82,13 @@ namespace Wander
private Task parseTileTask; private Task parseTileTask;
private List<VectorTileLayer> layers; private List<VectorTileLayer> layers;
private List<List<TriangulatedPolygon>> polygonLayers; private List<List<TriangulatedPolygon>> polygonLayers;
private QuadTreeNode root;
internal UnityWebRequest request; internal UnityWebRequest request;
public void StartDownload() public void StartDownload()
{ {
Debug.Assert(!started); UnityEngine.Debug.Assert(!started);
request.SendWebRequest(); request.SendWebRequest();
started = true; started = true;
} }
...@@ -175,7 +186,7 @@ namespace Wander ...@@ -175,7 +186,7 @@ namespace Wander
// } // }
//} //}
polygons[polygons.Count-1] = poly; polygons[polygons.Count-1] = poly;
Debug.Assert( poly.vertices.Count % 3 == 0 ); UnityEngine.Debug.Assert( poly.vertices.Count % 3 == 0 );
} }
catch (Exception) catch (Exception)
{ {
...@@ -190,9 +201,92 @@ namespace Wander ...@@ -190,9 +201,92 @@ namespace Wander
public void OptimizeForPointIsInsideTriangle() public void OptimizeForPointIsInsideTriangle()
{ {
Stopwatch sw = new Stopwatch();
sw.Restart();
for (int i = 0;i < polygonLayers.Count;i++) for (int i = 0;i < polygonLayers.Count;i++)
{
for (int j = 0;j < polygonLayers[i].Count;j++) for (int j = 0;j < polygonLayers[i].Count;j++)
{
polygonLayers[i][j].OptimizeForIsPointInTriangle(); polygonLayers[i][j].OptimizeForIsPointInTriangle();
}
}
// Generate quadtree
List<QuadTreeNode> stack = new List<QuadTreeNode>();
root = new QuadTreeNode();
root.triangles = new List<(int, int, int)>();
root.min = new Vector2( 0, 0 );
root.max = new Vector2( 4096, 4096 ); // TODO 4096 should be max of all layer extents.
for (int l = 0;l < polygonLayers.Count;l++)
{
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)
continue;
var poly = polygonLayers[l][f];
for (int t = 0;t < poly.vertices.Count/3;t++)
{
root.triangles.Add( (l, f, t) );
}
}
}
stack.Add( root );
while (stack.Count > 0)
{
var node = stack[0];
stack.RemoveAt( 0 );
if (node.triangles.Count < 4)
continue;
if (node.depth > 7)
continue;
node.children = new QuadTreeNode[4];
var hs = (node.max - node.min) / 2;
Vector2 [] mins = new [] {
node.min,
new Vector2(node.min.x+hs.x, node.min.y),
new Vector2(node.min.x, node.min.y+hs.y),
new Vector2(node.min.x+hs.x, node.min.y+hs.y)
};
for (int i = 0;i < 4;i++)
{
var n2 = new QuadTreeNode();
n2.depth = node.depth+1;
n2.min = mins[i];
n2.max = n2.min + hs;
n2.triangles = new List<(int, int, int)>();
for ( int t = 0; t < node.triangles.Count; t++ )
{
int l = node.triangles[t].Item1;
int f = node.triangles[t].Item2;
int t2 = node.triangles[t].Item3;
var min2 = polygonLayers[l][f].mins[t2];
var max2 = polygonLayers[l][f].maxs[t2];
if ( (min2.x > n2.max.x) || (min2.y > n2.max.y) || (max2.x < n2.min.x) || (max2.y < n2.min.y) )
{
continue;
}
n2.triangles.Add( (l, f, t2) );
}
node.children[i] = n2;
stack.Add( n2 );
}
node.triangles = null;
}
UnityEngine.Debug.Log( "Optimize for raytracing took " + sw.ElapsedMilliseconds );
} }
// Identify feature by specifying a (unique) index based on some criteria. This can very per // Identify feature by specifying a (unique) index based on some criteria. This can very per
...@@ -230,103 +324,101 @@ namespace Wander ...@@ -230,103 +324,101 @@ namespace Wander
int resolution, int resolution,
out Dictionary<byte, byte> remappedLayerIndices, out Dictionary<byte, byte> remappedLayerIndices,
out List<Vector3Int> failedPixels, out List<Vector3Int> failedPixels,
bool? cancelToken ) ref bool cancelToken )
{ {
Debug.Assert( triangulated, "First call Triangulate." ); Stopwatch sw = new Stopwatch();
Debug.Assert( layersIdentified, "Identify layers first." ); sw.Restart();
Debug.Assert( resolution > 1, "Must be at least 2." );
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]; byte [] texture = new byte[resolution*resolution];
failedPixels = new List<Vector3Int>(); failedPixels = new List<Vector3Int>();
remappedLayerIndices = new Dictionary<byte, byte>(); remappedLayerIndices = new Dictionary<byte, byte>();
float oneOverRes = 1.0f / resolution; float oneOverRes = 1.0f / resolution;
int cachedTriIdx = 0;
float fx = 4096 * oneOverRes; // TODO 4096 may be different in other implementations.
int cachedTriIdx = 0; QuadTreeNode node = null;
int cachedLyrIdx = 0; List<QuadTreeNode> stack = new List<QuadTreeNode>();
int cachedFtrIdx = 0;
// For each pixel. // For each pixel.
for (int y = 0;y < resolution ;y++) for (int y = 0;y < resolution ;y++)
{ {
for (int x = 0;x < resolution && !cancelToken.Value ;x++) for (int x = 0;x < resolution && !cancelToken ;x++)
{ {
bool hit = false; bool hit = false;
Vector2 p = new Vector2(fx*x+0.5f, fx*y+0.5f);
// For each layer, check triangle intersections. stack.Add( root );
for (int l = 0;l < layers.Count && !cancelToken.Value;l++) while (stack.Count > 0)
{ {
var lIdx = (l+cachedLyrIdx) % layers.Count; // Try last layer/feature/triangle first. node = stack[0];
var layer = layers[lIdx]; stack.RemoveAt( 0 );
float fx = layer.Extent * oneOverRes; if (p.x < node.min.x || p.y < node.min.y || p.x > node.max.x || p.y > node.max.y)
Vector2 p = new Vector2(fx*x+0.5f, fx*y+0.5f);
for (int f = 0;f < layer.VectorTileFeatures.Count;f++)
{ {
var ftrIdx = (f + cachedFtrIdx) % layer.VectorTileFeatures.Count; continue;
var feature = layer.VectorTileFeatures[ftrIdx]; }
if (feature.GeometryType != Tile.GeomType.Polygon)
continue;
var polygons = polygonLayers[lIdx][ftrIdx];
if (polygons.vertices.Count == 0)
continue;
if (feature.SelectedLayerIdx == 254)
continue;
if (feature.RelativeHeight > 0)
continue;
var poly = polygonLayers[lIdx][ftrIdx]; if ( node.children != null )
var mins = poly.mins; {
var maxs = poly.maxs; for ( int c = 0; c < node.children.Length; c++ )
var denoms = poly.denoms; {
var vertices = poly.vertices; stack.Add( node.children[c] );
var triCount = vertices.Count/3; }
for (int t = 0; t < triCount;t++) }
else if (node.triangles != null) // Is leaf
{
for( int t = 0; t < node.triangles.Count; t++ )
{ {
int triIdx = (t+cachedTriIdx) % triCount; int ti = (cachedTriIdx + t) % node.triangles.Count;
if (p.x < mins[triIdx].x || p.x > maxs[triIdx].x) continue; int l = node.triangles[ti].Item1;
if (p.y < mins[triIdx].y || p.y > maxs[triIdx].y) continue; int f = node.triangles[ti].Item2;
hit = GeomUtil.PointIsInsideTriangle2( p, vertices[triIdx*3], vertices[triIdx*3+1], vertices[triIdx*3+2], denoms[triIdx] ); int t2 = node.triangles[ti].Item3;
var min2 = polygonLayers[l][f].mins[t2];
var max2 = polygonLayers[l][f].maxs[t2];
if (p.x < min2.x || p.x > max2.x) continue;
if (p.y < min2.y || p.y > max2.y) continue;
var vertices = polygonLayers[l][f].vertices;
var denoms = polygonLayers[l][f].denoms;
hit = GeomUtil.PointIsInsideTriangle2( p, vertices[t2*3], vertices[t2*3+1], vertices[t2*3+2], denoms[t2] );
if (hit) if (hit)
{ {
cachedTriIdx = triIdx; // TODO caching the TriIdx doees not work, I have no clue why, it should only function
cachedLyrIdx = lIdx; // as a hint, as where to start. But using this hint results in massive number of triangles going wrong...
cachedFtrIdx = ftrIdx; // cachedTriIdx = ti;
texture[(resolution - y -1)*resolution+x] = (byte)feature.SelectedLayerIdx; var layerIdx = (byte)layers[l].VectorTileFeatures[f].SelectedLayerIdx;
texture[(resolution - y -1)*resolution+x] = layerIdx;
break; break;
} }
} }
if (hit) break;
cachedTriIdx += 1;
if(cachedTriIdx == triCount) cachedTriIdx = 0;
} }
if (hit) break; if (hit) break;
cachedFtrIdx = 0; } // end while
}
stack.Clear();
if (!hit) if (!hit)
{ {
cachedLyrIdx = 0;
texture[(resolution - y -1)*resolution+x] = 255; texture[(resolution - y -1)*resolution+x] = 255;
failedPixels.Add( new Vector3Int( x, y, 255 ) ); failedPixels.Add( new Vector3Int( x, y, 255 ) );
} }
else
{
stack.Add( node ); // Add the node that was hit as the next pixel may likely hit this poly again.
}
} }
} }
// Determine number of different layers. For instance, if only layer 3, 8, 14 and 15 are used, we select // 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. // 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 = 255;
int remappedIndexCounter = -1; int remappedIndexCounter = -1;
byte remappedPixel = 255; byte remappedPixel = 255;
int addr = 0; int addr = 0;
for (int y = 0;y < resolution && !cancelToken.Value;y++) for (int y = 0;y < resolution && !cancelToken;y++)
{ {
for (int x = 0;x < resolution;x++) for (int x = 0;x < resolution;x++)
{ {
...@@ -353,7 +445,9 @@ namespace Wander ...@@ -353,7 +445,9 @@ namespace Wander
} }
} }
Debug.Assert(remappedIndexCounter <= 254, "Exceeded layer count." ); UnityEngine.Debug.Assert(remappedIndexCounter <= 254, "Exceeded layer count." );
UnityEngine.Debug.Log( "Render to texture took " + sw.ElapsedMilliseconds );
return texture; return texture;
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment