vtm-gdx-poi3d module (#600)

This commit is contained in:
Izumi Kawashima
2018-11-07 15:12:07 +09:00
committed by Emux
parent e20b941eb9
commit 09a6cf2497
17 changed files with 57 additions and 12 deletions

View File

@@ -19,6 +19,7 @@ package org.oscim.test.gdx.poi3d;
import org.oscim.core.MapPosition;
import org.oscim.gdx.GdxMapApp;
import org.oscim.gdx.poi3d.Poi3DLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;

View File

@@ -1,181 +0,0 @@
/*
* Copyright 2014 Hannes Janetzek
* 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.assets.AssetManager;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.model.Node;
import com.badlogic.gdx.utils.Array;
import org.oscim.core.MapPosition;
import org.oscim.core.MercatorProjection;
import org.oscim.core.Tile;
import org.oscim.event.Event;
import org.oscim.gdx.GdxAssets;
import org.oscim.layers.Layer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.map.Map;
import org.oscim.model.VtmModels;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
/**
* Experimental layer to display 3d models.
*/
public class GdxModelLayer extends Layer implements Map.UpdateListener {
private static final Logger log = LoggerFactory.getLogger(GdxModelLayer.class);
private static final int MIN_ZOOM = BuildingLayer.MIN_ZOOM;
private Array<ModelInstance> mAdded = new Array<>();
private AssetManager mAssets;
private GdxRenderer3D2 mG3d;
private boolean mLoading;
private java.util.Map<ModelPosition, ModelHolder> mScenes = new HashMap<>();
public GdxModelLayer(Map map) {
super(map);
mRenderer = mG3d = new GdxRenderer3D2(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);
mAssets = new AssetManager();
}
public ModelPosition addModel(VtmModels model, double lat, double lon, float rotation) {
return addModel(GdxAssets.getAssetPath(model.getPath()), lat, lon, rotation);
}
/**
* Add model with specified path and position.
*
* @return the models position, can be modified during rendering e.g. to make animations.
* Don't forget to trigger map events (as it usually does if something changes).
*/
public ModelPosition addModel(String path, double lat, double lon, float rotation) {
ModelPosition pos = new ModelPosition(lat, lon, rotation);
mScenes.put(pos, new ModelHolder(path));
mAssets.load(path, Model.class);
if (!mLoading)
mLoading = true;
return pos;
}
private void doneLoading() {
for (ModelHolder poiModel : mScenes.values()) {
Model model = mAssets.get(poiModel.getPath());
for (Node node : model.nodes) {
log.debug("loader node " + node.id);
/* 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);
}
poiModel.setModel(model);
}
mLoading = false;
}
@Override
public void onMapEvent(Event ev, MapPosition pos) {
// if (ev == Map.CLEAR_EVENT) {
// synchronized (g3d) {
// g3d.instances.clear();
// }
// }
if (mLoading && mAssets.update()) {
doneLoading();
refreshModelInstances();
}
if (mLoading)
return;
double lat = MercatorProjection.toLatitude(pos.y);
float groundscale = (float) MercatorProjection
.groundResolutionWithScale(lat, 1 << pos.zoomLevel);
float scale = 1f / groundscale;
synchronized (mG3d) {
// remove if out of visible zoom range
mG3d.instances.removeAll(mAdded, true);
if (pos.getZoomLevel() >= MIN_ZOOM) {
mG3d.instances.addAll(mAdded);
}
for (ModelInstance inst : mAdded) {
ModelPosition p = (ModelPosition) inst.userData;
float dx = (float) ((p.x - pos.x) * (Tile.SIZE << pos.zoomLevel));
float dy = (float) ((p.y - pos.y) * (Tile.SIZE << pos.zoomLevel));
inst.transform.idt();
inst.transform.scale(scale, scale, scale);
inst.transform.translate(dx / scale, dy / scale, 0);
inst.transform.rotate(0, 0, 1, p.getRotation());
}
}
mG3d.cam.setMapPosition(pos.x, pos.y, 1 << pos.getZoomLevel());
}
public void refreshModelInstances() {
for (java.util.Map.Entry<ModelPosition, ModelHolder> scene : mScenes.entrySet()) {
mAdded.clear();
mG3d.instances.clear();
ModelInstance inst = new ModelInstance(scene.getValue().getModel());
inst.userData = scene.getKey();
mAdded.add(inst); // Local stored
mG3d.instances.add(inst); // g3d stored
}
}
public void removeModel(ModelPosition position) {
mScenes.remove(position);
if (!mLoading)
refreshModelInstances();
}
}

View File

@@ -1,189 +0,0 @@
package org.oscim.test.gdx.poi3d;
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.ModelInstance;
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;
import org.oscim.backend.GL;
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 static org.oscim.backend.GLAdapter.gl;
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<ModelInstance> instances = new Array<>();
public Shader shader;
public RenderContext renderContext;
public Model model;
private ModelBatch mBatch = new ModelBatch();
public GdxModelRenderer(Map map) {
mMap = map;
}
@Override
public 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
public 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
public void render(GLViewport v) {
if (instances.size == 0)
return;
// GLUtils.checkGlError(">" + TAG);
gl.depthMask(true);
if (v.pos.zoomLevel < 16)
gl.clear(GL.DEPTH_BUFFER_BIT);
// Unbind via GLState to ensure no buffer is replaced by accident
GLState.bindElementBuffer(GLState.UNBIND);
GLState.bindBuffer(GL.ARRAY_BUFFER, GLState.UNBIND);
// set state that is expected after modelBatch.end();
// modelBatch keeps track of its own state
GLState.enableVertexArrays(GLState.DISABLED, GLState.DISABLED);
GLState.bindTex2D(GLState.DISABLED);
GLState.useProgram(GLState.DISABLED);
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 (ModelInstance 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.depthMask(false);
GLState.bindElementBuffer(GLState.UNBIND);
GLState.bindBuffer(GL.ARRAY_BUFFER, GLState.UNBIND);
}
// @Override
// public void dispose () {
// modelBatch.dispose();
// assets.dispose();
// assets = null;
// axesModel.dispose();
// axesModel = null;
// }
}

View File

@@ -1,203 +0,0 @@
package org.oscim.test.gdx.poi3d;
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.ModelInstance;
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;
import org.oscim.backend.GL;
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 static org.oscim.backend.GLAdapter.gl;
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<ModelInstance> instances = new Array<>();
public Shader shader;
public RenderContext renderContext;
public Model model;
public GdxRenderer3D(Map map) {
mMap = map;
}
@Override
public 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
public 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
public void render(GLViewport v) {
if (instances.size == 0)
return;
// GLUtils.checkGlError(">" + TAG);
gl.depthMask(true);
// if (position.zoomLevel < 17)
// GL.clear(GL20.DEPTH_BUFFER_BIT);
// Unbind via GLState to ensure no buffer is replaced by accident
GLState.bindElementBuffer(GLState.UNBIND);
GLState.bindBuffer(GL.ARRAY_BUFFER, GLState.UNBIND);
// set state that is expected after modelBatch.end();
// modelBatch keeps track of its own state
GLState.enableVertexArrays(GLState.DISABLED, GLState.DISABLED);
GLState.bindTex2D(GLState.DISABLED);
GLState.useProgram(GLState.DISABLED);
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 (ModelInstance 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.depthMask(false);
GLState.bindElementBuffer(GLState.UNBIND);
GLState.bindBuffer(GL.ARRAY_BUFFER, GLState.UNBIND);
}
// @Override
// public void dispose () {
// modelBatch.dispose();
// assets.dispose();
// assets = null;
// axesModel.dispose();
// axesModel = null;
// }
}

View File

@@ -1,179 +0,0 @@
/*
* Copyright 2014 Hannes Janetzek
* Copyright 2018 Gustl22
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
* 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.Environment;
import com.badlogic.gdx.graphics.g3d.ModelBatch;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
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.math.Vector3;
import com.badlogic.gdx.utils.Array;
import org.oscim.backend.GL;
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 static org.oscim.backend.GLAdapter.gl;
/**
* Gdx renderer for more complex 3D models.
*/
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<ModelInstance> instances = new Array<>();
public GdxRenderer3D2(Map map) {
mMap = map;
}
@Override
public boolean setup() {
modelBatch = new ModelBatch(new DefaultShaderProvider());
lights = new Environment();
lights.add(new DirectionalLight().set(0.7f, 0.7f, 0.7f, 0, 1, -0.2f));
lights.set(new ColorAttribute(ColorAttribute.AmbientLight, 1f, 1f, 1f, 1f));
cam = new MapCamera(mMap);
return true;
}
@Override
public 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
public void render(GLViewport v) {
if (instances.size == 0)
return;
// GLUtils.checkGlError(">" + TAG);
gl.depthMask(true);
if (v.pos.zoomLevel < 17)
gl.clear(GL.DEPTH_BUFFER_BIT);
// Unbind via GLState to ensure no buffer is replaced by accident
GLState.bindElementBuffer(GLState.UNBIND);
GLState.bindBuffer(GL.ARRAY_BUFFER, GLState.UNBIND);
// set state that is expected after modelBatch.end();
// modelBatch keeps track of its own state
GLState.enableVertexArrays(GLState.DISABLED, GLState.DISABLED);
GLState.bindTex2D(GLState.DISABLED);
GLState.useProgram(GLState.DISABLED);
GLState.test(false, false);
GLState.blend(false);
// GL.cullFace(GL20.BACK);
// GL.frontFace(GL20.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 (ModelInstance 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, lights);
rnd++;
}
modelBatch.end();
}
log.debug(">>> " + (System.currentTimeMillis() - time) + " " + cnt + "/" + rnd);
// GLUtils.checkGlError("<" + TAG);
gl.depthMask(false);
GLState.bindElementBuffer(GLState.UNBIND);
GLState.bindBuffer(GL.ARRAY_BUFFER, GLState.UNBIND);
// GLState.bindTex2D(-1);
// GLState.useProgram(-1);
}
// @Override
// public void dispose () {
// modelBatch.dispose();
// assets.dispose();
// assets = null;
// axesModel.dispose();
// axesModel = null;
// }
}

View File

@@ -1,70 +0,0 @@
package org.oscim.test.gdx.poi3d;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.math.Matrix4;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.map.Map;
import org.oscim.renderer.GLViewport;
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

@@ -1,38 +0,0 @@
/*
* 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

@@ -1,45 +0,0 @@
/*
* 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 org.oscim.core.MercatorProjection;
public class ModelPosition {
public double x;
public double y;
public float rotation;
public ModelPosition(double lat, double lon, float rotation) {
setPosition(lat, lon, rotation);
}
public double getLat() {
return MercatorProjection.toLatitude(y);
}
public double getLon() {
return MercatorProjection.toLongitude(x);
}
public float getRotation() {
return rotation;
}
public void setPosition(double lat, double lon, float rotation) {
this.y = MercatorProjection.latitudeToY(lat);
this.x = MercatorProjection.longitudeToX(lon);
this.rotation = rotation;
}
}

View File

@@ -1,400 +0,0 @@
/*
* Copyright 2014 Hannes Janetzek
* 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.assets.AssetManager;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.model.Node;
import com.badlogic.gdx.utils.Array;
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.gdx.GdxAssets;
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;
/**
* Experimental Layer to display POIs with 3D models.
*/
public class Poi3DLayer extends Layer implements Map.UpdateListener {
private static final Logger log = LoggerFactory.getLogger(Poi3DLayer.class);
static class Poi3DTileData extends TileData {
public final HashMap<ModelHolder, List<SymbolItem>> symbols = new HashMap<>();
@Override
protected void dispose() {
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();
public final static boolean RANDOM_TRANSFORM = true; // TODO customizable for each tag
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();
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 (tile.zoomLevel < MIN_ZOOM) return false;
Poi3DTileData td = get(tile);
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
public void complete(MapTile tile, boolean success) {
}
});
mTileLayer = tileLayer;
mRenderer = mG3d = new GdxRenderer3D2(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);
mAssets = new AssetManager();
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() {
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);
/* 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);
}
holder.setModel(model);
}
}
mLoading = false;
}
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;
}
/**
* @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) {
if (ev == Map.CLEAR_EVENT) {
mTileSet = new TileSet();
mPrevTiles = new TileSet();
mTileMap = new LinkedHashMap<>();
synchronized (mG3d) {
mG3d.instances.clear();
}
}
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 (mLoading)
return;
// log.debug("update");
mTileLayer.tileRenderer().getVisibleTiles(mTileSet);
if (mTileSet.cnt == 0) {
mTileSet.releaseTiles();
return;
}
boolean changed = false;
Array<ModelInstance> added = new Array<>();
Array<ModelInstance> removed = new Array<>();
for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = mTileSet.tiles[i];
if (mPrevTiles.contains(t))
continue;
Array<ModelInstance> instances = new Array<>();
Poi3DTileData ld = (Poi3DTileData) t.getData(POI_DATA);
if (ld == null)
continue;
for (Entry<ModelHolder, Inlist.List<SymbolItem>> entry : ld.symbols.entrySet()) {
for (SymbolItem it : entry.getValue()) {
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);
}
}
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<ModelInstance> 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;
float groundScale = mTileSet.tiles[0].getGroundScale();
TileSet tmp = mPrevTiles;
mPrevTiles = mTileSet;
mTileSet = tmp;
if (!changed)
return;
// scale relative to latitude
float scale = 1f / groundScale;
double tileX = (pos.x * (Tile.SIZE << zoom));
double tileY = (pos.y * (Tile.SIZE << zoom));
synchronized (mG3d) {
for (Entry<Tile, Array<ModelInstance>> e : mTileMap.entrySet()) {
Tile t = e.getKey();
float dx = (float) (t.tileX * Tile.SIZE - tileX);
float dy = (float) (t.tileY * Tile.SIZE - tileY);
for (ModelInstance inst : e.getValue()) {
SymbolItem it = (SymbolItem) inst.userData;
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);
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);
}
}
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);
}
}