refactor ExtrusionRenderer

- extract tile specific parts to BuildingRenderer
- sort tiles by distance for compilation
- slow down fade out animation
This commit is contained in:
Hannes Janetzek 2014-05-24 23:06:50 +02:00
parent 7466b6206f
commit 4ddf9dcf39
19 changed files with 257 additions and 231 deletions

View File

@ -9,7 +9,7 @@ import java.util.Arrays;
import org.jeo.data.VectorDataset;
import org.jeo.map.Style;
import org.oscim.layers.OSMIndoorLayer;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.test.JeoTest;
import org.oscim.theme.VtmThemes;

View File

@ -18,7 +18,7 @@ import org.oscim.android.MapActivity;
import org.oscim.android.MapView;
import org.oscim.layers.TileGridLayer;
import org.oscim.layers.tile.bitmap.BitmapTileLayer;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.renderer.MapRenderer;
import org.oscim.theme.IRenderTheme;

View File

@ -2,7 +2,7 @@ package org.oscim.android.test;
import org.oscim.android.cache.TileCache;
import org.oscim.layers.tile.TileLayer;
import org.oscim.layers.tile.s3db.S3DBLayer;
import org.oscim.layers.tile.buildings.S3DBLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.theme.VtmThemes;
import org.oscim.tiling.TileSource;

View File

@ -15,7 +15,7 @@
* this program. If not, see <http://www.gnu.org/licenses/>.
*/package org.oscim.android.test;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.map.Layers;
import org.oscim.map.Map;

View File

@ -3,7 +3,7 @@ package org.oscim.android.test;
import static org.oscim.utils.ColorUtil.modHsv;
import static org.oscim.utils.ColorUtil.shiftHue;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.map.Layers;

View File

@ -1,7 +1,7 @@
package org.oscim.android.start;
import org.oscim.android.MapActivity;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.map.Map;

View File

@ -17,7 +17,7 @@
package org.oscim.gdx;
import org.oscim.layers.TileGridLayer;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.map.Layers;

View File

@ -21,7 +21,7 @@ import org.jeo.geojson.GeoJSONReader;
import org.jeo.geom.GeomBuilder;
import org.jeo.map.Style;
import org.oscim.layers.OSMIndoorLayer;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.map.Map;
import org.oscim.renderer.MapRenderer;

View File

@ -2,6 +2,7 @@ package org.oscim.test;
import org.oscim.gdx.GdxMap;
import org.oscim.gdx.GdxMapApp;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.theme.VtmThemes;
@ -18,7 +19,7 @@ public class MapTest extends GdxMap {
VectorTileLayer l = mMap.setBaseMap(new OSciMap4TileSource());
mMap.setTheme(VtmThemes.DEFAULT);
mMap.layers().add(new BuildingLayer(mMap, l));
mMap.layers().add(new LabelLayer(mMap, l));
mMap.setMapPosition(53.08, 8.82, 1 << 17);

View File

@ -2,7 +2,7 @@ package org.oscim.test;
import org.oscim.gdx.GdxMapApp;
import org.oscim.layers.tile.bitmap.BitmapTileLayer;
import org.oscim.layers.tile.s3db.S3DBLayer;
import org.oscim.layers.tile.buildings.S3DBLayer;
import org.oscim.tiling.TileSource;
import org.oscim.tiling.source.bitmap.DefaultSources;
import org.oscim.tiling.source.oscimap4.OSciMap4TileSource;

View File

@ -2,7 +2,7 @@ package org.oscim.test.gdx.poi3d;
import org.oscim.gdx.GdxMap;
import org.oscim.gdx.GdxMapApp;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.renderer.MapRenderer;

View File

@ -26,8 +26,8 @@ import org.oscim.gdx.client.GwtGdxGraphics;
import org.oscim.gdx.client.MapConfig;
import org.oscim.gdx.client.MapUrl;
import org.oscim.layers.tile.bitmap.BitmapTileLayer;
import org.oscim.layers.tile.s3db.S3DBLayer;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.buildings.S3DBLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;
import org.oscim.renderer.MapRenderer;

View File

@ -4,7 +4,7 @@ import org.oscim.core.MapPosition;
import org.oscim.layers.GenericLayer;
import org.oscim.layers.Layer;
import org.oscim.layers.TileGridLayer;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.layers.tile.buildings.BuildingLayer;
import org.oscim.layers.tile.vector.OsmTileLayer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.labeling.LabelLayer;

View File

@ -14,18 +14,17 @@
* You should have received a copy of the GNU Lesser General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.oscim.layers.tile.vector;
package org.oscim.layers.tile.buildings;
import org.oscim.core.MapElement;
import org.oscim.core.MercatorProjection;
import org.oscim.core.Tag;
import org.oscim.layers.Layer;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.layers.tile.vector.VectorTileLayer.TileLoaderThemeHook;
import org.oscim.map.Map;
import org.oscim.renderer.ExtrusionRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.MapRenderer;
import org.oscim.renderer.OffscreenRenderer;
import org.oscim.renderer.OffscreenRenderer.Mode;
import org.oscim.renderer.elements.ElementLayers;
@ -33,7 +32,6 @@ import org.oscim.renderer.elements.ExtrusionLayer;
import org.oscim.renderer.elements.ExtrusionLayers;
import org.oscim.theme.styles.ExtrusionStyle;
import org.oscim.theme.styles.RenderStyle;
import org.oscim.utils.FastMath;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -46,13 +44,8 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
private static final Object BUILDING_DATA = BuildingLayer.class.getName();
private final int mMinZoom;
private final int mMaxZoom;
private ExtrusionRenderer mExtRenderer;
private final float mFadeTime = 300;
public BuildingLayer(Map map, VectorTileLayer tileLayer) {
this(map, tileLayer, MIN_ZOOM, MAX_ZOOM);
@ -70,64 +63,8 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
super(map);
tileLayer.addHook(this);
mMinZoom = zoomMin;
mMaxZoom = zoomMax;
mExtRenderer = new ExtrusionRenderer(tileLayer.tileRenderer(),
mMinZoom, mMaxZoom,
false, true) {
private long mStartTime;
@Override
protected boolean setup() {
mAlpha = 0;
return super.setup();
}
@Override
public void update(GLViewport v) {
int diff = (v.pos.zoomLevel - mMinZoom);
/* if below min zoom or already faded out */
if ((diff < -1)) {// || (diff < 0 && mAlpha == 0)){
setReady(false);
return;
}
boolean show = diff >= 0;
if (show) {
if (mAlpha < 1) {
//log.debug("fade in {}", mAlpha);
long now = System.currentTimeMillis();
if (mStartTime == 0) {
mStartTime = now;
}
float a = (now - mStartTime) / mFadeTime;
mAlpha = FastMath.clamp(a, 0, 1);
MapRenderer.animate();
} else
mStartTime = 0;
} else {
if (mAlpha > 0) {
//log.debug("fade out {} {}", mAlpha, mStartTime);
long now = System.currentTimeMillis();
if (mStartTime == 0) {
mStartTime = now;
}
long dt = (now - mStartTime);
if (dt > 0) {
float a = 1 - dt / mFadeTime;
mAlpha = FastMath.clamp(a, 0, 1);
}
MapRenderer.animate();
} else
mStartTime = 0;
}
super.update(v);
}
};
mExtRenderer = new BuildingRenderer(tileLayer.tileRenderer(),
zoomMin, zoomMax, false, true);
if (POST_AA) {
OffscreenRenderer or = new OffscreenRenderer(Mode.FXAA);
@ -138,6 +75,7 @@ public class BuildingLayer extends Layer implements TileLoaderThemeHook {
}
}
/** TileLoaderThemeHook */
@Override
public boolean render(MapTile tile, ElementLayers layers, MapElement element,
RenderStyle style, int level) {

View File

@ -0,0 +1,204 @@
package org.oscim.layers.tile.buildings;
import static org.oscim.layers.tile.MapTile.State.NEW_DATA;
import static org.oscim.layers.tile.MapTile.State.READY;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileDistanceSort;
import org.oscim.layers.tile.TileRenderer;
import org.oscim.layers.tile.TileSet;
import org.oscim.renderer.ExtrusionRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.MapRenderer;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.renderer.elements.ExtrusionLayers;
import org.oscim.utils.FastMath;
public class BuildingRenderer extends ExtrusionRenderer {
private final TileRenderer mTileLayer;
private final TileSet mTileSet;
private final int mZoomMin;
private final int mZoomMax;
private final float mFadeInTime = 300;
private final float mFadeOutTime = 500;
private long mStartTime;
public BuildingRenderer(TileRenderer tileRenderLayer, int zoomMin, int zoomMax,
boolean mesh, boolean alpha) {
super(mesh, alpha);
mZoomMax = zoomMax;
mZoomMin = zoomMin;
mTileLayer = tileRenderLayer;
mTileSet = new TileSet();
}
@Override
protected boolean setup() {
mAlpha = 0;
return super.setup();
}
@Override
public void update(GLViewport v) {
int diff = (v.pos.zoomLevel - mZoomMin);
/* if below min zoom or already faded out */
if ((diff < -1)) {
setReady(false);
return;
}
boolean show = diff >= 0;
if (show) {
if (mAlpha < 1) {
//log.debug("fade in {}", mAlpha);
long now = System.currentTimeMillis();
if (mStartTime == 0) {
mStartTime = now;
}
float a = (now - mStartTime) / mFadeInTime;
mAlpha = FastMath.clamp(a, 0, 1);
MapRenderer.animate();
} else
mStartTime = 0;
} else {
if (mAlpha > 0) {
//log.debug("fade out {} {}", mAlpha, mStartTime);
long now = System.currentTimeMillis();
if (mStartTime == 0) {
mStartTime = now + 200; // delay hide a little
}
long dt = (now - mStartTime);
if (dt > 0) {
float a = 1 - dt / mFadeOutTime;
mAlpha = FastMath.clamp(a, 0, 1);
}
MapRenderer.animate();
} else
mStartTime = 0;
}
if (mAlpha == 0 || v.pos.zoomLevel < (mZoomMin - 1)) {
setReady(false);
return;
}
mTileLayer.getVisibleTiles(mTileSet);
if (mTileSet.cnt == 0) {
mTileLayer.releaseTiles(mTileSet);
setReady(false);
return;
}
MapTile[] tiles = mTileSet.tiles;
TileDistanceSort.sort(tiles, 0, mTileSet.cnt);
/* keep a list of tiles available for rendering */
if (mExtrusionLayerSet == null || mExtrusionLayerSet.length < mTileSet.cnt * 4)
mExtrusionLayerSet = new ExtrusionLayers[mTileSet.cnt * 4];
/* compile one tile max per frame */
boolean compiled = false;
int activeTiles = 0;
int zoom = tiles[0].zoomLevel;
if (zoom >= mZoomMin && zoom <= mZoomMax) {
/* TODO - if tile is not available try parent or children */
for (int i = 0; i < mTileSet.cnt; i++) {
ExtrusionLayers els = getLayer(tiles[i]);
if (els == null)
continue;
if (els.compiled)
mExtrusionLayerSet[activeTiles++] = els;
else if (!compiled && els.compileLayers()) {
mExtrusionLayerSet[activeTiles++] = els;
compiled = true;
}
}
} else if (zoom == mZoomMax + 1) {
/* special case for s3db: render from parent tiles */
for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = tiles[i].node.parent();
if (t == null)
continue;
// for (MapTile c : mTiles)
// if (c == t)
// continue O;
ExtrusionLayers els = getLayer(t);
if (els == null)
continue;
if (els.compiled)
mExtrusionLayerSet[activeTiles++] = els;
else if (!compiled && els.compileLayers()) {
mExtrusionLayerSet[activeTiles++] = els;
compiled = true;
}
}
} else if (zoom == mZoomMin - 1) {
/* check if proxy children are ready */
for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = tiles[i];
for (byte j = 0; j < 4; j++) {
if (!t.hasProxy(1 << j))
continue;
MapTile c = t.node.child(j);
ExtrusionLayers el = getLayer(c);
if (el == null || !el.compiled)
continue;
mExtrusionLayerSet[activeTiles++] = el;
}
}
}
/* load more tiles on next frame */
if (compiled)
MapRenderer.animate();
mExtrusionLayerCnt = activeTiles;
//log.debug("active tiles: {}", mExtrusionLayerCnt);
if (activeTiles == 0) {
mTileLayer.releaseTiles(mTileSet);
setReady(false);
return;
}
setReady(true);
}
@Override
public void render(GLViewport v) {
super.render(v);
/* release lock on tile data */
mTileLayer.releaseTiles(mTileSet);
}
private static ExtrusionLayers getLayer(MapTile t) {
ElementLayers layers = t.getLayers();
if (layers != null && !t.state(READY | NEW_DATA))
return null;
return BuildingLayer.get(t);
}
}

View File

@ -1,11 +1,10 @@
package org.oscim.layers.tile.s3db;
package org.oscim.layers.tile.buildings;
import org.oscim.backend.canvas.Color;
import org.oscim.layers.tile.TileLayer;
import org.oscim.layers.tile.TileManager;
import org.oscim.layers.tile.TileRenderer;
import org.oscim.map.Map;
import org.oscim.renderer.ExtrusionRenderer;
import org.oscim.renderer.GLViewport;
import org.oscim.renderer.OffscreenRenderer;
import org.oscim.renderer.OffscreenRenderer.Mode;
@ -43,12 +42,11 @@ public class S3DBLayer extends TileLayer {
}
public static class S3DBRenderer extends TileRenderer {
ExtrusionRenderer mExtRenderer;
BuildingRenderer mExtRenderer;
OffscreenRenderer or;
public S3DBRenderer() {
mExtRenderer = new ExtrusionRenderer(this, SRC_ZOOM, SRC_ZOOM,
true, false);
mExtRenderer = new BuildingRenderer(this, SRC_ZOOM, SRC_ZOOM, true, false);
if (POST_FXAA) {
or = new OffscreenRenderer(Mode.FXAA);
@ -76,6 +74,17 @@ public class S3DBLayer extends TileLayer {
mExtRenderer.render(v);
}
}
@Override
protected boolean setup() {
if (POST_FXAA) {
or.setup();
} else {
mExtRenderer.setup();
}
return super.setup();
}
}
static int getColor(String color, boolean roof) {

View File

@ -1,6 +1,6 @@
package org.oscim.layers.tile.s3db;
package org.oscim.layers.tile.buildings;
import static org.oscim.layers.tile.s3db.S3DBLayer.getMaterialColor;
import static org.oscim.layers.tile.buildings.S3DBLayer.getMaterialColor;
import org.oscim.backend.canvas.Color;
import org.oscim.core.GeometryBuffer;
@ -11,7 +11,6 @@ import org.oscim.core.Tag;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileLoader;
import org.oscim.layers.tile.TileManager;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.renderer.elements.ExtrusionLayer;
import org.oscim.renderer.elements.ExtrusionLayers;
import org.oscim.tiling.ITileDataSource;

View File

@ -16,47 +16,30 @@
*/
package org.oscim.renderer;
import static org.oscim.layers.tile.MapTile.State.NEW_DATA;
import static org.oscim.layers.tile.MapTile.State.READY;
import org.oscim.backend.GL20;
import org.oscim.core.Tile;
import org.oscim.layers.tile.MapTile;
import org.oscim.layers.tile.TileRenderer;
import org.oscim.layers.tile.TileSet;
import org.oscim.layers.tile.vector.BuildingLayer;
import org.oscim.renderer.elements.ElementLayers;
import org.oscim.renderer.elements.ExtrusionLayer;
import org.oscim.renderer.elements.ExtrusionLayers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ExtrusionRenderer extends LayerRenderer {
public abstract class ExtrusionRenderer extends LayerRenderer {
static final Logger log = LoggerFactory.getLogger(ExtrusionRenderer.class);
private final boolean debug = false;
static final boolean debugDraw = false;
private final TileRenderer mTileLayer;
private final int mZoomMin;
private final int mZoomMax;
private final boolean drawAlpha;
protected float mAlpha = 1;
private final int mMode;
private Shader mShader;
public ExtrusionRenderer(TileRenderer tileRenderLayer,
int zoomMin, int zoomMax, boolean mesh, boolean alpha) {
mTileLayer = tileRenderLayer;
mTileSet = new TileSet();
mZoomMin = zoomMin;
mZoomMax = zoomMax;
protected ExtrusionLayers[] mExtrusionLayerSet;
protected int mExtrusionLayerCnt;
protected float mAlpha = 1;
public ExtrusionRenderer(boolean mesh, boolean alpha) {
mMode = mesh ? 1 : 0;
drawAlpha = alpha;
}
private final TileSet mTileSet;
private ExtrusionLayers[] mExtrusionLayerSet;
private int mExtrusionLayerCnt;
public static class Shader extends GLShader {
int uMVP, uColor, uAlpha, uMode, aPos, aLight;
@ -73,124 +56,16 @@ public class ExtrusionRenderer extends LayerRenderer {
}
}
protected Shader mShader;
@Override
protected boolean setup() {
if (mMode == 0)
mShader = new Shader("extrusion_layer_ext");
else
mShader = new Shader("extrusion_layer_mesh");
return true;
}
@Override
public void update(GLViewport v) {
if (mAlpha == 0 || v.pos.zoomLevel < (mZoomMin - 1)) {
setReady(false);
return;
}
int activeTiles = 0;
mTileLayer.getVisibleTiles(mTileSet);
MapTile[] tiles = mTileSet.tiles;
if (mTileSet.cnt == 0) {
mTileLayer.releaseTiles(mTileSet);
setReady(false);
return;
}
/* keep a list of tiles available for rendering */
if (mExtrusionLayerSet == null || mExtrusionLayerSet.length < mTileSet.cnt * 4)
mExtrusionLayerSet = new ExtrusionLayers[mTileSet.cnt * 4];
int zoom = tiles[0].zoomLevel;
/* compile one tile max per frame */
boolean compiled = false;
if (zoom >= mZoomMin && zoom <= mZoomMax) {
/* TODO - if tile is not available try parent or children */
for (int i = 0; i < mTileSet.cnt; i++) {
ExtrusionLayers els = getLayer(tiles[i]);
if (els == null)
continue;
if (els.compiled)
mExtrusionLayerSet[activeTiles++] = els;
else if (!compiled && els.compileLayers()) {
mExtrusionLayerSet[activeTiles++] = els;
compiled = true;
}
}
} else if (zoom == mZoomMax + 1) {
/* special case for s3db: render from parent tiles */
for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = tiles[i].node.parent();
if (t == null)
continue;
// for (MapTile c : mTiles)
// if (c == t)
// continue O;
ExtrusionLayers els = getLayer(t);
if (els == null)
continue;
if (els.compiled)
mExtrusionLayerSet[activeTiles++] = els;
else if (!compiled && els.compileLayers()) {
mExtrusionLayerSet[activeTiles++] = els;
compiled = true;
}
}
} else if (zoom == mZoomMin - 1) {
/* check if proxy children are ready */
for (int i = 0; i < mTileSet.cnt; i++) {
MapTile t = tiles[i];
for (byte j = 0; j < 4; j++) {
if (!t.hasProxy(1 << j))
continue;
MapTile c = t.node.child(j);
ExtrusionLayers el = getLayer(c);
if (el == null || !el.compiled)
continue;
mExtrusionLayerSet[activeTiles++] = el;
}
}
}
/* load more tiles on next frame */
if (compiled)
MapRenderer.animate();
mExtrusionLayerCnt = activeTiles;
//log.debug("active tiles: {}", mExtrusionLayerCnt);
if (activeTiles > 0)
setReady(true);
else
mTileLayer.releaseTiles(mTileSet);
}
private static ExtrusionLayers getLayer(MapTile t) {
ElementLayers layers = t.getLayers();
if (layers != null && !t.state(READY | NEW_DATA))
return null;
return BuildingLayer.get(t);
}
private void renderCombined(int vertexPointer, ExtrusionLayers els) {
if (els.vboIndices == null)
return;
@ -222,13 +97,10 @@ public class ExtrusionRenderer extends LayerRenderer {
// with alpha... might be faster and would allow postprocessing outlines.
ExtrusionLayers[] els = mExtrusionLayerSet;
Shader s = mShader; //[mMode];
Shader s = mShader;
if (debug) {
if (debugDraw) {
s.useProgram();
//GLState.useProgram(shaderProgram[mMode]);
GLState.enableVertexArrays(s.aPos, s.aLight);
GL.glUniform1i(s.uMode, 0);
GLUtils.glUniform4fv(s.uColor, 4, DEBUG_COLOR);
@ -393,10 +265,7 @@ public class ExtrusionRenderer extends LayerRenderer {
GL.glDepthMask(false);
GL.glDisable(GL20.GL_CULL_FACE);
GL.glBindBuffer(GL20.GL_ELEMENT_ARRAY_BUFFER, 0);
mTileLayer.releaseTiles(mTileSet);
}
private static void setMatrix(GLViewport v, ExtrusionLayers l, int delta) {

View File

@ -159,6 +159,12 @@ public class OffscreenRenderer extends LayerRenderer {
mRenderer = renderer;
}
@Override
public boolean setup() {
mRenderer.setup();
return super.setup();
}
@Override
public void update(GLViewport viewport) {
if (texW != viewport.getWidth() || texH != viewport.getHeight()) {