package org.osm2world.core.target.jogl;
import static javax.media.opengl.GL.GL_ARRAY_BUFFER;
import static javax.media.opengl.GL.GL_TRIANGLES;
import static org.osm2world.core.math.GeometryUtil.calculateTangentVectorsForTexLayer;
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.GL3;
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.Primitive.Type;
import org.osm2world.core.target.common.material.Material;
/**
* class that keeps a VBO id along with associated information for the shader based OpenGL pipeline
*/
abstract class VBODataShader<BufferT extends Buffer> extends VBOData<BufferT> {
protected GL3 gl;
protected AbstractPrimitiveShader shader;
/**
* @see VBOData#VBOData(javax.media.opengl.GL, JOGLTextureManager, Material, Collection)
*/
public VBODataShader(GL3 gl, JOGLTextureManager textureManager, Material material, Collection<Primitive> primitives) {
super(gl, textureManager, material, primitives);
this.gl = gl;
}
/**
* Set the shader this VBO uses when rendering (e.g. calls
* {@link AbstractPrimitiveShader#setMaterial(Material, JOGLTextureManager)}.
*/
public void setShader(AbstractPrimitiveShader shader) {
this.shader = shader;
}
@Override
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;
}
}
List<VectorXYZW> primTangents = null;
if (material.hasBumpMap()) {
primTangents = calculateTangentVectorsForTexLayer(primVertices, primNormals, primTexCoordLists.get(material.getBumpMapInd()));
}
/* put the values into the buffer, in the right order */
for (int i = 0; i < primVertices.size(); i++) {
int count = 0;
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++) {
if (!material.hasBumpMap() || t != material.getBumpMapInd()) {
VectorXZ textureCoord = primTexCoordLists.get(t).get(i);
put(buffer, textureCoord);
//System.out.println("put tex coord");
count += 2;
}
}
put(buffer, primNormals.get(i));
count += 3;
if (material.hasBumpMap()) {
put(buffer, primTangents.get(i));
count += 4;
put(buffer, primTexCoordLists.get(material.getBumpMapInd()).get(i));
count += 2;
}
put(buffer, primVertices.get(i));
count += 3;
if (count != JOGLRendererVBO.getValuesPerVertex(material)) {
throw new RuntimeException("put: "+count +" values:" + JOGLRendererVBO.getValuesPerVertex(material));
}
}
}
@Override
public void render() {
gl.glBindBuffer(GL_ARRAY_BUFFER, id[0]);
setPointerLayout();
if (shader.setMaterial(material, textureManager))
gl.glDrawArrays(GL_TRIANGLES, 0, vertexCount);
for (int i=1; i<DefaultShader.MAX_TEXTURE_LAYERS; i++) {
shader.glDisableVertexAttribArray(shader.getVertexTexCoordID(i));
}
shader.glDisableVertexAttribArray(shader.getVertexBumpMapCoordID());
shader.glDisableVertexAttribArray(shader.getVertexTangentID());
}
private void setPointerLayout() {
int stride = valueTypeSize * JOGLRendererVBO.getValuesPerVertex(material);
int offset = 0;
for (int i = 0; i < material.getNumTextureLayers(); i++) {
if (!material.hasBumpMap() || i != material.getBumpMapInd()) {
shader.glEnableVertexAttribArray(shader.getVertexTexCoordID(i));
shader.glVertexAttribPointer(shader.getVertexTexCoordID(i), 2, glValueType(), false, stride, offset);
offset += 2 * valueTypeSize;
}
}
shader.glVertexAttribPointer(shader.getVertexNormalID(), 3, glValueType(), false, stride, offset);
offset += valueTypeSize() * 3;
if (material.hasBumpMap()) {
shader.glEnableVertexAttribArray(shader.getVertexTangentID());
shader.glVertexAttribPointer(shader.getVertexTangentID(), 4, glValueType(), false, stride, offset);
offset += valueTypeSize() * 4;
shader.glEnableVertexAttribArray(shader.getVertexBumpMapCoordID());
shader.glVertexAttribPointer(shader.getVertexBumpMapCoordID(), 2, glValueType(), false, stride, offset);
offset += valueTypeSize() * 2;
}
if (offset != stride - 3*valueTypeSize()) {
throw new RuntimeException("offset: "+offset + " stride:"+stride +" valueTypeSize:"+valueTypeSize());
}
shader.glVertexAttribPointer(shader.getVertexPositionID(), 3, glValueType(), false, stride, offset);
}
@Override
protected int getValuesPerVertex(Material material) {
return JOGLRendererVBO.getValuesPerVertex(material);
}
/** add 4d vertex data to the vbo buffer */
protected abstract void put(BufferT buffer, VectorXYZW t);
}