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 java.util.List;
import javax.media.opengl.GL3;
import org.osm2world.core.math.VectorXYZW;
import org.osm2world.core.target.common.Primitive;
import org.osm2world.core.target.common.material.Material;
import org.osm2world.core.target.common.material.Material.Shadow;
import org.osm2world.core.target.common.material.Material.Transparency;
import com.jogamp.common.nio.Buffers;
/**
* Renders the shadow volumes for the contents of a {@link PrimitiveBuffer} using JOGL.
* 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 JOGLRendererVBOShadowVolume {
protected GL3 gl;
protected AbstractPrimitiveShader shader;
protected VectorXYZW lightPos;
protected static final boolean DOUBLE_PRECISION_RENDERING = false;
/** VBOs with static, non-alphablended geometry for each material */
protected List<VBODataShadowVolume<?>> vbos = new ArrayList<VBODataShadowVolume<?>>();
private final class VBODataDouble extends VBODataShadowVolume<DoubleBuffer> {
public VBODataDouble(GL3 gl, Collection<Primitive> primitives, VectorXYZW lightPos) {
super(gl, primitives, lightPos);
}
@Override
protected DoubleBuffer createBuffer(int numValues) {
return Buffers.newDirectDoubleBuffer(numValues);
}
@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 VBODataShadowVolume<FloatBuffer> {
public VBODataFloat(GL3 gl, Collection<Primitive> primitives, VectorXYZW lightPos) {
super(gl, primitives, lightPos);
}
@Override
protected FloatBuffer createBuffer(int numValues) {
return Buffers.newDirectFloatBuffer(numValues);
}
@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 the shadow volumes (and VBOs) for all primitives in a {@link PrimitiveBuffer}.
* @param primitiveBuffer the primitives to create the shadow volumes for
* @param lightPos the position of the light source to create the shadow volumes for. If ligthPose.w == 0, the light is directional.
*/
JOGLRendererVBOShadowVolume(GL3 gl, PrimitiveBuffer primitiveBuffer, VectorXYZW lightPos) {
this.gl = gl;
this.lightPos = lightPos;
this.init(primitiveBuffer);
}
protected void init(PrimitiveBuffer primitiveBuffer) {
Collection<Primitive> combinedPrimitives = new ArrayList<Primitive>();
for (Material material : primitiveBuffer.getMaterials()) {
if (material.getTransparency() == Transparency.FALSE && material.getShadow() == Shadow.TRUE) {
Collection<Primitive> primitives = primitiveBuffer.getPrimitives(material);
combinedPrimitives.addAll(primitives);
}
}
vbos.add(this.createVBOData(combinedPrimitives));
}
VBODataShadowVolume<?> createVBOData(Collection<Primitive> primitives) {
if (DOUBLE_PRECISION_RENDERING)
return new VBODataDouble(gl, primitives, lightPos);
else
return new VBODataFloat(gl, primitives, lightPos);
}
/**
* Render all shadow volume VBOs
*/
public void render() {
/* render static geometry */
shader.glEnableVertexAttribArray(shader.getVertexPositionID());
for (VBODataShadowVolume<?> vboData : vbos) {
vboData.setShader(shader);
vboData.render();
}
shader.glDisableVertexAttribArray(shader.getVertexPositionID());
}
/**
* frees all OpenGL resources associated with this object. Rendering will no longer be possible afterwards!
*/
public void freeResources() {
gl = null;
if (vbos != null) {
for (VBODataShadowVolume<?> vbo : vbos) {
vbo.delete();
}
vbos = null;
}
}
/**
* Set the shader used for rendering.
*/
public void setShader(AbstractPrimitiveShader shader) {
this.shader = shader;
}
}