/* * 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.RenderableLiquid; import com.opengrave.og.base.VertexLiquid; 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 TerrainLiquidLayer extends RenderableLiquid { public static final int size = 64; // ArrayList<TerrainLiquidVertex> vertList = new ArrayList<TerrainLiquidVertex>(); 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 float[] flowx = new float[size * size]; private float[] flowy = new float[size * size]; private float[] flowlocx = new float[size * size]; private float[] flowlocy = new float[size * size]; private RenderStyle style; private TerrainWorld world; private CommonAreaLoc loc; private int layerNumber; private TextureEditable flowtexture; private TextureEditable colourtexture; private ArrayList<TextureEditableDeferedChanges> colChange = new ArrayList<TextureEditableDeferedChanges>(); private ArrayList<TextureEditableDeferedChanges> flowChange = new ArrayList<TextureEditableDeferedChanges>(); public TerrainLiquidLayer(TerrainWorld world, CommonAreaLoc locationInWorld, int layer, OGInputStream stream) { this.world = world; this.loc = locationInWorld; this.layerNumber = layer; this.textureAtlas = (TextureAtlas) world.getLiquidTextures(); this.normalAtlas = (TextureAtlas) world.getLiquidNormTextures(); if (stream == null) { randomise(); } else { try { for (int x = 0; x < TerrainLayer.size; x++) { for (int y = 0; y < TerrainLayer.size; y++) { // TerrainLiquidEditableVertex tVtx = getVertexAt(x, y, true); int i = x + (y * size); Vector4f col = stream.readVector4f(); setColour(x, y, col); height[i] = stream.readFloat(); texture[i] = stream.readInt(); flowx[i] = stream.readFloat(); flowy[i] = stream.readFloat(); } } 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(); } } } private void randomise() { Vector4f waterCol = new Vector4f(0f, 0.2f, 1f, 1f); for (int x = 0; x < TerrainLayer.size; x++) { for (int y = 0; y < TerrainLayer.size; y++) { int i = x + (y * size); height[i] = 1f * layerNumber; setColour(x, y, waterCol); texture[i] = 1; flowx[i] = 0f; flowy[i] = 0f; 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 VertexLiquid(x, y, height[i], texture[i], nx[i], ny[i], nz[i])); } @Override public void update(float delta) { delta = delta * 0.005f; for (int x = 0; x < TerrainLayer.size - 1; x++) { for (int y = 0; y < TerrainLayer.size - 1; y++) { int i = x + (y * size); if (texture[i] < 0) { continue; } flowlocx[i] = (flowlocx[i] + (flowx[i] * delta)) % 1f; flowlocy[i] = (flowlocy[i] + (flowy[i] * delta)) % 1f; setLastLiquidFlow(x + 1, y + 1, flowlocx[i], flowlocy[i]); } } changed = true; } public void setLastLiquidFlow(int x, int y, float fx, float fy) { if (flowtexture != null) { this.flowtexture.setColourAt(x, y, new Vector4f(fx, fy, 0f, 0f)); } else { flowChange.add(new TextureEditableDeferedChanges(x, y, new Vector4f(fx, fy, 0f, 0f))); } } public void setColour(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 void render(Matrix4f offset) { render(offset, style); } public void setRenderStyle(RenderStyle renderStyle) { style = renderStyle; } public void setAllTextures(int in) { for (int i = 0; i < texture.length; i++) { texture[i] = in; } } public void save(OGOutputStream stream) { try { for (int x = 0; x < TerrainLayer.size; x++) { for (int y = 0; y < TerrainLayer.size; y++) { // TerrainLiquidEditableVertex tVtx = getVertexAt(x, y, true); int i = x + (y * size); stream.writeVector4f(new Vector4f(r[i], g[i], b[i], 1f)); stream.writeFloat(height[i]); stream.writeInt(texture[i]); stream.writeFloat(flowx[i]); stream.writeFloat(flowy[i]); } } } catch (IOException e) { new DebugExceptionHandler(e); } } public void alter(int x, int y, TerrainLiquidLayerAlteration alter) { int i = x + (y * size); if (alter.setColour) { setColour(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.texture; changed = true; } if (alter.setFlow) { flowx[i] = alter.flowx; flowy[i] = alter.flowy; } } public float getCurrentFlowXAt(int x, int y) { return flowlocx[x + (y * size)]; } public float getCurrentFlowYAt(int x, int y) { return flowlocy[x + (y * size)]; } public int getTextureAt(int x, int y) { int i = x + (y * size); return texture[i]; } private void calculateNormal(int x, int y) { if (x < 0 || x > size - 1 || y < 0 || y > size - 1) { 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 getFlowTexture() { if (flowtexture == null) { flowtexture = new TextureEditable(size + 2, 1f, 1f, 1f, 1f); if (flowChange.size() > 0) { flowtexture.dumpChanges(flowChange); } } return flowtexture; } @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; } }