Tesselate layers instead of tiles

This commit is contained in:
Maximilian Ammann 2022-01-15 15:12:38 +01:00
parent cb76b75eb0
commit 635df81f1c
4 changed files with 126 additions and 143 deletions

View File

@ -6,23 +6,24 @@ use log::{error, info};
use crate::coords::TileCoords;
use vector_tile::parse_tile_bytes;
use vector_tile::tile::{Feature, Layer, Tile};
use crate::io::web_tile_fetcher::WebTileFetcher;
use crate::io::{HttpFetcherConfig, TileFetcher};
use crate::render::ShaderVertex;
use crate::tesselation::{IndexDataType, OverAlignedVertexBuffer, Tesselated};
#[derive(Clone)]
pub struct TesselatedTile {
pub id: u32,
pub struct TesselatedLayer {
pub coords: TileCoords,
pub over_aligned: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
pub buffer: OverAlignedVertexBuffer<ShaderVertex, IndexDataType>,
pub feature_vertices: Vec<u32>,
pub layer_data: Layer,
}
#[derive(Clone)]
pub struct WorkerLoop {
requests: Arc<WorkQueue<TileCoords>>,
responses: Arc<WorkQueue<TesselatedTile>>,
responses: Arc<WorkQueue<TesselatedLayer>>,
}
impl Drop for WorkerLoop {
@ -44,7 +45,7 @@ impl WorkerLoop {
self.requests.push(coords);
}
pub fn pop_all(&self) -> Vec<TesselatedTile> {
pub fn pop_all(&self) -> Vec<TesselatedLayer> {
self.responses.try_pop_all()
}
@ -54,7 +55,6 @@ impl WorkerLoop {
});
// let fetcher = StaticTileFetcher::new();
let mut current_id = 0;
loop {
while let Some(coords) = self.requests.pop() {
match fetcher.fetch_tile(&coords).await {
@ -63,13 +63,20 @@ impl WorkerLoop {
let tile = parse_tile_bytes(bytemuck::cast_slice(data.as_slice()))
.expect("failed to load tile");
let buffer = tile.tesselate_stroke();
self.responses.push(TesselatedTile {
id: current_id,
coords,
over_aligned: buffer.into(),
});
current_id += 1;
for layer in tile.layers() {
if let Some((buffer, feature_vertices)) = layer.tesselate() {
if buffer.indices.is_empty() {
continue;
}
self.responses.push(TesselatedLayer {
coords,
buffer: buffer.into(),
feature_vertices,
layer_data: layer.clone(),
});
}
}
info!("tile ready: {:?}", &coords);
}
Err(err) => {

101
src/tesselation/layer.rs Normal file
View File

@ -0,0 +1,101 @@
use std::collections::HashMap;
use std::ops::Add;
use bytemuck::Pod;
use log::info;
use lyon::lyon_tessellation::VertexBuffers;
use lyon::tessellation::geometry_builder::MaxIndex;
use lyon::tessellation::{
BuffersBuilder, FillOptions, FillTessellator, StrokeOptions, StrokeTessellator,
};
use lyon_path::traits::SvgPathBuilder;
use lyon_path::Path;
use vector_tile::geometry::{Command, Geometry};
use vector_tile::tile::{Feature, Layer, Tile};
use crate::render::ShaderVertex;
use crate::tesselation::{Tesselated, VertexConstructor, DEFAULT_TOLERANCE};
impl<I: Add + From<lyon::lyon_tessellation::VertexId> + MaxIndex + Pod> Tesselated<I> for Layer {
fn tesselate(&self) -> Option<(VertexBuffers<ShaderVertex, I>, Vec<u32>)> {
let mut buffer: VertexBuffers<ShaderVertex, I> = VertexBuffers::new();
let mut feature_vertices: Vec<u32> = Vec::new();
let mut last = 0;
info!("features: {}", self.features().len());
for feature in self.features() {
match feature.geometry() {
Geometry::GeometryPolygon(polygon) => {
let mut tile_builder = Path::builder().with_svg();
for command in &polygon.commands {
match command {
Command::MoveTo(cmd) => {
tile_builder.relative_move_to(lyon_path::math::vector(
cmd.x as f32,
cmd.y as f32,
));
}
Command::LineTo(cmd) => {
tile_builder.relative_line_to(lyon_path::math::vector(
cmd.x as f32,
cmd.y as f32,
));
}
Command::Close => {
tile_builder.close();
}
};
}
let mut tesselator = FillTessellator::new();
tesselator
.tessellate_path(
&tile_builder.build(),
&FillOptions::tolerance(DEFAULT_TOLERANCE),
&mut BuffersBuilder::new(&mut buffer, VertexConstructor {}),
)
.ok()?;
}
Geometry::GeometryLineString(polygon) => {
let mut tile_builder = Path::builder().with_svg();
for command in &polygon.commands {
match command {
Command::MoveTo(cmd) => {
tile_builder.relative_move_to(lyon_path::math::vector(
cmd.x as f32,
cmd.y as f32,
));
}
Command::LineTo(cmd) => {
tile_builder.relative_line_to(lyon_path::math::vector(
cmd.x as f32,
cmd.y as f32,
));
}
Command::Close => {
panic!("error")
}
};
}
let mut tesselator = StrokeTessellator::new();
tesselator
.tessellate_path(
&tile_builder.build(),
&StrokeOptions::tolerance(DEFAULT_TOLERANCE),
&mut BuffersBuilder::new(&mut buffer, VertexConstructor {}),
)
.ok()?;
}
_ => {}
};
let new_length = buffer.indices.len();
feature_vertices.push((new_length - last) as u32);
last = new_length;
}
Some((buffer, feature_vertices))
}
}

View File

@ -7,24 +7,20 @@ use crate::render::ShaderVertex;
use lyon::tessellation::{
FillVertex, FillVertexConstructor, StrokeVertex, StrokeVertexConstructor, VertexBuffers,
};
use lyon_path::Path;
use wgpu::BufferAddress;
pub mod tile;
mod layer;
const DEFAULT_TOLERANCE: f32 = 0.02;
pub type IndexDataType = u16; // Must match INDEX_FORMAT
pub trait Tesselated<I: Add> {
fn tesselate_stroke(&self) -> VertexBuffers<ShaderVertex, I>;
fn tesselate_fill(&self) -> VertexBuffers<ShaderVertex, I>;
fn empty_range(&self) -> VertexBuffers<ShaderVertex, I> {
VertexBuffers::new()
}
fn tesselate(&self) -> Option<(VertexBuffers<ShaderVertex, I>, Vec<u32>)>;
}
pub struct VertexConstructor();
pub struct VertexConstructor {}
impl FillVertexConstructor<ShaderVertex> for VertexConstructor {
fn new_vertex(&mut self, vertex: FillVertex) -> ShaderVertex {

View File

@ -1,121 +0,0 @@
use std::ops::Add;
use bytemuck::Pod;
use lyon::tessellation::geometry_builder::MaxIndex;
use lyon::tessellation::{
BuffersBuilder, FillOptions, FillTessellator, StrokeOptions, StrokeTessellator, VertexBuffers,
};
use lyon_path::builder::SvgPathBuilder;
use lyon_path::Path;
use crate::render::ShaderVertex;
use vector_tile::geometry::{Command, Geometry};
use vector_tile::tile::Tile;
use crate::tesselation::{Tesselated, VertexConstructor, DEFAULT_TOLERANCE};
fn build_path(tile: &Tile, fill: bool) -> Path {
let mut tile_builder = Path::builder().with_svg();
for layer in tile.layers() {
if layer.name() != "transportation" {
continue;
}
for feature in layer.features() {
let geo = feature.geometry();
match geo {
Geometry::GeometryPolygon(polygon) => {
for command in &polygon.commands {
match command {
Command::MoveTo(cmd) => {
tile_builder.relative_move_to(lyon_path::math::vector(
cmd.x as f32,
cmd.y as f32,
));
}
Command::LineTo(cmd) => {
tile_builder.relative_line_to(lyon_path::math::vector(
cmd.x as f32,
cmd.y as f32,
));
}
Command::Close => {
tile_builder.close();
}
};
}
}
Geometry::GeometryLineString(polygon) => {
if !fill {
for command in &polygon.commands {
match command {
Command::MoveTo(cmd) => {
tile_builder.relative_move_to(lyon_path::math::vector(
cmd.x as f32,
cmd.y as f32,
));
//print!("M{} {} ", cmd.x, cmd.y);
}
Command::LineTo(cmd) => {
tile_builder.relative_line_to(lyon_path::math::vector(
cmd.x as f32,
cmd.y as f32,
));
//print!("l{} {} ", cmd.x, cmd.y);
}
Command::Close => {
panic!("error")
}
};
}
}
}
_ => {}
};
tile_builder.move_to(lyon_path::math::point(0.0, 0.0));
}
}
tile_builder.build()
}
impl<I: Add + From<lyon::lyon_tessellation::VertexId> + MaxIndex + Pod> Tesselated<I> for Tile {
fn tesselate_stroke(&self) -> VertexBuffers<ShaderVertex, I> {
let mut buffer: VertexBuffers<ShaderVertex, I> = VertexBuffers::new();
let mut tesselator = StrokeTessellator::new();
let tile_path = build_path(self, false);
tesselator
.tessellate_path(
&tile_path,
&StrokeOptions::tolerance(DEFAULT_TOLERANCE),
&mut BuffersBuilder::new(&mut buffer, VertexConstructor()),
)
.unwrap();
buffer
}
fn tesselate_fill(&self) -> VertexBuffers<ShaderVertex, I> {
let mut buffer: VertexBuffers<ShaderVertex, I> = VertexBuffers::new();
let mut tesselator = FillTessellator::new();
let tile_path = build_path(self, true);
tesselator
.tessellate_path(
&tile_path,
&FillOptions::tolerance(DEFAULT_TOLERANCE),
&mut BuffersBuilder::new(&mut buffer, VertexConstructor()),
)
.unwrap();
buffer
}
}