Update to Worldwind release 0.4.0
[worldwind-tracker.git] / gov / nasa / worldwind / render / SurfaceTileRenderer.java
blob61cc3b9126766862c792a8ad3fcd04d5eca4bffb
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
7 package gov.nasa.worldwind.render;
9 import com.sun.opengl.util.BufferUtil;
10 import com.sun.opengl.util.texture.*;
11 import gov.nasa.worldwind.Disposable;
12 import gov.nasa.worldwind.globes.SectorGeometry;
13 import gov.nasa.worldwind.util.Logging;
15 import javax.media.opengl.GL;
16 import java.nio.ByteBuffer;
17 import java.util.ArrayList;
18 import java.util.logging.Level;
20 /**
21 * @author tag
22 * @version $Id: SurfaceTileRenderer.java 3685 2007-12-03 19:51:05Z tgaskins $
24 public abstract class SurfaceTileRenderer implements Disposable
26 private static final int DEFAULT_ALPHA_TEXTURE_SIZE = 2;
28 private Texture alphaTexture;
29 private Texture outlineTexture;
30 private boolean showImageTileOutlines = false;
32 public void dispose() // TODO: but waiting to implement a global texture disposal system
36 public boolean isShowImageTileOutlines()
38 return showImageTileOutlines;
41 public void setShowImageTileOutlines(boolean showImageTileOutlines)
43 this.showImageTileOutlines = showImageTileOutlines;
46 public void renderTile(DrawContext dc, SurfaceTile tile)
48 if (tile == null)
50 String message = Logging.getMessage("nullValue.TileIsNull");
51 Logging.logger().severe(message);
52 throw new IllegalStateException(message);
55 ArrayList<SurfaceTile> al = new ArrayList<SurfaceTile>(1);
56 al.add(tile);
57 this.renderTiles(dc, al);
58 al.clear();
61 protected static class Transform
63 double HScale;
64 double VScale;
65 double HShift;
66 double VShift;
69 abstract protected void preComputeTransform(DrawContext dc, SectorGeometry sg);
70 abstract protected void computeTransform(DrawContext dc, SurfaceTile tile, Transform t);
71 abstract protected Iterable<SurfaceTile> getIntersectingTiles(DrawContext dc, SectorGeometry sg,
72 Iterable<? extends SurfaceTile> tiles);
74 public void renderTiles(DrawContext dc, Iterable<? extends SurfaceTile> tiles)
76 if (tiles == null)
78 String message = Logging.getMessage("nullValue.TileIterableIsNull");
79 Logging.logger().severe(message);
80 throw new IllegalStateException(message);
83 if (dc == null)
85 String message = Logging.getMessage("nullValue.DrawContextIsNull");
86 Logging.logger().severe(message);
87 throw new IllegalStateException(message);
90 GL gl = dc.getGL();
92 gl.glPushAttrib(GL.GL_COLOR_BUFFER_BIT // for alpha func
93 | GL.GL_ENABLE_BIT
94 | GL.GL_CURRENT_BIT
95 | GL.GL_DEPTH_BUFFER_BIT // for depth func
96 | GL.GL_TEXTURE_BIT // for texture env
97 | GL.GL_TRANSFORM_BIT);
99 try
101 this.alphaTexture = dc.getTextureCache().get(this);
102 if (this.alphaTexture == null)
104 this.initAlphaTexture(DEFAULT_ALPHA_TEXTURE_SIZE); // TODO: choose size to match incoming tile sizes?
105 dc.getTextureCache().put(this, this.alphaTexture);
108 boolean showOutlines = this.showImageTileOutlines && dc.getNumTextureUnits() > 2;
109 if (showOutlines && this.outlineTexture == null)
110 this.initOutlineTexture(128);
112 gl.glEnable(GL.GL_DEPTH_TEST);
113 gl.glDepthFunc(GL.GL_LEQUAL);
115 gl.glEnable(GL.GL_ALPHA_TEST);
116 gl.glAlphaFunc(GL.GL_GREATER, 0.01f);
118 gl.glActiveTexture(GL.GL_TEXTURE0);
119 gl.glEnable(GL.GL_TEXTURE_2D);
120 gl.glMatrixMode(GL.GL_TEXTURE);
121 gl.glPushMatrix();
122 if (!dc.isPickingMode())
124 gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
126 else
128 gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_COMBINE);
129 gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_SRC0_RGB, GL.GL_PREVIOUS);
130 gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_COMBINE_RGB, GL.GL_REPLACE);
133 int numTexUnitsUsed = 2;
134 int alphaTextureUnit = GL.GL_TEXTURE1;
135 if (showOutlines)
137 numTexUnitsUsed = 3;
138 alphaTextureUnit = GL.GL_TEXTURE2;
139 gl.glActiveTexture(GL.GL_TEXTURE1);
140 gl.glEnable(GL.GL_TEXTURE_2D);
141 gl.glMatrixMode(GL.GL_TEXTURE);
142 gl.glPushMatrix();
143 gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_ADD);
146 gl.glActiveTexture(alphaTextureUnit);
147 gl.glEnable(GL.GL_TEXTURE_2D);
148 gl.glMatrixMode(GL.GL_TEXTURE);
149 gl.glPushMatrix();
150 gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
152 // For each current geometry tile, find the intersecting image tiles and render the geometry
153 // tile once for each intersecting image tile.
154 Transform transform = new Transform();
155 for (SectorGeometry sg : dc.getSurfaceGeometry())
157 Iterable<SurfaceTile> tilesToRender = this.getIntersectingTiles(dc, sg, tiles);
158 if (tilesToRender == null)
159 continue;
161 // Pre-load info to compute the texture transform
162 this.preComputeTransform(dc, sg);
164 // For each interesecting tile, establish the texture transform necessary to map the image tile
165 // into the geometry tile's texture space. Use an alpha texture as a mask to prevent changing the
166 // frame buffer where the image tile does not overlap the geometry tile. Render both the image and
167 // alpha textures via multi-texture rendering.
168 // TODO: Figure out how to apply multi-texture to more than one tile at a time, most likely via a
169 // fragment shader.
170 for (SurfaceTile tile : tilesToRender)
172 gl.glActiveTexture(GL.GL_TEXTURE0);
174 if (tile.bind(dc))
176 gl.glMatrixMode(GL.GL_TEXTURE);
177 gl.glLoadIdentity();
178 tile.applyInternalTransform(dc);
180 // Determine and apply texture transform to map image tile into geometry tile's texture space
181 this.computeTransform(dc, tile, transform);
182 gl.glScaled(transform.HScale, transform.VScale, 1d);
183 gl.glTranslated(transform.HShift, transform.VShift, 0d);
185 if (showOutlines)
187 gl.glActiveTexture(GL.GL_TEXTURE1);
188 this.outlineTexture.bind();
190 // Apply the same texture transform to the outline texture. The outline textures uses a
191 // different texture unit than the tile, so the transform made above does not carry over.
192 gl.glMatrixMode(GL.GL_TEXTURE);
193 gl.glLoadIdentity();
194 gl.glScaled(transform.HScale, transform.VScale, 1d);
195 gl.glTranslated(transform.HShift, transform.VShift, 0d);
198 // Prepare the alpha texture to be used as a mask where texture coords are outside [0,1]
199 gl.glActiveTexture(alphaTextureUnit);
200 this.alphaTexture.bind();
202 // Apply the same texture transform to the alpha texture. The alpha texture uses a
203 // different texture unit than the tile, so the transform made above does not carry over.
204 gl.glMatrixMode(GL.GL_TEXTURE);
205 gl.glLoadIdentity();
206 gl.glScaled(transform.HScale, transform.VScale, 1d);
207 gl.glTranslated(transform.HShift, transform.VShift, 0d);
209 // Render the geometry tile
210 sg.renderMultiTexture(dc, numTexUnitsUsed);
215 gl.glActiveTexture(alphaTextureUnit);
216 gl.glMatrixMode(GL.GL_TEXTURE);
217 gl.glPopMatrix();
218 gl.glDisable(GL.GL_TEXTURE_2D);
220 gl.glActiveTexture(GL.GL_TEXTURE0);
221 gl.glMatrixMode(GL.GL_TEXTURE);
222 gl.glPopMatrix();
223 gl.glDisable(GL.GL_TEXTURE_2D);
225 if (showOutlines)
227 gl.glActiveTexture(GL.GL_TEXTURE1);
228 gl.glMatrixMode(GL.GL_TEXTURE);
229 gl.glPopMatrix();
230 gl.glDisable(GL.GL_TEXTURE_2D);
233 catch (Exception e)
235 Logging.logger().log(Level.SEVERE,
236 Logging.getMessage("generic.ExceptionWhileRenderingLayer", this.getClass().getName()), e);
238 finally
240 // TODO: pop matrix stack too
241 gl.glPopAttrib();
245 private static void fillByteBuffer(ByteBuffer buffer, byte value)
247 for (int i = 0; i < buffer.capacity(); i++)
249 buffer.put(value);
253 private void initAlphaTexture(int size)
255 ByteBuffer textureBytes = BufferUtil.newByteBuffer(size * size);
256 fillByteBuffer(textureBytes, (byte) 0xff);
257 TextureData textureData = new TextureData(GL.GL_ALPHA, size, size, 0, GL.GL_ALPHA,
258 GL.GL_UNSIGNED_BYTE, false, false, false, textureBytes.rewind(), null);
259 this.alphaTexture = TextureIO.newTexture(textureData);
261 this.alphaTexture.bind();
262 this.alphaTexture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
263 this.alphaTexture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
264 this.alphaTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_BORDER);
265 this.alphaTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_BORDER);
266 // Assume the default border color of (0, 0, 0, 0).
269 private void initOutlineTexture(int size)
271 ByteBuffer textureBytes = BufferUtil.newByteBuffer(size * size);
272 for (int row = 0; row < size; row++)
274 for (int col = 0; col < size; col++)
276 byte p;
277 if (row == 0 || col == 0 || row == size - 1 || col == size - 1)
278 p = (byte) 0xff;
279 else
280 p = (byte) 0;
281 textureBytes.put(row * size + col, p);
285 TextureData textureData = new TextureData(GL.GL_LUMINANCE, size, size, 0, GL.GL_LUMINANCE,
286 GL.GL_UNSIGNED_BYTE, false, false, false, textureBytes.rewind(), null);
287 this.outlineTexture = TextureIO.newTexture(textureData);
289 this.outlineTexture.bind();
290 this.outlineTexture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
291 this.outlineTexture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
292 this.outlineTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
293 this.outlineTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);