/** * * Copyright 2008 - 2011 * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * @project loon * @author cping * @email javachenpeng@yahoo.com * @version 0.1.0 */ package loon.action.map.tmx; import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.zip.GZIPInputStream; import loon.LSystem; import loon.core.LRelease; import loon.core.graphics.device.LLight; import loon.core.graphics.opengl.GLEx; import loon.core.graphics.opengl.GLUtils; import loon.core.graphics.opengl.LTexture; import loon.core.graphics.opengl.LTextureBatch; import loon.core.graphics.opengl.LTextureBatch.GLCache; import loon.net.Base64Coder; import loon.utils.xml.XMLElement; public class TMXLayer extends LLight implements LRelease { private class MapTileSet { GLCache cache; LTexture texture; } private MapTileSet mapTileSet; private HashMap<Integer, MapTileSet> lazyMaps = new HashMap<Integer, MapTileSet>( 10); private int keyHashCode = 1; private int cx = 0, cy = 0; private TMXTileSet tmxTileSet; // 基础地图 private final TMXTiledMap tmx; // 图层索引 public int index; // XML文件名 public String name; // 图层数据 public int[][][] data; // 图层宽度(TMX格式的宽,即实际宽/瓦片大小) public int width; // 图层高度(TMX格式的高,即实际高/瓦片大小) public int height; // 图层属性 public TMXProperty props; /** * 根据TMX地图描述创建一个新层 * * @param map * @param element * @throws RuntimeException */ public TMXLayer(TMXTiledMap map, XMLElement element) throws RuntimeException { this.tmx = map; this.name = element.getAttribute("name", ""); this.width = element.getIntAttribute("width", 0); this.height = element.getIntAttribute("height", 0); this.data = new int[width][height][3]; this.maxLightSize(width, height); // 获得当前图层属性 XMLElement propsElement = element.getChildrenByName("properties"); if (propsElement != null) { props = new TMXProperty(); ArrayList<XMLElement> properties = propsElement.list("property"); for (int i = 0; i < properties.size(); i++) { XMLElement propElement = properties.get(i); String name = propElement.getAttribute("name", null); String value = propElement.getAttribute("value", null); props.setProperty(name, value); } } XMLElement dataNode = element.getChildrenByName("data"); String encoding = dataNode.getAttribute("encoding", null); String compression = dataNode.getAttribute("compression", null); // 进行base64的压缩解码 if ("base64".equals(encoding) && "gzip".equals(compression)) { try { char[] enc = dataNode.getContents().trim().toCharArray(); byte[] dec = Base64Coder.decodeBase64(enc); GZIPInputStream is = new GZIPInputStream( new ByteArrayInputStream(dec)); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int tileId = 0; tileId |= is.read(); tileId |= is.read() << 8; tileId |= is.read() << 16; tileId |= is.read() << 24; if (tileId == 0) { data[x][y][0] = -1; data[x][y][1] = 0; data[x][y][2] = 0; } else { TMXTileSet set = map.findTileSet(tileId); if (set != null) { data[x][y][0] = set.index; data[x][y][1] = tileId - set.firstGID; } data[x][y][2] = tileId; } } } } catch (IOException e) { throw new RuntimeException("Unable to decode base64 !"); } } else { throw new RuntimeException("Unsupport tiled map type " + encoding + "," + compression + " only gzip base64 Support !"); } } public void clearCache() { if (lazyMaps != null) { lazyMaps.clear(); } } /** * 获得指定位置的瓦片ID * * @param x * @param y * @return */ public int getTileID(int x, int y) { return data[x][y][2]; } /** * 设置指定位置的瓦片ID * * @param x * @param y * @param tile */ public void setTileID(int x, int y, int tile) { if (tile == 0) { data[x][y][0] = -1; data[x][y][1] = 0; data[x][y][2] = 0; } else { TMXTileSet set = tmx.findTileSet(tile); data[x][y][0] = set.index; data[x][y][1] = tile - set.firstGID; data[x][y][2] = tile; } } /** * 渲染当前层画面到LGraphics之上 * * @param g * @param x * @param y * @param sx * @param sy * @param width * @param ty * @param isLine * @param mapTileWidth * @param mapTileHeight */ public void draw(GLEx g, int x, int y, int sx, int sy, int width, int height, boolean isLine, int mapTileWidth, int mapTileHeight) { if (width == 0 || height == 0) { return; } if (lightingOn) { GLUtils.setShadeModelSmooth(GLEx.gl10); } this.tmxTileSet = null; this.mapTileSet = null; for (int tileset = 0; tileset < tmx.getTileSetCount(); tileset++) { keyHashCode = 1; keyHashCode = LSystem.unite(keyHashCode, tileset); keyHashCode = LSystem.unite(keyHashCode, sx); keyHashCode = LSystem.unite(keyHashCode, sy); keyHashCode = LSystem.unite(keyHashCode, width); keyHashCode = LSystem.unite(keyHashCode, height); keyHashCode = LSystem.unite(keyHashCode, mapTileWidth); keyHashCode = LSystem.unite(keyHashCode, mapTileHeight); keyHashCode = LSystem.unite(keyHashCode, lightingOn); mapTileSet = lazyMaps.get(keyHashCode); if (!isLightDirty && mapTileSet != null) { mapTileSet.cache.x = x; mapTileSet.cache.y = y; LTextureBatch.commit(mapTileSet.texture, mapTileSet.cache); if (isLine) { tmx.draw(g, x, y, sx, sy, width, height, index); } if (lightingOn) { GLUtils.setShadeModelFlat(GLEx.gl10); } return; } for (int ty = 0; ty < height; ty++) { for (int tx = 0; tx < width; tx++) { if ((sx + tx < 0) || (sy + ty < 0)) { continue; } if ((sx + tx >= this.width) || (sy + ty >= this.height)) { continue; } if (data[sx + tx][sy + ty][0] == tileset) { if (tmxTileSet == null) { tmxTileSet = tmx.getTileSet(tileset); tmxTileSet.tiles.glBegin(); } int sheetX = tmxTileSet .getTileX(data[sx + tx][sy + ty][1]); int sheetY = tmxTileSet .getTileY(data[sx + tx][sy + ty][1]); int tileOffsetY = tmxTileSet.tileHeight - mapTileHeight; cx = tx * mapTileWidth; cy = ty * mapTileHeight - tileOffsetY; if (lightingOn) { setLightColor(cx / mapTileWidth, cy / mapTileHeight); } tmxTileSet.tiles .draw(g, cx, cy, sheetX, sheetY, colors); } } } if (tmxTileSet != null) { tmxTileSet.tiles.glEnd(); if (mapTileSet == null) { mapTileSet = new MapTileSet(); } else { mapTileSet.texture = null; mapTileSet.cache.dispose(); mapTileSet.cache = null; } mapTileSet.texture = tmxTileSet.tiles.getTarget(); mapTileSet.cache = tmxTileSet.tiles.newCache(); mapTileSet.cache.x = x; mapTileSet.cache.y = y; lazyMaps.put(keyHashCode, mapTileSet); if (lightingOn) { GLUtils.setShadeModelFlat(GLEx.gl10); } if (isLine) { tmx.draw(g, x, y, sx, sy, width, height, index); } isLightDirty = false; tmxTileSet = null; } } } @Override public void dispose() { if (lazyMaps != null) { lazyMaps.clear(); } } }