/*
* Copyright 2016 Nathan Howard
*
* This file is part of OpenGrave
*
* OpenGrave is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenGrave is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenGrave. If not, see <http://www.gnu.org/licenses/>.
*/
package com.opengrave.og.terrain;
import java.io.IOException;
import java.util.ArrayList;
import com.opengrave.common.DebugExceptionHandler;
import com.opengrave.common.OGInputStream;
import com.opengrave.common.OGOutputStream;
import com.opengrave.common.world.CommonAreaLoc;
import com.opengrave.og.base.Pickable;
import com.opengrave.og.base.RenderableMultitex;
import com.opengrave.og.base.VertexMultiTex;
import com.opengrave.og.engine.Location;
import com.opengrave.og.resources.RenderStyle;
import com.opengrave.og.resources.TextureAtlas;
import com.opengrave.og.resources.TextureEditable;
import com.opengrave.og.resources.TextureEditableDeferedChanges;
import com.opengrave.og.util.Matrix4f;
import com.opengrave.og.util.Vector4f;
public class TerrainLayer extends RenderableMultitex implements Pickable {
TerrainWorld world = null; // The world this belongs to.
CommonAreaLoc loc = null; // The location inside of the world
public static final int size = 64;
// private ArrayList<TerrainVertex> vertList = new ArrayList<TerrainVertex>( size * size);
// private TerrainVertex[] vertList = new TerrainVertex[size * size];
private float[] height = new float[size * size];
private float[] r = new float[size * size];
private float[] g = new float[size * size];
private float[] b = new float[size * size];
private float[] nx = new float[size * size];
private float[] ny = new float[size * size];
private float[] nz = new float[size * size];
private int[] texture = new int[size * size];
private int layerNumber;
private RenderStyle style;
private TextureEditable colourtexture;
public static int detail = 0;
private ArrayList<TextureEditableDeferedChanges> colChange = new ArrayList<TextureEditableDeferedChanges>();
public TextureAtlas getTextureList() {
return textureAtlas;
}
public void show() {
}
public void hide() {
}
public TerrainLayer(TerrainWorld world, CommonAreaLoc loc, int layerNumber, OGInputStream stream) {
this.layerNumber = layerNumber;
this.world = world;
this.loc = loc;
if (stream == null) {
randomise();
} else {
try {
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
int i = x + (y * size);
Vector4f col = stream.readVector4f();
float h = stream.readFloat();
int t = stream.readInt();
setFloorRGB(x, y, col);
height[i] = h;
texture[i] = t;
}
}
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
calculateNormal(x, y);
}
}
} catch (IOException e) {
System.out.println("Bad map format. Old or possible malformed. Using random data");
randomise();
}
}
textureAtlas = world.getTextures();
normalAtlas = world.getNormalTextures();
}
public void save(OGOutputStream stream) {
try {
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
int i = x + (y * size);
stream.writeVector4f(new Vector4f(r[i], g[i], b[i], 1f));
stream.writeFloat(height[i]);
stream.writeInt(texture[i]);
}
}
} catch (IOException e) {
new DebugExceptionHandler(e);
}
}
public void randomise() {
Vector4f col = new Vector4f((float) Math.random(), (float) Math.random(), (float) Math.random(), 1f);
for (int x = 0; x < size; x++) {
for (int y = 0; y < size; y++) {
int i = x + (y * size);
setFloorRGB(x, y, col);
height[i] = layerNumber;
nz[i] = 1f;
}
}
}
@Override
public void recreate() {
int maxTex = this.textureAtlas.size(), minTex = 0;
for (int x = 0; x < size - 1; x++) {
for (int y = 0; y < size - 1; y++) {
int i0 = x + (y * size);
int i1 = (x + 1) + (y * size);
int i2 = (x + 1) + ((y + 1) * size);
int i3 = x + ((y + 1) * size);
if (texture[i2] < minTex || texture[i2] > maxTex) {
// Bottom right missing, check if a top left is needed
if (texture[i0] < minTex || texture[i0] > maxTex || texture[i1] < minTex || texture[i1] > maxTex || texture[i3] < minTex
|| texture[i3] > maxTex) {
continue;
}
addVertexToBuffer(x, y, i0);
addVertexToBuffer(x + 1, y, i1);
addVertexToBuffer(x, y + 1, i3);
continue;
} else if (texture[i0] < minTex || texture[i0] > maxTex) {
// Top Left is missing, check is a bottom right is needed
if (texture[i2] < minTex || texture[i2] > maxTex || texture[i1] < minTex || texture[i1] > maxTex || texture[i3] < minTex
|| texture[i3] > maxTex) {
continue;
}
addVertexToBuffer(x, y + 1, i3);
addVertexToBuffer(x + 1, y, i1);
addVertexToBuffer(x + 1, y + 1, i2);
continue;
}
if (!(texture[i1] < minTex || texture[i1] > maxTex)) {
addVertexToBuffer(x, y, i0);
addVertexToBuffer(x + 1, y, i1);
addVertexToBuffer(x + 1, y + 1, i2);
}
if (!(texture[i3] < minTex || texture[i3] > maxTex)) {
addVertexToBuffer(x, y, i0);
addVertexToBuffer(x + 1, y + 1, i2);
addVertexToBuffer(x, y + 1, i3);
}
}
}
}
private void addVertexToBuffer(int x, int y, int i) {
addVertex(new VertexMultiTex(x, y, height[i], x, y, texture[i], nx[i], ny[i], nz[i]));
}
@Override
public void update(float delta) {
}
@Override
public void delete() {
// TODO Delete links to this object
}
public void setFloorRGB(int x, int y, Vector4f col) {
if (colourtexture != null) {
this.colourtexture.setColourAt(x + 1, y + 1, col);
} else {
colChange.add(new TextureEditableDeferedChanges(x + 1, y + 1, col));
}
if (x < 0 || y < 0 || x >= 64 || y >= 64) {
return;
}
int i = (x) + ((y) * size);
r[i] = col.x;
g[i] = col.y;
b[i] = col.z;
}
public int getLayer() {
return layerNumber;
}
public void setRenderStyle(RenderStyle style) {
this.style = style;
}
public void render(Matrix4f offset) {
render(offset, style);
}
/**
* Only to be used on init. Will destroy all data in layer to be more
* helpful for creators
*
* @param i
*/
public void setAllTextures(int in) {
for (int x = 0; x < size - 1; x++) {
for (int y = 0; y < size - 1; y++) {
int i = x + (y * size);
height[i] = 1f;
texture[i] = in;
}
}
}
public float getHeightAt(Location loc) {
// TODO getHeight... Not perfect, assume N-W most tile height will work for all the tile
int x = (int) loc.getTileX();
int y = (int) loc.getTileY();
int i = x + (y * size);
return height[i];
}
public void alter(int x, int y, TerrainLayerAlteration alter) {
int i = x + (y * size);
if (alter.setColour) {
setFloorRGB(x, y, alter.col);
}
if (alter.setHeight) {
height[i] = alter.height;
// Change normals
calculateNormal(x - 1, y);
calculateNormal(x, y);
calculateNormal(x + 1, y);
calculateNormal(x, y - 1);
calculateNormal(x, y + 1);
changed = true;
}
if (alter.setTexture) {
texture[i] = alter.tex;
changed = true;
}
}
private void calculateNormal(int x, int y) {
if (x < 0 || x >= size || y < 0 || y >= size) {
return;
}
int lefti = (x - 1) + (y * size), righti = (x + 1) + (y * size), upi = x + ((y - 1) * size), downi = x + ((y + 1) * size);
boolean hasLeft = (x > 0 && texture[lefti] != -1), hasRight = (x < size - 1 && texture[righti] != -1), hasUp = (y > 0 && texture[upi] != -1), hasDown = (y < size - 1 && texture[downi] != -1);
int thisi = x + (y * size);
float sx = height[hasRight ? righti : thisi] - height[hasLeft ? lefti : thisi];
if (!hasRight || !hasLeft) {
sx *= 2f;
}
float sy = height[hasDown ? downi : thisi] - height[hasUp ? upi : thisi];
if (!hasUp || !hasDown) {
sy *= 2f;
}
nx[thisi] = -sx;
ny[thisi] = -sy;
nz[thisi] = 2f;
changed = true;
}
@Override
public TextureEditable getColourTexture() {
if (colourtexture == null) {
colourtexture = new TextureEditable(size + 2, 1f, 1f, 1f, 1f);
if (colChange.size() > 0) {
colourtexture.dumpChanges(colChange);
}
}
return colourtexture;
}
}