Improve Poi3DLayer:
- insert MIN_ZOOM - support multiple models / multiple tags - parameter RANDOM_TRANSFORM - use ground scale
This commit is contained in:
parent
d36320967c
commit
7c92d1045a
38
vtm-playground/src/org/oscim/test/gdx/poi3d/ModelHolder.java
Normal file
38
vtm-playground/src/org/oscim/test/gdx/poi3d/ModelHolder.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user