Updated to worldwind release 20070817
[worldwind-tracker.git] / gov / nasa / worldwind / SurfaceTileRenderer.java
blob5ef655c425ecb21c7911652e20018d27a3799ace
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;
9 import gov.nasa.worldwind.layers.*;
10 import gov.nasa.worldwind.geom.*;
12 import javax.media.opengl.*;
14 import com.sun.opengl.util.texture.*;
16 import java.nio.*;
17 import java.util.*;
19 /**
20 * @author tag
21 * @version $Id: SurfaceTileRenderer.java 1884 2007-05-25 21:15:33Z dcollins $
23 public class SurfaceTileRenderer implements Disposable
25 private static final int DEFAULT_ALPHA_TEXTURE_SIZE = 2;
27 private Texture alphaTexture;
28 private Texture outlineTexture;
29 private boolean showImageTileOutlines = false;
31 public void dispose() // TODO: but waiting to implement a global texture disposal system
35 public boolean isShowImageTileOutlines()
37 return showImageTileOutlines;
40 public void setShowImageTileOutlines(boolean showImageTileOutlines)
42 this.showImageTileOutlines = showImageTileOutlines;
45 public void renderTile(DrawContext dc, TextureTile tile)
47 if (tile == null)
49 String message = WorldWind.retrieveErrMsg("nullValue.TileIsNull");
50 WorldWind.logger().log(java.util.logging.Level.FINE, message);
51 throw new IllegalStateException(message);
54 ArrayList<TextureTile> al = new ArrayList<TextureTile>(1);
55 al.add(tile);
56 this.renderTiles(dc, al);
57 al.clear();
60 public void renderTiles(DrawContext dc, Iterable<? extends TextureTile> tiles)
62 if (tiles == null)
64 String message = WorldWind.retrieveErrMsg("nullValue.TileIterableIsNull");
65 WorldWind.logger().log(java.util.logging.Level.FINE, message);
66 throw new IllegalStateException(message);
69 if (dc == null)
71 String message = WorldWind.retrieveErrMsg("nullValue.DrawContextIsNull");
72 WorldWind.logger().log(java.util.logging.Level.FINE, message);
73 throw new IllegalStateException(message);
76 GL gl = dc.getGL();
78 gl.glPushAttrib(GL.GL_COLOR_BUFFER_BIT // for alpha func
79 | GL.GL_ENABLE_BIT
80 | GL.GL_CURRENT_BIT
81 | GL.GL_DEPTH_BUFFER_BIT // for depth func
82 | GL.GL_TEXTURE_BIT // for texture env
83 | GL.GL_TRANSFORM_BIT);
85 try
87 if (this.alphaTexture == null)
88 this.initAlphaTexture(DEFAULT_ALPHA_TEXTURE_SIZE); // TODO: choose size to match incoming tile sizes?
90 boolean showOutlines = this.showImageTileOutlines && dc.getNumTextureUnits() > 2;
91 if (showOutlines && this.outlineTexture == null)
92 this.initOutlineTexture(128);
94 gl.glEnable(GL.GL_DEPTH_TEST);
95 gl.glDepthFunc(GL.GL_LEQUAL);
97 gl.glEnable(GL.GL_ALPHA_TEST);
98 gl.glAlphaFunc(GL.GL_GREATER, 0.01f);
100 gl.glActiveTexture(GL.GL_TEXTURE0);
101 gl.glEnable(GL.GL_TEXTURE_2D);
102 gl.glMatrixMode(GL.GL_TEXTURE);
103 gl.glPushMatrix();
104 if (!dc.isPickingMode())
106 gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE);
108 else
110 gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_COMBINE);
111 gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_SRC0_RGB, GL.GL_PREVIOUS);
112 gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_COMBINE_RGB, GL.GL_REPLACE);
115 int numTexUnitsUsed = 2;
116 int alphaTextureUnit = GL.GL_TEXTURE1;
117 if (showOutlines)
119 numTexUnitsUsed = 3;
120 alphaTextureUnit = GL.GL_TEXTURE2;
121 gl.glActiveTexture(GL.GL_TEXTURE1);
122 gl.glEnable(GL.GL_TEXTURE_2D);
123 gl.glMatrixMode(GL.GL_TEXTURE);
124 gl.glPushMatrix();
125 gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_ADD);
128 gl.glActiveTexture(alphaTextureUnit);
129 gl.glEnable(GL.GL_TEXTURE_2D);
130 gl.glMatrixMode(GL.GL_TEXTURE);
131 gl.glPushMatrix();
132 gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_MODULATE);
134 // For each current geometry tile, find the intersecting image tiles and render the geometry
135 // tile once for each intersecting image tile.
136 // System.out.printf("%d geo tiles\n", dc.getSurfaceGeometry().size());
137 for (SectorGeometry sg : dc.getSurfaceGeometry())
139 Iterable<TextureTile> tilesToRender = this.getIntersectingTiles(sg, tiles);
140 if (tilesToRender == null)
141 continue;
142 // System.out.printf("%d, ", tilesToRender.length);
144 // Pre-load info to compute the texture transform below
145 Sector st = sg.getSector();
146 double geoDeltaLat = st.getDeltaLatRadians();
147 double geoDeltaLon = st.getDeltaLonRadians();
148 double geoMinLat = st.getMinLatitude().radians;
149 double geoMinLon = st.getMinLongitude().radians;
151 // For each interesecting tile, establish the texture transform necessary to map the image tile
152 // into the geometry tile's texture space. Use an alpha texture as a mask to prevent changing the
153 // frame buffer where the image tile does not overlap the geometry tile. Render both the image and
154 // alpha textures via multi-texture rendering.
155 // TODO: Figure out how to apply multi-texture to more than one tile at a time, most likely via a
156 // fragment shader.
157 for (TextureTile tile : tilesToRender)
159 gl.glActiveTexture(GL.GL_TEXTURE0);
161 if (tile.bindTexture(dc))
163 gl.glMatrixMode(GL.GL_TEXTURE);
164 gl.glLoadIdentity();
165 tile.applyTextureTransform(dc);
167 // Determine and apply texture transform to map image tile into geometry tile's texture space
168 Sector si = tile.getSector();
169 double latScale = si.getDeltaLatRadians() > 0 ? geoDeltaLat / si.getDeltaLatRadians() : 1;
170 double lonScale = si.getDeltaLonRadians() > 0 ? geoDeltaLon / si.getDeltaLonRadians() : 1;
171 gl.glScaled(lonScale, latScale, 1d);
173 double latShift = -(si.getMinLatitude().radians - geoMinLat) / geoDeltaLat;
174 double lonShift = -(si.getMinLongitude().radians - geoMinLon) / geoDeltaLon;
175 gl.glTranslated(lonShift, latShift, 0d);
177 if (showOutlines)
179 gl.glActiveTexture(GL.GL_TEXTURE1);
180 this.outlineTexture.bind();
182 // Apply the same texture transform to the outline texture. The outline textures uses a
183 // different texture unit than the tile, so the transform made above does not carry over.
184 gl.glMatrixMode(GL.GL_TEXTURE);
185 gl.glLoadIdentity();
186 gl.glScaled(lonScale, latScale, 1d);
187 gl.glTranslated(lonShift, latShift, 0d);
190 // Prepare the alpha texture to be used as a mask where texture coords are outside [0,1]
191 gl.glActiveTexture(alphaTextureUnit);
192 this.alphaTexture.bind();
194 // Apply the same texture transform to the alpha texture. The alpha texture uses a
195 // different texture unit than the tile, so the transform made above does not carry over.
196 gl.glMatrixMode(GL.GL_TEXTURE);
197 gl.glLoadIdentity();
198 gl.glScaled(lonScale, latScale, 1d);
199 gl.glTranslated(lonShift, latShift, 0d);
201 // Render the geometry tile
202 sg.renderMultiTexture(dc, numTexUnitsUsed);
206 // System.out.println();
208 gl.glActiveTexture(alphaTextureUnit);
209 gl.glMatrixMode(GL.GL_TEXTURE);
210 gl.glPopMatrix();
211 gl.glDisable(GL.GL_TEXTURE_2D);
213 gl.glActiveTexture(GL.GL_TEXTURE0);
214 gl.glMatrixMode(GL.GL_TEXTURE);
215 gl.glPopMatrix();
216 gl.glDisable(GL.GL_TEXTURE_2D);
218 if (showOutlines)
220 gl.glActiveTexture(GL.GL_TEXTURE1);
221 gl.glMatrixMode(GL.GL_TEXTURE);
222 gl.glPopMatrix();
223 gl.glDisable(GL.GL_TEXTURE_2D);
226 catch (Exception e)
228 String message = WorldWind.retrieveErrMsg("generic.ExceptionWhileRenderingLayer");
229 message += this.getClass().getName();
230 WorldWind.logger().log(java.util.logging.Level.FINE, message, e);
232 finally
234 gl.glPopAttrib();
238 private Iterable<TextureTile> getIntersectingTiles(SectorGeometry sg, Iterable<? extends TextureTile> tiles)
240 ArrayList<TextureTile> intersectingTiles = null;
242 for (TextureTile tile : tiles)
244 if (!tile.getSector().intersects(sg.getSector()))
245 continue;
247 if (intersectingTiles == null)
248 intersectingTiles = new ArrayList<TextureTile>();
250 intersectingTiles.add(tile);
253 if (intersectingTiles == null)
254 return null;
256 return intersectingTiles;
259 private static void fillByteBuffer(ByteBuffer buffer, byte value)
261 for (int i = 0; i < buffer.capacity(); i++)
263 buffer.put(value);
267 private void initAlphaTexture(int size)
269 ByteBuffer textureBytes = com.sun.opengl.util.BufferUtil.newByteBuffer(size * size);
270 fillByteBuffer(textureBytes, (byte) 0xff);
271 TextureData textureData = new TextureData(GL.GL_ALPHA, size, size, 0, GL.GL_ALPHA,
272 GL.GL_UNSIGNED_BYTE, false, false, false, textureBytes.rewind(), null);
273 this.alphaTexture = TextureIO.newTexture(textureData);
275 this.alphaTexture.bind();
276 this.alphaTexture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
277 this.alphaTexture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
278 this.alphaTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_BORDER);
279 this.alphaTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_BORDER);
280 // Assume the default border color of (0, 0, 0, 0).
283 private void initOutlineTexture(int size)
285 ByteBuffer textureBytes = com.sun.opengl.util.BufferUtil.newByteBuffer(size * size);
286 for (int row = 0; row < size; row++)
288 for (int col = 0; col < size; col++)
290 byte p;
291 if (row == 0 || col == 0 || row == size - 1 || col == size - 1)
292 p = (byte) 0xff;
293 else
294 p = (byte) 0;
295 textureBytes.put(row * size + col, p);
299 TextureData textureData = new TextureData(GL.GL_LUMINANCE, size, size, 0, GL.GL_LUMINANCE,
300 GL.GL_UNSIGNED_BYTE, false, false, false, textureBytes.rewind(), null);
301 this.outlineTexture = TextureIO.newTexture(textureData);
303 this.outlineTexture.bind();
304 this.outlineTexture.setTexParameteri(GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
305 this.outlineTexture.setTexParameteri(GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
306 this.outlineTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
307 this.outlineTexture.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);