From fded8cd1926933e1293de5a4ece7c3edec08af94 Mon Sep 17 00:00:00 2001 From: Frank v/d Haterd Date: Mon, 13 Jan 2014 03:05:49 +0100 Subject: Whole terrain added Todo: 1D textures instead of colors new heightAt function instead of default one (however it works) normals improved ?? --- src/Terrain.java | 219 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 214 insertions(+), 5 deletions(-) diff --git a/src/Terrain.java b/src/Terrain.java index 4b2c366..3287322 100644 --- a/src/Terrain.java +++ b/src/Terrain.java @@ -1,27 +1,236 @@ +import java.awt.Color; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import javax.media.opengl.GL; +import javax.media.opengl.GL2; +import static javax.media.opengl.GL2.*; +import robotrace.Vector; + /** * Implementation of the terrain. */ -class Terrain { +class Terrain extends BetterBase { + + /** + * The display list containing the terrain. + */ + private int terrainDisplayList; + /** + * The array containing the height map of the terrain. + */ + private float[][] heightMap; + /** + * The array containing all vertex normals of the terrain. + */ + private Vector[][] normalMap; /** * Can be used to set up a display list. */ public Terrain() { - // code goes here ... + this.terrainDisplayList = 0; + this.heightMap = new float[41][41]; + this.normalMap = new Vector[41][41]; + + // Fill the height map array + for (int y = 0; y < 41; y++) { + for (int x = 0; x < 41; x++) { + heightMap[x][y] = heightAt(x, y); + } + } + + // Fill the normal map array + for (int y = 0; y < 40; y++) { + for (int x = 0; x < 40; x++) { + /* Check (if possible!) what the height of the left vertex is + * and subtract it with the height of right neighbor. + * + * Do the same for the top and bottom neightbors. + * + * For the border cases, multiply by two to compensate for the + * result. + * + * Use two for the up axis so it is averaged. + * + * Finally update the normalMap array with the new normal vector. + */ + float xComponent = heightAt(x > 0 ? x - 1 : x, y) + - heightAt(x < 40 - 1 ? x + 1 : x, y); + float yComponent = heightAt(x, y > 0 ? y - 1 : y) + - heightAt(x, y < 40 - 1 ? y + 1 : y); + + if (x == 0 || x == 40 - 1) { + xComponent *= 2; + } + + if (y == 0 || y == 40 - 1) { + yComponent *= 2; + } + + normalMap[x][y] = new Vector(xComponent, yComponent, 2).normalized(); + } + } + } + + /** + * Create the terrain and store all calls in a display list + * terrainDisplayList, so drawing the terrain is more efficient. + */ + public void createTerrain() { + /*/ Load textures first + Color[] sand = new Color[1]; + Color[] water = new Color[1]; + Color[] grass = new Color[1]; + + sand[0] = new Color(255, 242, 0); + water[0] = new Color(0, 0, 255); + grass[0] = new Color(0, 255, 0); + + int sandTextureID = create1DTexture(gl, sand); + int waterTextureID = create1DTexture(gl, water); + int grassTextureID = create1DTexture(gl, grass); */ + + // Create display list + this.terrainDisplayList = gl.glGenLists(1); + + gl.glNewList(terrainDisplayList, GL2.GL_COMPILE); + + // Create all points of the terrain based on the heightAt function + for (int y = -20; y < 20; y++) { + gl.glBegin(GL_TRIANGLE_STRIP); + + for (int x = -20; x < 20; x++) { + // Normalize values + int arrayX = x + 20; + int arrayY = y + 20; + + /* + * Determine texture, if 0 < height <= 0.5 then the ground will + * be yellow. If height < 0 then the ground will be blue. + * And if otherwise the ground will be green. + */ + float height = heightMap[arrayX][arrayY]; + + if (height > 0 && height <= 0.5f) { + // Sand + //gl.glBindTexture(GL_TEXTURE_1D, sandTextureID); + gl.glColor3f(255 / 255.0f, 242 / 255.0f, 0); + } else if (height < 0) { + // Water + //gl.glBindTexture(GL_TEXTURE_1D, waterTextureID); + gl.glColor3f(0, 0, 255 / 250.0f); + } else { + // Grass + //gl.glBindTexture(GL_TEXTURE_1D, grassTextureID); + gl.glColor3f(0, 255 / 255.0f, 0); + } + + // Create all vertices + Vector pointA = new Vector(x, y, heightMap[arrayX][arrayY]); + Vector pointB = new Vector(x + 1, y, heightMap[arrayX + 1][arrayY]); + Vector pointC = new Vector(x, y + 1, heightMap[arrayX][arrayY + 1]); + Vector pointD = new Vector(x + 1, y + 1, heightMap[arrayX + 1][arrayY + 1]); + + // Draw the vertices and their normals + glNormal(normalMap[arrayX][arrayY]); + + // Set the color + //gl.glTexCoord1f(1); + + // Draw the vertices + glVertex(pointA); + glVertex(pointB); + glVertex(pointC); + glVertex(pointD); + + //unbindTextures(); + } + + gl.glEnd(); + } + + // Add water to the terrain at z = 0 + gl.glBegin(GL_TRIANGLE_STRIP); + + Vector pointA = new Vector(-20, 20, 0); + Vector pointB = new Vector(-20, -20, 0); + Vector pointC = new Vector(20, 20, 0); + Vector pointD = new Vector(20, -20, 0); + + gl.glColor4f(100f, 100f, 100f, 0.5f); + + glVertex(pointA); + glVertex(pointB); + glVertex(pointC); + glVertex(pointD); + + gl.glEnd(); + + gl.glEndList(); + + System.out.println("Terrain created"); + } + + /** + * Creates a normal vector based on three input vectors, for example an + * triangle. + * + * @param v1 Vector 1 + * @param v2 Vector 2 + * @param v3 Vector 3 + * @return + */ + private Vector calculateNormal(Vector v1, Vector v2, Vector v3) { + Vector firstVector = v2.add(v1.scale(-1)); + Vector secondVector = v3.add(v1.scale(-1)); + + return firstVector.cross(secondVector).normalized(); } /** * Draws the terrain. */ public void draw() { - // code goes here ... + gl.glCallList(terrainDisplayList); } /** * Computes the elevation of the terrain at ({@code x}, {@code y}). */ - public float heightAt(float x, float y) { - return 0; // <- code goes here + public final float heightAt(float x, float y) { + float height = (float) (0.6f * Math.cos(0.3f * x + 0.2f * y) + 0.4f + * Math.cos(x - 0.5f * y)); + + return height; + } + + /** + * Creates a new 1D - texture. + * + * @param gl + * @param colors + * @return the texture ID for the generated texture. + */ + public int create1DTexture(GL2 gl, Color[] colors) { + gl.glDisable(GL_TEXTURE_2D); + gl.glEnable(GL_TEXTURE_1D); + int[] texid = new int[]{-1}; + gl.glGenTextures(1, texid, 0); + ByteBuffer bb = ByteBuffer.allocateDirect(colors.length * 4).order(ByteOrder.nativeOrder()); + for (Color color : colors) { + int pixel = color.getRGB(); + bb.put((byte) ((pixel >> 16) & 0xFF)); // Red component + bb.put((byte) ((pixel >> 8) & 0xFF)); // Green component + bb.put((byte) (pixel & 0xFF)); // Blue component + bb.put((byte) ((pixel >> 24) & 0xFF)); // Alpha component + } + bb.flip(); + gl.glBindTexture(GL_TEXTURE_1D, texid[0]); + gl.glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA8, colors.length, 0, GL_RGBA, GL_UNSIGNED_BYTE, bb); + gl.glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + gl.glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + gl.glBindTexture(GL_TEXTURE_1D, 0); + return texid[0]; } } -- cgit v1.2.1