package fr.lyrgard.hexScape.service;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import com.google.common.io.ByteStreams;
import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue.Bucket;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.VertexBuffer.Type;
import com.jme3.scene.shape.Box;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.MinFilter;
import com.jme3.util.BufferUtils;
import fr.lyrgard.hexScape.HexScapeCore;
import fr.lyrgard.hexScape.HexScapeJme3Application;
import fr.lyrgard.hexScape.control.PieceControlerAppState;
import fr.lyrgard.hexScape.io.virtualScape.VirtualScapeMapReader;
import fr.lyrgard.hexScape.model.map.Direction;
import fr.lyrgard.hexScape.model.map.Decor;
import fr.lyrgard.hexScape.model.map.Map;
import fr.lyrgard.hexScape.model.map.Tile;
import fr.lyrgard.hexScape.model.model3d.TileMesh;
import fr.lyrgard.hexScape.model.player.ColorEnum;
import fr.lyrgard.hexScape.utils.CoordinateUtils;
public class MapManager {
private static VirtualScapeMapReader mapReader = new VirtualScapeMapReader();
private Map map;
private Node sceneNode;
public Node getSelectablePieceNode() {
return selectablePieceNode;
}
private Node selectablePieceNode;
private Node mapNode;
private Node mapWithoutDecorsNode;
private java.util.Map<String, PieceManager> pieceManagersByPieceIds = new ConcurrentHashMap<String, PieceManager>();
public MapManager(Map map) {
super();
this.map = map;
sceneNode = new Node("sceneNode");
selectablePieceNode = new Node("selectablePiece");
sceneNode.attachChild(selectablePieceNode);
}
public static MapManager fromFile(File file) {
return mapReader.readMap(file);
}
public static MapManager fromInputStream(InputStream stream) {
byte[] bytes;
try {
bytes = ByteStreams.toByteArray(stream);
return mapReader.readMap(bytes, "");
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public void beginPlacingPiece(PieceManager piece) {
PieceControlerAppState pieceController = HexScapeCore.getInstance().getHexScapeJme3Application().getPieceControlerAppState();
pieceController.beginAddingPiece(piece);
}
public boolean placePiece(PieceManager piece, int x, int y, int z, Direction direction) {
for (PieceManager otherPiece : pieceManagersByPieceIds.values()) {
if (otherPiece != piece &&
otherPiece.getPiece().getX() == x &&
otherPiece.getPiece().getY() == y &&
otherPiece.getPiece().getZ() == z ) {
//already another piece here !
return false;
}
}
if (!pieceManagersByPieceIds.containsKey(piece.getPiece().getId())) {
pieceManagersByPieceIds.put(piece.getPiece().getId(), piece);
}
if (!selectablePieceNode.hasChild(piece.getSpatial())) {
selectablePieceNode.attachChild(piece.getSpatial());
}
piece.moveTo(x, y, z, direction);
return true;
}
public Map getMap() {
return map;
}
public void moveSelectedPiece() {
PieceControlerAppState pieceController = HexScapeCore.getInstance().getHexScapeJme3Application().getPieceControlerAppState();
pieceController.beginMovingSelectedPiece();
}
public void removePiece(PieceManager piece) {
if (pieceManagersByPieceIds.containsKey(piece.getPiece().getId())) {
pieceManagersByPieceIds.remove(piece.getPiece().getId());
selectablePieceNode.detachChild(piece.getSpatial());
}
}
public void addTile(int x, int y, int z, boolean halfSize, int topTexture, int sideTexture, boolean visible, boolean startZone, int startZoneNumber) {
map.addTile(x, y, z, halfSize, topTexture, sideTexture, visible, startZone, startZoneNumber);
}
public Tile getNearestTile(int x, int y, int z) {
Tile nearestTile = null;
List<Tile> tiles = getTiles(x, y);
int minDistanceZ = Integer.MAX_VALUE;
for (Tile tile : tiles) {
int distanceZ = Math.abs(tile.getZ() - z + 1);
if (distanceZ < minDistanceZ) {
minDistanceZ = distanceZ;
nearestTile = tile;
}
}
return nearestTile;
}
public List<Tile> getTiles(int x, int y) {
List<Tile> results = new ArrayList<>();
for (java.util.Map<Integer, java.util.Map<Integer, Tile>> byZ : map.getTilesMap().values()) {
java.util.Map<Integer, Tile> byY = byZ.get(y);
if (byY != null) {
Tile tile = byY.get(x);
if (tile != null) {
Tile topNeighbours = tile.getNeighbours().get(Direction.TOP);
if (topNeighbours == null || !topNeighbours.isVisible()) {
// if the tile doesn't have a tile on top of it, we add it
results.add(tile);
}
}
}
}
return results;
}
public PieceManager getNearestPiece(int x, int y, int z) {
PieceManager nearestPiece = null;
List<PieceManager> pieces = getPieces(x, y);
int minDistanceZ = Integer.MAX_VALUE;
for (PieceManager pieceManager : pieces) {
int distanceZ = Math.abs(pieceManager.getPiece().getZ() - z);
if (distanceZ < minDistanceZ) {
minDistanceZ = distanceZ;
nearestPiece = pieceManager;
}
}
return nearestPiece;
}
public List<PieceManager> getPieces(int x, int y) {
List<PieceManager> results = new ArrayList<>();
for (PieceManager piece : pieceManagersByPieceIds.values()) {
if (piece.getPiece().getX() == x && piece.getPiece().getY() == y) {
results.add(piece);
}
}
return results;
}
public boolean contains(PieceManager piece) {
return pieceManagersByPieceIds.containsKey(piece.getPiece().getId());
}
public List<Decor> getDecors() {
return map.getDecors();
}
public Spatial getSpatial() {
if (mapNode == null) {
mapNode = new Node("mapNode");
populateMapNode();
sceneNode.attachChild(mapNode);
}
return sceneNode;
}
public void redraw() {
populateMapNode();
}
private void populateMapNode() {
mapNode.detachAllChildren();
mapWithoutDecorsNode = new Node("mapWithoutDecorsNode");
Spatial visibleMap = getMapSpatial();
Spatial invisibleMap = getInvisibleTilesSpatial();
Collection<Spatial> decorNodes = getDecorsSpatials();
Node startZones = getStartZones();
mapWithoutDecorsNode.attachChild(visibleMap);
if (invisibleMap != null) {
mapWithoutDecorsNode.attachChild(invisibleMap);
}
mapNode.attachChild(mapWithoutDecorsNode);
for (Spatial decorNode : decorNodes) {
mapNode.attachChild(decorNode);
}
if (startZones != null) {
mapNode.attachChild(startZones);
}
// Box b = new Box(50, 10, 1); // create cube shape
// Geometry geom = new Geometry("Box", b); // create cube geometry from the shape
// Material mat = new Material(HexScapeCore.getInstance().getHexScapeJme3Application().getAssetManager(),
// "Common/MatDefs/Misc/Unshaded.j3md"); // create a simple material
// mat.setColor("Color", ColorRGBA.Red); // set color of material to blue
// geom.setMaterial(mat); // set the cube's material
// mapNode.attachChild(geom);
}
private Node getStartZones() {
Node node = new Node();
node.setShadowMode(ShadowMode.Off);
java.util.Map<Integer, List<Tile>> startZones = new HashMap<Integer, List<Tile>>();
for (java.util.Map<Integer, java.util.Map<Integer, Tile>> byZ : map.getTilesMap().values()) {
for (java.util.Map<Integer, Tile> byY : byZ.values()) {
for (Tile tile : byY.values()) {
if (tile.isStartZone()) {
List<Tile> list = startZones.get(tile.getStartZoneNumber());
if (list == null) {
list = new ArrayList<>();
startZones.put(tile.getStartZoneNumber(), list);
}
list.add(tile);
}
}
}
}
if (startZones.isEmpty()) {
return null;
}
for (Entry<Integer, List<Tile>> startZone : startZones.entrySet()) {
Mesh lineMesh = new Mesh();
lineMesh.setMode(Mesh.Mode.Lines);
List<Vector3f> vertices = new ArrayList<Vector3f>();
List<Integer> indexes = new ArrayList<Integer>();
for (Tile tile : startZone.getValue()) {
float x3d = (2 * tile.getX() + tile.getY()) * TileMesh.TRANSLATION_X;
float y3d = tile.getZ() * TileMesh.HEX_SIZE_Y;
float z3d = (tile.getY()) * TileMesh.TRANSLATION_Z;
for (Direction dir : Direction.values()) {
if (dir != Direction.BOTTOM && dir != Direction.TOP) {
Tile neighbours = tile.getNeighbours().get(dir);
if (neighbours == null || !neighbours.isStartZone()) {
int firstIndex = vertices.size();
vertices.addAll(TileMesh.getEdgeVertices(dir, tile.isHalfSize(), x3d, y3d, z3d));
indexes.addAll(TileMesh.getEdgeIndex(firstIndex));
}
}
}
}
lineMesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices.toArray(new Vector3f[vertices.size()])));
lineMesh.setBuffer(Type.Index, 2, BufferUtils.createIntBuffer(toIntArray(indexes)));
lineMesh.setLineWidth(50f);
lineMesh.updateBound();
lineMesh.updateCounts();
Geometry lineGeometry = new Geometry("line", lineMesh);
AssetManager assetManager = HexScapeCore.getInstance().getHexScapeJme3Application().getAssetManager();
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
ColorEnum color = ColorEnum.values()[startZone.getKey() % ColorEnum.values().length];
;
mat.setColor("Color", new ColorRGBA(color.getColor().getRed()/255f, color.getColor().getGreen()/255f, color.getColor().getBlue()/255f, 0));
lineGeometry.setMaterial(mat);
node.attachChild(lineGeometry);
}
return node;
}
private Collection<Spatial> getDecorsSpatials() {
Collection<Spatial> results = new ArrayList<>();
ExternalModelService externalModelService = ExternalModelService.getInstance();
Vector3f spacePos = new Vector3f();
for (Decor decor : getDecors()) {
if (decor != null) {
Spatial decorSpatial = externalModelService.getModel(decor.getName());
if (decorSpatial != null) {
CoordinateUtils.toSpaceCoordinate(decor.getX(), decor.getY(), decor.getZ(), spacePos);
decorSpatial.setLocalRotation(new Quaternion().fromAngleAxis(DirectionService.getInstance().getAngle(decor.getDirection()), Vector3f.UNIT_Y));
decorSpatial.setLocalTranslation(spacePos.x, spacePos.y, spacePos.z);
results.add(decorSpatial);
}
}
}
return results;
}
private Spatial getMapSpatial() {
Mesh mapMesh = new Mesh();
Texture tileTexture = TextureService.getInstance().getTileTexture();
List<Vector3f> vertices = new ArrayList<Vector3f>();
List<Vector2f> texCoord = new ArrayList<Vector2f>();
List<Integer> indexes = new ArrayList<Integer>();
List<Vector3f> normals = new ArrayList<Vector3f>();
Queue<Tile> notAddedYetTiles = new LinkedList<>();
for (java.util.Map<Integer, java.util.Map<Integer, Tile>> byZ : map.getTilesMap().values()) {
for (java.util.Map<Integer, Tile> byY : byZ.values()) {
for (Tile tile : byY.values()) {
if (tile.isVisible()) {
notAddedYetTiles.add(tile);
}
}
}
}
while (notAddedYetTiles.size() != 0) {
Tile tile = notAddedYetTiles.poll();
float x3d = (2 * tile.getX() + tile.getY()) * TileMesh.TRANSLATION_X;
float y3d = tile.getZ() * TileMesh.HEX_SIZE_Y;
float z3d = (tile.getY()) * TileMesh.TRANSLATION_Z;
addTileToMesh(tile, vertices, texCoord, indexes, normals, x3d, y3d, z3d);
//addTileAndNeighbours(tile, vertices, texCoord, indexes, normals, notAddedYetTiles, x3d, y3d, z3d);
}
mapMesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices.toArray(new Vector3f[vertices.size()])));
mapMesh.setBuffer(Type.TexCoord, 2, BufferUtils.createFloatBuffer(texCoord.toArray(new Vector2f[texCoord.size()])));
mapMesh.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(toIntArray(indexes)));
mapMesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals.toArray(new Vector3f[normals.size()])));
mapMesh.updateBound();
Geometry geo = new Geometry("mapMesh", mapMesh);
AssetManager assetManager = HexScapeCore.getInstance().getHexScapeJme3Application().getAssetManager();
tileTexture.setMinFilter(MinFilter.BilinearNoMipMaps);
Material mat = new Material(assetManager,
"Common/MatDefs/Light/Lighting.j3md");
mat.setBoolean("UseMaterialColors",true);
mat.setTexture("DiffuseMap", tileTexture);
mat.setColor("Ambient", ColorRGBA.White);
mat.setColor("Diffuse",ColorRGBA.White); // minimum material color
mat.setColor("Specular",ColorRGBA.White); // for shininess
mat.setFloat("Shininess", 50f); // [1,128] for shininess
geo.setMaterial(mat);
return geo;
}
private Spatial getInvisibleTilesSpatial() {
Mesh invisibleTilesMesh = new Mesh();
List<Vector3f> vertices = new ArrayList<Vector3f>();
List<Integer> indexes = new ArrayList<Integer>();
List<Vector3f> normals = new ArrayList<Vector3f>();
Queue<Tile> notAddedYetTiles = new LinkedList<>();
for (java.util.Map<Integer, java.util.Map<Integer, Tile>> byZ : map.getTilesMap().values()) {
for (java.util.Map<Integer, Tile> byY : byZ.values()) {
for (Tile tile : byY.values()) {
if (!tile.isVisible()) {
notAddedYetTiles.add(tile);
}
}
}
}
if (notAddedYetTiles.isEmpty()) {
return null;
}
while (notAddedYetTiles.size() != 0) {
Tile tile = notAddedYetTiles.poll();
float x3d = (2 * tile.getX() + tile.getY()) * TileMesh.TRANSLATION_X;
float y3d = tile.getZ() * TileMesh.HEX_SIZE_Y;
float z3d = (tile.getY()) * TileMesh.TRANSLATION_Z;
addTileDirectionToMesh(tile, vertices, null, indexes, normals, x3d, y3d, z3d, Direction.TOP);
}
invisibleTilesMesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices.toArray(new Vector3f[vertices.size()])));
invisibleTilesMesh.setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(toIntArray(indexes)));
invisibleTilesMesh.setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals.toArray(new Vector3f[normals.size()])));
invisibleTilesMesh.updateBound();
Geometry geo = new Geometry("invisibleTilesMesh", invisibleTilesMesh);
AssetManager assetManager = HexScapeCore.getInstance().getHexScapeJme3Application().getAssetManager();
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", new ColorRGBA(1, 1, 1, 0));
mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
geo.setMaterial(mat);
geo.setQueueBucket(Bucket.Translucent);
geo.setShadowMode(ShadowMode.Off);
return geo;
}
private void addTileToMesh(Tile tile, List<Vector3f> vertices, List<Vector2f> texCoord, List<Integer> indexes, List<Vector3f> normals, float currentX, float currentY, float currentZ) {
for (Direction dir : Direction.values()) {
Tile neighboor = tile.getNeighbours().get(dir);
if (neighboor == null || !neighboor.isVisible() || neighboor.isHalfSize()) {
addTileDirectionToMesh(tile, vertices, texCoord, indexes, normals, currentX, currentY, currentZ, dir);
}
}
}
private void addTileDirectionToMesh(Tile tile, List<Vector3f> vertices, List<Vector2f> texCoord, List<Integer> indexes, List<Vector3f> normals, float currentX, float currentY, float currentZ, Direction dir) {
int firstIndex = vertices.size();
vertices.addAll(TileMesh.getVertices(dir, tile.isHalfSize(), currentX, currentY, currentZ));
if (texCoord != null) {
texCoord.addAll(TileMesh.getTexCoord(dir, tile.getTopTexture(), tile.getSideTexture()));
}
indexes.addAll(TileMesh.getIndex(dir, firstIndex));
normals.addAll(TileMesh.getNormals(dir));
}
private int[] toIntArray(List<Integer> list){
int[] ret = new int[list.size()];
for(int i = 0;i < ret.length;i++)
ret[i] = list.get(i);
return ret;
}
public Spatial getMapWithoutDecorsNode() {
return mapWithoutDecorsNode;
}
public java.util.Map<String, PieceManager> getPieceManagersByPieceIds() {
return pieceManagersByPieceIds;
}
}