package org.osm2world.core.target.jogl;
import static javax.media.opengl.GL.GL_ARRAY_BUFFER;
import static javax.media.opengl.GL.GL_STATIC_DRAW;
import static javax.media.opengl.GL.GL_TRIANGLES;
import static org.osm2world.core.math.GeometryUtil.triangleVertexListFromTriangleFan;
import static org.osm2world.core.math.GeometryUtil.triangleVertexListFromTriangleStrip;
import java.nio.Buffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javax.media.opengl.GL3;
import org.osm2world.core.math.GeometryUtil;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXYZW;
import org.osm2world.core.target.common.Primitive;
import org.osm2world.core.target.common.Primitive.Type;
/**
* class that keeps a VBO id along with associated information for shadow volumes
*/
abstract class VBODataShadowVolume<BufferT extends Buffer> {
protected GL3 gl;
protected AbstractPrimitiveShader shader;
/** position of the light source casting the shadow volumes */
protected VectorXYZW lightPos;
/** array with one element containing the VBO id */
protected final int[] id;
/** number of vertices in the vbo */
protected final int vertexCount;
/** size of each value in the vbo */
protected final int valueTypeSize;
/** gl constant for the value type in the vbo */
protected final int glValueType;
/** create a buffer to store the vbo data for upload to graphics memory */
protected abstract BufferT createBuffer(int numValues);
/** returns the size of each value in the vbo */
protected abstract int valueTypeSize();
/** returns the gl constant for the value type in the vbo */
protected abstract int glValueType();
/** add 4d shadow volume vertex data to the vbo buffer */
protected abstract void put(BufferT buffer, VectorXYZW sv);
/**
* Creates a new vertex buffer object, calculates the shadow volumes for all
* primitives and adds them to the buffer and uploads it to graphics memory.
*/
public VBODataShadowVolume(GL3 gl, Collection<Primitive> primitives, VectorXYZW lightPos) {
this.gl = gl;
this.lightPos = lightPos;
valueTypeSize = valueTypeSize();
glValueType = glValueType();
//vertexCount = VBOData.countVertices(primitives)*8;
/* create the buffer */
id = new int[1];
gl.glGenBuffers(1, id, 0);
/* collect the data for the buffer */
List<VectorXYZW> shadowVolumeVertices = new ArrayList<VectorXYZW>();
for (Primitive primitive : primitives) {
shadowVolumeVertices.addAll(getPrimitivesShadowVolumes(primitive));
}
vertexCount = shadowVolumeVertices.size();
BufferT valueBuffer = createBuffer(
vertexCount * getValuesPerVertex());
addVerticesToValueBuffer(valueBuffer, shadowVolumeVertices);
valueBuffer.rewind();
/* write the data into the buffer */
gl.glBindBuffer(GL_ARRAY_BUFFER, id[0]);
gl.glBufferData(
GL_ARRAY_BUFFER,
valueBuffer.capacity() * valueTypeSize,
valueBuffer,
GL_STATIC_DRAW);
}
/**
* Set the shader this VBO uses when rendering the shadow volumes.
*/
public void setShader(AbstractPrimitiveShader shader) {
this.shader = shader;
}
/**
* Put the values of a shadow volume into the buffer.
*/
protected void addVerticesToValueBuffer(BufferT buffer,
List<VectorXYZW> shadowVolumeVertices) {
/* put the values into the buffer, in the right order */
for (VectorXYZW v : shadowVolumeVertices) {
put(buffer, v);
}
}
/**
* Calculate the shadow volume for a primitive.
*/
protected List<VectorXYZW> getPrimitivesShadowVolumes(Primitive primitive) {
/*
* rearrange the lists of vertices, normals and texture coordinates
* to turn triangle strips and triangle fans into separate triangles
*/
List<VectorXYZ> primVertices = primitive.vertices;
if (primitive.type == Type.TRIANGLE_STRIP) {
primVertices = triangleVertexListFromTriangleStrip(primVertices);
} else if (primitive.type == Type.TRIANGLE_FAN) {
primVertices = triangleVertexListFromTriangleFan(primVertices);
}
/*
* NOTE: performance could be improved a lot if shadow volume geometry would be minimized.
* Suggestions:
* * calculate volumes only for silhouette
* * skip back facing triangles (from light perspective). All shadow casting objectes have to be closed then.
* * use low poly model for shadow volume generation (only useful for high poly objects)
*/
List<VectorXYZW> shadowVolumeVertices = GeometryUtil.calculateShadowVolumesPerTriangle(primVertices, lightPos);
return shadowVolumeVertices;
}
/**
* Bind and render this vertex buffer object.
*/
public void render() {
gl.glBindBuffer(GL_ARRAY_BUFFER, id[0]);
setPointerLayout();
gl.glDrawArrays(GL_TRIANGLES, 0, vertexCount);
}
private void setPointerLayout() {
int stride = valueTypeSize * getValuesPerVertex();
int offset = 0;
shader.glVertexAttribPointer(shader.getVertexPositionID(), 4, glValueType(), false, stride, offset);
}
/**
* Returns the number of values for each vertex in the vertex buffer layout.
*/
protected int getValuesPerVertex() {
return 4;
}
/**
* Delete the vertex buffer object from graphics memory.
*/
public void delete() {
gl.glDeleteBuffers(id.length, id, 0);
}
}