package org.osm2world.core.target.jogl;
import static javax.media.opengl.GL.GL_FLOAT;
import static javax.media.opengl.GL2GL3.GL_DOUBLE;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Collection;
import javax.media.opengl.GL3;
import org.osm2world.core.math.AxisAlignedBoundingBoxXYZ;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXYZW;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.target.common.Primitive;
import org.osm2world.core.target.common.material.Material;
import org.osm2world.core.target.common.rendering.Camera;
import org.osm2world.core.target.common.rendering.Projection;
import com.jogamp.common.nio.Buffers;
/**
* Renders the contents of a {@link PrimitiveBuffer} using JOGL and the new shader based OpenGL pipeline.
* Uses vertex buffer objects (VBO) to speed up the process.
*
* If you don't need the renderer anymore, it's recommended to manually call
* {@link #freeResources()} to delete the VBOs and other resources.
*/
public class JOGLRendererVBOShader extends JOGLRendererVBO {
protected GL3 gl;
protected AbstractPrimitiveShader shader;
protected AxisAlignedBoundingBoxXYZ boundingBox = null;
private final class VBODataDouble extends VBODataShader<DoubleBuffer> {
public VBODataDouble(GL3 gl, JOGLTextureManager textureManager, Material material, Collection<Primitive> primitives) {
super(gl, textureManager, material, primitives);
}
@Override
protected DoubleBuffer createBuffer(int numValues) {
return Buffers.newDirectDoubleBuffer(numValues);
}
@Override
protected void put(DoubleBuffer buffer, VectorXZ texCoord) {
buffer.put(texCoord.x);
buffer.put(texCoord.z);
}
@Override
protected void put(DoubleBuffer buffer, VectorXYZ v) {
buffer.put(v.x);
buffer.put(v.y);
buffer.put(-v.z);
}
@Override
protected void put(DoubleBuffer buffer, VectorXYZW t) {
buffer.put(t.x);
buffer.put(t.y);
buffer.put(-t.z);
buffer.put(t.w);
}
@Override
protected int valueTypeSize() {
return Buffers.SIZEOF_DOUBLE;
}
@Override
protected int glValueType() {
return GL_DOUBLE;
}
}
private final class VBODataFloat extends VBODataShader<FloatBuffer> {
public VBODataFloat(GL3 gl, JOGLTextureManager textureManager, Material material, Collection<Primitive> primitives) {
super(gl, textureManager, material, primitives);
}
@Override
protected FloatBuffer createBuffer(int numValues) {
return Buffers.newDirectFloatBuffer(numValues);
}
@Override
protected void put(FloatBuffer buffer, VectorXZ texCoord) {
buffer.put((float)texCoord.x);
buffer.put((float)texCoord.z);
}
@Override
protected void put(FloatBuffer buffer, VectorXYZ v) {
buffer.put((float)v.x);
buffer.put((float)v.y);
buffer.put((float)-v.z);
}
@Override
protected void put(FloatBuffer buffer, VectorXYZW t) {
buffer.put((float)t.x);
buffer.put((float)t.y);
buffer.put((float)-t.z);
buffer.put((float)t.w);
}
@Override
protected int valueTypeSize() {
return Buffers.SIZEOF_FLOAT;
}
@Override
protected int glValueType() {
return GL_FLOAT;
}
}
/**
* Creates vertex buffer objects for all primitives and computes a bounding box around them.
* @param primitiveBuffer the primitives to create the VBOs for
* @param xzBoundary the boundary of the OSM file. Used to tighten the bounding box to only primitives within these bounds.
*/
JOGLRendererVBOShader(GL3 gl, JOGLTextureManager textureManager,
PrimitiveBuffer primitiveBuffer, AxisAlignedBoundingBoxXZ xzBoundary) {
super(textureManager);
this.gl = gl;
this.init(primitiveBuffer);
ArrayList<VectorXYZ> boundedVertices = new ArrayList<VectorXYZ>();
for (Material m : primitiveBuffer.getMaterials()) {
for (Primitive p : primitiveBuffer.getPrimitives(m)) {
for (VectorXYZ v : p.vertices) {
if (xzBoundary == null || xzBoundary.contains(v.xz())) {
boundedVertices.add(new VectorXYZ(v.x, v.y, -v.z));
}
}
}
}
boundingBox = new AxisAlignedBoundingBoxXYZ(boundedVertices);
}
@Override
VBOData<?> createVBOData(JOGLTextureManager textureManager, Material material, Collection<Primitive> primitives) {
if (DOUBLE_PRECISION_RENDERING)
return new VBODataDouble(gl, textureManager, material, primitives);
else
return new VBODataFloat(gl, textureManager, material, primitives);
}
/**
* Render the stored VBOS. Uses the currently set shader. Transparent primitives are not sorted.
* If they have to be sorted for the set shader then use {@link #render(Camera, Projection)}.
*/
public void render() {
/* render static geometry */
shader.glEnableVertexAttribArray(shader.getVertexPositionID());
shader.glEnableVertexAttribArray(shader.getVertexNormalID());
for (VBOData<?> vboData : vbos) {
((VBODataShader<?>)vboData).setShader(shader);
vboData.render();
}
/* render transparent primitives unsorted */
for (PrimitiveWithMaterial p : transparentPrimitives) {
((VBODataShader<?>)p.vbo).setShader(shader);
p.vbo.render();
}
shader.glDisableVertexAttribArray(shader.getVertexPositionID());
shader.glDisableVertexAttribArray(shader.getVertexNormalID());
}
/**
* Render the stored VBOs. Uses the currently set shader. Transparent objects get sorted first back to front
* relative to the given camera and projection.
*/
@Override
public void render(final Camera camera, final Projection projection) {
/* render static geometry */
shader.glEnableVertexAttribArray(shader.getVertexPositionID());
shader.glEnableVertexAttribArray(shader.getVertexNormalID());
for (VBOData<?> vboData : vbos) {
((VBODataShader<?>)vboData).setShader(shader);
vboData.render();
}
/* render transparent primitives back-to-front */
sortPrimitivesBackToFront(camera, projection);
for (PrimitiveWithMaterial p : transparentPrimitives) {
((VBODataShader<?>)p.vbo).setShader(shader);
p.vbo.render();
}
shader.glDisableVertexAttribArray(shader.getVertexPositionID());
shader.glDisableVertexAttribArray(shader.getVertexNormalID());
}
@Override
public void freeResources() {
gl = null;
super.freeResources();
}
/**
* Change the shader used to render the VBOs
*/
public void setShader(AbstractPrimitiveShader shader) {
this.shader = shader;
}
/**
* Get the bounding box around all relevant primitives computed at {@link #JOGLRendererVBOShader(GL3, JOGLTextureManager, PrimitiveBuffer, AxisAlignedBoundingBoxXZ)}
*/
public AxisAlignedBoundingBoxXYZ getBoundingBox() {
return boundingBox;
}
}