src.gov.nasa.worldwind.render.SurfaceTileRenderer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwindx Show documentation
Show all versions of worldwindx Show documentation
World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/
package gov.nasa.worldwind.render;
import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.util.texture.*;
import gov.nasa.worldwind.Disposable;
import gov.nasa.worldwind.terrain.SectorGeometry;
import gov.nasa.worldwind.util.*;
import javax.media.opengl.*;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.logging.Level;
/**
* @author tag
* @version $Id: SurfaceTileRenderer.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public abstract class SurfaceTileRenderer implements Disposable
{
private static final int DEFAULT_ALPHA_TEXTURE_SIZE = 1024;
protected Texture alphaTexture;
protected Texture outlineTexture;
private boolean showImageTileOutlines = false;
/**
* Free internal resources held by this surface tile renderer. A GL context must be current when this method is
* called.
*
* @throws javax.media.opengl.GLException - If an OpenGL context is not current when this method is called.
*/
public void dispose()
{
GLContext context = GLContext.getCurrent();
if (context == null || context.getGL() == null)
return;
GL gl = context.getGL();
if (this.alphaTexture != null)
this.alphaTexture.destroy(gl);
this.alphaTexture = null;
if (this.outlineTexture != null)
this.outlineTexture.destroy(gl);
this.outlineTexture = null;
}
public boolean isShowImageTileOutlines()
{
return showImageTileOutlines;
}
public void setShowImageTileOutlines(boolean showImageTileOutlines)
{
this.showImageTileOutlines = showImageTileOutlines;
}
public void renderTile(DrawContext dc, SurfaceTile tile)
{
if (tile == null)
{
String message = Logging.getMessage("nullValue.TileIsNull");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
ArrayList al = new ArrayList(1);
al.add(tile);
this.renderTiles(dc, al);
al.clear();
}
protected static class Transform
{
double HScale;
double VScale;
double HShift;
double VShift;
double rotationDegrees;
}
abstract protected void preComputeTextureTransform(DrawContext dc, SectorGeometry sg, Transform t);
abstract protected void computeTextureTransform(DrawContext dc, SurfaceTile tile, Transform t);
abstract protected Iterable getIntersectingTiles(DrawContext dc, SectorGeometry sg,
Iterable tiles);
public void renderTiles(DrawContext dc, Iterable tiles)
{
if (tiles == null)
{
String message = Logging.getMessage("nullValue.TileIterableIsNull");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
GL2 gl = dc.getGL().getGL2(); // GL initialization checks for GL2 compatibility.
int alphaTextureUnit = GL.GL_TEXTURE1;
boolean showOutlines = this.showImageTileOutlines && dc.getGLRuntimeCapabilities().getNumTextureUnits() > 2;
gl.glPushAttrib(GL2.GL_COLOR_BUFFER_BIT // for alpha func
| GL2.GL_ENABLE_BIT
| GL2.GL_CURRENT_BIT
| GL2.GL_DEPTH_BUFFER_BIT // for depth func
| GL2.GL_TRANSFORM_BIT);
try
{
this.alphaTexture = dc.getTextureCache().getTexture(this);
if (this.alphaTexture == null)
{
this.initAlphaTexture(dc, DEFAULT_ALPHA_TEXTURE_SIZE); // TODO: choose size to match incoming tile size?
dc.getTextureCache().put(this, this.alphaTexture);
}
if (showOutlines && this.outlineTexture == null)
this.initOutlineTexture(dc, 128);
gl.glEnable(GL.GL_DEPTH_TEST);
gl.glDepthFunc(GL.GL_LEQUAL);
gl.glEnable(GL2.GL_ALPHA_TEST);
gl.glAlphaFunc(GL2.GL_GREATER, 0.01f);
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glPushMatrix();
if (!dc.isPickingMode())
{
gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_MODULATE);
}
else
{
gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_COMBINE);
gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_SRC0_RGB, GL2.GL_PREVIOUS);
gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_COMBINE_RGB, GL2.GL_REPLACE);
}
int numTexUnitsUsed = 2;
if (showOutlines)
{
numTexUnitsUsed = 3;
alphaTextureUnit = GL.GL_TEXTURE2;
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glPushMatrix();
gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_ADD);
}
gl.glActiveTexture(alphaTextureUnit);
gl.glEnable(GL.GL_TEXTURE_2D);
gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glPushMatrix();
gl.glTexEnvi(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, GL2.GL_MODULATE);
dc.getSurfaceGeometry().beginRendering(dc);
// For each current geometry tile, find the intersecting image tiles and render the geometry
// tile once for each intersecting image tile.
Transform transform = new Transform();
for (SectorGeometry sg : dc.getSurfaceGeometry())
{
Iterable tilesToRender = this.getIntersectingTiles(dc, sg, tiles);
if (tilesToRender == null)
continue;
sg.beginRendering(dc, numTexUnitsUsed); // TODO: wrap in try/catch in case of exception
// Pre-load info to compute the texture transform
this.preComputeTextureTransform(dc, sg, transform);
// For each intersecting tile, establish the texture transform necessary to map the image tile
// into the geometry tile's texture space. Use an alpha texture as a mask to prevent changing the
// frame buffer where the image tile does not overlap the geometry tile. Render both the image and
// alpha textures via multi-texture rendering.
// TODO: Figure out how to apply multi-texture to more than one tile at a time. Use fragment shader?
for (SurfaceTile tile : tilesToRender)
{
gl.glActiveTexture(GL.GL_TEXTURE0);
if (tile.bind(dc))
{
gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glLoadIdentity();
tile.applyInternalTransform(dc, true);
// Determine and apply texture transform to map image tile into geometry tile's texture space
this.computeTextureTransform(dc, tile, transform);
gl.glScaled(transform.HScale, transform.VScale, 1d);
gl.glTranslated(transform.HShift, transform.VShift, 0d);
if (showOutlines)
{
gl.glActiveTexture(GL.GL_TEXTURE1);
this.outlineTexture.bind(gl);
// Apply the same texture transform to the outline texture. The outline textures uses a
// different texture unit than the tile, so the transform made above does not carry over.
gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glLoadIdentity();
gl.glScaled(transform.HScale, transform.VScale, 1d);
gl.glTranslated(transform.HShift, transform.VShift, 0d);
}
// Prepare the alpha texture to be used as a mask where texture coords are outside [0,1]
gl.glActiveTexture(alphaTextureUnit);
this.alphaTexture.bind(gl);
// Apply the same texture transform to the alpha texture. The alpha texture uses a
// different texture unit than the tile, so the transform made above does not carry over.
gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glLoadIdentity();
gl.glScaled(transform.HScale, transform.VScale, 1d);
gl.glTranslated(transform.HShift, transform.VShift, 0d);
// Render the geometry tile
sg.renderMultiTexture(dc, numTexUnitsUsed);
}
}
sg.endRendering(dc);
}
}
catch (Exception e)
{
Logging.logger().log(Level.SEVERE,
Logging.getMessage("generic.ExceptionWhileRenderingLayer", this.getClass().getName()), e);
}
finally
{
dc.getSurfaceGeometry().endRendering(dc);
gl.glActiveTexture(alphaTextureUnit);
gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glPopMatrix();
gl.glDisable(GL.GL_TEXTURE_2D);
if (showOutlines)
{
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glPopMatrix();
gl.glDisable(GL.GL_TEXTURE_2D);
}
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glMatrixMode(GL2.GL_TEXTURE);
gl.glPopMatrix();
gl.glDisable(GL.GL_TEXTURE_2D);
gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_TEXTURE_ENV_MODE, OGLUtil.DEFAULT_TEX_ENV_MODE);
if (dc.isPickingMode())
{
gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_SRC0_RGB, OGLUtil.DEFAULT_SRC0_RGB);
gl.glTexEnvf(GL2.GL_TEXTURE_ENV, GL2.GL_COMBINE_RGB, OGLUtil.DEFAULT_COMBINE_RGB);
}
gl.glPopAttrib();
}
}
private static void fillByteBuffer(ByteBuffer buffer, byte value)
{
for (int i = 0; i < buffer.capacity(); i++)
{
buffer.put(value);
}
}
protected void initAlphaTexture(DrawContext dc, int size)
{
ByteBuffer textureBytes = Buffers.newDirectByteBuffer(size * size);
fillByteBuffer(textureBytes, (byte) 0xff);
GL gl = dc.getGL();
TextureData textureData = new TextureData(gl.getGLProfile(), GL.GL_ALPHA, size, size, 0, GL.GL_ALPHA,
GL.GL_UNSIGNED_BYTE, false, false, false, textureBytes.rewind(), null);
this.alphaTexture = TextureIO.newTexture(textureData);
this.alphaTexture.bind(gl);
this.alphaTexture.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
this.alphaTexture.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
this.alphaTexture.setTexParameteri(gl, GL.GL_TEXTURE_WRAP_S, GL2.GL_CLAMP_TO_BORDER);
this.alphaTexture.setTexParameteri(gl, GL.GL_TEXTURE_WRAP_T, GL2.GL_CLAMP_TO_BORDER);
// Assume the default border color of (0, 0, 0, 0).
}
protected void initOutlineTexture(DrawContext dc, int size)
{
ByteBuffer textureBytes = Buffers.newDirectByteBuffer(size * size);
for (int row = 0; row < size; row++)
{
for (int col = 0; col < size; col++)
{
byte p;
if (row == 0 || col == 0 || row == size - 1 || col == size - 1)
p = (byte) 0xff;
else
p = (byte) 0;
textureBytes.put(row * size + col, p);
}
}
GL gl = dc.getGL();
TextureData textureData = new TextureData(gl.getGLProfile(), GL.GL_LUMINANCE, size, size, 0, GL.GL_LUMINANCE,
GL.GL_UNSIGNED_BYTE, false, false, false, textureBytes.rewind(), null);
this.outlineTexture = TextureIO.newTexture(textureData);
this.outlineTexture.bind(gl);
this.outlineTexture.setTexParameteri(gl, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
this.outlineTexture.setTexParameteri(gl, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
this.outlineTexture.setTexParameteri(gl, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
this.outlineTexture.setTexParameteri(gl, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
}
}