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 org.osm2world.core.math.GeometryUtil.triangleNormalListFromTriangleStripOrFan;
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.GL;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXZ;
import org.osm2world.core.target.common.Primitive;
import org.osm2world.core.target.common.Primitive.Type;
import org.osm2world.core.target.common.material.Material;
/**
* Base class that keeps a VBO id along with associated information.
*/
public abstract class VBOData<BufferT extends Buffer> {
/** material associated with this VBO, determines VBO layout */
protected Material material;
/** 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);
/** add a texture coordinate to the vbo buffer */
protected abstract void put(BufferT buffer, VectorXZ texCoord);
/** add 3d vertex data to the vbo buffer */
protected abstract void put(BufferT buffer, VectorXYZ v);
/** 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();
private GL gl;
protected JOGLTextureManager textureManager;
/**
* Creates a new vertex buffer object, adds all primitives to the buffer and uploads it to graphics memory.
*/
public VBOData(GL gl, JOGLTextureManager textureManager, Material material, Collection<Primitive> primitives) {
this.gl = gl;
this.textureManager = textureManager;
this.material = material;
valueTypeSize = valueTypeSize();
glValueType = glValueType();
vertexCount = countVertices(primitives);
/* create the buffer */
id = new int[1];
gl.glGenBuffers(1, id, 0);
/* collect the data for the buffer */
BufferT valueBuffer = createBuffer(
vertexCount * getValuesPerVertex(material));
for (Primitive primitive : primitives) {
addPrimitiveToValueBuffer(valueBuffer, primitive);
}
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);
}
/**
* returns the number of vertices required to represent a collection
* of primitives with individual triangles
*/
static int countVertices(Collection<Primitive> primitives) {
int vertexCount = 0;
for (Primitive primitive : primitives) {
if (primitive.type == Type.TRIANGLES) {
vertexCount += primitive.vertices.size();
} else {
vertexCount += 3 * (primitive.vertices.size() - 2);
}
}
return vertexCount;
}
/**
* put the values for a primitive's vertices into the buffer
*/
protected void addPrimitiveToValueBuffer(BufferT buffer,
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;
List<VectorXYZ> primNormals = primitive.normals;
List<List<VectorXZ>> primTexCoordLists = primitive.texCoordLists;
if (primitive.type == Type.TRIANGLE_STRIP) {
// TODO: support smooth interpolation of normals
primVertices = triangleVertexListFromTriangleStrip(primVertices);
primNormals = triangleNormalListFromTriangleStripOrFan(primNormals);
if (primTexCoordLists != null) {
List<List<VectorXZ>> newPrimTexCoordLists = new ArrayList<List<VectorXZ>>();
for (List<VectorXZ> primTexCoordList : primTexCoordLists) {
newPrimTexCoordLists.add(triangleVertexListFromTriangleStrip(primTexCoordList));
}
primTexCoordLists = newPrimTexCoordLists;
}
} else if (primitive.type == Type.TRIANGLE_FAN) {
// TODO: support smooth interpolation of normals
primVertices = triangleVertexListFromTriangleFan(primVertices);
primNormals = triangleNormalListFromTriangleStripOrFan(primNormals);
if (primTexCoordLists != null) {
List<List<VectorXZ>> newPrimTexCoordLists = new ArrayList<List<VectorXZ>>();
for (List<VectorXZ> primTexCoordList : primTexCoordLists) {
newPrimTexCoordLists.add(triangleVertexListFromTriangleFan(primTexCoordList));
}
primTexCoordLists = newPrimTexCoordLists;
}
}
/* put the values into the buffer, in the right order */
for (int i = 0; i < primVertices.size(); i++) {
assert (primTexCoordLists == null
&& material.getNumTextureLayers() == 0)
|| (primTexCoordLists != null
&& primTexCoordLists.size() == material.getNumTextureLayers())
: "WorldModules need to provide the correct number of tex coords";
if (primTexCoordLists == null && material.getNumTextureLayers() > 0) {
System.out.println(material);
}
for (int t = 0; t < material.getNumTextureLayers(); t++) {
VectorXZ textureCoord = primTexCoordLists.get(t).get(i);
put(buffer, textureCoord);
}
put(buffer, primNormals.get(i));
put(buffer, primVertices.get(i));
}
}
/**
* Bind and render this vertex buffer object.
*/
public abstract void render();
/**
* Returns the number of values for each vertex in the vertex buffer layout appropriate for a given material.
*/
protected abstract int getValuesPerVertex(Material material);
/**
* Delete the vertex buffer object from graphics memory.
*/
public void delete() {
gl.glDeleteBuffers(id.length, id, 0);
}
}