Java tutorial
package com.badlogic.gdx.tests; import java.util.Random; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.InputProcessor; import com.badlogic.gdx.graphics.GL10; import com.badlogic.gdx.graphics.Mesh; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture.TextureFilter; import com.badlogic.gdx.graphics.VertexAttribute; import com.badlogic.gdx.graphics.VertexAttributes; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g3d.PerspectiveCamera; import com.badlogic.gdx.math.Intersector; import com.badlogic.gdx.math.Plane; import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.math.collision.Ray; import com.badlogic.gdx.tests.utils.GdxTest; public class Water extends GdxTest implements InputProcessor { static final short WIDTH = 50; static final short HEIGHT = 50; static final float INV_WIDTH = 1.0f / WIDTH; static final float INV_HEIGHT = 1.0f / HEIGHT; static final float DAMPING = 0.9f; static final float DISPLACEMENT = -10; static final float TICK = 0.033f; static final int RADIUS = 3; float accum; boolean initialized = false; PerspectiveCamera camera; SpriteBatch batch; //Font font; Mesh mesh; Texture texture; Plane plane = new Plane(new Vector3(), new Vector3(1, 0, 0), new Vector3(0, 1, 0)); Vector3 point = new Vector3(); float[][] last; float[][] curr; float[][] intp; float[] vertices; @Override public void create() { camera = new PerspectiveCamera(); camera.getPosition().set((WIDTH) / 2.0f, (HEIGHT) / 2.0f, WIDTH / 2.0f); camera.setViewport(Gdx.graphics.getWidth(), Gdx.graphics.getWidth()); camera.setFov(90); camera.setNear(0.1f); camera.setFar(1000); last = new float[WIDTH + 1][HEIGHT + 1]; curr = new float[WIDTH + 1][HEIGHT + 1]; intp = new float[WIDTH + 1][HEIGHT + 1]; vertices = new float[(WIDTH + 1) * (HEIGHT + 1) * 5]; mesh = new Mesh(false, (WIDTH + 1) * (HEIGHT + 1), WIDTH * HEIGHT * 6, new VertexAttribute(VertexAttributes.Usage.Position, 3, "a_Position"), new VertexAttribute(VertexAttributes.Usage.TextureCoordinates, 2, "a_texCoords")); texture = new Texture(Gdx.files.internal("data/stones.jpg")); texture.setFilter(TextureFilter.Linear, TextureFilter.Linear); createIndices(); updateVertices(curr); initialized = true; batch = new SpriteBatch(); Gdx.input.setInputProcessor(this); //font = Gdx.graphics.newFont("Arial", 12, FontStyle.Plain); } private void createIndices() { short[] indices = new short[WIDTH * HEIGHT * 6]; int idx = 0; short vidx = 0; for (int y = 0; y < HEIGHT; y++) { vidx = (short) (y * (WIDTH + 1)); for (int x = 0; x < WIDTH; x++) { indices[idx++] = vidx; indices[idx++] = (short) (vidx + 1); indices[idx++] = (short) (vidx + WIDTH + 1); indices[idx++] = (short) (vidx + 1); indices[idx++] = (short) (vidx + WIDTH + 2); indices[idx++] = (short) (vidx + WIDTH + 1); vidx++; } } mesh.setIndices(indices); } private void updateVertices(float[][] curr) { int idx = 0; for (int y = 0; y <= HEIGHT; y++) { for (int x = 0; x <= WIDTH; x++) { float xOffset = 0; float yOffset = 0; if (x > 0 && x < WIDTH && y > 0 && y < HEIGHT) { xOffset = (curr[x - 1][y] - curr[x + 1][y]); yOffset = (curr[x][y - 1] - curr[x][y + 1]); } vertices[idx++] = x; vertices[idx++] = y; vertices[idx++] = 0; vertices[idx++] = (x + xOffset) * INV_WIDTH; vertices[idx++] = (y + yOffset) * INV_HEIGHT; } } mesh.setVertices(vertices); } private void updateWater() { for (int y = 0; y < HEIGHT + 1; y++) { for (int x = 0; x < WIDTH + 1; x++) { if (x > 0 && x < WIDTH && y > 0 && y < HEIGHT) { curr[x][y] = (last[x - 1][y] + last[x + 1][y] + last[x][y + 1] + last[x][y - 1]) / 4 - curr[x][y]; } curr[x][y] *= DAMPING; } } } private void interpolateWater(float alpha) { for (int y = 0; y < HEIGHT; y++) { for (int x = 0; x < WIDTH; x++) { intp[x][y] = (alpha * last[x][y] + (1 - alpha) * curr[x][y]); } } } private void touchWater(Vector3 point) { for (int y = Math.max(0, (int) point.y - RADIUS); y < Math.min(HEIGHT, (int) point.y + RADIUS); y++) { for (int x = Math.max(0, (int) point.x - RADIUS); x < Math.min(WIDTH, (int) point.x + RADIUS); x++) { float val = curr[x][y] + DISPLACEMENT * Math.max(0, (float) Math.cos(Math.PI / 2 * Math.sqrt(point.dst2(x, y, 0)) / RADIUS)); if (val < DISPLACEMENT) val = DISPLACEMENT; else if (val > -DISPLACEMENT) val = -DISPLACEMENT; curr[x][y] = val; } } } long lastTick = System.nanoTime(); Random rand = new Random(); @Override public void render() { GL10 gl = Gdx.graphics.getGL10(); if (gl == null) { return; } gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); gl.glClear(GL10.GL_COLOR_BUFFER_BIT); camera.update(); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadMatrixf(camera.getCombinedMatrix().val, 0); gl.glMatrixMode(GL10.GL_MODELVIEW); accum += Gdx.graphics.getDeltaTime(); while (accum > TICK) { for (int i = 0; i < 5; i++) { if (Gdx.input.isTouched(i)) { Ray ray = camera.getPickRay(Gdx.input.getX(i), (int) (Gdx.input.getY(i) / (float) Gdx.graphics.getHeight() * Gdx.graphics.getWidth())); Intersector.intersectRayPlane(ray, plane, point); touchWater(point); } } updateWater(); float[][] tmp = curr; curr = last; last = tmp; accum -= TICK; } float alpha = accum / TICK; interpolateWater(alpha); updateVertices(intp); gl.glEnable(GL10.GL_TEXTURE_2D); texture.bind(); mesh.render(GL10.GL_TRIANGLES); batch.begin(); //batch.drawText(font, "fps: " + Gdx.graphics.getFramesPerSecond(), 10, 20, Color.WHITE); batch.end(); } @Override public boolean keyDown(int keycode) { return false; } @Override public boolean keyUp(int keycode) { return false; } @Override public boolean keyTyped(char character) { return false; } @Override public boolean touchDown(int x, int y, int pointer, int newParam) { //Ray ray = camera.getPickRay( x, (int)(y / (float)Gdx.graphics.getHeight() * Gdx.graphics.getWidth())); //Intersector.intersectRayPlane( ray, plane, point ); //touchWater( point ); return false; } @Override public boolean touchUp(int x, int y, int pointer, int button) { return false; } @Override public boolean touchDragged(int x, int y, int pointer) { //Ray ray = camera.getPickRay( x, (int)(y / (float)Gdx.graphics.getHeight() * Gdx.graphics.getWidth())); //Intersector.intersectRayPlane( ray, plane, point ); //touchWater( point ); return false; } @Override public boolean needsGL20() { return false; } @Override public boolean touchMoved(int x, int y) { return false; } @Override public boolean scrolled(int amount) { return false; } }