/*
* Copyright 2012, 2013 Hannes Janetzek
*
* 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 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 License for more details.
*
* You should have received a copy of the GNU Lesser General License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.renderer;
import static org.oscim.backend.GLAdapter.gl;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.ShortBuffer;
import org.oscim.backend.GL;
import org.oscim.backend.GLAdapter;
import org.oscim.backend.canvas.Color;
import org.oscim.map.Map;
import org.oscim.renderer.bucket.RenderBuckets;
import org.oscim.renderer.bucket.TextureItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MapRenderer {
static final Logger log = LoggerFactory.getLogger(MapRenderer.class);
/** scale factor used for short vertices */
public static final float COORD_SCALE = 8.0f;
private final Map mMap;
private final GLViewport mViewport;
private static float[] mClearColor;
private static int mQuadIndicesID;
private static int mQuadVerticesID;
/** Number of Quads that can be rendered with bindQuadIndicesVBO() */
public final static int MAX_QUADS = 512;
/** Number of Indices that can be rendered with bindQuadIndicesVBO() */
public final static int MAX_INDICES = MAX_QUADS * 6;
public static long frametime;
private static boolean rerender;
private static NativeBufferPool mBufferPool;
public MapRenderer(Map map) {
mMap = map;
mViewport = new GLViewport();
mBufferPool = new NativeBufferPool();
/* FIXME should be done in 'destroy' method
* clear all previous vbo refs */
BufferObject.clear();
setBackgroundColor(Color.DKGRAY);
}
public static void setBackgroundColor(int color) {
mClearColor = GLUtils.colorToFloat(color);
}
public void onDrawFrame() {
frametime = System.currentTimeMillis();
draw();
mBufferPool.releaseBuffers();
TextureItem.disposeTextures();
}
private void draw() {
GLState.setClearColor(mClearColor);
gl.depthMask(true);
gl.stencilMask(0xFF);
gl.clear(GL.COLOR_BUFFER_BIT
| GL.DEPTH_BUFFER_BIT
| GL.STENCIL_BUFFER_BIT);
gl.depthMask(false);
gl.stencilMask(0);
GLState.test(false, false);
GLState.blend(false);
GLState.bindTex2D(-1);
GLState.useProgram(-1);
GLState.bindElementBuffer(-1);
GLState.bindVertexBuffer(-1);
mMap.animator().updateAnimation();
mViewport.setFrom(mMap.viewport());
if (GLAdapter.debugView) {
/* modify this to scale only the view, to see
* which tiles are rendered */
mViewport.mvp.setScale(0.5f, 0.5f, 1);
mViewport.viewproj.multiplyLhs(mViewport.mvp);
mViewport.proj.multiplyLhs(mViewport.mvp);
}
/* update layers */
LayerRenderer[] layers = mMap.layers().getLayerRenderer();
for (int i = 0, n = layers.length; i < n; i++) {
LayerRenderer renderer = layers[i];
if (!renderer.isInitialized) {
renderer.setup();
renderer.isInitialized = true;
}
renderer.update(mViewport);
if (renderer.isReady)
renderer.render(mViewport);
if (GLAdapter.debug)
GLUtils.checkGlError(renderer.getClass().getName());
}
if (GLUtils.checkGlOutOfMemory("finish")) {
BufferObject.checkBufferUsage(true);
// FIXME also throw out some textures etc
}
if (rerender) {
mMap.render();
rerender = false;
}
}
public void onSurfaceChanged(int width, int height) {
//log.debug("onSurfaceChanged: new={}, {}x{}", mNewSurface, width, height);
if (width <= 0 || height <= 0)
return;
//mMap.viewport().getMatrix(null, mMatrices.proj, null);
mViewport.initFrom(mMap.viewport());
gl.viewport(0, 0, width, height);
//GL.scissor(0, 0, width, height);
//GL.enable(GL20.SCISSOR_TEST);
gl.clearStencil(0x00);
gl.disable(GL.CULL_FACE);
gl.blendFunc(GL.ONE, GL.ONE_MINUS_SRC_ALPHA);
gl.frontFace(GL.CW);
gl.cullFace(GL.BACK);
if (!mNewSurface) {
mMap.updateMap(false);
return;
}
mNewSurface = false;
/** initialize quad indices used by Texture- and LineTexRenderer */
int[] vboIds = GLUtils.glGenBuffers(2);
mQuadIndicesID = vboIds[0];
short[] indices = new short[MAX_INDICES];
for (int i = 0, j = 0; i < MAX_INDICES; i += 6, j += 4) {
indices[i + 0] = (short) (j + 0);
indices[i + 1] = (short) (j + 1);
indices[i + 2] = (short) (j + 2);
indices[i + 3] = (short) (j + 2);
indices[i + 4] = (short) (j + 1);
indices[i + 5] = (short) (j + 3);
}
ShortBuffer buf = MapRenderer.getShortBuffer(indices.length);
buf.put(indices);
buf.flip();
GLState.bindElementBuffer(mQuadIndicesID);
gl.bufferData(GL.ELEMENT_ARRAY_BUFFER,
indices.length * 2, buf,
GL.STATIC_DRAW);
GLState.bindElementBuffer(0);
/** initialize default quad */
FloatBuffer floatBuffer = MapRenderer.getFloatBuffer(8);
float[] quad = new float[] { -1, -1, -1, 1, 1, -1, 1, 1 };
floatBuffer.put(quad);
floatBuffer.flip();
mQuadVerticesID = vboIds[1];
GLState.bindVertexBuffer(mQuadVerticesID);
gl.bufferData(GL.ARRAY_BUFFER,
quad.length * 4, floatBuffer,
GL.STATIC_DRAW);
GLState.bindVertexBuffer(0);
GLState.init();
mMap.updateMap(true);
}
public void onSurfaceCreated() {
// log.debug(GL.getString(GL20.EXTENSIONS));
String vendor = gl.getString(GL.VENDOR);
String renderer = gl.getString(GL.RENDERER);
String version = gl.getString(GL.VERSION);
log.debug("{}/{}/{}", vendor, renderer, version);
// Prevent issue with Adreno 3xx series
if (renderer != null && renderer.startsWith("Adreno (TM) 3")) {
log.debug("==> not using glBufferSubData");
GLAdapter.NO_BUFFER_SUB_DATA = true;
}
GLState.init();
// Set up some vertex buffer objects
BufferObject.init(200);
// classes that require GL context for initialization
RenderBuckets.initRenderer();
mNewSurface = true;
}
private boolean mNewSurface;
/**
* Bind VBO for a simple quad. Handy for simple custom RenderLayers
* Vertices: float[]{ -1, -1, -1, 1, 1, -1, 1, 1 }
*
* GL.drawArrays(GL20.TRIANGLE_STRIP, 0, 4);
*/
public static void bindQuadVertexVBO(int location) {
if (location >= 0) {
GLState.bindVertexBuffer(mQuadVerticesID);
GLState.enableVertexArrays(location, -1);
gl.vertexAttribPointer(location, 2, GL.FLOAT, false, 0, 0);
}
}
/**
* Bind indices for rendering up to MAX_QUADS (512),
* ie. MAX_INDICES (512*6) in one draw call.
* Vertex order is 0-1-2 2-1-3
*/
public static void bindQuadIndicesVBO() {
GLState.bindElementBuffer(mQuadIndicesID);
}
/**
* Trigger next redraw from GL-Thread. This should be used to animate
* LayerRenderers instead of calling Map.render().
*/
public static void animate() {
rerender = true;
}
public static FloatBuffer getFloatBuffer(int size) {
return mBufferPool.getFloatBuffer(size);
}
public static ShortBuffer getShortBuffer(int size) {
return mBufferPool.getShortBuffer(size);
}
public static IntBuffer getIntBuffer(int size) {
return mBufferPool.getIntBuffer(size);
}
}