package org.osm2world.core.target.jogl;
import static java.util.Arrays.asList;
import static javax.media.opengl.GL.GL_ARRAY_BUFFER;
import static javax.media.opengl.GL.GL_BACK;
import static javax.media.opengl.GL.GL_CCW;
import static javax.media.opengl.GL.GL_CULL_FACE;
import static javax.media.opengl.GL.GL_DEPTH_TEST;
import static javax.media.opengl.GL.GL_FRONT_AND_BACK;
import static javax.media.opengl.GL.GL_REPEAT;
import static javax.media.opengl.GL.GL_STATIC_DRAW;
import static javax.media.opengl.GL.GL_TEXTURE0;
import static javax.media.opengl.GL.GL_TEXTURE_2D;
import static javax.media.opengl.GL.GL_TEXTURE_WRAP_S;
import static javax.media.opengl.GL.GL_TEXTURE_WRAP_T;
import static javax.media.opengl.GL2GL3.GL_FILL;
import static javax.media.opengl.GL2GL3.GL_LINE;
import static javax.media.opengl.fixedfunc.GLMatrixFunc.GL_MODELVIEW;
import static javax.media.opengl.fixedfunc.GLMatrixFunc.GL_PROJECTION;
import java.awt.Color;
import java.io.File;
import java.nio.FloatBuffer;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GL2GL3;
import javax.media.opengl.GL3;
import org.osm2world.core.math.AxisAlignedBoundingBoxXYZ;
import org.osm2world.core.math.AxisAlignedBoundingBoxXZ;
import org.osm2world.core.math.VectorXYZ;
import org.osm2world.core.math.VectorXYZW;
import org.osm2world.core.target.common.lighting.GlobalLightingParameters;
import org.osm2world.core.target.common.rendering.Camera;
import org.osm2world.core.target.common.rendering.Projection;
import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.math.FloatUtil;
import com.jogamp.opengl.util.PMVMatrix;
import com.jogamp.opengl.util.texture.Texture;
public class JOGLTargetShader extends AbstractJOGLTarget implements JOGLTarget {
private DefaultShader defaultShader;
private ShadowMapShader shadowMapShader;
private ShadowVolumeShader shadowVolumeShader;
//private DepthBufferShader depthBufferShader;
private SSAOShader ssaoShader;
private NonAreaShader nonAreaShader;
private BackgroundShader backgroundShader;
private GL3 gl;
/**
* PMVMatrix for rendering the world from camera perspective.
*/
private PMVMatrix pmvMatrix;
private JOGLRendererVBONonAreaShader nonAreaRenderer;
private JOGLRendererVBOShader rendererShader;
private JOGLRendererVBOShadowVolume rendererShadowVolume;
private AxisAlignedBoundingBoxXZ xzBoundary;
private boolean showShadowPerspective;
public JOGLTargetShader(GL3 gl, JOGLRenderingParameters renderingParameters,
GlobalLightingParameters globalLightingParameters) {
super(gl, renderingParameters, globalLightingParameters);
defaultShader = new DefaultShader(gl);
shadowMapShader = new ShadowMapShader(gl);
//depthBufferShader = new DepthBufferShader(gl);
ssaoShader = new SSAOShader(gl);
shadowVolumeShader = new ShadowVolumeShader(gl);
nonAreaShader = new NonAreaShader(gl);
backgroundShader = new BackgroundShader(gl);
this.gl = gl;
pmvMatrix = new PMVMatrix();
reset();
}
@Override
public void drawBackgoundImage(File backgroundImage,
int startPixelX, int startPixelY, int pixelWidth, int pixelHeight,
JOGLTextureManager textureManager) {
backgroundShader.useShader();
PMVMatrix backgroundPMVMatrix = new PMVMatrix();
backgroundPMVMatrix.glMatrixMode(GL_PROJECTION);
backgroundPMVMatrix.glLoadIdentity();
backgroundPMVMatrix.glOrthof(0, 1, 0, 1, 0, 1);
backgroundPMVMatrix.glMatrixMode(GL_MODELVIEW);
backgroundPMVMatrix.glLoadIdentity();
backgroundShader.setPMVMatrix(backgroundPMVMatrix);
gl.glDepthMask( false );
/* texture binding */
gl.glActiveTexture(GL_TEXTURE0);
Texture backgroundTexture =
textureManager.getTextureForFile(backgroundImage);
backgroundTexture.bind(gl);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
gl.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
gl.glUniform1i(backgroundShader.getTextureID(), 0);
int texWidth = backgroundTexture.getImageWidth();
int texHeight = backgroundTexture.getImageHeight();
/* draw quad */
/* create the buffer */
int[] id = new int[1];
gl.glGenBuffers(1, id, 0);
/* collect the data for the buffer */
int verticeCount = 4;
int valueCount = 2;
FloatBuffer valueBuffer = Buffers.newDirectFloatBuffer(verticeCount*(2*valueCount));
valueBuffer.put(0);
valueBuffer.put(0);
valueBuffer.put(1f);
valueBuffer.put(0);
valueBuffer.put(0);
valueBuffer.put(1f);
valueBuffer.put(1f);
valueBuffer.put(1f);
valueBuffer.put((float) startPixelX / texWidth);
valueBuffer.put((float) startPixelY / texHeight);
valueBuffer.put((float) (startPixelX + pixelWidth) / texWidth);
valueBuffer.put((float) startPixelY / texHeight);
valueBuffer.put((float) startPixelX / texWidth);
valueBuffer.put((float) (startPixelY + pixelHeight) / texHeight);
valueBuffer.put((float) (startPixelX + pixelWidth) / texWidth);
valueBuffer.put((float) (startPixelY + pixelHeight) / texHeight);
valueBuffer.rewind();
/* write the data into the buffer */
gl.glBindBuffer(GL_ARRAY_BUFFER, id[0]);
gl.glBufferData(
GL_ARRAY_BUFFER,
valueBuffer.capacity() * Buffers.SIZEOF_FLOAT,
valueBuffer,
GL_STATIC_DRAW);
gl.glEnableVertexAttribArray(backgroundShader.getVertexPositionID());
gl.glEnableVertexAttribArray(backgroundShader.getVertexTexCoordID());
int stride = 0;
gl.glVertexAttribPointer(backgroundShader.getVertexPositionID(), valueCount, GL.GL_FLOAT, false, stride, 0);
gl.glVertexAttribPointer(backgroundShader.getVertexTexCoordID(), valueCount, GL.GL_FLOAT, false, stride, Buffers.SIZEOF_FLOAT * valueCount * verticeCount);
gl.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4);
gl.glDisableVertexAttribArray(backgroundShader.getVertexPositionID());
gl.glDisableVertexAttribArray(backgroundShader.getVertexTexCoordID());
/* restore some settings */
gl.glDepthMask( true );
backgroundShader.disableShader();
}
/**
* Calculate tighter near and far planes for the boundingBox around the visible world objects.
* @param camera the current camera for which the planes are calculated
* @param projection the current projection
* @param boundingBox the bounding box around all visible world objects
* @return a new projection with near and far planes as tight as possible
*/
public static Projection updateClippingPlanesForCamera(Camera camera, Projection projection, AxisAlignedBoundingBoxXYZ boundingBox) {
double nearPlane = Double.POSITIVE_INFINITY, farPlane = 0;
PMVMatrix camMat = new PMVMatrix();
VectorXYZ pos = camera.getPos();
VectorXYZ lookAt = camera.getLookAt();
VectorXYZ up = camera.getUp();
camMat.gluLookAt(
(float)pos.x, (float)pos.y, (float)-pos.z,
(float)lookAt.x, (float)lookAt.y, (float)-lookAt.z,
(float)up.x, (float)up.y, (float)-up.z);
for (VectorXYZ corner : boundingBox.corners()) {
float[] result = new float[4];
FloatUtil.multMatrixVecf(camMat.glGetMvMatrixf(),
new float[]{(float)corner.x, (float)corner.y, (float)corner.z, 1}, result);
VectorXYZ cornerCam = new VectorXYZ(result[0]/result[3], result[1]/result[3], result[2]/result[3]);
double depth = -cornerCam.z;
nearPlane = Math.min(depth, nearPlane);
farPlane = Math.max(depth, farPlane);
}
if (nearPlane == Double.POSITIVE_INFINITY)
nearPlane = projection.getNearClippingDistance();
else
nearPlane = Math.max(projection.getNearClippingDistance(), nearPlane);
if (farPlane == 0)
farPlane = projection.getFarClippingDistance();
else
farPlane = Math.min(projection.getFarClippingDistance(), farPlane);
return new Projection(projection.isOrthographic(), projection.getAspectRatio(),
projection.getVertAngle(), projection.getVolumeHeight(), nearPlane, farPlane);
}
@Override
public void renderPart(Camera camera, Projection projection, double xStart,
double xEnd, double yStart, double yEnd) {
if (renderer == null) {
throw new IllegalStateException("finish must be called first");
}
if (renderingParameters.overwriteProjectionClippingPlanes) {
projection = updateClippingPlanesForCamera(camera, projection, rendererShader.getBoundingBox());
}
applyProjectionMatricesForPart(pmvMatrix, projection,
xStart, xEnd, yStart, yEnd);
applyCameraMatrices(pmvMatrix, camera);
if (renderingParameters.useSSAO) {
defaultShader.setSSAOkernelSize(renderingParameters.SSAOkernelSize);
defaultShader.setSSAOradius(renderingParameters.SSAOradius);
// based on http://john-chapman-graphics.blogspot.de/2013/01/ssao-tutorial.html
// render depth buffer only
ssaoShader.useShader();
ssaoShader.setPMVMatrix(pmvMatrix);
applyRenderingParameters(gl, renderingParameters);
rendererShader.setShader(ssaoShader);
rendererShader.render();
ssaoShader.disableShader();
}
if (renderingParameters.useShadowMaps) {
// TODO: render only part?
shadowMapShader.setCameraFrustumPadding(renderingParameters.shadowMapCameraFrustumPadding);
shadowMapShader.setShadowMapSize(renderingParameters.shadowMapWidth, renderingParameters.shadowMapHeight);
shadowMapShader.useShader();
shadowMapShader.preparePMVMatrix(globalLightingParameters, pmvMatrix, rendererShader.getBoundingBox());
// render opaque shadow casters only when not using shadow volumes simultaneously, as those will be rendered there
shadowMapShader.setRenderOpaque(!renderingParameters.useShadowVolumes);
//shadowMapShader.setPMVMatrix(pmvMatrix);
/* render primitives to shadow map*/
rendererShader.setShader(shadowMapShader);
rendererShader.render();
//ShaderManager.saveDepthBuffer(new File("/home/sebastian/shadowmap"+xStart+"_"+yStart+".png"), shadowMapShader.getShadowMapHandle(), shadowMapShader.shadowMapWidth, shadowMapShader.shadowMapHeight, gl);
//shadowMapShader.saveShadowMap(new File("/home/sebastian/shadowmap.bmp"));
//shadowMapShader.saveColorBuffer(new File("/home/sebastian/shadowmap_color"+xStart+"_"+yStart+".png"));
shadowMapShader.disableShader();
}
/* apply camera and projection information */
defaultShader.useShader();
defaultShader.loadDefaults();
if (showShadowPerspective)
defaultShader.setPMVMatrix(shadowMapShader.getPMVMatrix());
else
defaultShader.setPMVMatrix(pmvMatrix);
/* apply global rendering parameters */
applyRenderingParameters(gl, renderingParameters);
applyLightingParameters(defaultShader, globalLightingParameters);
if (renderingParameters.useShadowMaps) {
defaultShader.bindShadowMap(shadowMapShader.getShadowMapHandle());
defaultShader.setShadowMatrix(shadowMapShader.getPMVMatrix());
}
if (!showShadowPerspective && renderingParameters.useSSAO) {
defaultShader.enableSSAOwithDepthMap(ssaoShader.getDepthBuferHandle());
}
// if using shadow volumes render semi-transparent objects later
defaultShader.setRenderSemiTransparent(!renderingParameters.useShadowVolumes);
/* render primitives */
rendererShader.setShader(defaultShader);
rendererShader.render(camera, projection);
defaultShader.disableShader();
/* non area primitives */
nonAreaShader.useShader();
nonAreaShader.loadDefaults();
if (showShadowPerspective)
nonAreaShader.setPMVMatrix(shadowMapShader.getPMVMatrix());
else
nonAreaShader.setPMVMatrix(pmvMatrix);
nonAreaRenderer.render();
nonAreaShader.disableShader();
/* render shadows with shadow volumes on top of world with lighting */
if (renderingParameters.useShadowVolumes) {
/* Render shadow volumes with depth-fail algorithm. Uses the previously filled depth buffer */
int[] drawbuffer = new int[1];
gl.glGetIntegerv(GL2GL3.GL_DRAW_BUFFER, drawbuffer, 0);
gl.glDrawBuffer(GL.GL_NONE);
gl.glEnable(GL.GL_STENCIL_TEST);
gl.glDepthMask(false);
gl.glEnable(GL3.GL_DEPTH_CLAMP); // used to clamp the infinity big shadow volumes
gl.glDisable(GL_CULL_FACE);
// We need the stencil test to be enabled but we want it
// to succeed always. Only the depth test matters.
gl.glStencilFunc(GL.GL_ALWAYS, 0, 0xff);
// Set the stencil test per the depth fail algorithm
gl.glStencilOpSeparate(GL.GL_BACK, GL.GL_KEEP, GL.GL_INCR_WRAP, GL.GL_KEEP);
gl.glStencilOpSeparate(GL.GL_FRONT, GL.GL_KEEP, GL.GL_DECR_WRAP, GL.GL_KEEP);
// relax depth test to prevent z-fighting with self shadowing
//gl.glDepthFunc(GL.GL_LEQUAL);
// if using shadow volumes render semi-transparent objects later
shadowVolumeShader.setRenderSemiTransparent(!renderingParameters.useShadowVolumes);
shadowVolumeShader.useShader();
shadowVolumeShader.setPMVMatrix(pmvMatrix);
rendererShadowVolume.setShader(shadowVolumeShader);
rendererShadowVolume.render();
shadowVolumeShader.disableShader();
// Restore local stuff
gl.glDepthMask(true);
gl.glDisable(GL3.GL_DEPTH_CLAMP);
gl.glEnable(GL_CULL_FACE);
/* Render scene in shadow */
gl.glDrawBuffer(drawbuffer[0]);
// Draw only if the corresponding stencil value is NOT zero
gl.glStencilFunc(GL.GL_NOTEQUAL, 0x0, 0xFF);
// prevent update to the stencil buffer
gl.glStencilOpSeparate(GL.GL_BACK, GL.GL_KEEP, GL.GL_KEEP, GL.GL_KEEP);
gl.glStencilOpSeparate(GL.GL_FRONT, GL.GL_KEEP, GL.GL_KEEP, GL.GL_KEEP);
// Draw on top of the already rendered world with lighting
gl.glDepthFunc(GL.GL_LEQUAL);
applyRenderingParameters(gl, renderingParameters);
defaultShader.useShader();
defaultShader.loadDefaults();
defaultShader.setPMVMatrix(pmvMatrix);
defaultShader.setShadowed(true);
if (!showShadowPerspective && renderingParameters.useSSAO) {
defaultShader.enableSSAOwithDepthMap(ssaoShader.getDepthBuferHandle());
}
// if using shadow volumes render semi-transparent objects later
defaultShader.setRenderSemiTransparent(!renderingParameters.useShadowVolumes);
// /* render primitives */
rendererShader.setShader(defaultShader);
rendererShader.render(camera, projection);
defaultShader.setShadowed(false);
defaultShader.disableShader();
/* non area primitives */
nonAreaShader.useShader();
nonAreaShader.loadDefaults();
nonAreaShader.setPMVMatrix(pmvMatrix);
nonAreaRenderer.render();
nonAreaShader.disableShader();
// Render shadow volumes for debug
/*gl.glDisable(GL.GL_STENCIL_TEST);
//gl.glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
shadowVolumeShader.useShader();
shadowVolumeShader.setPMVMatrix(pmvMatrix);
rendererShadowVolume.setShader(shadowVolumeShader);
rendererShadowVolume.render(camera, projection);
shadowVolumeShader.disableShader();*/
// reset
gl.glDisable(GL.GL_STENCIL_TEST);
gl.glDepthFunc(GL.GL_LESS);
/* render semi-transparent objects now */
// NOTE: results could be improved slightly, if the depth-fail algorithm is executed here as well
// the result would be that the topmost semi-transparent pixel would receive shadow volume shadows
// (needs to be investigated, if the difference is noticeable in practice)
defaultShader.useShader();
defaultShader.loadDefaults();
defaultShader.setPMVMatrix(pmvMatrix);
/* apply global rendering parameters */
applyRenderingParameters(gl, renderingParameters);
applyLightingParameters(defaultShader, globalLightingParameters);
if (renderingParameters.useShadowMaps) {
defaultShader.bindShadowMap(shadowMapShader.getShadowMapHandle());
defaultShader.setShadowMatrix(shadowMapShader.getPMVMatrix());
}
if (!showShadowPerspective && renderingParameters.useSSAO) {
defaultShader.enableSSAOwithDepthMap(ssaoShader.getDepthBuferHandle());
}
defaultShader.setRenderOnlySemiTransparent(true);
/* render primitives */
rendererShader.setShader(defaultShader);
rendererShader.render(camera, projection);
defaultShader.disableShader();
}
}
static final void applyRenderingParameters(GL3 gl,
JOGLRenderingParameters parameters) {
/* backface culling */
if (parameters.frontFace == null) {
gl.glDisable(GL_CULL_FACE);
} else {
gl.glFrontFace(GL_CCW);
gl.glCullFace(GL_BACK);
gl.glEnable (GL_CULL_FACE);
}
/* wireframe mode */
if (parameters.wireframe) {
gl.glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
} else {
gl.glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
/* z buffer */
if (parameters.useZBuffer) {
gl.glEnable(GL_DEPTH_TEST);
} else {
gl.glDisable(GL_DEPTH_TEST);
}
}
static final void applyCameraMatrices(PMVMatrix pmvMatrix, Camera camera) {
pmvMatrix.glMatrixMode(GL_MODELVIEW);
pmvMatrix.glLoadIdentity();
VectorXYZ pos = camera.getPos();
VectorXYZ lookAt = camera.getLookAt();
VectorXYZ up = camera.getUp();
pmvMatrix.gluLookAt(
(float)pos.x, (float)pos.y, (float)-pos.z,
(float)lookAt.x, (float)lookAt.y, (float)-lookAt.z,
(float)up.x, (float)up.y, (float)-up.z);
}
static final void applyProjectionMatrices(PMVMatrix pmvMatrix, Projection projection) {
applyProjectionMatricesForPart(pmvMatrix, projection, 0, 1, 0, 1);
}
/**
* similar to {@link #applyProjectionMatrices(GL2, Projection)},
* but allows rendering only a part of the "normal" image.
*/
static final void applyProjectionMatricesForPart(PMVMatrix pmvMatrix, Projection projection,
double xStart, double xEnd, double yStart, double yEnd) {
if ((xStart != 0 || xEnd != 1 || yStart != 0 || yEnd != 1)
&& !projection.isOrthographic()) {
throw new IllegalArgumentException("section rendering only supported "
+ "for orthographic projections");
}
pmvMatrix.glMatrixMode(GL_PROJECTION);
pmvMatrix.glLoadIdentity();
if (projection.isOrthographic()) {
double volumeWidth = projection.getAspectRatio() * projection.getVolumeHeight();
pmvMatrix.glOrthof(
(float)((-0.5 + xStart) * volumeWidth),
(float)((-0.5 + xEnd ) * volumeWidth),
(float)((-0.5 + yStart) * projection.getVolumeHeight()),
(float)((-0.5 + yEnd ) * projection.getVolumeHeight()),
(float)(projection.getNearClippingDistance()),
(float)(projection.getFarClippingDistance()));
} else { //perspective
pmvMatrix.gluPerspective(
(float)(projection.getVertAngle()),
(float)(projection.getAspectRatio()),
(float)(projection.getNearClippingDistance()),
(float)(projection.getFarClippingDistance()));
}
pmvMatrix.glMatrixMode(GL_MODELVIEW);
}
static final void applyLightingParameters(DefaultShader shader,
GlobalLightingParameters lighting) {
shader.setGlobalLighting(lighting);
}
// public void drawPrimitive(GL3 gl, int glPrimitiveType,
// List<VectorXYZ> vertices, List<VectorXYZ> normals,
// List<List<VectorXZ>> texCoordLists) {
// assert vertices.size() == normals.size();
//
// gl.glBegin(glPrimitiveType);
//
// for (int i = 0; i < vertices.size(); i++) {
//
// if (texCoordLists != null) {
// for (int texLayer = 0; texLayer < texCoordLists.size(); texLayer++) {
// VectorXZ textureCoord = texCoordLists.get(texLayer).get(i);
// if (i==0) {
// gl.glVertexAttrib2d(shader.getVertexTexCoordID(), textureCoord.x, textureCoord.z);
// }
// }
// }
//
// VectorXYZ n = normals.get(i);
// gl.glVertexAttrib3d(shader.getVertexNormalID(), n.x, n.y, -n.z);
//
// VectorXYZ v = vertices.get(i);
// gl.glVertexAttrib3d(shader.getVertexPositionID(), v.x, v.y, -v.z);
//
// }
//
// gl.glEnd();
// }
/**
* Draw the corners of a bounding box as colored lines.
* @param color the color of the lines
* @param bb the bounding box to draw
*/
protected final void drawBoundingBox(Color color, AxisAlignedBoundingBoxXYZ bb) {
// bottom
drawBox(color, new VectorXYZ(bb.minX, bb.minY, bb.minZ), new VectorXYZ(
bb.minX, bb.minY, bb.maxZ), new VectorXYZ(bb.maxX, bb.minY,
bb.maxZ), new VectorXYZ(bb.maxX, bb.minY, bb.minZ));
// top
drawBox(color, new VectorXYZ(bb.minX, bb.maxY, bb.minZ), new VectorXYZ(
bb.minX, bb.maxY, bb.maxZ), new VectorXYZ(bb.maxX, bb.maxY,
bb.maxZ), new VectorXYZ(bb.maxX, bb.maxY, bb.minZ));
// bottom/top connections
drawLineStrip(color, 1, new VectorXYZ(bb.minX, bb.minY, bb.minZ),
new VectorXYZ(bb.minX, bb.maxY, bb.minZ));
drawLineStrip(color, 1, new VectorXYZ(bb.minX, bb.minY, bb.maxZ),
new VectorXYZ(bb.minX, bb.maxY, bb.maxZ));
drawLineStrip(color, 1, new VectorXYZ(bb.maxX, bb.minY, bb.maxZ),
new VectorXYZ(bb.maxX, bb.maxY, bb.maxZ));
drawLineStrip(color, 1, new VectorXYZ(bb.maxX, bb.minY, bb.minZ),
new VectorXYZ(bb.maxX, bb.maxY, bb.minZ));
}
/**
* Draw a colored line between two points.
*/
protected final void drawLine(Color color,
VectorXYZ v1, VectorXYZ v2) {
drawLineLoop(color, 1, asList(v1, v2));
}
/**
* Draw a colored 2D box as line loop.
*/
protected final void drawBox(Color color,
VectorXYZ v1, VectorXYZ v2, VectorXYZ v3, VectorXYZ v4) {
drawLineLoop(color, 1, asList(v1, v2, v3, v4));
}
@Override
public void finish() {
if (isFinished()) return;
//this.drawLineLoop(Color.WHITE, 1, Arrays.asList(new VectorXYZ[]{xzBoundary.topLeft().xyz(0.1), xzBoundary.topRight().xyz(0.1), xzBoundary.bottomRight().xyz(0.1), xzBoundary.bottomLeft().xyz(0.1)}));
rendererShader = new JOGLRendererVBOShader(gl, textureManager, primitiveBuffer, xzBoundary);
renderer = rendererShader;
if (renderingParameters.drawBoundingBox) {
this.drawBoundingBox(Color.RED, rendererShader.getBoundingBox());
}
nonAreaRenderer = new JOGLRendererVBONonAreaShader(gl, nonAreaShader, nonAreaPrimitives);
if (renderingParameters.useShadowVolumes)
rendererShadowVolume = new JOGLRendererVBOShadowVolume(gl, primitiveBuffer, new VectorXYZW(globalLightingParameters.lightFromDirection, 0));
}
@Override
public void reset() {
super.reset();
if (rendererShadowVolume != null) {
rendererShadowVolume.freeResources();
rendererShadowVolume = null;
}
}
@Override
public void setXZBoundary(AxisAlignedBoundingBoxXZ boundary) {
this.xzBoundary = boundary;
}
/**
* Set whether to use the real camera PMVMatrix or the PMVMatrix normally
* used for drawing the shadow map when rendering the world.
*/
public void setShowShadowPerspective(boolean s) {
this.showShadowPerspective = s;
}
}