package clientdata.visitors;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.apache.mina.core.buffer.IoBuffer;
import clientdata.VisitorInterface;
import clientdata.visitors.terrain.BitmapFamily;
import clientdata.visitors.terrain.BitmapGroup;
import clientdata.visitors.terrain.FractalFamily;
import clientdata.visitors.terrain.layers.AffectorHeightConstant;
import clientdata.visitors.terrain.layers.AffectorHeightFractal;
import clientdata.visitors.terrain.layers.AffectorHeightTerrace;
import clientdata.visitors.terrain.layers.BoundaryCircle;
import clientdata.visitors.terrain.layers.BoundaryLayer;
import clientdata.visitors.terrain.layers.BoundaryPolyLine;
import clientdata.visitors.terrain.layers.BoundaryPolygon;
import clientdata.visitors.terrain.layers.BoundaryRectangle;
import clientdata.visitors.terrain.layers.FilterBitmap;
import clientdata.visitors.terrain.layers.FilterDirection;
import clientdata.visitors.terrain.layers.FilterFractal;
import clientdata.visitors.terrain.layers.FilterHeight;
import clientdata.visitors.terrain.layers.FilterLayer;
import clientdata.visitors.terrain.layers.FilterRectangle;
import clientdata.visitors.terrain.layers.FilterShader;
import clientdata.visitors.terrain.layers.FilterSlope;
import clientdata.visitors.terrain.layers.HeightLayer;
import clientdata.visitors.terrain.layers.Layer;
import clientdata.visitors.terrain.layers.LayerType;
import clientdata.visitors.terrain.layers.ListLayer;
import clientdata.visitors.terrain.layers.NullLayer;
public class TerrainVisitor implements VisitorInterface {
//Temporary Data
private Deque<Pair<Layer, Integer>> layer_stack;
private Deque<String> foldername_stack;
private CharsetDecoder ascii_charset;
FractalFamily lastFamily;
private BitmapGroup bitmapGroup;
//Static Data
private static Map<String, Class<? extends Layer>> layerLookup;
//Header Data
private String filename;
private float map_width;
private float chunk_width;
private int tiles_per_chunk;
private int use_global_water_height;
private float global_water_height;
Map<Integer,FractalFamily> fractal_families;
ListLayer head_nodes;
List<BoundaryPolygon> water_boundaries;
public TerrainVisitor() {
//Static Init
if(layerLookup == null) {
Map<String, Class<? extends Layer>> map = new TreeMap<String, Class<? extends Layer>>();
map.put("LAYRFORM", ListLayer.class);
map.put("AHCNFORM", AffectorHeightConstant.class);
map.put("AHFRFORM", AffectorHeightFractal.class);
map.put("AHTRFORM", AffectorHeightTerrace.class);
map.put("BCIRFORM", BoundaryCircle.class);
map.put("BPOLFORM", BoundaryPolygon.class);
map.put("BPLNFORM", BoundaryPolyLine.class);
map.put("BRECFORM", BoundaryRectangle.class);
map.put("FBITFORM", FilterBitmap.class);
map.put("FDIRFORM", FilterDirection.class);
map.put("FFRAFORM", FilterFractal.class);
map.put("FHGTFORM", FilterHeight.class);
map.put("FSLPFORM", FilterSlope.class);
map.put("FSHDFORM", FilterShader.class);
layerLookup = map;
}
//These are only used during input processing.
layer_stack = new LinkedList<Pair<Layer, Integer>>();
foldername_stack = new LinkedList<String>();
ascii_charset = Charset.forName("US-ASCII").newDecoder();
//These contain the data required.
fractal_families = new TreeMap<Integer, FractalFamily>();
water_boundaries = new ArrayList<BoundaryPolygon>();
bitmapGroup = new BitmapGroup();
head_nodes = new ListLayer();
layer_stack.push(new Pair<Layer, Integer>(head_nodes, -1));
}
public void cleanup() {
//Clean up leftover data.
foldername_stack = null;
layer_stack = null;
ascii_charset = null;
//Prune all layers that are not related to height.
prune_layers(head_nodes);
}
private int prune_layers(ListLayer l) {
Iterator<Layer> itr = l.getChildren().iterator();
Layer lay;
LayerType lt;
int count = 0;
while(itr.hasNext()) {
lay = itr.next();
lt = lay.getType();
if(lt == LayerType.CONTAINER) {
int inner_count = prune_layers((ListLayer)lay);
if(inner_count > 0) {
count += inner_count;
} else {
itr.remove();
}
} else if( lt == LayerType.AHCN || lt == LayerType.AHFR || lt == LayerType.AHTR ||
lt == LayerType.FHGT || lt == LayerType.FFRA ||
lt == LayerType.FSLP || lt == LayerType.FDIR || lt == LayerType.FBIT) {
++count;
} else if( lt == LayerType.BCIR || lt == LayerType.BPLN ||
lt == LayerType.BPOL || lt == LayerType.BREC) {
//We want to keep this unless no heights were found, so
//NOP
} else {
itr.remove();
}
}
return count;
}
private void popToParent(int depth) {
while(depth < foldername_stack.size()) {
foldername_stack.pop();
}
while(layer_stack.size() > 0 && depth < layer_stack.peek().second) {
layer_stack.pop();
}
}
public String getFilename() {
return filename;
}
public void setFilename(String filename) {
this.filename = filename;
}
public float getMapWidth() {
return map_width;
}
public void setMapWidth(float map_width) {
this.map_width = map_width;
}
public float getChunkWidth() {
return chunk_width;
}
public void setChunkWidth(float chunk_width) {
this.chunk_width = chunk_width;
}
public int getTilesPerChunk() {
return tiles_per_chunk;
}
public void setTilesPerChunk(int tiles_per_chunk) {
this.tiles_per_chunk = tiles_per_chunk;
}
public int getUseGlobalWaterHeight() {
return use_global_water_height;
}
public void setUseGlobalWaterHeight(int use_global_water_height) {
this.use_global_water_height = use_global_water_height;
}
public float getGlobalWaterHeight() {
return global_water_height;
}
public void setGlobalWaterHeight(float global_water_height) {
this.global_water_height = global_water_height;
}
public FractalFamily getFractal(int id) {
return fractal_families.get(id);
}
@Override
public void parseData(String nodename, IoBuffer data, int depth, int size) throws Exception {
popToParent(depth);
String last_folder = foldername_stack.peekFirst();
//System.out.println(last_folder);
if(nodename.endsWith("DATA")) {
if("IHDRFORM".equals(last_folder)) {
//Find out what type to make
Iterator<String> it = foldername_stack.iterator();
String layerType = null;
for(int i = 0; i < 3; ++i) {
layerType = it.next();
}
//Make that type.
Class<? extends Layer> cla = layerLookup.get(layerType);
if(cla != null) {
Layer newLayer = cla.newInstance();
newLayer.loadHeader(data);
((ListLayer)layer_stack.peek().first).addChild(newLayer);
layer_stack.push(new Pair<Layer, Integer>(newLayer, depth-3));
if(newLayer instanceof BoundaryPolygon) {
if(((BoundaryPolygon)newLayer).useWaterHeight()) {
water_boundaries.add((BoundaryPolygon) newLayer);
}
}
} else {
Layer newLayer = new NullLayer();
layer_stack.push(new Pair<Layer, Integer>(newLayer, depth-3));
}
} else if("PTATFORM".equals(last_folder)) {
//Header
filename = data.getString(ascii_charset);
ascii_charset.reset();
map_width = data.getFloat();
chunk_width = data.getFloat();
tiles_per_chunk = data.getInt();
use_global_water_height = data.getInt();
global_water_height = data.getFloat();
} /*else if("MGRPFORM".equals(last_folder)) {
if("MFAMDATA".equals(nodename)) {
BitmapFamily family = new BitmapFamily();
family.loadData(data);
bitmapGroup.addBitmapFamily(family);
}
}*/ else if("MFAMDATA".equals(nodename)) {
FractalFamily fractal = new FractalFamily();
int position = data.position();
fractal.setFractal_id(data.getInt());
fractal.setFractal_label(data.getString(ascii_charset));
ascii_charset.reset();
if(data.hasRemaining()) {
data.position(position);
BitmapFamily family = new BitmapFamily();
family.loadData(data);
bitmapGroup.addBitmapFamily(family);
ascii_charset.reset();
} else {
fractal_families.put(fractal.getFractal_id(), fractal);
lastFamily = fractal;
}
} else if("MFRCFORM".equals(last_folder)) {
FractalFamily f = lastFamily;
f.setSeed(data.getInt());
f.setUse_bias(data.getInt());
f.setBias(data.getFloat());
f.setUse_gain(data.getInt());
f.setGain(data.getFloat());
f.setOctaves(data.getInt());
f.setOctaves_arg(data.getFloat());
f.setAmplitude(data.getFloat());
f.setFreq_x(data.getFloat());
f.setFreq_z(data.getFloat());
f.setOffset_x(data.getFloat());
f.setOffset_y(data.getFloat());
f.setCombination_type(data.getInt());
} else if(nodename.equals("DATA")) {
if(data.remaining() >= 4)
layer_stack.peek().first.loadData(data);
}
} else if(nodename.equals("ADTA")) {
layer_stack.peek().first.loadData(data);
} else if(nodename.equals("DATAPARM")) {
layer_stack.peek().first.loadData(data);
}
}
@Override
public void notifyFolder(String nodeName, int depth) throws Exception {
popToParent(depth);
foldername_stack.push(nodeName);
}
public float getWaterHeight(float x, float z)
{
for (BoundaryPolygon boundary : water_boundaries)
{
if (boundary.isContained(x, z))
{
return boundary.getWaterHeight();
}
}
if (use_global_water_height != 0)
{
return global_water_height;
}
return Float.NaN;
}
public boolean isWater(float x, float z)
{
float water_height;
float height;
if ((water_height = getWaterHeight(x, z)) != Float.NaN)
{
height = getHeight(x, z);
if (height <= water_height)
return true;
}
return false;
}
@SuppressWarnings("unused")
private Layer findLayer(float x, float z)
{
for (Layer layer : head_nodes.getChildren())
{
if(layer instanceof ListLayer) {
ListLayer ll = (ListLayer) layer;
for (BoundaryLayer boundary : ll.getBoundaries())
{
if (boundary.isContained(x, z))
return findLayerRecursive(x, z, ll);
}
}
}
return head_nodes.getChildren().get(0);
}
private Layer findLayerRecursive(float x, float z, ListLayer rootLayer)
{
for (Layer layer : rootLayer.getChildren())
{
if(layer instanceof ListLayer) {
ListLayer ll = (ListLayer)layer;
for(BoundaryLayer boundary : ll.getBoundaries())
{
if (boundary.isContained(x, z))
return findLayerRecursive(x, z, ll);
}
}
}
return rootLayer;
}
@SuppressWarnings("unused")
public float getHeight(float x, float z)
{
float affector_transform = 1.0f;
float transform_value = 0.0f;
float height_result = 0.0f;
for(Layer layer : head_nodes.getChildren()) {
if (layer.isEnabled()) {
if(layer instanceof ListLayer) {
Pair<Float, Float> res = processLayerHeight((ListLayer)layer, x, z, height_result, affector_transform);
transform_value = res.first;
height_result = res.second;
}
}
}
return height_result;
}
private Pair<Float, Float> processLayerHeight(ListLayer layer, float x, float z, float base_value, float affector_transform)
{
List<BoundaryLayer> boundaries = layer.getBoundaries();
List<HeightLayer> heights = layer.getHeights();
List<FilterLayer> filters = layer.getFilters();
float transform_value = 0.0f;
boolean has_boundaries = false;
//float result = 0.0f;
FilterRectangle rectangle = new FilterRectangle();
rectangle.minX = Float.MAX_VALUE;
rectangle.minZ = Float.MAX_VALUE;
//rectangle.maxX = Float.MIN_VALUE;
//rectangle.maxZ = Float.MIN_VALUE;
rectangle.maxX = -Float.MAX_VALUE;
rectangle.maxZ = -Float.MAX_VALUE;
for (BoundaryLayer boundary : boundaries)
{
if (!boundary.isEnabled())
continue;
else
has_boundaries = true;
float boundaryResult = boundary.process(x, z);
if(boundaryResult != 0) {
if(boundary.getMinX() < rectangle.minX)
rectangle.minX = boundary.getMinX();
if(boundary.getMinZ() < rectangle.minZ)
rectangle.minZ = boundary.getMinZ();
if(boundary.getMaxX() > rectangle.maxX)
rectangle.maxX = boundary.getMaxX();
if(boundary.getMaxZ() > rectangle.maxZ)
rectangle.maxZ = boundary.getMaxZ();
}
float r = calculateFeathering(boundaryResult, boundary.getFeatherType());
if (r > transform_value)
transform_value = r;
if (transform_value >= 1)
break;
}
if (has_boundaries == false)
transform_value = 1.0f;
if (layer.boundariesInverted())
transform_value = 1.0f - transform_value;
if (transform_value != 0)
{
for(FilterLayer filter : filters) {
if (!filter.isEnabled())
continue;
float r = calculateFeathering(filter.process(x, z, transform_value, base_value, this, rectangle), filter.getFeatherType());
if (transform_value > r)
transform_value = r;
if (transform_value == 0)
break;
}
//System.out.println("Tranform value after filter: " + transform_value);
if (layer.filtersInverted())
transform_value = 1.0f - transform_value;
if (transform_value != 0)
{
for(HeightLayer affector : heights)
{
if (affector.isEnabled())
{
base_value = affector.process(x, z, transform_value * affector_transform, base_value, this);
//System.out.println("Affector Height: " + base_value);
}
}
List<Layer> children = layer.getChildren();
for(Layer child : children)
{
if (child.isEnabled() && child instanceof ListLayer) {
Pair<Float, Float> pair = processLayerHeight((ListLayer)child, x, z, base_value, affector_transform * transform_value);
//transform_value = pair.first;
base_value = pair.second;
//System.out.println("Child Layer Height: " + base_value);
} /*else {
break;
}*/
}
}
}
return new Pair<Float, Float>(transform_value, base_value);
}
private float calculateFeathering(float value, int featheringType) {
switch (featheringType) {
case 0:
return value;
case 1:
return value * value;
case 2:
return (float) Math.sqrt(value);
case 3:
return value * value * (3 - 2 * value);
default:
return 0;
}
}
public BitmapGroup getBitmapGroup() {
return bitmapGroup;
}
public void setBitmapGroup(BitmapGroup bitmapGroup) {
this.bitmapGroup = bitmapGroup;
}
public class Pair<T,V> {
public T first;
public V second;
public Pair(T first, V second) {
this.first = first;
this.second = second;
}
}
}