package au.gov.ga.earthsci.model.core.render;
import gov.nasa.worldwind.util.OGLStackHandler;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.media.opengl.GL2;
import javax.media.opengl.GL2GL3;
import javax.media.opengl.GLContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import au.gov.ga.earthsci.common.color.ColorType;
import au.gov.ga.earthsci.common.util.Validate;
import au.gov.ga.earthsci.model.geometry.IMeshGeometry;
import au.gov.ga.earthsci.model.geometry.IModelGeometry;
import au.gov.ga.earthsci.model.geometry.IVertexBasedGeometry;
import au.gov.ga.earthsci.model.geometry.IVertexColourMappedGeometry;
import au.gov.ga.earthsci.model.geometry.IVertexColouredGeometry;
import au.gov.ga.earthsci.model.render.IModelGeometryRenderer;
import au.gov.ga.earthsci.worldwind.common.WorldWindowRegistry;
import au.gov.ga.earthsci.worldwind.common.exaggeration.VerticalExaggerationService;
import au.gov.ga.earthsci.worldwind.common.render.fastshape.AbstractVBO;
/**
* A basic {@link IModelGeometryRenderer} that supports
* {@link IVertexBasedGeometry} and {@link IVertexColouredGeometry} instances
*
* @author James Navin (james.navin@ga.gov.au)
*
*/
public class BasicRenderer implements IModelGeometryRenderer
{
private static final Logger logger = LoggerFactory.getLogger(BasicRenderer.class);
private VerticalExaggerationService veService = VerticalExaggerationService.INSTANCE;
private WorldWindowRegistry wwRegistry;
private IVertexBasedGeometry geometry;
private AtomicBoolean isInitialised = new AtomicBoolean(false);
private AbstractVBO<?> vertexVBO;
private AbstractVBO<?> vertexColourVBO;
private AbstractVBO<?> edgesVBO;
private Integer renderMode;
private BasicRendererShader shader = new BasicRendererShader();
/**
* Create a new instance of the renderer for the given geometry
*
* @param geometry
* The geometry to create the renderer for
*
* @param wwRegistry
* The {@link WorldWindowRegistry} used to obtain the current
* view and globe etc.
*/
public BasicRenderer(IVertexBasedGeometry geometry, WorldWindowRegistry wwRegistry)
{
Validate.notNull(geometry, "A geometry is required"); //$NON-NLS-1$
this.geometry = geometry;
this.wwRegistry = wwRegistry;
}
@Override
public void render()
{
logger.trace("Rendering {} ({})", geometry.getName(), geometry.getId()); //$NON-NLS-1$
GLContext context = GLContext.getCurrent();
if (context == null)
{
logger.debug("No current GL context found - aborting render"); //$NON-NLS-1$
return;
}
GL2 gl = (GL2) context.getGL();
init();
OGLStackHandler stack = new OGLStackHandler();
stack.pushAttrib(gl, GL2.GL_CURRENT_BIT | GL2.GL_POINT_BIT | GL2.GL_POLYGON_BIT | GL2.GL_ALPHA_BITS);
stack.pushClientAttrib(gl, GL2.GL_CLIENT_VERTEX_ARRAY_BIT);
try
{
shader.setGlobe(wwRegistry.getRenderingView().getGlobe());
shader.setVerticalExaggeration((float) veService.get());
shader.setNodata((Float) geometry.getVertices().getNoDataValue());
shader.setOpacity((float) geometry.getOpacity());
boolean bound = shader.bind(gl);
if (!bound)
{
logger.debug("Unable to bind shader. Aborting.", shader.getLastError()); //$NON-NLS-1$
return;
}
gl.glEnable(GL2.GL_BLEND);
gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA);
if (vertexColourVBO != null)
{
gl.glEnableClientState(GL2.GL_COLOR_ARRAY);
vertexColourVBO.bind(gl);
gl.glColorPointer(getColourTypeForGeometry().getNumComponents(), GL2.GL_FLOAT, 0, 0);
}
gl.glEnableClientState(GL2.GL_VERTEX_ARRAY);
vertexVBO.bind(gl);
gl.glVertexPointer(geometry.getVertices().getGroupSize(), GL2.GL_FLOAT, 0, 0);
if (edgesVBO != null)
{
edgesVBO.bind(gl);
gl.glDrawElements(renderMode, ((IMeshGeometry) geometry).getEdgeIndices().getNumberOfValues(),
GL2.GL_UNSIGNED_INT, 0);
}
else
{
gl.glDrawArrays(renderMode, 0, geometry.getVertices().getNumberOfGroups());
}
checkForError(gl);
gl.glBindBuffer(GL2.GL_ARRAY_BUFFER, 0);
gl.glBindBuffer(GL2.GL_ELEMENT_ARRAY_BUFFER, 0);
}
finally
{
shader.unbind(gl);
stack.pop(gl);
}
}
private void init()
{
if (isInitialised.get())
{
return;
}
if (vertexVBO == null)
{
vertexVBO = ModelDataVBO.createDataVBO(geometry.getVertices());
}
if (edgesVBO == null && geometryHasEdges())
{
edgesVBO = ModelDataVBO.createIndexVBO(((IMeshGeometry) geometry).getEdgeIndices());
}
if (vertexColourVBO == null && geometryHasVertexColours())
{
vertexColourVBO = ModelDataVBO.createDataVBO(((IVertexColouredGeometry) geometry).getVertexColour());
}
if (renderMode == null)
{
renderMode = getModeForGeometry();
}
shader.setUseVertexColouring(geometryHasVertexColours());
isInitialised.set(true);
}
private boolean geometryHasEdges()
{
return geometry instanceof IMeshGeometry && ((IMeshGeometry) geometry).hasEdgeIndices();
}
private boolean geometryHasVertexColours()
{
return geometry instanceof IVertexColouredGeometry && ((IVertexColouredGeometry) geometry).hasVertexColour();
}
private boolean geometryHasColourMap()
{
return geometry instanceof IVertexColourMappedGeometry
&& ((IVertexColourMappedGeometry) geometry).hasColorMap();
}
private void checkForError(GL2 gl)
{
int error = gl.glGetError();
if (error != GL2.GL_NO_ERROR)
{
StringBuffer buf = new StringBuffer("OpenGL error detected: "); //$NON-NLS-1$
switch (error)
{
case GL2.GL_INVALID_ENUM:
buf.append("GL_INVALID_ENUM "); //$NON-NLS-1$
break;
case GL2.GL_INVALID_VALUE:
buf.append("GL_INVALID_VALUE "); //$NON-NLS-1$
break;
case GL2.GL_INVALID_OPERATION:
buf.append("GL_INVALID_OPERATION "); //$NON-NLS-1$
break;
case GL2GL3.GL_STACK_OVERFLOW:
buf.append("GL_STACK_OVERFLOW "); //$NON-NLS-1$
break;
case GL2GL3.GL_STACK_UNDERFLOW:
buf.append("GL_STACK_UNDERFLOW "); //$NON-NLS-1$
break;
case GL2.GL_OUT_OF_MEMORY:
buf.append("GL_OUT_OF_MEMORY "); //$NON-NLS-1$
break;
default:
buf.append("Unknown (0x").append(Integer.toHexString(error)).append(")"); //$NON-NLS-1$//$NON-NLS-2$
}
buf.append(". Relaunch with -Djogl.debug=all and -Dnewt.debug=all for more information."); //$NON-NLS-1$
logger.error(buf.toString());
}
}
private int getModeForGeometry()
{
int mode = GL2.GL_POINTS;
if (geometry instanceof IMeshGeometry)
{
switch (((IMeshGeometry) geometry).getFaceType())
{
case QUADS:
mode = GL2.GL_QUADS;
break;
case QUAD_STRIP:
mode = GL2.GL_QUAD_STRIP;
break;
case TRIANGLES:
mode = GL2.GL_TRIANGLES;
break;
case TRIANGLE_FAN:
mode = GL2.GL_TRIANGLE_FAN;
break;
case TRIANGLE_STRIP:
mode = GL2.GL_TRIANGLE_STRIP;
break;
case LINES:
mode = GL2.GL_LINES;
break;
case LINE_STRIP:
mode = GL2.GL_LINE_STRIP;
break;
case LINE_LOOP:
mode = GL2.GL_LINE_LOOP;
break;
default:
mode = GL2.GL_POINTS;
}
}
return mode;
}
private ColorType getColourTypeForGeometry()
{
if (geometry instanceof IVertexColouredGeometry)
{
return ((IVertexColouredGeometry) geometry).getColourType();
}
return ColorType.RGB;
}
@Override
public IModelGeometry getGeometry()
{
return geometry;
}
}