Merge pull request #588 from Gustl22/poi3DLayer

Improve Poi3DLayer
This commit is contained in:
Emux 2018-09-11 15:14:53 +03:00 committed by GitHub
commit 219b943d59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 207 additions and 65 deletions

View File

@ -0,0 +1,38 @@
/*
* Copyright 2018 Gustl22
*
* This program is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.test.gdx.poi3d;
import com.badlogic.gdx.graphics.g3d.Model;
class ModelHolder {
Model mModel;
String mPath;
ModelHolder(String path) {
mPath = path;
}
public Model getModel() {
return mModel;
}
public String getPath() {
return mPath;
}
public void setModel(Model model) {
mModel = model;
}
}

View File

@ -33,16 +33,21 @@ import org.oscim.layers.Layer;
import org.oscim.layers.tile.MapTile; import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.MapTile.TileData; import org.oscim.layers.tile.MapTile.TileData;
import org.oscim.layers.tile.TileSet; import org.oscim.layers.tile.TileSet;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.VectorTileLayer; import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderProcessHook; import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderProcessHook;
import org.oscim.map.Map; import org.oscim.map.Map;
import org.oscim.model.VtmModels; import org.oscim.model.VtmModels;
import org.oscim.renderer.bucket.RenderBuckets; import org.oscim.renderer.bucket.RenderBuckets;
import org.oscim.renderer.bucket.SymbolItem; import org.oscim.renderer.bucket.SymbolItem;
import org.oscim.utils.pool.Inlist;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
/** /**
@ -53,48 +58,90 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
private static final Logger log = LoggerFactory.getLogger(Poi3DLayer.class); private static final Logger log = LoggerFactory.getLogger(Poi3DLayer.class);
static class Poi3DTileData extends TileData { static class Poi3DTileData extends TileData {
public final List<SymbolItem> symbols = new List<>(); public final HashMap<ModelHolder, List<SymbolItem>> symbols = new HashMap<>();
@Override @Override
protected void dispose() { protected void dispose() {
SymbolItem.pool.releaseAll(symbols.clear()); for (List<SymbolItem> symbolItems : symbols.values()) {
SymbolItem.pool.releaseAll(symbolItems.clear());
}
symbols.clear();
} }
} }
public final static int MIN_ZOOM = BuildingLayer.MIN_ZOOM;
static final String POI_DATA = Poi3DLayer.class.getSimpleName(); static final String POI_DATA = Poi3DLayer.class.getSimpleName();
static final Tag TREE_TAG = new Tag("natural", "tree"); public final static boolean RANDOM_TRANSFORM = true; // TODO customizable for each tag
AssetManager assets; public final static Tag TAG_TREE = new Tag("natural", "tree");
GdxRenderer3D2 g3d; public final static Tag TAG_MEMORIAL = new Tag("historic", "memorial");
boolean loading; public final static Tag TAG_FOREST = new Tag("landuse", "forest");
Model mModel; public final static Tag TAG_WOOD = new Tag("natural", "wood");
// Not supported by Oscim Tiles
public final static Tag TAG_ARTWORK = new Tag("tourism", "artwork");
public final static Tag TAG_TREE_BROADLEAVED = new Tag("leaf_type", "broadleaved");
public final static Tag TAG_TREE_NEEDLELEAVED = new Tag("leaf_type", "needleleaved");
public final static Tag TAG_STREETLAMP = new Tag("highway", "street_lamp");
AssetManager mAssets;
GdxRenderer3D2 mG3d;
boolean mLoading;
LinkedHashMap<Tag, List<ModelHolder>> mScenes = new LinkedHashMap<>();
VectorTileLayer mTileLayer; VectorTileLayer mTileLayer;
LinkedHashMap<Tile, Array<ModelInstance>> mTileMap = new LinkedHashMap<>(); LinkedHashMap<Tile, Array<ModelInstance>> mTileMap = new LinkedHashMap<>();
TileSet mTileSet = new TileSet(); TileSet mTileSet = new TileSet();
TileSet mPrevTiles = new TileSet(); TileSet mPrevTiles = new TileSet();
private final String pathToTree;
public Poi3DLayer(Map map, VectorTileLayer tileLayer) { public Poi3DLayer(Map map, VectorTileLayer tileLayer) {
this(map, tileLayer, true);
}
public Poi3DLayer(Map map, VectorTileLayer tileLayer, boolean useDefaults) {
super(map); super(map);
tileLayer.addHook(new TileLoaderProcessHook() { tileLayer.addHook(new TileLoaderProcessHook() {
@Override @Override
public boolean process(MapTile tile, RenderBuckets buckets, MapElement element) { public boolean process(MapTile tile, RenderBuckets buckets, MapElement element) {
if (!element.tags.contains(TREE_TAG)) if (tile.zoomLevel < MIN_ZOOM) return false;
return false;
Poi3DTileData td = get(tile); Poi3DTileData td = get(tile);
PointF p = element.getPoint(0);
SymbolItem s = SymbolItem.pool.get();
s.x = p.x;
s.y = p.y;
td.symbols.push(s);
return true; for (Entry<Tag, List<ModelHolder>> scene : mScenes.entrySet()) {
if (!element.tags.contains(scene.getKey()))
continue;
List<ModelHolder> holders = scene.getValue();
PointF p;
SymbolItem s;
// TODO fill poly area with items
for (int i = 0; i < element.getNumPoints(); i++) {
p = element.getPoint(i);
s = SymbolItem.pool.get();
s.x = p.x;
s.y = p.y;
ModelHolder holder;
if (holders.size() > 1) {
// Use random for tags with multiple models.
int random = getPosHash(tile, s) % holders.size();
holder = holders.get(random);
} else
holder = holders.get(0);
Inlist.List<SymbolItem> symbolItems = td.symbols.get(holder);
if (symbolItems == null) {
symbolItems = new Inlist.List<>();
td.symbols.put(holder, symbolItems);
}
symbolItems.push(s);
}
return true;
}
return false;
} }
@Override @Override
@ -103,7 +150,7 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
}); });
mTileLayer = tileLayer; mTileLayer = tileLayer;
mRenderer = g3d = new GdxRenderer3D2(mMap); mRenderer = mG3d = new GdxRenderer3D2(mMap);
// Material mat = new // Material mat = new
// Material(ColorAttribute.createDiffuse(Color.BLUE)); // Material(ColorAttribute.createDiffuse(Color.BLUE));
@ -114,36 +161,56 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
// mModel = modelBuilder.createSphere(10f, 10f, 10f, 12, 12, // mModel = modelBuilder.createSphere(10f, 10f, 10f, 12, 12,
// mat, attributes); // mat, attributes);
pathToTree = GdxAssets.getAssetPath(VtmModels.TREE.getPath()); mAssets = new AssetManager();
assets = new AssetManager(); if (useDefaults)
assets.load(pathToTree, Model.class); useDefaults();
loading = true; }
public void addModel(VtmModels model, Tag tag) {
addModel(GdxAssets.getAssetPath(model.getPath()), tag);
}
/**
* Assign model with specified path to an OSM tag. You can assign multiple models to one tag, too.
*/
public void addModel(String path, Tag tag) {
List<ModelHolder> scene = mScenes.get(tag);
if (scene == null) {
scene = new ArrayList<>();
mScenes.put(tag, scene);
}
scene.add(new ModelHolder(path));
mAssets.load(path, Model.class);
if (!mLoading)
mLoading = true;
} }
private void doneLoading() { private void doneLoading() {
Model model = assets.get(pathToTree, Model.class); for (List<ModelHolder> holders : mScenes.values()) {
for (int i = 0; i < model.nodes.size; i++) { for (ModelHolder holder : holders) {
for (Node node : model.nodes) { Model model = mAssets.get(holder.getPath());
log.debug("loader node " + node.id); for (Node node : model.nodes) {
/* Use with {@link GdxRenderer3D} */
if (node.hasChildren() && ((Object) g3d) instanceof GdxRenderer3D) {
if (model.nodes.size != 1) {
throw new RuntimeException("Model has more than one node with GdxRenderer: " + model.toString());
}
node = node.getChild(0);
log.debug("loader node " + node.id); log.debug("loader node " + node.id);
model.nodes.removeIndex(0); /* Use with {@link GdxRenderer3D} */
model.nodes.add(node); if (node.hasChildren() && ((Object) mG3d) instanceof GdxRenderer3D) {
if (model.nodes.size != 1) {
throw new RuntimeException("Model has more than one node with GdxRenderer: " + model.toString());
}
node = node.getChild(0);
log.debug("loader node " + node.id);
model.nodes.removeIndex(0);
model.nodes.add(node);
}
node.rotation.setFromAxis(1, 0, 0, 90);
} }
node.rotation.setFromAxis(1, 0, 0, 90); holder.setModel(model);
} }
mModel = model;
} }
loading = false; mLoading = false;
} }
private Poi3DTileData get(MapTile tile) { private Poi3DTileData get(MapTile tile) {
@ -155,6 +222,15 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
return ld; return ld;
} }
/**
* @return an int which is equal in all zoom levels
*/
private int getPosHash(Tile tile, SymbolItem item) {
int a = (int) (((tile.tileX + ((double) item.x / Tile.SIZE)) * 1000000000) / (1 << tile.zoomLevel));
int b = (int) (((tile.tileY + ((double) item.y / Tile.SIZE)) * 1000000000) / (1 << tile.zoomLevel));
return Math.abs(a * b);
}
@Override @Override
public void onMapEvent(Event ev, MapPosition pos) { public void onMapEvent(Event ev, MapPosition pos) {
@ -162,19 +238,19 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
mTileSet = new TileSet(); mTileSet = new TileSet();
mPrevTiles = new TileSet(); mPrevTiles = new TileSet();
mTileMap = new LinkedHashMap<>(); mTileMap = new LinkedHashMap<>();
synchronized (g3d) { synchronized (mG3d) {
g3d.instances.clear(); mG3d.instances.clear();
} }
} }
if (loading && assets.update()) { if (mLoading && mAssets.update()) {
doneLoading(); doneLoading();
// Renderable renderable = new Renderable(); // Renderable renderable = new Renderable();
// new ModelInstance(mModel).getRenderable(renderable); // new ModelInstance(mModel).getRenderable(renderable);
// Shader shader = new DefaultShader(renderable, true, false, // Shader shader = new DefaultShader(renderable, true, false,
// false, false, 1, 0, 0, 0); // false, false, 1, 0, 0, 0);
} }
if (loading) if (mLoading)
return; return;
// log.debug("update"); // log.debug("update");
@ -202,20 +278,22 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
if (ld == null) if (ld == null)
continue; continue;
for (SymbolItem it : ld.symbols) { for (Entry<ModelHolder, Inlist.List<SymbolItem>> entry : ld.symbols.entrySet()) {
for (SymbolItem it : entry.getValue()) {
ModelInstance inst = new ModelInstance(mModel); ModelInstance inst = new ModelInstance(entry.getKey().getModel());
inst.userData = it; inst.userData = it;
// float r = 0.5f + 0.5f * (float) Math.random(); // float r = 0.5f + 0.5f * (float) Math.random();
// float g = 0.5f + 0.5f * (float) Math.random(); // float g = 0.5f + 0.5f * (float) Math.random();
// float b = 0.5f + 0.5f * (float) Math.random(); // float b = 0.5f + 0.5f * (float) Math.random();
// inst.transform.setTranslation(new Vector3(it.x, it.y, // inst.transform.setTranslation(new Vector3(it.x, it.y,
// 10)); // 10));
// inst.materials.get(0).set(ColorAttribute.createDiffuse(r, // inst.materials.get(0).set(ColorAttribute.createDiffuse(r,
// g, b, 0.8f)); // g, b, 0.8f));
instances.add(inst); instances.add(inst);
added.add(inst); added.add(inst);
}
} }
if (instances.size == 0) if (instances.size == 0)
@ -247,6 +325,7 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
mPrevTiles.releaseTiles(); mPrevTiles.releaseTiles();
int zoom = mTileSet.tiles[0].zoomLevel; int zoom = mTileSet.tiles[0].zoomLevel;
float groundScale = mTileSet.tiles[0].getGroundScale();
TileSet tmp = mPrevTiles; TileSet tmp = mPrevTiles;
mPrevTiles = mTileSet; mPrevTiles = mTileSet;
@ -255,13 +334,13 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
if (!changed) if (!changed)
return; return;
// scale aka tree height // scale relative to latitude
float scale = (float) (1f / Math.pow(2, (17 - zoom))) * 8; float scale = 1f / groundScale;
double tileX = (pos.x * (Tile.SIZE << zoom)); double tileX = (pos.x * (Tile.SIZE << zoom));
double tileY = (pos.y * (Tile.SIZE << zoom)); double tileY = (pos.y * (Tile.SIZE << zoom));
synchronized (g3d) { synchronized (mG3d) {
for (Entry<Tile, Array<ModelInstance>> e : mTileMap.entrySet()) { for (Entry<Tile, Array<ModelInstance>> e : mTileMap.entrySet()) {
Tile t = e.getKey(); Tile t = e.getKey();
@ -272,9 +351,16 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
for (ModelInstance inst : e.getValue()) { for (ModelInstance inst : e.getValue()) {
SymbolItem it = (SymbolItem) inst.userData; SymbolItem it = (SymbolItem) inst.userData;
// variable height float s = scale;
float s = scale + (it.x * it.y) % 3; float r = 0f;
float r = (it.x * it.y) % 360;
// random/variable height and rotation
if (RANDOM_TRANSFORM) {
float deviationStep = s * 0.1f;
int hash = getPosHash(t, it); // Use absolute coordinates
s += ((hash % 4) - 2) * deviationStep;
r = hash % 360;
}
inst.transform.idt(); inst.transform.idt();
inst.transform.scale(s, s, s); inst.transform.scale(s, s, s);
@ -288,9 +374,27 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
} }
} }
g3d.instances.removeAll(removed, true); mG3d.instances.removeAll(removed, true);
g3d.instances.addAll(added); mG3d.instances.addAll(added);
g3d.cam.setMapPosition(pos.x, pos.y, 1 << zoom); mG3d.cam.setMapPosition(pos.x, pos.y, 1 << zoom);
} }
} }
public void useDefaults() {
/* Keep order (the upper tags have higher priority)
* Example: needle leaved woods only get fir model although it has the wood tag.
*/
addModel(VtmModels.MEMORIAL, TAG_ARTWORK);
addModel(VtmModels.MEMORIAL, TAG_MEMORIAL);
addModel(VtmModels.STREETLAMP, TAG_STREETLAMP);
addModel(VtmModels.TREE_FIR, TAG_TREE_NEEDLELEAVED);
addModel(VtmModels.TREE_OAK, TAG_TREE_BROADLEAVED);
addModel(VtmModels.TREE_ASH, TAG_TREE);
addModel(VtmModels.TREE_FIR, TAG_WOOD);
addModel(VtmModels.TREE_OAK, TAG_WOOD);
addModel(VtmModels.TREE_ASH, TAG_WOOD);
addModel(VtmModels.TREE_OAK, TAG_FOREST);
addModel(VtmModels.TREE_ASH, TAG_FOREST);
addModel(VtmModels.TREE_FIR, TAG_FOREST);
}
} }