OSDN Git Service

ye ol' cpu burner
authorbadlogic <badlogicgames@gmail.com>
Thu, 11 Jul 2013 22:36:48 +0000 (23:36 +0100)
committerbadlogic <badlogicgames@gmail.com>
Thu, 11 Jul 2013 22:36:48 +0000 (23:36 +0100)
gdx/src/com/badlogic/gdx/graphics/g3d/shaders/graph/ShaderGraph.java
gdx/src/com/badlogic/gdx/graphics/g3d/utils/CameraInputController.java
gdx/src/com/badlogic/gdx/graphics/g3d/utils/FirstPersonCameraController.java [new file with mode: 0644]
tests/gdx-tests-android/assets/data/g3d/tiles.png [new file with mode: 0644]
tests/gdx-tests-lwjgl/src/com/badlogic/gdx/tests/lwjgl/LwjglDebugStarter.java
tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/PerlinNoiseGenerator.java [new file with mode: 0644]
tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/VoxelChunk.java [new file with mode: 0644]
tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/VoxelTest.java [new file with mode: 0644]
tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/VoxelWorld.java [new file with mode: 0644]

index 2517b8e..5423432 100644 (file)
@@ -1,5 +1,7 @@
 package com.badlogic.gdx.graphics.g3d.shaders.graph;
 
+import java.util.Arrays;
+
 import com.badlogic.gdx.graphics.g3d.shaders.graph.ShaderNode.ShaderNodeType;
 import com.badlogic.gdx.utils.Array;
 import com.badlogic.gdx.utils.GdxRuntimeException;
index e9550e4..cf5c8d0 100644 (file)
@@ -36,13 +36,13 @@ public class CameraInputController extends InputAdapter {
        public boolean forwardTarget = true;
        /** Whether to update the target on scroll */
        public boolean scrollTarget = false;
-       public int forwardKey = Keys.UP;
+       public int forwardKey = Keys.W;
        protected boolean forwardPressed;
-       public int backwardKey = Keys.DOWN;
+       public int backwardKey = Keys.S;
        protected boolean backwardPressed;
-       public int rotateRightKey = Keys.RIGHT;
+       public int rotateRightKey = Keys.A;
        protected boolean rotateRightPressed;
-       public int rotateLeftKey = Keys.LEFT;
+       public int rotateLeftKey = Keys.D;
        protected boolean rotateLeftPressed;
        /** The camera. */
        public Camera camera;
diff --git a/gdx/src/com/badlogic/gdx/graphics/g3d/utils/FirstPersonCameraController.java b/gdx/src/com/badlogic/gdx/graphics/g3d/utils/FirstPersonCameraController.java
new file mode 100644 (file)
index 0000000..ced922d
--- /dev/null
@@ -0,0 +1,104 @@
+package com.badlogic.gdx.graphics.g3d.utils;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.Input.Keys;
+import com.badlogic.gdx.InputAdapter;
+import com.badlogic.gdx.graphics.Camera;
+import com.badlogic.gdx.math.Vector3;
+import com.badlogic.gdx.utils.IntIntMap;
+
+/**
+ * Takes a {@link Camera} instance and controlls it via w,a,s,d and
+ * mouse panning.  
+ * @author badlogic
+ *
+ */
+public class FirstPersonCameraController extends InputAdapter {
+       private final Camera camera;
+       private final IntIntMap keys = new IntIntMap();
+       private int STRAFE_LEFT = Keys.A;
+       private int STRAFE_RIGHT = Keys.D;
+       private int FORWARD = Keys.W;
+       private int BACKWARD = Keys.S;
+       private int UP = Keys.Q;
+       private int DOWN = Keys.E;
+       private float velocity = 5;
+       private float degreesPerPixel = 0.5f;
+       private final Vector3 tmp = new Vector3();
+       private final Vector3 tmp2 = new Vector3();
+       
+       public FirstPersonCameraController(Camera camera) {
+               this.camera = camera;
+       }
+       
+       @Override
+       public boolean keyDown (int keycode) {
+               keys.put(keycode, keycode);
+               return true;
+       }
+
+       @Override
+       public boolean keyUp (int keycode) {
+               keys.remove(keycode, 0);
+               return true;
+       }
+       
+       /**
+        * Sets the velocity in units per second for moving forward, backward and strafing left/right.
+        * @param velocity the velocity in units per second
+        */
+       public void setVelocity(float velocity) {
+               this.velocity = velocity;
+       }
+       
+       /**
+        * Sets how many degrees to rotate per pixel the mouse moved.
+        * @param degreesPerPixel
+        */
+       public void setDegreesPerPixel(float degreesPerPixel) {
+               this.degreesPerPixel = degreesPerPixel;
+       }
+
+       @Override
+       public boolean touchDragged (int screenX, int screenY, int pointer) {
+               float deltaX = -Gdx.input.getDeltaX() * degreesPerPixel;
+               float deltaY = -Gdx.input.getDeltaY() * degreesPerPixel;
+               camera.direction.rotate(camera.up, deltaX);
+               tmp.set(camera.direction).crs(camera.up).nor();
+               camera.direction.rotate(tmp, deltaY);
+//             camera.up.rotate(tmp, deltaY);
+               return true;
+       }
+
+       public void update() {
+               update(Gdx.graphics.getDeltaTime());
+       }
+       
+       public void update(float deltaTime) {
+               if(keys.containsKey(FORWARD)) {
+                       tmp.set(camera.direction).nor().scl(deltaTime * velocity);
+                       camera.position.add(tmp);
+               }
+               if(keys.containsKey(BACKWARD)) {
+                       tmp.set(camera.direction).nor().scl(-deltaTime * velocity);
+                       camera.position.add(tmp);
+               }
+               if(keys.containsKey(STRAFE_LEFT)) {
+                       tmp.set(camera.direction).crs(camera.up).nor().scl(-deltaTime * velocity);
+                       camera.position.add(tmp);
+               }
+               if(keys.containsKey(STRAFE_RIGHT)) {
+                       tmp.set(camera.direction).crs(camera.up).nor().scl(deltaTime * velocity);
+                       camera.position.add(tmp);
+               }
+               if(keys.containsKey(UP)) {
+                       tmp.set(camera.up).nor().scl(deltaTime * velocity);
+                       camera.position.add(tmp);
+               }
+               if(keys.containsKey(DOWN)) {
+                       tmp.set(camera.up).nor().scl(-deltaTime * velocity);
+                       camera.position.add(tmp);
+               }
+               camera.update(true);
+       }
+}
diff --git a/tests/gdx-tests-android/assets/data/g3d/tiles.png b/tests/gdx-tests-android/assets/data/g3d/tiles.png
new file mode 100644 (file)
index 0000000..8dd7472
Binary files /dev/null and b/tests/gdx-tests-android/assets/data/g3d/tiles.png differ
index 2ac1493..a17189c 100644 (file)
@@ -32,6 +32,7 @@ import com.badlogic.gdx.tests.g3d.Basic3DSceneTest;
 import com.badlogic.gdx.tests.g3d.Basic3DTest;\r
 import com.badlogic.gdx.tests.g3d.ModelLoaderTest;\r
 import com.badlogic.gdx.tests.g3d.ModelTest;\r
+import com.badlogic.gdx.tests.g3d.voxel.VoxelTest;\r
 import com.badlogic.gdx.tests.utils.GdxTest;\r
 \r
 public class LwjglDebugStarter {\r
@@ -43,7 +44,7 @@ public class LwjglDebugStarter {
 //             new SharedLibraryLoader("../../extensions/gdx-controllers/gdx-controllers-desktop/libs/gdx-controllers-desktop-natives.jar").load("gdx-controllers-desktop");\r
 //             new SharedLibraryLoader("../../gdx/libs/gdx-natives.jar").load("gdx");\r
 \r
-               GdxTest test = new MeshShaderTest();\r
+               GdxTest test = new VoxelTest();\r
                LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();\r
                config.useGL20 = test.needsGL20();\r
                config.width = 1024;\r
diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/PerlinNoiseGenerator.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/PerlinNoiseGenerator.java
new file mode 100644 (file)
index 0000000..8ad65e9
--- /dev/null
@@ -0,0 +1,128 @@
+
+package com.badlogic.gdx.tests.g3d.voxel;
+
+import com.badlogic.gdx.graphics.Color;
+import com.badlogic.gdx.graphics.Pixmap;
+import com.badlogic.gdx.graphics.Pixmap.Format;
+import com.badlogic.gdx.math.MathUtils;
+
+/** Adapted from <a href="http://devmag.org.za/2009/04/25/perlin-noise/">http://devmag.org.za/2009/04/25/perlin-noise/</a>
+ * @author badlogic */
+public class PerlinNoiseGenerator {
+       public static float[][] generateWhiteNoise (int width, int height) {
+               float[][] noise = new float[width][height];
+               for (int y = 0; y < height; y++) {
+                       for (int x = 0; x < width; x++) {
+                               noise[x][y] = MathUtils.random();
+                       }
+               }
+               return noise;
+       }
+
+       public static float interpolate (float x0, float x1, float alpha) {
+               return x0 * (1 - alpha) + alpha * x1;
+       }
+
+       public static float[][] generateSmoothNoise (float[][] baseNoise, int octave) {
+               int width = baseNoise.length;
+               int height = baseNoise[0].length;
+               float[][] smoothNoise = new float[width][height];
+
+               int samplePeriod = 1 << octave; // calculates 2 ^ k
+               float sampleFrequency = 1.0f / samplePeriod;
+               for (int i = 0; i < width; i++) {
+                       int sample_i0 = (i / samplePeriod) * samplePeriod;
+                       int sample_i1 = (sample_i0 + samplePeriod) % width; // wrap around
+                       float horizontal_blend = (i - sample_i0) * sampleFrequency;
+
+                       for (int j = 0; j < height; j++) {
+                               int sample_j0 = (j / samplePeriod) * samplePeriod;
+                               int sample_j1 = (sample_j0 + samplePeriod) % height; // wrap around
+                               float vertical_blend = (j - sample_j0) * sampleFrequency;
+                               float top = interpolate(baseNoise[sample_i0][sample_j0], baseNoise[sample_i1][sample_j0], horizontal_blend);
+                               float bottom = interpolate(baseNoise[sample_i0][sample_j1], baseNoise[sample_i1][sample_j1], horizontal_blend);
+                               smoothNoise[i][j] = interpolate(top, bottom, vertical_blend);
+                       }
+               }
+
+               return smoothNoise;
+       }
+
+       public static float[][] generatePerlinNoise (float[][] baseNoise, int octaveCount) {
+               int width = baseNoise.length;
+               int height = baseNoise[0].length;
+               float[][][] smoothNoise = new float[octaveCount][][]; // an array of 2D arrays containing
+               float persistance = 0.7f;
+
+               for (int i = 0; i < octaveCount; i++) {
+                       smoothNoise[i] = generateSmoothNoise(baseNoise, i);
+               }
+
+               float[][] perlinNoise = new float[width][height]; // an array of floats initialised to 0
+
+               float amplitude = 1.0f;
+               float totalAmplitude = 0.0f;
+
+               for (int octave = octaveCount - 1; octave >= 0; octave--) {
+                       amplitude *= persistance;
+                       totalAmplitude += amplitude;
+
+                       for (int i = 0; i < width; i++) {
+                               for (int j = 0; j < height; j++) {
+                                       perlinNoise[i][j] += smoothNoise[octave][i][j] * amplitude;
+                               }
+                       }
+               }
+
+               for (int i = 0; i < width; i++) {
+                       for (int j = 0; j < height; j++) {
+                               perlinNoise[i][j] /= totalAmplitude;
+                       }
+               }
+
+               return perlinNoise;
+       }
+
+       public static float[][] generatePerlinNoise (int width, int height, int octaveCount) {
+               float[][] baseNoise = generateWhiteNoise(width, height);
+               return generatePerlinNoise(baseNoise, octaveCount);
+       }
+
+       public static byte[] generateHeightMap(int width, int height, int min, int max, int octaveCount) {
+               float[][] baseNoise = generateWhiteNoise(width, height);
+               float[][] noise = generatePerlinNoise(baseNoise, octaveCount);
+               byte[] bytes = new byte[baseNoise.length * baseNoise[0].length];
+               int idx = 0;
+               int range = max - min;
+               for(int y = 0; y < height; y++) {
+                       for(int x = 0; x < width; x++) {
+                               bytes[idx++] = (byte)(noise[x][y] * range + min);
+                       }
+               }
+               return bytes;
+       }
+       
+       public static Pixmap generatePixmap(int width, int height, int min, int max, int octaveCount) {
+               byte[] bytes = generateHeightMap(width, height, min, max, octaveCount);
+               Pixmap pixmap = new Pixmap(width, height, Format.RGBA8888);
+               for(int i = 0, idx = 0; i < bytes.length; i++) {
+                       byte val = bytes[i];
+                       pixmap.getPixels().put(idx++, val);
+                       pixmap.getPixels().put(idx++, val);
+                       pixmap.getPixels().put(idx++, val);
+                       pixmap.getPixels().put(idx++, (byte)255);
+               }
+               return pixmap;
+       }
+       
+       public static void generateVoxels(VoxelWorld voxelWorld, int min, int max, int octaveCount) {
+               byte[] heightMap = PerlinNoiseGenerator.generateHeightMap(voxelWorld.voxelsX, voxelWorld.voxelsZ, min, max, 8);
+               int idx = 0;
+               for(int z = 0; z < voxelWorld.voxelsZ; z++) {
+                       for(int x = 0; x < voxelWorld.voxelsX; x++) {
+                               voxelWorld.setColumn(x, heightMap[idx++], z, (byte)1);
+//                             voxelWorld.set(x, heightMap[idx++], z, (byte)1);
+                       }
+               }
+       }
+}
diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/VoxelChunk.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/VoxelChunk.java
new file mode 100644 (file)
index 0000000..236fd66
--- /dev/null
@@ -0,0 +1,291 @@
+package com.badlogic.gdx.tests.g3d.voxel;
+
+import com.badlogic.gdx.math.Vector3;
+
+public class VoxelChunk {
+       public static final int VERTEX_SIZE = 6;
+       public final byte[] voxels;
+       public final int width;
+       public final int height;
+       public final int depth;
+       public final Vector3 offset = new Vector3();
+       private final int widthTimesHeight;
+       private final int topOffset;
+       private final int bottomOffset;
+       private final int leftOffset;
+       private final int rightOffset;
+       private final int frontOffset;
+       private final int backOffset;
+       
+       public VoxelChunk(int width, int height, int depth) {
+               this.voxels = new byte[width * height * depth];
+               this.width = width;
+               this.height = height;
+               this.depth = depth;
+               this.topOffset = width * depth;
+               this.bottomOffset = -width * depth;
+               this.leftOffset = -1;
+               this.rightOffset = 1;
+               this.frontOffset = - width;
+               this.backOffset = width;
+               this.widthTimesHeight = width * height;
+       }
+       
+       public byte get(int x, int y, int z) {
+               if(x < 0 || x >= width) return 0;
+               if(y < 0 || y >= height) return 0;
+               if(z < 0 || z >= depth) return 0;
+               return getFast(x, y, z);
+       }
+       
+       public byte getFast(int x, int y, int z) {
+               return voxels[x + z * width + y * widthTimesHeight];
+       }
+       
+       public void set(int x, int y, int z, byte voxel) {
+               if(x < 0 || x >= width) return;
+               if(y < 0 || y >= height) return;
+               if(z < 0 || z >= depth) return;
+               setFast(x, y, z, voxel);
+       }
+       
+       public void setFast(int x, int y, int z, byte voxel) {
+               voxels[x + z * width + y * widthTimesHeight] = voxel;
+       }
+       
+       /**
+        * Creates a mesh out of the chunk, returning the number of
+        * indices produced
+        * @return the number of vertices produced
+        */
+       public int calculateVertices(float[] vertices) {
+               int i = 0;
+               int vertexOffset = 0;
+               for(int y = 0; y < height; y++) {
+                       for(int z = 0; z < depth; z++) {
+                               for(int x = 0; x < width; x++, i++) {
+                                       byte voxel = voxels[i];
+                                       if(voxel == 0) continue;
+                                       
+                                       if(y < height - 1) {
+                                               if(voxels[i+topOffset] == 0) vertexOffset = createTop(offset, x, y, z, vertices, vertexOffset);
+                                       } else {
+                                               vertexOffset = createTop(offset, x, y, z, vertices, vertexOffset);
+                                       }
+                                       if(y > 0) {
+                                               if(voxels[i+bottomOffset] == 0) vertexOffset = createBottom(offset, x, y, z, vertices, vertexOffset);
+                                       } else {
+                                               vertexOffset = createBottom(offset, x, y, z, vertices, vertexOffset);
+                                       }
+                                       if(x > 0) {
+                                               if(voxels[i+leftOffset] == 0) vertexOffset = createLeft(offset, x, y, z, vertices, vertexOffset);
+                                       } else {
+                                               vertexOffset = createLeft(offset, x, y, z, vertices, vertexOffset);
+                                       }
+                                       if(x < width - 1) {
+                                               if(voxels[i+rightOffset] == 0) vertexOffset = createRight(offset, x, y, z, vertices, vertexOffset);
+                                       } else {
+                                               vertexOffset = createRight(offset, x, y, z, vertices, vertexOffset);
+                                       }
+                                       if(z > 0) {
+                                               if(voxels[i+frontOffset] == 0) vertexOffset = createFront(offset, x, y, z, vertices, vertexOffset);
+                                       } else {
+                                               vertexOffset = createFront(offset, x, y, z, vertices, vertexOffset);
+                                       }
+                                       if(z < depth - 1) {
+                                               if(voxels[i+backOffset] == 0) vertexOffset = createBack(offset, x, y, z, vertices, vertexOffset);
+                                       } else {
+                                               vertexOffset = createBack(offset, x, y, z, vertices, vertexOffset);
+                                       }
+                               }
+                       }
+               }
+               return vertexOffset / VERTEX_SIZE;
+       }
+       
+       public static int createTop(Vector3 offset, int x, int y, int z, float[] vertices, int vertexOffset) {
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               vertices[vertexOffset++] = 0;
+               return vertexOffset;
+       }
+       
+       public static int createBottom(Vector3 offset, int x, int y, int z, float[] vertices, int vertexOffset) {
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = -1;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = -1;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = -1;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = -1;
+               vertices[vertexOffset++] = 0;
+               return vertexOffset;
+       }
+       
+       public static int createLeft(Vector3 offset, int x, int y, int z, float[] vertices, int vertexOffset) {
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = -1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = -1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = -1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = -1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               return vertexOffset;
+       }
+       
+       public static int createRight(Vector3 offset, int x, int y, int z, float[] vertices, int vertexOffset) {
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               vertices[vertexOffset++] = 0;
+               
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               vertices[vertexOffset++] = 0;
+               return vertexOffset;
+       }
+       
+       public static int createFront(Vector3 offset, int x, int y, int z, float[] vertices, int vertexOffset) {
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 1;
+               return vertexOffset;
+       }
+       
+       public static int createBack(Vector3 offset, int x, int y, int z, float[] vertices, int vertexOffset) {
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = -1;
+               
+               vertices[vertexOffset++] = offset.x + x;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = -1;
+               
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y + 1;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = -1;
+               
+               vertices[vertexOffset++] = offset.x + x + 1;
+               vertices[vertexOffset++] = offset.y + y;
+               vertices[vertexOffset++] = offset.z + z + 1;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = 0;
+               vertices[vertexOffset++] = -1;
+               return vertexOffset;
+       }
+}
\ No newline at end of file
diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/VoxelTest.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/VoxelTest.java
new file mode 100644 (file)
index 0000000..c8bb934
--- /dev/null
@@ -0,0 +1,95 @@
+package com.badlogic.gdx.tests.g3d.voxel;
+
+import com.badlogic.gdx.Gdx;
+import com.badlogic.gdx.graphics.Color;
+import com.badlogic.gdx.graphics.GL10;
+import com.badlogic.gdx.graphics.GL20;
+import com.badlogic.gdx.graphics.Mesh;
+import com.badlogic.gdx.graphics.PerspectiveCamera;
+import com.badlogic.gdx.graphics.Texture;
+import com.badlogic.gdx.graphics.VertexAttribute;
+import com.badlogic.gdx.graphics.VertexAttributes.Usage;
+import com.badlogic.gdx.graphics.g2d.BitmapFont;
+import com.badlogic.gdx.graphics.g2d.SpriteBatch;
+import com.badlogic.gdx.graphics.g2d.TextureRegion;
+import com.badlogic.gdx.graphics.g3d.Model;
+import com.badlogic.gdx.graphics.g3d.ModelBatch;
+import com.badlogic.gdx.graphics.g3d.ModelInstance;
+import com.badlogic.gdx.graphics.g3d.lights.DirectionalLight;
+import com.badlogic.gdx.graphics.g3d.lights.Lights;
+import com.badlogic.gdx.graphics.g3d.materials.ColorAttribute;
+import com.badlogic.gdx.graphics.g3d.materials.Material;
+import com.badlogic.gdx.graphics.g3d.shaders.DefaultShader;
+import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
+import com.badlogic.gdx.graphics.g3d.utils.FirstPersonCameraController;
+import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
+import com.badlogic.gdx.math.MathUtils;
+import com.badlogic.gdx.math.Vector3;
+import com.badlogic.gdx.tests.utils.GdxTest;
+import com.badlogic.gdx.tests.utils.PerspectiveCamController;
+
+public class VoxelTest extends GdxTest {
+       SpriteBatch spriteBatch;
+       BitmapFont font;
+       ModelBatch modelBatch;
+       PerspectiveCamera camera;
+       Lights lights;
+       FirstPersonCameraController controller;
+       VoxelWorld voxelWorld;
+
+       @Override
+       public void create () {
+               spriteBatch = new SpriteBatch();
+               font = new BitmapFont();
+               modelBatch = new ModelBatch();
+               DefaultShader.defaultCullFace = GL20.GL_FRONT;
+               camera = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
+               camera.near = 1;
+               camera.far = 1000;
+               controller = new FirstPersonCameraController(camera);
+               Gdx.input.setInputProcessor(controller);
+               
+               lights = new Lights();
+               lights.ambientLight.set(0.4f, 0.4f, 0.4f, 1f);
+               lights.add(new DirectionalLight().set(1, 1, 1, -0.7f, -0.8f, -0.2f));
+               
+               Texture texture = new Texture(Gdx.files.internal("data/g3d/tiles.png"));
+               TextureRegion[][] tiles = TextureRegion.split(texture, 32, 32);
+               
+               MathUtils.random.setSeed(0);
+               voxelWorld = new VoxelWorld(tiles[0], 20, 4, 20);
+               PerlinNoiseGenerator.generateVoxels(voxelWorld, 0, 63, 3);
+//             voxelWorld.setCube(0, 0, 0, 16, 16, 16, (byte)1);
+               float camX = voxelWorld.voxelsX / 2f;
+               float camZ = voxelWorld.voxelsZ / 2f;
+               float camY = voxelWorld.getHighest(camX, camZ);
+               camera.position.set(camX, camY, camZ);
+       }
+
+       @Override
+       public void render () {
+               Gdx.gl.glClearColor(0.4f, 0.4f, 0.4f, 1f);
+               Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);
+               modelBatch.begin(camera);
+               modelBatch.render(voxelWorld, lights);
+               modelBatch.end();
+               controller.update();
+               
+               spriteBatch.begin();
+               font.draw(spriteBatch, "fps: " + Gdx.graphics.getFramesPerSecond(), 0, 20);
+               spriteBatch.end();
+       }
+       
+       @Override
+       public void resize (int width, int height) {
+               spriteBatch.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
+               camera.viewportWidth = width;
+               camera.viewportHeight = height;
+               camera.update();
+       }
+
+       @Override
+       public boolean needsGL20 () {
+               return true;
+       }
+}
diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/VoxelWorld.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/g3d/voxel/VoxelWorld.java
new file mode 100644 (file)
index 0000000..cc47ab6
--- /dev/null
@@ -0,0 +1,181 @@
+package com.badlogic.gdx.tests.g3d.voxel;
+
+import com.badlogic.gdx.graphics.Color;
+import com.badlogic.gdx.graphics.GL10;
+import com.badlogic.gdx.graphics.GL20;
+import com.badlogic.gdx.graphics.Mesh;
+import com.badlogic.gdx.graphics.VertexAttribute;
+import com.badlogic.gdx.graphics.g2d.TextureRegion;
+import com.badlogic.gdx.graphics.g3d.Renderable;
+import com.badlogic.gdx.graphics.g3d.RenderableProvider;
+import com.badlogic.gdx.graphics.g3d.materials.ColorAttribute;
+import com.badlogic.gdx.graphics.g3d.materials.Material;
+import com.badlogic.gdx.math.MathUtils;
+import com.badlogic.gdx.utils.Array;
+import com.badlogic.gdx.utils.Pool;
+
+public class VoxelWorld implements RenderableProvider {
+       public static final int CHUNK_SIZE_X = 16;
+       public static final int CHUNK_SIZE_Y = 16;
+       public static final int CHUNK_SIZE_Z = 16;
+       
+       public final VoxelChunk[] chunks;
+       public final Mesh[] meshes;
+       public final Material[] materials;
+       public final boolean[] dirty;
+       public final int[] numVertices;
+       public float[] vertices;
+       public final int chunksX;
+       public final int chunksY;
+       public final int chunksZ;
+       public final int voxelsX;
+       public final int voxelsY;
+       public final int voxelsZ;
+       private final TextureRegion[] tiles;
+       
+       public VoxelWorld(TextureRegion[] tiles, int chunksX, int chunksY, int chunksZ) {
+               this.tiles = tiles;
+               this.chunks = new VoxelChunk[chunksX * chunksY * chunksZ];
+               this.chunksX = chunksX;
+               this.chunksY = chunksY;
+               this.chunksZ = chunksZ;
+               this.voxelsX = chunksX * CHUNK_SIZE_X;
+               this.voxelsY = chunksY * CHUNK_SIZE_Y;
+               this.voxelsZ = chunksZ * CHUNK_SIZE_Z;
+               int i = 0;
+               for(int y = 0; y < chunksY; y++) {
+                       for(int z = 0; z < chunksZ; z++) {
+                               for(int x = 0; x < chunksX; x++) {
+                                       VoxelChunk chunk = new VoxelChunk(CHUNK_SIZE_X, CHUNK_SIZE_Y, CHUNK_SIZE_Z);
+                                       chunk.offset.set(x * CHUNK_SIZE_X, y * CHUNK_SIZE_Y, z * CHUNK_SIZE_Z);
+                                       chunks[i++] = chunk;
+                               }
+                       }
+               }
+               int len = CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z * 6 * 6 / 3;
+               short[] indices = new short[len];
+               short j = 0;
+               for (i = 0; i < len; i += 6, j += 4) {
+                       indices[i + 0] = (short)(j + 0);
+                       indices[i + 1] = (short)(j + 1);
+                       indices[i + 2] = (short)(j + 2);
+                       indices[i + 3] = (short)(j + 2);
+                       indices[i + 4] = (short)(j + 3);
+                       indices[i + 5] = (short)(j + 0);
+               }
+               this.meshes = new Mesh[chunksX * chunksY * chunksZ];
+               for(i = 0; i < meshes.length; i++) {
+                       meshes[i] = new Mesh(true, 
+                                                                               CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z * 6 * 4, 
+                                                                               CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z * 36 / 3,
+                                                                               VertexAttribute.Position(), VertexAttribute.Normal());
+                       meshes[i].setIndices(indices);
+               }
+               this.dirty = new boolean[chunksX * chunksY * chunksZ];
+               for(i = 0; i < dirty.length; i++) dirty[i] = true;
+               
+               this.numVertices = new int[chunksX * chunksY * chunksZ];
+               for(i = 0; i < numVertices.length; i++) numVertices[i] = 0;
+               
+               this.vertices = new float[VoxelChunk.VERTEX_SIZE * 6 * CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
+               this.materials = new Material[chunksX * chunksY * chunksZ];
+               for(i = 0; i < materials.length; i++) {
+                       materials[i] = new Material(new ColorAttribute(ColorAttribute.Diffuse, MathUtils.random(0.5f, 1f), MathUtils.random(0.5f, 1f), MathUtils.random(0.5f, 1f), 1));
+               }
+       }
+       
+       public void set(float x, float y, float z, byte voxel) {
+               int ix = (int)x;
+               int iy = (int)y;
+               int iz = (int)z;
+               int chunkX = ix / CHUNK_SIZE_X;
+               if(chunkX < 0 || chunkX >= chunksX) return;
+               int chunkY = iy / CHUNK_SIZE_Y;
+               if(chunkY < 0 || chunkY >= chunksY) return;
+               int chunkZ = iz / CHUNK_SIZE_Z;
+               if(chunkZ < 0 || chunkZ >= chunksZ) return;
+               chunks[chunkX + chunkZ * chunksX + chunkY * chunksX * chunksZ].set(ix % CHUNK_SIZE_X, iy % CHUNK_SIZE_Y, iz % CHUNK_SIZE_Z, voxel);
+       }
+       
+       public byte get(float x, float y, float z) {
+               int ix = (int)x;
+               int iy = (int)y;
+               int iz = (int)z;
+               int chunkX = ix / CHUNK_SIZE_X;
+               if(chunkX < 0 || chunkX >= chunksX) return 0;
+               int chunkY = iy / CHUNK_SIZE_Y;
+               if(chunkY < 0 || chunkY >= chunksY) return 0;
+               int chunkZ = iz / CHUNK_SIZE_Z;
+               if(chunkZ < 0 || chunkZ >= chunksZ) return 0;
+               return chunks[chunkX + chunkZ * chunksX + chunkY * chunksX * chunksZ].get(ix % CHUNK_SIZE_X, iy % CHUNK_SIZE_Y, iz % CHUNK_SIZE_Z);
+       }
+       
+       public float getHighest (float x, float z) {
+               int ix = (int)x;
+               int iz = (int)z;
+               if(ix < 0 || ix >= voxelsX) return 0;
+               if(iz < 0 || iz >= voxelsZ) return 0;
+               // FIXME optimize
+               for(int y = voxelsY - 1; y > 0; y--) {
+                       if(get(ix, y, iz) > 0) return y + 1;
+               }
+               return 0;
+       }
+       
+       public void setColumn(float x, float y, float z, byte voxel) {
+               int ix = (int)x;
+               int iy = (int)y;
+               int iz = (int)z;
+               if(ix < 0 || ix >= voxelsX) return;
+               if(iy < 0 || iy >= voxelsY) return;
+               if(iz < 0 || iz >= voxelsZ) return;
+               // FIXME optimize
+               for(; iy > 0; iy--) {
+                       set(ix, iy, iz, voxel);
+               }
+       }
+       
+       public void setCube(float x, float y, float z, float width, float height, float depth, byte voxel) {
+               int ix = (int)x;
+               int iy = (int)y;
+               int iz = (int)z;
+               int iwidth = (int)width;
+               int iheight = (int)height;
+               int idepth = (int)depth;
+               int startX = Math.max(ix, 0);
+               int endX = Math.min(voxelsX, ix + iwidth);
+               int startY = Math.max(iy, 0);
+               int endY = Math.min(voxelsY, iy + iheight);
+               int startZ = Math.max(iz, 0);
+               int endZ = Math.min(voxelsZ, iz + idepth);
+               // FIXME optimize
+               for(iy = startY; iy < endY; iy++) {
+                       for(iz = startZ; iz < endZ; iz++) {
+                               for(ix = startX; ix < endX; ix++) {
+                                       set(ix, iy, iz, voxel);
+                               }
+                       }
+               }       
+       }
+       
+       @Override
+       public void getRenderables (Array<Renderable> renderables, Pool<Renderable> pool) {
+               for(int i = 0; i < chunks.length; i++) {
+                       VoxelChunk chunk = chunks[i];
+                       Mesh mesh = meshes[i];
+                       if(dirty[i]) {
+                               int numVerts = chunk.calculateVertices(vertices);
+                               numVertices[i] = numVerts / 4 * 6;
+                               mesh.setVertices(vertices, 0, numVerts * VoxelChunk.VERTEX_SIZE);
+                               dirty[i] = false;
+                       }
+                       Renderable renderable = pool.obtain();
+                       renderable.material = materials[i];
+                       renderable.mesh = mesh;
+                       renderable.meshPartOffset = 0;
+                       renderable.meshPartSize = numVertices[i];
+                       renderable.primitiveType = GL20.GL_TRIANGLES;
+                       renderables.add(renderable);
+               }
+       }
+}