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.TileData;
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.TileLoaderProcessHook;
import org.oscim.map.Map;
import org.oscim.model.VtmModels;
import org.oscim.renderer.bucket.RenderBuckets;
import org.oscim.renderer.bucket.SymbolItem;
import org.oscim.utils.pool.Inlist;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
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);
static class Poi3DTileData extends TileData {
public final List<SymbolItem> symbols = new List<>();
public final HashMap<ModelHolder, List<SymbolItem>> symbols = new HashMap<>();
@Override
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 Tag TREE_TAG = new Tag("natural", "tree");
public final static boolean RANDOM_TRANSFORM = true; // TODO customizable for each tag
AssetManager assets;
GdxRenderer3D2 g3d;
boolean loading;
Model mModel;
public final static Tag TAG_TREE = new Tag("natural", "tree");
public final static Tag TAG_MEMORIAL = new Tag("historic", "memorial");
public final static Tag TAG_FOREST = new Tag("landuse", "forest");
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;
LinkedHashMap<Tile, Array<ModelInstance>> mTileMap = new LinkedHashMap<>();
TileSet mTileSet = new TileSet();
TileSet mPrevTiles = new TileSet();
private final String pathToTree;
public Poi3DLayer(Map map, VectorTileLayer tileLayer) {
this(map, tileLayer, true);
}
public Poi3DLayer(Map map, VectorTileLayer tileLayer, boolean useDefaults) {
super(map);
tileLayer.addHook(new TileLoaderProcessHook() {
@Override
public boolean process(MapTile tile, RenderBuckets buckets, MapElement element) {
if (!element.tags.contains(TREE_TAG))
return false;
if (tile.zoomLevel < MIN_ZOOM) return false;
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
@ -103,7 +150,7 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
});
mTileLayer = tileLayer;
mRenderer = g3d = new GdxRenderer3D2(mMap);
mRenderer = mG3d = new GdxRenderer3D2(mMap);
// Material mat = new
// 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,
// mat, attributes);
pathToTree = GdxAssets.getAssetPath(VtmModels.TREE.getPath());
mAssets = new AssetManager();
assets = new AssetManager();
assets.load(pathToTree, Model.class);
loading = true;
if (useDefaults)
useDefaults();
}
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() {
Model model = assets.get(pathToTree, Model.class);
for (int i = 0; i < model.nodes.size; i++) {
for (Node node : model.nodes) {
log.debug("loader node " + node.id);
/* 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);
for (List<ModelHolder> holders : mScenes.values()) {
for (ModelHolder holder : holders) {
Model model = mAssets.get(holder.getPath());
for (Node node : model.nodes) {
log.debug("loader node " + node.id);
model.nodes.removeIndex(0);
model.nodes.add(node);
/* Use with {@link GdxRenderer3D} */
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) {
@ -155,6 +222,15 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
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
public void onMapEvent(Event ev, MapPosition pos) {
@ -162,19 +238,19 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
mTileSet = new TileSet();
mPrevTiles = new TileSet();
mTileMap = new LinkedHashMap<>();
synchronized (g3d) {
g3d.instances.clear();
synchronized (mG3d) {
mG3d.instances.clear();
}
}
if (loading && assets.update()) {
if (mLoading && mAssets.update()) {
doneLoading();
// Renderable renderable = new Renderable();
// new ModelInstance(mModel).getRenderable(renderable);
// Shader shader = new DefaultShader(renderable, true, false,
// false, false, 1, 0, 0, 0);
}
if (loading)
if (mLoading)
return;
// log.debug("update");
@ -202,20 +278,22 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
if (ld == null)
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);
inst.userData = it;
// float r = 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();
ModelInstance inst = new ModelInstance(entry.getKey().getModel());
inst.userData = it;
// float r = 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();
// inst.transform.setTranslation(new Vector3(it.x, it.y,
// 10));
// inst.materials.get(0).set(ColorAttribute.createDiffuse(r,
// g, b, 0.8f));
instances.add(inst);
added.add(inst);
// inst.transform.setTranslation(new Vector3(it.x, it.y,
// 10));
// inst.materials.get(0).set(ColorAttribute.createDiffuse(r,
// g, b, 0.8f));
instances.add(inst);
added.add(inst);
}
}
if (instances.size == 0)
@ -247,6 +325,7 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
mPrevTiles.releaseTiles();
int zoom = mTileSet.tiles[0].zoomLevel;
float groundScale = mTileSet.tiles[0].getGroundScale();
TileSet tmp = mPrevTiles;
mPrevTiles = mTileSet;
@ -255,13 +334,13 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
if (!changed)
return;
// scale aka tree height
float scale = (float) (1f / Math.pow(2, (17 - zoom))) * 8;
// scale relative to latitude
float scale = 1f / groundScale;
double tileX = (pos.x * (Tile.SIZE << zoom));
double tileY = (pos.y * (Tile.SIZE << zoom));
synchronized (g3d) {
synchronized (mG3d) {
for (Entry<Tile, Array<ModelInstance>> e : mTileMap.entrySet()) {
Tile t = e.getKey();
@ -272,9 +351,16 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
for (ModelInstance inst : e.getValue()) {
SymbolItem it = (SymbolItem) inst.userData;
// variable height
float s = scale + (it.x * it.y) % 3;
float r = (it.x * it.y) % 360;
float s = scale;
float r = 0f;
// 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.scale(s, s, s);
@ -288,9 +374,27 @@ public class Poi3DLayer extends Layer implements Map.UpdateListener {
}
}
g3d.instances.removeAll(removed, true);
g3d.instances.addAll(added);
g3d.cam.setMapPosition(pos.x, pos.y, 1 << zoom);
mG3d.instances.removeAll(removed, true);
mG3d.instances.addAll(added);
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);
}
}