/*
Copyright (C) 2001, 2006 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.layers;
import com.sun.opengl.util.texture.*;
import gov.nasa.worldwind.*;
import gov.nasa.worldwind.cache.*;
import gov.nasa.worldwind.geom.*;
import gov.nasa.worldwind.globes.*;
import gov.nasa.worldwind.render.*;
import gov.nasa.worldwind.util.*;
import javax.media.opengl.*;
/**
* @author tag
* @version $Id: TextureTile.java 5215 2008-04-30 04:37:46Z tgaskins $
*/
public class TextureTile extends Tile implements SurfaceTile
{
private volatile TextureData textureData;
private TextureTile fallbackTile = null; // holds texture to use if own texture not available
private Vec4 centroid; // Cartesian coordinate of lat/lon center
private Extent extent = null; // bounding volume
private double extentVerticalExaggertion = Double.MIN_VALUE; // VE used to calculate the extent
private Globe globe;
private Object globeStateKey;
private double minDistanceToEye = Double.MAX_VALUE;
private boolean usingMipmaps = false;
public TextureTile(Sector sector)
{
super(sector);
}
public TextureTile(Sector sector, Level level, int row, int col)
{
super(sector, level, row, col);
}
@Override
public final long getSizeInBytes()
{
long size = super.getSizeInBytes();
if (this.textureData != null)
size += this.textureData.getEstimatedMemorySize();
return size;
}
public TextureTile getFallbackTile()
{
return this.fallbackTile;
}
public void setFallbackTile(TextureTile fallbackTile)
{
this.fallbackTile = fallbackTile;
}
public TextureData getTextureData()
{
return this.textureData;
}
public void setTextureData(TextureData textureData)
{
this.textureData = textureData;
if (textureData.getMipmapData() != null)
this.usingMipmaps = true;
}
public Texture getTexture(TextureCache tc)
{
if (tc == null)
{
String message = Logging.getMessage("nullValue.TextureCacheIsNull");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
return tc.get(this.getTileKey());
}
public boolean isTextureInMemory(TextureCache tc)
{
if (tc == null)
{
String message = Logging.getMessage("nullValue.TextureCacheIsNull");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
return this.getTexture(tc) != null || this.getTextureData() != null;
}
public void setTexture(TextureCache tc, Texture texture)
{
if (tc == null)
{
String message = Logging.getMessage("nullValue.TextureCacheIsNull");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
tc.put(this.getTileKey(), texture);
// No more need for texture data; allow garbage collector and memory cache to reclaim it.
this.textureData = null;
this.updateMemoryCache();
}
public Vec4 getCentroidPoint(Globe globe)
{
if (globe == null)
{
String msg = Logging.getMessage("nullValue.GlobeIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (this.centroid == null)
{
LatLon c = this.getSector().getCentroid();
this.centroid = globe.computePointFromPosition(c.getLatitude(), c.getLongitude(), 0);
}
return this.centroid;
}
public double getMinDistanceToEye()
{
return this.minDistanceToEye;
}
public void setMinDistanceToEye(double minDistanceToEye)
{
if (minDistanceToEye < 0)
{
String msg = Logging.getMessage("layers.TextureTile.MinDistanceToEyeNegative");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
this.minDistanceToEye = minDistanceToEye;
}
public Extent getExtent(DrawContext dc)
{
if (dc == null)
{
String msg = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
if (this.extent == null || !this.isExtentValid(dc))
{
this.extent = dc.getGlobe().computeBoundingCylinder(dc.getVerticalExaggeration(), this.getSector());
this.extentVerticalExaggertion = dc.getVerticalExaggeration();
this.globe = dc.getGlobe();
this.globeStateKey = this.globe != null ? this.globe.getStateKey() : null;
this.centroid = null;
}
return this.extent;
}
private boolean isExtentValid(DrawContext dc)
{
return !(dc.getGlobe() == null || this.globe == null || this.globeStateKey == null)
&& this.extentVerticalExaggertion == dc.getVerticalExaggeration()
&& this.globe == dc.getGlobe()
&& this.globeStateKey.equals(dc.getGlobe().getStateKey());
}
public TextureTile[] createSubTiles(Level nextLevel)
{
if (nextLevel == null)
{
String msg = Logging.getMessage("nullValue.LevelIsNull");
Logging.logger().severe(msg);
throw new IllegalArgumentException(msg);
}
Angle p0 = this.getSector().getMinLatitude();
Angle p2 = this.getSector().getMaxLatitude();
Angle p1 = Angle.midAngle(p0, p2);
Angle t0 = this.getSector().getMinLongitude();
Angle t2 = this.getSector().getMaxLongitude();
Angle t1 = Angle.midAngle(t0, t2);
String nextLevelCacheName = nextLevel.getCacheName();
int nextLevelNum = nextLevel.getLevelNumber();
int row = this.getRow();
int col = this.getColumn();
TextureTile[] subTiles = new TextureTile[4];
TileKey key = new TileKey(nextLevelNum, 2 * row, 2 * col, nextLevelCacheName);
TextureTile subTile = this.getTileFromMemoryCache(key);
if (subTile != null)
subTiles[0] = subTile;
else
subTiles[0] = new TextureTile(new Sector(p0, p1, t0, t1), nextLevel, 2 * row, 2 * col);
key = new TileKey(nextLevelNum, 2 * row, 2 * col + 1, nextLevelCacheName);
subTile = this.getTileFromMemoryCache(key);
if (subTile != null)
subTiles[1] = subTile;
else
subTiles[1] = new TextureTile(new Sector(p0, p1, t1, t2), nextLevel, 2 * row, 2 * col + 1);
key = new TileKey(nextLevelNum, 2 * row + 1, 2 * col, nextLevelCacheName);
subTile = this.getTileFromMemoryCache(key);
if (subTile != null)
subTiles[2] = subTile;
else
subTiles[2] = new TextureTile(new Sector(p1, p2, t0, t1), nextLevel, 2 * row + 1, 2 * col);
key = new TileKey(nextLevelNum, 2 * row + 1, 2 * col + 1, nextLevelCacheName);
subTile = this.getTileFromMemoryCache(key);
if (subTile != null)
subTiles[3] = subTile;
else
subTiles[3] = new TextureTile(new Sector(p1, p2, t1, t2), nextLevel, 2 * row + 1, 2 * col + 1);
return subTiles;
}
private TextureTile getTileFromMemoryCache(TileKey tileKey)
{
return (TextureTile) WorldWind.getMemoryCache(TextureTile.class.getName()).getObject(tileKey);
}
private void updateMemoryCache()
{
if (this.getTileFromMemoryCache(this.getTileKey()) != null)
WorldWind.getMemoryCache(TextureTile.class.getName()).add(this.getTileKey(), this);
}
private Texture initializeTexture(DrawContext dc)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
Texture t = this.getTexture(dc.getTextureCache());
if (t != null)
return t;
if (this.getTextureData() == null)
{
String msg = Logging.getMessage("nullValue.TextureDataIsNull");
Logging.logger().severe(msg);
throw new IllegalStateException(msg);
}
try
{
t = TextureIO.newTexture(this.getTextureData());
}
catch (Exception e)
{
Logging.logger().log(
java.util.logging.Level.SEVERE, "layers.TextureLayer.ExceptionAttemptingToReadTextureFile", e);
return null;
}
this.setTexture(dc.getTextureCache(), t);
t.bind();
GL gl = dc.getGL();
if (this.usingMipmaps && this.getSector().getMaxLatitude().degrees < 80d
&& this.getSector().getMinLatitude().degrees > -80)
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR_MIPMAP_LINEAR);
else
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);
return t;
}
public boolean bind(DrawContext dc)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
Texture t = this.getTexture(dc.getTextureCache());
if (t == null && this.getTextureData() != null)
{
t = this.initializeTexture(dc);
if (t != null)
return true; // texture was bound during initialization.
}
if (t == null && this.getFallbackTile() != null)
{
TextureTile resourceTile = this.getFallbackTile();
t = resourceTile.getTexture(dc.getTextureCache());
if (t == null)
{
t = resourceTile.initializeTexture(dc);
if (t != null)
return true; // texture was bound during initialization.
}
}
if (t != null)
t.bind();
return t != null;
}
public void applyInternalTransform(DrawContext dc)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
// Use the tile's texture if available.
Texture t = this.getTexture(dc.getTextureCache());
if (t == null && this.getTextureData() != null)
t = this.initializeTexture(dc);
if (t != null)
{
if (t.getMustFlipVertically())
{
GL gl = GLContext.getCurrent().getGL();
gl.glMatrixMode(GL.GL_TEXTURE);
gl.glLoadIdentity();
gl.glScaled(1, -1, 1);
gl.glTranslated(0, -1, 0);
}
return;
}
// Use the tile's fallback texture if its primary texture is not available.
TextureTile resourceTile = this.getFallbackTile();
if (resourceTile == null) // no fallback specified
return;
t = resourceTile.getTexture(dc.getTextureCache());
if (t == null && resourceTile.getTextureData() != null)
t = resourceTile.initializeTexture(dc);
if (t == null) // was not able to initialize the fallback texture
return;
// Apply necessary transforms to the fallback texture.
GL gl = GLContext.getCurrent().getGL();
gl.glMatrixMode(GL.GL_TEXTURE);
gl.glLoadIdentity();
if (t.getMustFlipVertically())
{
gl.glScaled(1, -1, 1);
gl.glTranslated(0, -1, 0);
}
this.applyResourceTextureTransform(dc);
}
private void applyResourceTextureTransform(DrawContext dc)
{
if (dc == null)
{
String message = Logging.getMessage("nullValue.DrawContextIsNull");
Logging.logger().severe(message);
throw new IllegalStateException(message);
}
if (this.getLevel() == null)
return;
int levelDelta = this.getLevelNumber() - this.getFallbackTile().getLevelNumber();
if (levelDelta <= 0)
return;
double twoToTheN = Math.pow(2, levelDelta);
double oneOverTwoToTheN = 1 / twoToTheN;
double sShift = oneOverTwoToTheN * (this.getColumn() % twoToTheN);
double tShift = oneOverTwoToTheN * (this.getRow() % twoToTheN);
dc.getGL().glTranslated(sShift, tShift, 0);
dc.getGL().glScaled(oneOverTwoToTheN, oneOverTwoToTheN, 1);
}
@Override
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
final TextureTile tile = (TextureTile) o;
return !(this.getTileKey() != null ? !this.getTileKey().equals(tile.getTileKey()) : tile.getTileKey() != null);
}
@Override
public int hashCode()
{
return (this.getTileKey() != null ? this.getTileKey().hashCode() : 0);
}
@Override
public String toString()
{
return this.getSector().toString();
}
}