gdx poi3d layer

This commit is contained in:
Hannes Janetzek 2014-03-29 19:01:28 +01:00
parent 44974d90a7
commit 21d32bacb7
12 changed files with 6545 additions and 0 deletions

BIN
data/g3d/test.g3db Normal file

Binary file not shown.

4748
data/g3d/test.g3dj Normal file

File diff suppressed because it is too large Load Diff

BIN
data/g3d/tree.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

177
data/g3d/treeA.g3dj Normal file
View File

@ -0,0 +1,177 @@
{
"version": [ 0, 1],
"id": "",
"meshes": [
{
"attributes": ["POSITION", "NORMAL", "TEXCOORD0"],
"vertices": [
-0.003717, -0.043092, 0.000001, 0.000000, -1.000000, 0.000000, 0.000000, 0.000000,
-0.003717, -0.043092, -0.019798, 0.000000, -0.609973, -0.792383, 0.000000, 0.000000,
0.013430, -0.043092, -0.009898, 0.686239, -0.609973, -0.396191, 0.000000, 0.000000,
-0.003717, 0.527242, 0.000001, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000,
0.013430, 0.527242, -0.009898, 0.686239, 0.609973, -0.396191, 0.000000, 0.000000,
-0.003717, 0.527242, -0.019798, 0.000000, 0.609973, -0.792383, 0.000000, 0.000000,
0.013430, -0.043092, 0.009901, 0.686239, -0.609973, 0.396191, 0.000000, 0.000000,
0.013430, 0.527242, 0.009901, 0.686239, 0.609973, 0.396191, 0.000000, 0.000000,
-0.003717, -0.043092, 0.019801, 0.000000, -0.609973, 0.792383, 0.000000, 0.000000,
-0.003717, 0.527242, 0.019801, 0.000000, 0.609973, 0.792383, 0.000000, 0.000000,
-0.020865, -0.043092, 0.009901, -0.686239, -0.609973, 0.396191, 0.000000, 0.000000,
-0.020865, 0.527242, 0.009901, -0.686239, 0.609973, 0.396191, 0.000000, 0.000000,
-0.020865, -0.043092, -0.009898, -0.686239, -0.609973, -0.396191, 0.000000, 0.000000,
-0.020865, 0.527242, -0.009898, -0.686239, 0.609973, -0.396191, 0.000000, 0.000000,
-0.000052, 1.708732, 0.000001, 0.000000, 1.000000, 0.000000, 0.627643, 0.963260,
-0.056858, 1.692897, -0.056805, -0.359325, 0.861232, -0.359325, 0.811107, 0.943917,
-0.080388, 1.692897, 0.000001, -0.508194, 0.861232, 0.000000, 0.930977, 0.971962,
-0.112293, 1.619116, -0.112239, -0.584552, 0.562639, -0.584552, 0.871491, 0.884983,
-0.158784, 1.619116, 0.000001, -0.826655, 0.562639, 0.000000, 1.012361, 0.910828,
-0.176420, 1.422079, -0.176367, -0.675863, 0.293832, -0.675863, 0.903795, 0.728324,
-0.249474, 1.422079, 0.000001, -0.955840, 0.293832, 0.000000, 1.035246, 0.751852,
-0.210746, 1.159339, -0.210693, -0.704306, 0.088870, -0.704306, 0.922129, 0.451029,
-0.298019, 1.159339, 0.000001, -0.996033, 0.088870, 0.000000, 1.045340, 0.473093,
-0.210605, 0.903175, -0.210552, -0.705252, -0.072024, -0.705252, 0.936767, 0.254108,
-0.297820, 0.903175, 0.000001, -0.997375, -0.072024, 0.000000, 1.052424, 0.274946,
-0.180964, 0.617981, -0.180911, -0.685263, -0.246529, -0.685263, 0.956801, 0.142573,
-0.255900, 0.617981, 0.000001, -0.969115, -0.246529, 0.000000, 1.061122, 0.161660,
-0.116291, 0.378825, -0.116238, -0.610279, -0.505051, -0.610279, 0.991689, 0.079506,
-0.164439, 0.378825, 0.000001, -0.863063, -0.505051, 0.000000, 1.074527, 0.095355,
-0.059153, 0.290116, -0.059100, -0.407514, -0.817194, -0.407544, 1.038003, 0.052079,
-0.083634, 0.290116, 0.000001, -0.576312, -0.817194, 0.000000, 1.090933, 0.063214,
-0.083634, 0.290116, 0.000001, -0.576312, -0.817194, 0.000000, 0.090933, 0.063214,
-0.059153, 0.290116, -0.059100, -0.407514, -0.817194, -0.407544, 0.038003, 0.052079,
-0.000125, 0.265210, -0.000071, -0.000244, -0.999969, -0.000244, 0.127500, 0.036749,
-0.000051, 0.290116, -0.083580, 0.000000, -0.817194, -0.576312, 0.994047, 0.033372,
-0.000125, 0.265210, -0.000071, -0.000244, -0.999969, -0.000244, 1.127500, 0.036749,
-0.000051, 0.378825, -0.164386, 0.000000, -0.505051, -0.863063, 0.893772, 0.053663,
-0.000052, 0.617981, -0.255847, 0.000000, -0.246529, -0.969115, 0.836458, 0.114704,
-0.000052, 0.903175, -0.297766, 0.000000, -0.072024, -0.997375, 0.812221, 0.226047,
-0.000052, 1.159339, -0.297966, 0.000000, 0.088870, -0.996033, 0.797565, 0.423137,
-0.000052, 1.422079, -0.249421, 0.000000, 0.293832, -0.955840, 0.781975, 0.700922,
-0.000052, 1.619116, -0.158731, 0.000000, 0.562639, -0.826655, 0.759783, 0.858997,
-0.000052, 1.692897, -0.080335, 0.000000, 0.861232, -0.508194, 0.728165, 0.921879,
0.056754, 1.692897, -0.056805, 0.359325, 0.861232, -0.359325, 0.652361, 0.910987,
0.112189, 1.619116, -0.112239, 0.584552, 0.562639, -0.584552, 0.659906, 0.845090,
0.176316, 1.422079, -0.176367, 0.675863, 0.293832, -0.675863, 0.665734, 0.684958,
0.210643, 1.159339, -0.210693, 0.704306, 0.088870, -0.704306, 0.670277, 0.405750,
0.210502, 0.903175, -0.210552, 0.705252, -0.072024, -0.705252, 0.675032, 0.207335,
0.180860, 0.617981, -0.180910, 0.685263, -0.246529, -0.685263, 0.684415, 0.093825,
0.116188, 0.378825, -0.116237, 0.610279, -0.505051, -0.610279, 0.723612, 0.027920,
0.059050, 0.290116, -0.059099, 0.407483, -0.817255, -0.407422, 1.018456, 0.011649,
0.059050, 0.290116, -0.059099, 0.407483, -0.817255, -0.407422, 0.018456, 0.011649,
0.083530, 0.290116, 0.000002, 0.576159, -0.817316, 0.000000, 0.267608, 0.018648,
0.164335, 0.378825, 0.000002, 0.863063, -0.505051, 0.000000, 0.457225, 0.035136,
0.255796, 0.617981, 0.000002, 0.969115, -0.246529, 0.000000, 0.515690, 0.098717,
0.297716, 0.903175, 0.000002, 0.997375, -0.072024, 0.000000, 0.532803, 0.211513,
0.297915, 1.159339, 0.000002, 0.996033, 0.088870, 0.000000, 0.541872, 0.409545,
0.249370, 1.422079, 0.000002, 0.955840, 0.293832, 0.000000, 0.550746, 0.688375,
0.158680, 1.619116, 0.000002, 0.826655, 0.562639, 0.000000, 0.562378, 0.848006,
0.080284, 1.692897, 0.000001, 0.508194, 0.861232, 0.000000, 0.577714, 0.913236,
0.056754, 1.692897, 0.056808, 0.359325, 0.861232, 0.359325, 0.500528, 0.928224,
0.112188, 1.619116, 0.112242, 0.584552, 0.562670, 0.584552, 0.459880, 0.866817,
0.176316, 1.422079, 0.176370, 0.675863, 0.293832, 0.675863, 0.433163, 0.709519,
0.210642, 1.159339, 0.210696, 0.704306, 0.088870, 0.704306, 0.415507, 0.432148,
0.210501, 0.903175, 0.210555, 0.705252, -0.072024, 0.705252, 0.399773, 0.235351,
0.180860, 0.617981, 0.180914, 0.685263, -0.246529, 0.685263, 0.375517, 0.124299,
0.116187, 0.378825, 0.116241, 0.610279, -0.505051, 0.610279, 0.325269, 0.063109,
0.059049, 0.290116, 0.059103, 0.407361, -0.817347, 0.407392, 0.248452, 0.040330,
-0.000053, 0.290116, 0.083583, 0.000000, -0.817316, 0.576159, 0.199939, 0.056802,
-0.000053, 0.378825, 0.164389, 0.000000, -0.505051, 0.863063, 0.234895, 0.086200,
-0.000053, 0.617981, 0.255850, 0.000000, -0.246529, 0.969115, 0.262269, 0.150492,
-0.000052, 0.903175, 0.297769, 0.000000, -0.072024, 0.997375, 0.278892, 0.262611,
-0.000052, 1.159339, 0.297968, 0.000000, 0.088870, 0.996033, 0.291619, 0.459894,
-0.000052, 1.422079, 0.249424, 0.000000, 0.293832, 0.955840, 0.308400, 0.737559,
-0.000052, 1.619116, 0.158734, 0.000000, 0.562670, 0.826655, 0.340691, 0.894603,
-0.000052, 1.692897, 0.080338, 0.000000, 0.861232, 0.508194, 0.411564, 0.953109,
-0.056858, 1.692897, 0.056808, -0.359325, 0.861232, 0.359325, 0.247502, 0.980115,
-0.112293, 1.619116, 0.112242, -0.584552, 0.562670, 0.584552, 0.186319, 0.915857,
-0.176420, 1.422079, 0.176369, -0.675863, 0.293832, 0.675863, 0.173739, 0.755927,
-0.210747, 1.159339, 0.210696, -0.704306, 0.088870, 0.704306, 0.168491, 0.476740,
-0.210606, 0.903175, 0.210555, -0.705252, -0.072024, 0.705252, 0.164886, 0.278291,
-0.180964, 0.617981, 0.180913, -0.685263, -0.246529, 0.685263, 0.160531, 0.164632,
-0.116292, 0.378825, 0.116240, -0.610279, -0.505051, 0.610279, 0.153922, 0.097744,
-0.059154, 0.290116, 0.059102, -0.407453, -0.817255, 0.407483, 0.145909, 0.064880,
-0.164439, 0.378825, 0.000001, -0.863063, -0.505051, 0.000000, 0.074527, 0.095355,
-0.255900, 0.617981, 0.000001, -0.969115, -0.246529, 0.000000, 0.061122, 0.161660,
-0.297820, 0.903175, 0.000001, -0.997375, -0.072024, 0.000000, 0.052424, 0.274946,
-0.298019, 1.159339, 0.000001, -0.996033, 0.088870, 0.000000, 0.045340, 0.473093,
-0.249474, 1.422079, 0.000001, -0.955840, 0.293832, 0.000000, 0.035246, 0.751852,
-0.158784, 1.619116, 0.000001, -0.826655, 0.562639, 0.000000, 0.012361, 0.910828,
-0.056858, 1.692897, 0.056808, -0.359325, 0.861232, 0.359325, 1.247502, 0.980115,
-0.112293, 1.619116, 0.112242, -0.584552, 0.562670, 0.584552, 1.186319, 0.915857
],
"parts": [
{
"id": "mpart1",
"type": "TRIANGLES",
"indices": [
0, 1, 2, 3, 4, 5, 0, 2, 6, 3, 7, 4,
0, 6, 8, 3, 9, 7, 0, 8, 10, 3, 11, 9,
0, 10, 12, 3, 13, 11, 12, 1, 0, 3, 5, 13,
1, 5, 4, 1, 4, 2, 2, 4, 7, 2, 7, 6,
6, 7, 9, 6, 9, 8, 8, 9, 11, 8, 11, 10,
10, 11, 13, 10, 13, 12, 5, 1, 12, 5, 12, 13,
14, 15, 16, 16, 15, 17, 16, 17, 18, 18, 17, 19,
18, 19, 20, 20, 19, 21, 20, 21, 22, 22, 21, 23,
22, 23, 24, 24, 23, 25, 24, 25, 26, 26, 25, 27,
26, 27, 28, 28, 27, 29, 28, 29, 30, 31, 32, 33,
29, 34, 35, 27, 36, 34, 27, 34, 29, 25, 37, 36,
25, 36, 27, 23, 38, 37, 23, 37, 25, 21, 39, 38,
21, 38, 23, 19, 40, 39, 19, 39, 21, 17, 41, 40,
17, 40, 19, 15, 42, 41, 15, 41, 17, 15, 14, 42,
42, 14, 43, 42, 43, 44, 42, 44, 41, 41, 44, 45,
41, 45, 40, 40, 45, 46, 40, 46, 39, 39, 46, 47,
39, 47, 38, 38, 47, 48, 38, 48, 37, 37, 48, 49,
37, 49, 36, 36, 49, 50, 36, 50, 34, 34, 50, 35,
51, 52, 33, 49, 53, 52, 49, 52, 50, 48, 54, 53,
48, 53, 49, 47, 55, 54, 47, 54, 48, 46, 56, 55,
46, 55, 47, 45, 57, 56, 45, 56, 46, 44, 58, 57,
44, 57, 45, 43, 59, 58, 43, 58, 44, 43, 14, 59,
59, 14, 60, 59, 60, 61, 59, 61, 58, 58, 61, 62,
58, 62, 57, 57, 62, 63, 57, 63, 56, 56, 63, 64,
56, 64, 55, 55, 64, 65, 55, 65, 54, 54, 65, 66,
54, 66, 53, 53, 66, 67, 53, 67, 52, 52, 67, 33,
67, 68, 33, 66, 69, 68, 66, 68, 67, 65, 70, 69,
65, 69, 66, 64, 71, 70, 64, 70, 65, 63, 72, 71,
63, 71, 64, 62, 73, 72, 62, 72, 63, 61, 74, 73,
61, 73, 62, 60, 75, 74, 60, 74, 61, 60, 14, 75,
75, 14, 76, 75, 76, 77, 75, 77, 74, 74, 77, 78,
74, 78, 73, 73, 78, 79, 73, 79, 72, 72, 79, 80,
72, 80, 71, 71, 80, 81, 71, 81, 70, 70, 81, 82,
70, 82, 69, 69, 82, 83, 69, 83, 68, 68, 83, 33,
83, 31, 33, 82, 84, 31, 82, 31, 83, 81, 85, 84,
81, 84, 82, 80, 86, 85, 80, 85, 81, 79, 87, 86,
79, 86, 80, 78, 88, 87, 78, 87, 79, 77, 89, 88,
77, 88, 78, 90, 16, 18, 90, 18, 91, 90, 14, 16
]
}
]
}
],
"materials": [
{
"id": "_tree.png",
"diffuse": [ 0.900000, 0.900000, 0.900000],
"specular": [ 0.100000, 0.100000, 0.100000],
"textures": [
{
"id": "_tree.png",
"filename": "tree.png",
"type": "DIFFUSE"
}
]
}
],
"nodes": [
{
"id": "treeA_root",
"parts": [
{
"meshpartid": "mpart1",
"materialid": "_tree.png",
"uvMapping": [[ 0]]
}
]
}
],
"animations": []
}

View File

@ -0,0 +1,48 @@
package org.oscim.test.gdx.poi3d;
import org.oscim.gdx.GdxMap;
import org.oscim.gdx.GdxMapApp;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.renderer.MapRenderer;
import org.oscim.theme.VtmThemes;
import org.oscim.tiling.TileSource;
import org.oscim.tiling.source.oscimap4.OSciMap4TileSource;
public class Gdx3DTest extends GdxMap {
@Override
public void createLayers() {
MapRenderer.setBackgroundColor(0xff888888);
mMap.setMapPosition(53.1, 8.8, 1 << 15);
TileSource ts = new OSciMap4TileSource();
// initDefaultLayers(ts, false, false, false);
VectorTileLayer mMapLayer = mMap.setBaseMap(ts);
mMap.setTheme(VtmThemes.DEFAULT);
// mMap.setTheme(VtmThemes.TRONRENDER);
mMap.layers().add(new BuildingLayer(mMap, mMapLayer));
// mMap.getLayers().add(new GenericLayer(mMap, new GridRenderer()));
// ts = new OSciMap4TileSource("http://opensciencemap.org/tiles/s3db");
// VectorTileLayer tl = new VectorTileLayer(mMap, 16, 16, 20);
// tl.setTileSource(ts);
// tl.setRenderTheme(ThemeLoader.load(VtmThemes.DEFAULT));
// mMap.getLayers().add(tl);
// mMap.getLayers().add(new BuildingLayer(mMap, tl.getTileLayer()));
mMap.layers().add(new Poi3DLayer(mMap, mMapLayer));
mMap.layers().add(new LabelLayer(mMap, mMapLayer));
}
public static void main(String[] args) {
GdxMapApp.init();
GdxMapApp.run(new Gdx3DTest(), null, 320);
}
}

View File

@ -0,0 +1,237 @@
package org.oscim.test.gdx.poi3d;
import org.oscim.core.MapPosition;
import org.oscim.event.Event;
import org.oscim.layers.Layer;
import org.oscim.map.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.model.Node;
public class GdxModelLayer extends Layer implements Map.UpdateListener {
static final Logger log = LoggerFactory.getLogger(GdxModelLayer.class);
GdxRenderer3D g3d;
//VectorTileLayer mTileLayer;
public GdxModelLayer(Map map) {
super(map);
// tileLayer.addHook(new TileLoaderProcessHook() {
//
// @Override
// public boolean process(MapTile tile, ElementLayers layers, MapElement element) {
//
// if (!element.tags.contains(TREE_TAG))
// 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;
// }
// });
//mTileLayer = tileLayer;
mRenderer = g3d = new GdxRenderer3D(mMap);
// Material mat = new
// Material(ColorAttribute.createDiffuse(Color.BLUE));
// ModelBuilder modelBuilder = new ModelBuilder();
// long attributes = Usage.Position | Usage.Normal |
// Usage.TextureCoordinates;
// mModel = modelBuilder.createSphere(10f, 10f, 10f, 12, 12,
// mat, attributes);
assets = new AssetManager();
assets.load("data/g3d/test.g3db", Model.class);
loading = true;
}
// TileSet mTileSet = new TileSet();
// TileSet mPrevTiles = new TileSet();
//
// LinkedHashMap<Tile, Array<SharedModel>> mTileMap =
// new LinkedHashMap<Tile, Array<SharedModel>>();
boolean loading;
Model mModel;
AssetManager assets;
private void doneLoading() {
Model model = assets.get("data/g3d/test.g3db", Model.class);
for (int i = 0; i < model.nodes.size; i++) {
Node node = model.nodes.get(i);
log.debug("loader node " + node.id);
if (node.id.equals("test_root")) {
node = node.getChild("Building", false, false);
log.debug("loader node " + node.id);
node.rotation.setFromAxis(1, 0, 0, 90);
mModel = model;
break;
}
//}
}
loading = false;
}
@Override
public void onMapEvent(Event ev, MapPosition pos) {
// if (ev == Map.CLEAR_EVENT) {
// mTileSet = new TileSet();
// mPrevTiles = new TileSet();
// mTileMap = new LinkedHashMap<Tile, Array<SharedModel>>();
// synchronized (g3d) {
// g3d.instances.clear();
// }
// }
//
if (loading && assets.update()) {
doneLoading();
// Renderable renderable = new Renderable();
// new SharedModel(mModel).getRenderable(renderable);
// Shader shader = new DefaultShader(renderable, true, false,
// false, false, 1, 0, 0, 0);
g3d.instances.add(new SharedModel(mModel));
}
if (loading)
return;
int x = 17185 << 1;
int y = 10662 << 1;
int z = 16;
double scale = 1 / (1 << z);
g3d.cam.setMapPosition(x * scale - pos.x, y * scale - pos.y, scale / pos.scale);
//
// // log.debug("update");
//
// mTileLayer.tileRenderer().getVisibleTiles(mTileSet);
//
// if (mTileSet.cnt == 0) {
// mTileSet.releaseTiles();
// return;
// }
//
// boolean changed = false;
//
// Array<SharedModel> added = new Array<SharedModel>();
// Array<SharedModel> removed = new Array<SharedModel>();
// for (int i = 0; i < mTileSet.cnt; i++) {
// MapTile t = mTileSet.tiles[i];
// if (mPrevTiles.contains(t))
// continue;
//
// Array<SharedModel> instances = new Array<SharedModel>();
//
// Poi3DTileData ld = (Poi3DTileData) t.getData(POI_DATA);
// if (ld == null)
// continue;
//
// for (SymbolItem it : ld.symbols) {
//
// SharedModel inst = new SharedModel(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();
//
// // 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)
// continue;
//
// log.debug("add " + t + " " + instances.size);
//
// changed = true;
//
// mTileMap.put(t, instances);
// }
//
// for (int i = 0; i < mPrevTiles.cnt; i++) {
// MapTile t = mPrevTiles.tiles[i];
// if (mTileSet.contains(t))
// continue;
//
// Array<SharedModel> instances = mTileMap.get(t);
// if (instances == null)
// continue;
//
// changed = true;
//
// removed.addAll(instances);
// mTileMap.remove(t);
// log.debug("remove " + t);
// }
//
// mPrevTiles.releaseTiles();
//
// int zoom = mTileSet.tiles[0].zoomLevel;
//
// TileSet tmp = mPrevTiles;
// mPrevTiles = mTileSet;
// mTileSet = tmp;
//
// if (!changed)
// return;
//
// // scale aka tree height
// float scale = (float) (1f / (1 << (17 - zoom))) * 8;
//
// double tileX = (pos.x * (Tile.SIZE << zoom));
// double tileY = (pos.y * (Tile.SIZE << zoom));
//
// synchronized (g3d) {
//
// for (Entry<Tile, Array<SharedModel>> e : mTileMap.entrySet()) {
// Tile t = e.getKey();
//
// float dx = (float) (t.tileX * Tile.SIZE - tileX);
// float dy = (float) (t.tileY * Tile.SIZE - tileY);
//
// for (SharedModel 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;
//
// inst.transform.idt();
// inst.transform.scale(s, s, s);
// inst.transform.translate((dx + it.x) / s, (dy + it.y) / s, 0);
// inst.transform.rotate(0, 0, 1, r);
//
// // inst.transform.setToTranslationAndScaling((dx +
// // it.x), (dy + it.y),
// // 0, s, s, s);
//
// }
// }
}
}

View File

@ -0,0 +1,184 @@
package org.oscim.test.gdx.poi3d;
import org.oscim.backend.GL20;
import org.oscim.core.Tile;
import org.oscim.map.Map;
import org.oscim.map.Viewport;
import org.oscim.renderer.GLState;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.LayerRenderer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.Renderable;
import com.badlogic.gdx.graphics.g3d.Shader;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.utils.DefaultShaderProvider;
import com.badlogic.gdx.graphics.g3d.utils.DefaultTextureBinder;
import com.badlogic.gdx.graphics.g3d.utils.RenderContext;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
public class GdxModelRenderer extends LayerRenderer {
static final Logger log = LoggerFactory.getLogger(GdxModelRenderer.class);
ModelBatch modelBatch;
public MapCamera cam;
Map mMap;
boolean loading;
public Environment lights;
public Array<SharedModel> instances = new Array<SharedModel>();
public Shader shader;
public RenderContext renderContext;
public Model model;
private ModelBatch mBatch = new ModelBatch();
public GdxModelRenderer(Map map) {
mMap = map;
}
@Override
protected boolean setup() {
modelBatch = new ModelBatch(new DefaultShaderProvider());
lights = new Environment();
lights.set(new ColorAttribute(ColorAttribute.AmbientLight, 1.0f, 1.0f, 1.0f, 1.f));
lights.add(new DirectionalLight().set(0.3f, 0.3f, 0.3f, 0, 1, -0.2f));
cam = new MapCamera(mMap);
renderContext =
new RenderContext(new DefaultTextureBinder(DefaultTextureBinder.WEIGHTED, 1));
// shader = new DefaultShader(renderable.material,
// renderable.mesh.getVertexAttributes(), true, false, 1, 0, 0, 0);
// shader.init();
return true;
}
@Override
protected synchronized void update(GLViewport v) {
// if (loading && assets.update())
// doneLoading();
if (!isReady()) {
cam.setPosition(v.pos);
setReady(true);
}
// if (changed) {
// cam.update(position, matrices);
// }
}
Vector3 tempVector = new Vector3();
float[] mBox = new float[8];
Renderable r = new Renderable();
@Override
protected void render(GLViewport v) {
if (instances.size == 0)
return;
// GLUtils.checkGlError(">" + TAG);
GL.glDepthMask(true);
if (v.pos.zoomLevel < 16)
GL.glClear(GL20.GL_DEPTH_BUFFER_BIT);
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
// set state that is expected after modelBatch.end();
// modelBatch keeps track of its own state
GLState.enableVertexArrays(-1, -1);
GLState.bindTex2D(-1);
GLState.useProgram(-1);
GLState.test(false, false);
GLState.blend(false);
cam.update(v);
Viewport p = mMap.viewport();
p.getMapExtents(mBox, 10);
float scale = (float) (cam.mMapPosition.scale / v.pos.scale);
float dx = (float) (cam.mMapPosition.x - v.pos.x)
* (Tile.SIZE << cam.mMapPosition.zoomLevel);
float dy = (float) (cam.mMapPosition.y - v.pos.y)
* (Tile.SIZE << cam.mMapPosition.zoomLevel);
for (int i = 0; i < 8; i += 2) {
mBox[i] *= scale;
mBox[i] -= dx;
mBox[i + 1] *= scale;
mBox[i + 1] -= dy;
}
//int w = mMap.getWidth() / 2;
//int h = mMap.getHeight() / 2;
//float sqRadius = (w * w + h * h) / scale;
synchronized (this) {
if (instances.size == 0)
return;
//renderContext.begin();
// if (shader == null) {
// r = instances.get(0).getRenderable(r);
// DefaultShader.Config c = new DefaultShader.Config();
// c.numBones = 0;
// c.numDirectionalLights = 1;
// r.environment = lights;
//
// shader = new DefaultShader(r, c);
// shader.init();
// }
mBatch.begin(cam);
//shader.begin(cam, renderContext);
for (SharedModel instance : instances) {
instance.transform.getTranslation(tempVector);
//instance.getRenderables(renderables, pool);
// if (tempVector.x * tempVector.x + tempVector.y * tempVector.y > sqRadius)
// continue;
// tempVector.scl(0.8f, 0.8f, 1);
// if (!GeometryUtils.pointInPoly(tempVector.x, tempVector.y, mBox, 8, 0))
// continue;
mBatch.render(instance);
//shader.render(r);
}
mBatch.end();
//shader.end();
//renderContext.end();
}
GL.glDepthMask(false);
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
GL.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
}
// @Override
// public void dispose () {
// modelBatch.dispose();
// assets.dispose();
// assets = null;
// axesModel.dispose();
// axesModel = null;
// }
}

View File

@ -0,0 +1,198 @@
package org.oscim.test.gdx.poi3d;
import org.oscim.backend.GL20;
import org.oscim.core.Tile;
import org.oscim.map.Map;
import org.oscim.map.Viewport;
import org.oscim.renderer.GLState;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.LayerRenderer;
import org.oscim.utils.geom.GeometryUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.Renderable;
import com.badlogic.gdx.graphics.g3d.Shader;
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.shaders.DefaultShader;
import com.badlogic.gdx.graphics.g3d.utils.DefaultShaderProvider;
import com.badlogic.gdx.graphics.g3d.utils.DefaultTextureBinder;
import com.badlogic.gdx.graphics.g3d.utils.RenderContext;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
public class GdxRenderer3D extends LayerRenderer {
static final Logger log = LoggerFactory.getLogger(GdxRenderer3D.class);
ModelBatch modelBatch;
public MapCamera cam;
Map mMap;
boolean loading;
public Environment lights;
public Array<SharedModel> instances = new Array<SharedModel>();
public Shader shader;
public RenderContext renderContext;
public Model model;
public GdxRenderer3D(Map map) {
mMap = map;
}
@Override
protected boolean setup() {
modelBatch = new ModelBatch(new DefaultShaderProvider());
lights = new Environment();
lights.set(new ColorAttribute(ColorAttribute.AmbientLight, 1.0f, 1.0f, 1.0f, 1.f));
lights.add(new DirectionalLight().set(0.3f, 0.3f, 0.3f, 0, 1, -0.2f));
cam = new MapCamera(mMap);
renderContext =
new RenderContext(new DefaultTextureBinder(DefaultTextureBinder.WEIGHTED, 1));
// shader = new DefaultShader(renderable.material,
// renderable.mesh.getVertexAttributes(), true, false, 1, 0, 0, 0);
// shader.init();
return true;
}
@Override
protected synchronized void update(GLViewport v) {
// if (loading && assets.update())
// doneLoading();
if (!isReady()) {
cam.setPosition(v.pos);
setReady(true);
}
// if (changed) {
// cam.update(position, matrices);
// }
}
Vector3 tempVector = new Vector3();
float[] mBox = new float[8];
Renderable r = new Renderable();
@Override
protected void render(GLViewport v) {
if (instances.size == 0)
return;
// GLUtils.checkGlError(">" + TAG);
GL.glDepthMask(true);
// if (position.zoomLevel < 17)
// GL.glClear(GL20.GL_DEPTH_BUFFER_BIT);
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
// set state that is expected after modelBatch.end();
// modelBatch keeps track of its own state
GLState.enableVertexArrays(-1, -1);
GLState.bindTex2D(-1);
GLState.useProgram(-1);
GLState.test(false, false);
GLState.blend(false);
cam.update(v);
long time = System.currentTimeMillis();
int cnt = 0;
int rnd = 0;
Viewport p = mMap.viewport();
p.getMapExtents(mBox, 10);
float scale = (float) (cam.mMapPosition.scale / v.pos.scale);
float dx = (float) (cam.mMapPosition.x - v.pos.x)
* (Tile.SIZE << cam.mMapPosition.zoomLevel);
float dy = (float) (cam.mMapPosition.y - v.pos.y)
* (Tile.SIZE << cam.mMapPosition.zoomLevel);
for (int i = 0; i < 8; i += 2) {
mBox[i] *= scale;
mBox[i] -= dx;
mBox[i + 1] *= scale;
mBox[i + 1] -= dy;
}
int w = mMap.getWidth() / 2;
int h = mMap.getHeight() / 2;
float sqRadius = (w * w + h * h) / scale;
synchronized (this) {
if (instances.size == 0)
return;
cnt = instances.size;
renderContext.begin();
if (shader == null) {
r = instances.get(0).getRenderable(r);
DefaultShader.Config c = new DefaultShader.Config();
c.numBones = 0;
c.numDirectionalLights = 1;
r.environment = lights;
// shader = new DefaultShader(r, true, false, false, false, 1,
// 0, 0, 0);
shader = new DefaultShader(r, c);
shader.init();
}
shader.begin(cam, renderContext);
for (SharedModel instance : instances) {
instance.transform.getTranslation(tempVector);
if (tempVector.x * tempVector.x + tempVector.y * tempVector.y > sqRadius)
continue;
tempVector.scl(0.8f, 0.8f, 1);
if (!GeometryUtils.pointInPoly(tempVector.x, tempVector.y, mBox, 8, 0))
continue;
instance.getRenderable(r);
// r.lights = lights;
// r.environment = lights;
shader.render(r);
rnd++;
}
shader.end();
renderContext.end();
}
log.debug(">>> " + (System.currentTimeMillis() - time) + " " + cnt + "/" + rnd);
GL.glDepthMask(false);
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
GL.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
}
// @Override
// public void dispose () {
// modelBatch.dispose();
// assets.dispose();
// assets = null;
// axesModel.dispose();
// axesModel = null;
// }
}

View File

@ -0,0 +1,164 @@
package org.oscim.test.gdx.poi3d;
import org.oscim.backend.GL20;
import org.oscim.core.Tile;
import org.oscim.map.Map;
import org.oscim.map.Viewport;
import org.oscim.renderer.GLState;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.LayerRenderer;
import org.oscim.utils.geom.GeometryUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.badlogic.gdx.graphics.g3d.Environment;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
import com.badlogic.gdx.graphics.g3d.utils.DefaultShaderProvider;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
public class GdxRenderer3D2 extends LayerRenderer {
static final Logger log = LoggerFactory.getLogger(GdxRenderer3D2.class);
ModelBatch modelBatch;
public MapCamera cam;
Map mMap;
boolean loading;
public Environment lights;
public Array<SharedModel> instances = new Array<SharedModel>();
public GdxRenderer3D2(Map map) {
mMap = map;
}
@Override
protected boolean setup() {
// if (assets == null)
// assets = new AssetManager();
// assets.load("data/g3d/invaders.g3dj", Model.class);
// loading = true;
modelBatch = new ModelBatch(new DefaultShaderProvider());
lights = new Environment();
// lights.ambientLight.set(1.0f, 1.0f, 1.0f, 1f);
// lights.ambientLight.set(215 / 255f,
// 240 / 255f,
// 51 / 255f, 1f);
lights.add(new DirectionalLight().set(0.9f, 0.9f, 0.9f, 0, 1, -0.2f));
cam = new MapCamera(mMap);
return true;
}
@Override
protected synchronized void update(GLViewport v) {
// if (loading && assets.update())
// doneLoading();
if (!isReady()) {
cam.setPosition(v.pos);
setReady(true);
}
// if (changed) {
// cam.update(position, matrices);
// }
}
Vector3 tempVector = new Vector3();
float[] mBox = new float[8];
@Override
protected void render(GLViewport v) {
if (instances.size == 0)
return;
// GLUtils.checkGlError(">" + TAG);
GL.glDepthMask(true);
if (v.pos.zoomLevel < 17)
GL.glClear(GL20.GL_DEPTH_BUFFER_BIT);
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
// set state that is expected after modelBatch.end();
// modelBatch keeps track of its own state
GLState.enableVertexArrays(-1, -1);
GLState.bindTex2D(-1);
GLState.useProgram(-1);
GLState.test(false, false);
GLState.blend(false);
// GL.glCullFace(GL20.GL_BACK);
// GL.glFrontFace(GL20.GL_CW);
cam.update(v);
long time = System.currentTimeMillis();
int cnt = 0;
int rnd = 0;
Viewport p = mMap.viewport();
p.getMapExtents(mBox, 10);
float scale = (float) (cam.mMapPosition.scale / v.pos.scale);
float dx =
(float) (cam.mMapPosition.x - v.pos.x)
* (Tile.SIZE << cam.mMapPosition.zoomLevel);
float dy =
(float) (cam.mMapPosition.y - v.pos.y)
* (Tile.SIZE << cam.mMapPosition.zoomLevel);
for (int i = 0; i < 8; i += 2) {
mBox[i] *= scale;
mBox[i] -= dx;
mBox[i + 1] *= scale;
mBox[i + 1] -= dy;
}
synchronized (this) {
modelBatch.begin(cam);
cnt = instances.size;
for (SharedModel instance : instances) {
instance.transform.getTranslation(tempVector);
tempVector.scl(0.9f, 0.9f, 1);
if (!GeometryUtils.pointInPoly(tempVector.x, tempVector.y, mBox, 8, 0))
continue;
modelBatch.render(instance);
rnd++;
}
modelBatch.end();
}
log.debug(">>> " + (System.currentTimeMillis() - time) + " " + cnt + "/" + rnd);
// GLUtils.checkGlError("<" + TAG);
GL.glDepthMask(false);
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
GL.glBindBuffer(GL20.GL_ARRAY_BUFFER, 0);
// GLState.bindTex2D(-1);
// GLState.useProgram(-1);
}
// @Override
// public void dispose () {
// modelBatch.dispose();
// assets.dispose();
// assets = null;
// axesModel.dispose();
// axesModel = null;
// }
}

View File

@ -0,0 +1,70 @@
package org.oscim.test.gdx.poi3d;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.map.Map;
import org.oscim.renderer.GLViewport;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.math.Matrix4;
public class MapCamera extends Camera {
private final Map mMap;
public MapCamera(Map map) {
mMap = map;
this.near = 1;
this.far = 8;
}
MapPosition mMapPosition = new MapPosition();
public void setPosition(MapPosition pos) {
mMapPosition.copy(pos);
this.viewportWidth = mMap.getWidth();
this.viewportHeight = mMap.getHeight();
}
public void setMapPosition(double x, double y, double scale) {
mMapPosition.setScale(scale);
mMapPosition.x = x;
mMapPosition.y = y;
}
public void update(GLViewport v) {
double scale = (v.pos.scale * Tile.SIZE);
float x = (float) ((mMapPosition.x - v.pos.x) * scale);
float y = (float) ((mMapPosition.y - v.pos.y) * scale);
float z = (float) (v.pos.scale / mMapPosition.scale);
v.proj.get(projection.getValues());
v.mvp.setTransScale(x, y, z);
v.mvp.setValue(10, z);
v.mvp.multiplyLhs(v.view);
v.mvp.get(view.getValues());
combined.set(projection);
Matrix4.mul(combined.val, view.val);
//if (updateFrustum) {
invProjectionView.set(combined);
Matrix4.inv(invProjectionView.val);
frustum.update(invProjectionView);
//}
}
@Override
public void update() {
}
@Override
public void update(boolean updateFrustum) {
}
}

View File

@ -0,0 +1,258 @@
package org.oscim.test.gdx.poi3d;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import org.oscim.core.MapElement;
import org.oscim.core.MapPosition;
import org.oscim.core.PointF;
import org.oscim.core.Tag;
import org.oscim.core.Tile;
import org.oscim.event.Event;
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.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderProcessHook;
import org.oscim.map.Map;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.renderer.elements.SymbolItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.model.Node;
import com.badlogic.gdx.utils.Array;
public class Poi3DLayer extends Layer implements Map.UpdateListener {
static final Logger log = LoggerFactory.getLogger(Poi3DLayer.class);
static class Poi3DTileData extends TileData {
public final List<SymbolItem> symbols = new List<SymbolItem>();
@Override
protected void dispose() {
SymbolItem.pool.releaseAll(symbols.clear());
}
}
final static String POI_DATA = Poi3DLayer.class.getSimpleName();
final static Tag TREE_TAG = new Tag("natural", "tree");
private Poi3DTileData get(MapTile tile) {
Poi3DTileData ld = (Poi3DTileData) tile.getData(POI_DATA);
if (ld == null) {
ld = new Poi3DTileData();
tile.addData(POI_DATA, ld);
}
return ld;
}
GdxRenderer3D g3d;
VectorTileLayer mTileLayer;
public Poi3DLayer(Map map, VectorTileLayer tileLayer) {
super(map);
tileLayer.addHook(new TileLoaderProcessHook() {
@Override
public boolean process(MapTile tile, ElementLayers layers, MapElement element) {
if (!element.tags.contains(TREE_TAG))
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;
}
});
mTileLayer = tileLayer;
mRenderer = g3d = new GdxRenderer3D(mMap);
// Material mat = new
// Material(ColorAttribute.createDiffuse(Color.BLUE));
// ModelBuilder modelBuilder = new ModelBuilder();
// long attributes = Usage.Position | Usage.Normal |
// Usage.TextureCoordinates;
// mModel = modelBuilder.createSphere(10f, 10f, 10f, 12, 12,
// mat, attributes);
assets = new AssetManager();
assets.load("data/g3d/treeA.g3dj", Model.class);
loading = true;
}
TileSet mTileSet = new TileSet();
TileSet mPrevTiles = new TileSet();
LinkedHashMap<Tile, Array<SharedModel>> mTileMap =
new LinkedHashMap<Tile, Array<SharedModel>>();
boolean loading;
Model mModel;
AssetManager assets;
private void doneLoading() {
Model model = assets.get("data/g3d/treeA.g3dj", Model.class);
for (int i = 0; i < model.nodes.size; i++) {
Node node = model.nodes.get(i);
if (node.id.equals("treeA_root")) {
node.rotation.setFromAxis(1, 0, 0, 90);
mModel = model;
}
}
loading = false;
}
@Override
public void onMapEvent(Event ev, MapPosition pos) {
if (ev == Map.CLEAR_EVENT) {
mTileSet = new TileSet();
mPrevTiles = new TileSet();
mTileMap = new LinkedHashMap<Tile, Array<SharedModel>>();
synchronized (g3d) {
g3d.instances.clear();
}
}
if (loading && assets.update()) {
doneLoading();
// Renderable renderable = new Renderable();
// new SharedModel(mModel).getRenderable(renderable);
// Shader shader = new DefaultShader(renderable, true, false,
// false, false, 1, 0, 0, 0);
}
if (loading)
return;
// log.debug("update");
mTileLayer.tileRenderer().getVisibleTiles(mTileSet);
if (mTileSet.cnt == 0) {
mTileSet.releaseTiles();
return;
}
boolean changed = false;
Array<SharedModel> added = new Array<SharedModel>();
Array<SharedModel> removed = new Array<SharedModel>();
for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = mTileSet.tiles[i];
if (mPrevTiles.contains(t))
continue;
Array<SharedModel> instances = new Array<SharedModel>();
Poi3DTileData ld = (Poi3DTileData) t.getData(POI_DATA);
if (ld == null)
continue;
for (SymbolItem it : ld.symbols) {
SharedModel inst = new SharedModel(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();
// 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)
continue;
log.debug("add " + t + " " + instances.size);
changed = true;
mTileMap.put(t, instances);
}
for (int i = 0; i < mPrevTiles.cnt; i++) {
MapTile t = mPrevTiles.tiles[i];
if (mTileSet.contains(t))
continue;
Array<SharedModel> instances = mTileMap.get(t);
if (instances == null)
continue;
changed = true;
removed.addAll(instances);
mTileMap.remove(t);
log.debug("remove " + t);
}
mPrevTiles.releaseTiles();
int zoom = mTileSet.tiles[0].zoomLevel;
TileSet tmp = mPrevTiles;
mPrevTiles = mTileSet;
mTileSet = tmp;
if (!changed)
return;
// scale aka tree height
float scale = (float) (1f / (1 << (17 - zoom))) * 8;
double tileX = (pos.x * (Tile.SIZE << zoom));
double tileY = (pos.y * (Tile.SIZE << zoom));
synchronized (g3d) {
for (Entry<Tile, Array<SharedModel>> e : mTileMap.entrySet()) {
Tile t = e.getKey();
float dx = (float) (t.tileX * Tile.SIZE - tileX);
float dy = (float) (t.tileY * Tile.SIZE - tileY);
for (SharedModel 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;
inst.transform.idt();
inst.transform.scale(s, s, s);
inst.transform.translate((dx + it.x) / s, (dy + it.y) / s, 0);
inst.transform.rotate(0, 0, 1, r);
// inst.transform.setToTranslationAndScaling((dx +
// it.x), (dy + it.y),
// 0, s, s, s);
}
}
g3d.instances.removeAll(removed, true);
g3d.instances.addAll(added);
g3d.cam.setMapPosition(pos.x, pos.y, 1 << zoom);
}
}
}

View File

@ -0,0 +1,461 @@
package org.oscim.test.gdx.poi3d;
import com.badlogic.gdx.graphics.g3d.Material;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.Renderable;
import com.badlogic.gdx.graphics.g3d.RenderableProvider;
import com.badlogic.gdx.graphics.g3d.model.Animation;
import com.badlogic.gdx.graphics.g3d.model.MeshPart;
import com.badlogic.gdx.graphics.g3d.model.Node;
import com.badlogic.gdx.graphics.g3d.model.NodeAnimation;
import com.badlogic.gdx.graphics.g3d.model.NodeKeyframe;
import com.badlogic.gdx.graphics.g3d.model.NodePart;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ArrayMap;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.Pool;
/** An instance of a {@link Model}, allows to specify global transform and modify the materials, as it
* has a copy of the model's materials. Multiple instances can be created from the same Model,
* all sharing the meshes and textures of the Model. The Model owns the meshes and textures, to
* dispose of these, the Model has to be disposed. Therefor, the Model must outlive all its ModelInstances</p>
*
* The ModelInstance creates a full copy of all materials, nodes and animations.
* @author badlogic, xoppa */
public class SharedModel implements RenderableProvider {
/** the materials of the model, used by nodes that have a graphical representation FIXME not sure if superfluous, allows modification of materials without having to traverse the nodes **/
public final Array<Material> materials = new Array<Material>();
/** root nodes of the model **/
public final Array<Node> nodes = new Array<Node>();
/** animations of the model, modifying node transformations **/
public final Array<Animation> animations = new Array<Animation>();
/** the {@link Model} this instances derives from **/
public final Model model;
/** the world transform **/
public Matrix4 transform;
/** user definable value, which is passed to the shader. */
public Object userData;
/** Constructs a new ModelInstance with all nodes and materials of the given model.
* @param model The {@link Model} to create an instance of. */
public SharedModel(final Model model) {
this(model, (String[])null);
}
/** @param model The source {@link Model}
* @param nodeId The ID of the root {@link Node} of the {@link Model} for the instance to contain
* @param mergeTransform True to apply the source node transform to the instance transform, resetting the node transform. */
public SharedModel(final Model model, final String nodeId, boolean mergeTransform) {
this(model, null, nodeId, false, false, mergeTransform);
}
/** @param model The source {@link Model}
* @param transform The {@link Matrix4} instance for this ModelInstance to reference or null to create a new matrix.
* @param nodeId The ID of the root {@link Node} of the {@link Model} for the instance to contain
* @param mergeTransform True to apply the source node transform to the instance transform, resetting the node transform. */
public SharedModel(final Model model, final Matrix4 transform, final String nodeId, boolean mergeTransform) {
this(model, transform, nodeId, false, false, mergeTransform);
}
/** Recursively searches the mode for the specified node.
* @param model The source {@link Model}
* @param nodeId The ID of the {@link Node} within the {@link Model} for the instance to contain
* @param parentTransform True to apply the parent's node transform to the instance (only applicable if recursive is true).
* @param mergeTransform True to apply the source node transform to the instance transform, resetting the node transform. */
public SharedModel(final Model model, final String nodeId, boolean parentTransform, boolean mergeTransform) {
this(model, null, nodeId, true, parentTransform, mergeTransform);
}
/** Recursively searches the mode for the specified node.
* @param model The source {@link Model}
* @param transform The {@link Matrix4} instance for this ModelInstance to reference or null to create a new matrix.
* @param nodeId The ID of the {@link Node} within the {@link Model} for the instance to contain
* @param parentTransform True to apply the parent's node transform to the instance (only applicable if recursive is true).
* @param mergeTransform True to apply the source node transform to the instance transform, resetting the node transform. */
public SharedModel(final Model model, final Matrix4 transform, final String nodeId, boolean parentTransform, boolean mergeTransform) {
this(model, transform, nodeId, true, parentTransform, mergeTransform);
}
/** @param model The source {@link Model}
* @param nodeId The ID of the {@link Node} within the {@link Model} for the instance to contain
* @param recursive True to recursively search the Model's node tree, false to only search for a root node
* @param parentTransform True to apply the parent's node transform to the instance (only applicable if recursive is true).
* @param mergeTransform True to apply the source node transform to the instance transform, resetting the node transform. */
public SharedModel(final Model model, final String nodeId, boolean recursive, boolean parentTransform, boolean mergeTransform) {
this(model, null, nodeId, recursive, parentTransform, mergeTransform);
}
/** @param model The source {@link Model}
* @param transform The {@link Matrix4} instance for this ModelInstance to reference or null to create a new matrix.
* @param nodeId The ID of the {@link Node} within the {@link Model} for the instance to contain
* @param recursive True to recursively search the Model's node tree, false to only search for a root node
* @param parentTransform True to apply the parent's node transform to the instance (only applicable if recursive is true).
* @param mergeTransform True to apply the source node transform to the instance transform, resetting the node transform. */
public SharedModel(final Model model, final Matrix4 transform, final String nodeId, boolean recursive, boolean parentTransform, boolean mergeTransform) {
this.model = model;
this.transform = transform == null ? new Matrix4() : transform;
nodePartBones.clear();
Node copy, node = model.getNode(nodeId, recursive);
this.nodes.add(copy = copyNode(null, node));
if (mergeTransform) {
this.transform.mul(parentTransform ? node.globalTransform : node.localTransform);
copy.translation.set(0,0,0);
copy.rotation.idt();
copy.scale.set(1,1,1);
} else if (parentTransform && copy.parent != null)
this.transform.mul(node.parent.globalTransform);
setBones();
copyAnimations(model.animations);
calculateTransforms();
}
/** Constructs a new ModelInstance with only the specified nodes and materials of the given model. */
public SharedModel(final Model model, final String... rootNodeIds) {
this(model, null, rootNodeIds);
}
/** Constructs a new ModelInstance with only the specified nodes and materials of the given model. */
public SharedModel(final Model model, final Matrix4 transform, final String... rootNodeIds) {
this.model = model;
this.transform = transform == null ? new Matrix4() : transform;
if (rootNodeIds == null)
copyNodes(model.nodes);
else
copyNodes(model.nodes, rootNodeIds);
copyAnimations(model.animations);
calculateTransforms();
}
/** Constructs a new ModelInstance with only the specified nodes and materials of the given model. */
public SharedModel(final Model model, final Array<String> rootNodeIds) {
this(model, null, rootNodeIds);
}
/** Constructs a new ModelInstance with only the specified nodes and materials of the given model. */
public SharedModel(final Model model, final Matrix4 transform, final Array<String> rootNodeIds) {
this.model = model;
this.transform = transform == null ? new Matrix4() : transform;
copyNodes(model.nodes, rootNodeIds);
copyAnimations(model.animations);
calculateTransforms();
}
/** Constructs a new ModelInstance at the specified position. */
public SharedModel(final Model model, Vector3 position) {
this(model);
this.transform.setToTranslation(position);
}
/** Constructs a new ModelInstance at the specified position. */
public SharedModel(final Model model, float x, float y, float z) {
this(model);
this.transform.setToTranslation(x, y, z);
}
/** Constructs a new ModelInstance with the specified transform. */
public SharedModel(final Model model, Matrix4 transform) {
this(model, transform, (String[])null);
}
/** Constructs a new ModelInstance which is an copy of the specified ModelInstance. */
public SharedModel(SharedModel copyFrom) {
this(copyFrom, copyFrom.transform.cpy());
}
/** Constructs a new ModelInstance which is an copy of the specified ModelInstance. */
public SharedModel(SharedModel copyFrom, final Matrix4 transform) {
this.model = copyFrom.model;
this.transform = transform == null ? new Matrix4() : transform;
copyNodes(copyFrom.nodes);
copyAnimations(copyFrom.animations);
calculateTransforms();
}
/** @return A newly created ModelInstance which is a copy of this ModelInstance */
public SharedModel copy() {
return new SharedModel(this);
}
private ObjectMap<NodePart, ArrayMap<Node, Matrix4>> nodePartBones = new ObjectMap<NodePart, ArrayMap<Node, Matrix4>>();
private void copyNodes (Array<Node> nodes) {
nodePartBones.clear();
for(int i = 0, n = nodes.size; i<n; ++i) {
final Node node = nodes.get(i);
this.nodes.add(copyNode(null, node));
}
setBones();
}
private void copyNodes (Array<Node> nodes, final String... nodeIds) {
nodePartBones.clear();
for(int i = 0, n = nodes.size; i<n; ++i) {
final Node node = nodes.get(i);
for (final String nodeId : nodeIds) {
if (nodeId.equals(node.id)) {
this.nodes.add(copyNode(null, node));
break;
}
}
}
setBones();
}
private void copyNodes (Array<Node> nodes, final Array<String> nodeIds) {
nodePartBones.clear();
for(int i = 0, n = nodes.size; i<n; ++i) {
final Node node = nodes.get(i);
for (final String nodeId : nodeIds) {
if (nodeId.equals(node.id)) {
this.nodes.add(copyNode(null, node));
break;
}
}
}
setBones();
}
private void setBones() {
for (ObjectMap.Entry<NodePart,ArrayMap<Node, Matrix4>> e : nodePartBones.entries()) {
if (e.key.invBoneBindTransforms == null)
e.key.invBoneBindTransforms = new ArrayMap<Node, Matrix4>(true, e.value.size, Node.class, Matrix4.class);
e.key.invBoneBindTransforms.clear();
for (final ObjectMap.Entry<Node, Matrix4> b : e.value.entries())
e.key.invBoneBindTransforms.put(getNode(b.key.id), b.value); // Share the inv bind matrix with the model
e.key.bones = new Matrix4[e.value.size];
for (int i = 0; i < e.key.bones.length; i++)
e.key.bones[i] = new Matrix4();
}
}
private Node copyNode(Node parent, Node node) {
Node copy = new Node();
copy.id = node.id;
//copy.boneId = node.boneId;
copy.parent = parent;
copy.translation.set(node.translation);
copy.rotation.set(node.rotation);
copy.scale.set(node.scale);
copy.localTransform.set(node.localTransform);
copy.globalTransform.set(node.globalTransform);
for(NodePart nodePart: node.parts) {
copy.parts.add(copyNodePart(nodePart));
}
for(Node child: node.children) {
copy.children.add(copyNode(copy, child));
}
return copy;
}
private NodePart copyNodePart (NodePart nodePart) {
NodePart copy = new NodePart();
copy.meshPart = new MeshPart();
copy.meshPart.id = nodePart.meshPart.id;
copy.meshPart.indexOffset = nodePart.meshPart.indexOffset;
copy.meshPart.numVertices = nodePart.meshPart.numVertices;
copy.meshPart.primitiveType = nodePart.meshPart.primitiveType;
copy.meshPart.mesh = nodePart.meshPart.mesh;
if (nodePart.invBoneBindTransforms != null)
nodePartBones.put(copy, nodePart.invBoneBindTransforms);
// final int index = materials.indexOf(nodePart.material, false);
// if (index < 0)
// materials.add(copy.material = nodePart.material.copy());
// else
// copy.material = materials.get(index);
//
copy.material = nodePart.material;
return copy;
}
private void copyAnimations (final Iterable<Animation> source) {
for (final Animation anim : source) {
Animation animation = new Animation();
animation.id = anim.id;
animation.duration = anim.duration;
for (final NodeAnimation nanim : anim.nodeAnimations) {
final Node node = getNode(nanim.node.id);
if (node == null)
continue;
NodeAnimation nodeAnim = new NodeAnimation();
nodeAnim.node = node;
for (final NodeKeyframe kf : nanim.keyframes) {
NodeKeyframe keyframe = new NodeKeyframe();
keyframe.keytime = kf.keytime;
keyframe.rotation.set(kf.rotation);
keyframe.scale.set(kf.scale);
keyframe.translation.set(kf.translation);
nodeAnim.keyframes.add(keyframe);
}
if (nodeAnim.keyframes.size > 0)
animation.nodeAnimations.add(nodeAnim);
}
if (animation.nodeAnimations.size > 0)
animations.add(animation);
}
}
/**
* Traverses the Node hierarchy and collects {@link Renderable} instances for every
* node with a graphical representation. Renderables are obtained from the provided
* pool. The resulting array can be rendered via a {@link ModelBatch}.
*
* @param renderables the output array
* @param pool the pool to obtain Renderables from
*/
public void getRenderables(Array<Renderable> renderables, Pool<Renderable> pool) {
for(Node node: nodes) {
getRenderables(node, renderables, pool);
}
}
/** @return The renderable of the first node's first part. */
public Renderable getRenderable(final Renderable out) {
return getRenderable(out, nodes.get(0));
}
/** @return The renderable of the node's first part. */
public Renderable getRenderable(final Renderable out, final Node node) {
return getRenderable(out, node, node.parts.get(0));
}
public Renderable getRenderable(final Renderable out, final Node node, final NodePart nodePart) {
nodePart.setRenderable(out);
if (nodePart.bones == null && transform != null)
out.worldTransform.set(transform).mul(node.globalTransform);
else if (transform != null)
out.worldTransform.set(transform);
else
out.worldTransform.idt();
out.userData = userData;
return out;
}
protected void getRenderables(Node node, Array<Renderable> renderables, Pool<Renderable> pool) {
if(node.parts.size > 0) {
for(NodePart nodePart: node.parts) {
renderables.add(getRenderable(pool.obtain(), node, nodePart));
}
}
for(Node child: node.children) {
getRenderables(child, renderables, pool);
}
}
/** Calculates the local and world transform of all {@link Node} instances in this model, recursively.
* First each {@link Node#localTransform} transform is calculated based on the translation, rotation and
* scale of each Node. Then each {@link Node#calculateWorldTransform()}
* is calculated, based on the parent's world transform and the local transform of each Node.
* Finally, the animation bone matrices are updated accordingly.</p>
*
* This method can be used to recalculate all transforms if any of the Node's local properties (translation, rotation, scale)
* was modified.
*/
public void calculateTransforms() {
final int n = nodes.size;
for(int i = 0; i < n; i++) {
nodes.get(i).calculateTransforms(true);
}
for(int i = 0; i < n; i++) {
nodes.get(i).calculateBoneTransforms(true);
}
}
/** Calculate the bounding box of this model instance.
* This is a potential slow operation, it is advised to cache the result.
* @param out the {@link BoundingBox} that will be set with the bounds.
* @return the out parameter for chaining */
public BoundingBox calculateBoundingBox(final BoundingBox out) {
out.inf();
return extendBoundingBox(out);
}
/** Extends the bounding box with the bounds of this model instance.
* This is a potential slow operation, it is advised to cache the result.
* @param out the {@link BoundingBox} that will be extended with the bounds.
* @return the out parameter for chaining */
public BoundingBox extendBoundingBox(final BoundingBox out) {
final int n = nodes.size;
for(int i = 0; i < n; i++)
nodes.get(i).extendBoundingBox(out);
return out;
}
/** @param id The ID of the animation to fetch (case sensitive).
* @return The {@link Animation} with the specified id, or null if not available. */
public Animation getAnimation(final String id) {
return getAnimation(id, true);
}
/** @param id The ID of the animation to fetch.
* @param ignoreCase whether to use case sensitivity when comparing the animation id.
* @return The {@link Animation} with the specified id, or null if not available. */
public Animation getAnimation(final String id, boolean ignoreCase) {
final int n = animations.size;
Animation animation;
if (ignoreCase) {
for (int i = 0; i < n; i++)
if ((animation = animations.get(i)).id.equalsIgnoreCase(id))
return animation;
} else {
for (int i = 0; i < n; i++)
if ((animation = animations.get(i)).id.equals(id))
return animation;
}
return null;
}
// /** @param id The ID of the material to fetch.
// * @return The {@link Material} with the specified id, or null if not available. */
// public Material getMaterial(final String id) {
// return getMaterial(id, true);
// }
//
// /** @param id The ID of the material to fetch.
// * @param ignoreCase whether to use case sensitivity when comparing the material id.
// * @return The {@link Material} with the specified id, or null if not available. */
// public Material getMaterial(final String id, boolean ignoreCase) {
// final int n = materials.size;
// Material material;
// if (ignoreCase) {
// for (int i = 0; i < n; i++)
// if ((material = materials.get(i)).id.equalsIgnoreCase(id))
// return material;
// } else {
// for (int i = 0; i < n; i++)
// if ((material = materials.get(i)).id.equals(id))
// return material;
// }
// return null;
// }
/** @param id The ID of the node to fetch.
* @return The {@link Node} with the specified id, or null if not found. */
public Node getNode(final String id) {
return getNode(id, true);
}
/** @param id The ID of the node to fetch.
* @param recursive false to fetch a root node only, true to search the entire node tree for the specified node.
* @return The {@link Node} with the specified id, or null if not found. */
public Node getNode(final String id, boolean recursive) {
return getNode(id, recursive, false);
}
/** @param id The ID of the node to fetch.
* @param recursive false to fetch a root node only, true to search the entire node tree for the specified node.
* @param ignoreCase whether to use case sensitivity when comparing the node id.
* @return The {@link Node} with the specified id, or null if not found. */
public Node getNode(final String id, boolean recursive, boolean ignoreCase) {
return Node.getNode(nodes, id, recursive, ignoreCase);
}
}