commit
219b943d59
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;
|
||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user