/******************************************************************************* * Copyright 2011 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.badlogic.gdx.tests.g3d.voxel; 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.Material; import com.badlogic.gdx.graphics.g3d.Renderable; import com.badlogic.gdx.graphics.g3d.RenderableProvider; import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute; 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; public int renderedChunks; public int numChunks; 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.numChunks = chunksX * chunksY * 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) { renderedChunks = 0; 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; } if (numVertices[i] == 0) continue; Renderable renderable = pool.obtain(); renderable.material = materials[i]; renderable.meshPart.mesh = mesh; renderable.meshPart.offset = 0; renderable.meshPart.size = numVertices[i]; renderable.meshPart.primitiveType = GL20.GL_TRIANGLES; renderables.add(renderable); renderedChunks++; } } }