/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package net.anzix.fsz.voxelworld;
import net.anzix.fsz.voxelworld.layers.LayerBlock;
import com.ardor3d.bounding.BoundingBox;
import com.ardor3d.math.FastMath;
import com.ardor3d.math.Vector3;
import com.ardor3d.math.Vector4;
import com.ardor3d.math.type.ReadOnlyVector3;
import com.ardor3d.scenegraph.Mesh;
import com.ardor3d.scenegraph.MeshData;
import com.ardor3d.util.geom.BufferUtils;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import net.anzix.fsz.voxelworld.octree.OctreeRoot;
/**
*
* @author kovacsandras
*/
public class VoxelBlockGenerator extends AbstractBlockGenerator {
public enum GeneratorStatus { READY, INITIALIZATION, DENSITY_GENERATION, MESH_GENERATION, ADDING_MESH_TO_SCENE, FINALIZING }
private GeneratorStatus status;
public GeneratorStatus getStatus() {
return status;
}
@Override
public String toString() {
return status.toString();
}
private VoxelBlock block;
private VoxelBlockPool pool;
//private DetailLevel level;
private ArrayList<Quad> quads;
private float densities[][][];
private Feature densitySources [][][];
private Vector3 basePosition;
private Voxel voxels[][][];
public VoxelBlockGenerator(VoxelBlockPool pool) {
status = GeneratorStatus.READY;
this.pool = pool;
block = null;
quads = new ArrayList<Quad>();
int blockSize = VoxelWorld.BLOCK_SIZE;
densities = new float[blockSize + 2][blockSize + 2][blockSize + 2];
densitySources = new Feature[blockSize + 2][blockSize + 2][blockSize + 2];
voxels = new Voxel[blockSize + 1][blockSize + 1][blockSize + 1];
}
@Override
public boolean tryToAttachABlock(AbstractBlock block) {
if (this.block == null) {
this.block = (VoxelBlock)block;
return true;
} else {
return false; //busy
}
}
@Override
public AbstractBlock getBlock() {
return block;
}
public void init() {
int blockSize = VoxelWorld.BLOCK_SIZE;
for (int x = 0; x < blockSize + 1; x++) {
for (int y = 0; y < blockSize + 1; y++) {
for (int z = 0; z < blockSize + 1; z++) {
voxels[x][y][z] = null;
}
}
}
for (int x = 0; x < blockSize + 2; x++) {
for (int y = 0; y < blockSize + 2; y++) {
for (int z = 0; z < blockSize + 2; z++) {
densities[x][y][z] = -1.0f;
densitySources[x][y][z] = null;
}
}
}
quads.clear();
voxelCounter = 0;
basePosition = block.getPosition().getAsWorldCoords(pool.getLevel());
}
@Override
public void generate() {
if (block == null || block.getStatus() != VoxelBlock.BlockStatus.UNDER_GENERATION) {
VoxelBlockPool.logInvalidBlockStatus(block, "UNDER_GENERATION");
return;
}
try {
ArrayList<Feature> localFeatures = pool.getWorld().getLocalFeatures(block, pool.getLevel());
if (localFeatures.size() > 0) {
status = GeneratorStatus.INITIALIZATION;
init();
status = GeneratorStatus.DENSITY_GENERATION;
LayerBlock layerBlock = block.getRelatedLayerBlock();
Iterator it = localFeatures.iterator();
while(it.hasNext()) {
Feature feature = (Feature)it.next();
feature.applyDensities(basePosition, densities, densitySources,pool.getLevel(), layerBlock);
}
status = GeneratorStatus.MESH_GENERATION;
generateQuadsAndVoxels();
status = GeneratorStatus.ADDING_MESH_TO_SCENE;
addModelToScene();
Iterator it2 = localFeatures.iterator();
while(it2.hasNext()) {
Feature feature = (Feature)it2.next();
feature.attachAdditionalNonVoxelModels(pool.getLevel(),block);
}
}
status = GeneratorStatus.FINALIZING;
pool.finalizeBlockGeneration(block, true);
} catch (Exception ex) {
VoxelWorld.logger.log(Level.SEVERE,"Unhandled exception while generating a block.");
status = GeneratorStatus.FINALIZING;
pool.finalizeBlockGeneration(block, false);
} catch (OutOfMemoryError err) {
VoxelWorld.logger.log(Level.SEVERE,"Out of memory errorr while generating a block.");
status = GeneratorStatus.FINALIZING;
pool.finalizeBlockGeneration(block, false);
}
status = GeneratorStatus.READY;
this.block = null; //i am ready to work again
}
/*public void generateTile(double[][] heightMap) {
try {
ArrayList<Feature> localFeatures = pool.getWorld().getLocalFeatures(block, pool.getLevel());
if (localFeatures.size() > 0) {
status = GeneratorStatus.INITIALIZATION;
init();
status = GeneratorStatus.DENSITY_GENERATION;
LayerBlock layerBlock = block.getRelatedLayerBlock();
Iterator it = localFeatures.iterator();
while(it.hasNext()) {
Feature feature = (Feature)it.next();
feature.applyDensities(basePosition, densities, densitySources,pool.getLevel(), layerBlock);
}
double maxHeight = 32;
status = GeneratorStatus.ADDING_MESH_TO_SCENE;
for (int x = 0; x< VoxelWorld.BLOCK_SIZE; x++)
for (int z = 0; z< VoxelWorld.BLOCK_SIZE; z++)
{
if (heightMap[x][z] < )
}
}
} catch (Exception ex) {
VoxelWorld.logger.log(Level.SEVERE,"Unhandled exception while generating a block.");
status = GeneratorStatus.FINALIZING;
pool.finalizeBlockGeneration(block, false);
} catch (OutOfMemoryError err) {
VoxelWorld.logger.log(Level.SEVERE,"Out of memory errorr while generating a block.");
status = GeneratorStatus.FINALIZING;
pool.finalizeBlockGeneration(block, false);
}
status = GeneratorStatus.READY;
this.block = null; //i am ready to work again
} */
private transient int i1x, i2x, i3x, i4x;
private transient int i1y, i2y, i3y, i4y;
private transient int i1z, i2z, i3z, i4z;
private transient float baseDensity, otherDensity;
private transient Vector3 crossingPoint = new Vector3();
private transient double levelScale = 0;
private transient double voxelArraySize = 0;
public void generateQuadsAndVoxels() {
int blockSize = VoxelWorld.BLOCK_SIZE;
levelScale = pool.getLevel().getScale();
voxelArraySize = blockSize + 1;
for (int x = 0; x < blockSize + 2; x++) {
for (int y = 0; y < blockSize + 2; y++) {
for (int z = 0; z < blockSize + 2; z++) {
baseDensity = densities[x][y][z];
if (baseDensity > 0.0) {
if (x < blockSize + 1) {
otherDensity = densities[x + 1][y][z];
if (otherDensity <= 0) {
setCoordsXPlus(x, y, z);
addQuad();
}
}
if (y < blockSize + 1) {
otherDensity = densities[x][y + 1][z];
if (otherDensity <= 0) {
setCoordsYPlus(x, y, z);
addQuad();
}
}
if (z < blockSize + 1) {
otherDensity = densities[x][y][z + 1];
if (otherDensity <= 0) {
setCoordsZPlus(x, y, z);
addQuad();
}
}
} else if (baseDensity < 0.0) {
if (x < blockSize + 1) {
otherDensity = densities[x + 1][y][z];
if (otherDensity >= 0) {
setCoordsXMinus(x, y, z);
addQuad();
}
}
if (y < blockSize + 1) {
otherDensity = densities[x][y + 1][z];
if (otherDensity >= 0) {
setCoordsYMinus(x, y, z);
addQuad();
}
}
if (z < blockSize + 1) {
otherDensity = densities[x][y][z + 1];
if (otherDensity >= 0) {
setCoordsZMinus(x, y, z);
addQuad();
}
}
} else {
if (x < blockSize + 1) {
otherDensity = densities[x + 1][y][z];
if (otherDensity < 0) {
setCoordsXPlus(x, y, z);
addQuad();
} else if (otherDensity > 0) {
setCoordsXMinus(x, y, z);
addQuad();
}
}
if (y < blockSize + 1) {
otherDensity = densities[x][y + 1][z];
if (otherDensity < 0) {
setCoordsYPlus(x, y, z);
addQuad();
} else if (otherDensity > 0) {
setCoordsYMinus(x, y, z);
addQuad();
}
}
if (z < blockSize + 1) {
otherDensity = densities[x][y][z + 1];
if (otherDensity < 0) {
setCoordsZPlus(x, y, z);
addQuad();
} else if (otherDensity < 0) {
setCoordsZMinus(x, y, z);
addQuad();
}
}
}
}
}
}
}
public void setCoordsXPlus(int x, int y, int z) {
i1x = x;
i1y = y;
i1z = z;
i2x = x;
i2y = (y - 1);
i2z = z;
i3x = x;
i3y = (y - 1);
i3z = (z - 1);
i4x = x;
i4y = y;
i4z = (z - 1);
double f = baseDensity / (baseDensity - otherDensity);
crossingPoint.set(x + f, y, z);
}
public void setCoordsXMinus(int x, int y, int z) {
i1x = x;
i1y = y;
i1z = z;
i2x = x;
i2y = y;
i2z = (z - 1);
i3x = x;
i3y = (y - 1);
i3z = (z - 1);
i4x = x;
i4y = (y - 1);
i4z = z;
double f = -baseDensity / (-baseDensity + otherDensity);
crossingPoint.set(x + f, y, z);
}
public void setCoordsYPlus(int x, int y, int z) {
i1x = x;
i1y = y;
i1z = z;
i2x = x;
i2y = y;
i2z = (z - 1);
i3x = (x - 1);
i3y = y;
i3z = (z - 1);
i4x = (x - 1);
i4y = y;
i4z = z;
double f = baseDensity / (baseDensity - otherDensity);
crossingPoint.set(x, y + f, z);
}
public void setCoordsYMinus(int x, int y, int z) {
i1x = x;
i1y = y;
i1z = z;
i2x = (x - 1);
i2y = y;
i2z = z;
i3x = (x - 1);
i3y = y;
i3z = (z - 1);
i4x = x;
i4y = y;
i4z = (z - 1);
double f = -baseDensity / (-baseDensity + otherDensity);
crossingPoint.set(x, y + f, z);
}
public void setCoordsZPlus(int x, int y, int z) {
i1x = x;
i1y = y;
i1z = z;
i2x = (x - 1);
i2y = y;
i2z = z;
i3x = (x - 1);
i3y = (y - 1);
i3z = z;
i4x = x;
i4y = (y - 1);
i4z = z;
double f = baseDensity / (baseDensity - otherDensity);
crossingPoint.set(x, y, z + f);
}
public void setCoordsZMinus(int x, int y, int z) {
i1x = x;
i1y = y;
i1z = z;
i2x = x;
i2y = (y - 1);
i2z = z;
i3x = (x - 1);
i3y = (y - 1);
i3z = z;
i4x = (x - 1);
i4y = y;
i4z = z;
double f = -baseDensity / (-baseDensity + otherDensity);
crossingPoint.set(x, y, z + f);
}
private void addQuad() {
Voxel voxel1 = addVoxel(i1x, i1y, i1z);
Voxel voxel2 = addVoxel(i2x, i2y, i2z);
Voxel voxel3 = addVoxel(i3x, i3y, i3z);
Voxel voxel4 = addVoxel(i4x, i4y, i4z);
if (voxel1 != null && voxel2 != null
&& voxel3 != null && voxel4 != null) {
quads.add(new Quad(voxel1, voxel2, voxel3, voxel4));
}
}
private int voxelCounter;
private Voxel addVoxel(int x, int y, int z) {
if (x>=0 && y>=0 && z>=0 && x<voxelArraySize && y<voxelArraySize && z<voxelArraySize) {
Voxel voxel = voxels[x][y][z];
Vector3 vertexPosition = new Vector3(crossingPoint).multiplyLocal(levelScale).addLocal(basePosition);
Vector4 material = new Vector4(x/32.0f,y/32.0f,z/32.0f,1.0);
if (voxel == null) {
voxels[x][y][z] = new Voxel(vertexPosition,material,voxelCounter);
voxelCounter++;
} else {
voxels[x][y][z].add(vertexPosition,material);
}
return voxels[x][y][z];
} else {
//nem kell vele számolni
return null;
}
}
public Vector3 computeVoxelNormal(int x, int y, int z, ReadOnlyVector3 position) {
Vector3 local = new Vector3(position);
local.subtractLocal(basePosition);
local.divideLocal(pool.getLevel().getScale());
try {
float d000 = densities[x][y][z];
float d100 = densities[x + 1][y][z];
float d010 = densities[x][y + 1][z];
float d110 = densities[x + 1][y + 1][z];
float d001 = densities[x][y][z + 1];
float d101 = densities[x + 1][y][z + 1];
float d011 = densities[x][y + 1][z + 1];
float d111 = densities[x + 1][y + 1][z + 1];
double sx1 = local.getX() - x;
double sx0 = 1.0 - sx1;
double sy1 = local.getY() - y;
double sy0 = 1.0 - sy1;
double sz1 = local.getZ() - z;
double sz0 = 1.0 - sz1;
double dx0 = (d100 - d000) * sy0 + (d110 - d010) * sy1;
double dx1 = (d101 - d001) * sy0 + (d111 - d011) * sy1;
double dx = dx0 * sz0 + dx1 * sz1;
double dy0 = (d010 - d000) * sz0 + (d011 - d001) * sz1;
double dy1 = (d110 - d100) * sz0 + (d111 - d101) * sz1;
double dy = dy0 * sx0 + dy1 * sx1;
double dz0 = (d001 - d000) * sx0 + (d101 - d100) * sx1;
double dz1 = (d011 - d010) * sx0 + (d111 - d110) * sx1;
double dz = dz0 * sy0 + dz1 * sy1;
Vector3 normal = new Vector3(-dx, -dy, -dz);
normal.normalizeLocal();
return normal;
} catch (Exception ex) {
VoxelWorld.logger.log(Level.SEVERE,"NORMÁLGENERÁLÁSI HIBA");
return new Vector3(0,1,0);
}
}
public void addModelToScene() {
OctreeRoot octree = block.getOctree();
Mesh mesh = new Mesh();
MeshData meshData = mesh.getMeshData();
int numOfQuads = quads.size();
int numOfVoxels = voxelCounter;
FloatBuffer vertexBuffer = BufferUtils.createVector3Buffer(numOfVoxels);
IntBuffer indexBuffer = BufferUtils.createIntBuffer(numOfQuads * 6);
FloatBuffer normalBuffer = BufferUtils.createVector3Buffer(numOfVoxels);
// FloatBuffer texCoordBuffer = BufferUtils.createVector2Buffer(numOfVoxels);
FloatBuffer colorBuffer = BufferUtils.createColorBuffer(numOfVoxels);
ArrayList<Integer> verticesWithGrass = new ArrayList<Integer>();
int blockSize = VoxelWorld.BLOCK_SIZE;
for (int x = 0; x < blockSize + 1; x++) {
for (int y = 0; y < blockSize + 1; y++) {
for (int z = 0; z < blockSize + 1; z++) {
Voxel voxel = voxels[x][y][z];
if (voxel != null) {
voxel.summarize();
octree.insertVertex(x, y, z, voxel.index);
int i3 = voxel.index * 3;
vertexBuffer.put(i3+0, voxel.vertexPosition.getXf());
vertexBuffer.put(i3+1, voxel.vertexPosition.getYf());
vertexBuffer.put(i3+2, voxel.vertexPosition.getZf());
Vector3 normal = computeVoxelNormal(x,y,z,voxel.vertexPosition);
normalBuffer.put(i3+0, normal.getXf());
normalBuffer.put(i3+1, normal.getYf());
normalBuffer.put(i3+2, normal.getZf());
int i4 = voxel.index * 4;
colorBuffer.put(i4+0, voxel.materials.getXf());
colorBuffer.put(i4+1, voxel.materials.getYf());
colorBuffer.put(i4+2, voxel.materials.getZf());
colorBuffer.put(i4+3, voxel.materials.getWf());
///GRASS
if (voxel.vertexPosition.getY() > 33 && voxel.vertexPosition.getYf() < 210 && normal.getY() > 0.7)
verticesWithGrass.add(voxel.index);
}
}
}
}
for (int q = 0; q < quads.size(); q++) {
Quad quad = quads.get(q);
indexBuffer.put(quad.voxel1.index);
indexBuffer.put(quad.voxel2.index);
indexBuffer.put(quad.voxel3.index);
indexBuffer.put(quad.voxel1.index);
indexBuffer.put(quad.voxel3.index);
indexBuffer.put(quad.voxel4.index);
}
indexBuffer.rewind();
//colorBuffer.flip();
meshData.setVertexBuffer(vertexBuffer);
meshData.setIndexBuffer(indexBuffer);
meshData.setNormalBuffer(normalBuffer);
meshData.setColorBuffer(colorBuffer);
// meshData.setColorBuffer(colorBuffer);
block.numOfTriangles = quads.size()*2;
block.numOfVertices = voxelCounter;
Vector3 center = basePosition;
double halfSize = pool.getLevel().getScale() * VoxelWorld.BLOCK_SIZE / 2.0;
center.addLocal(halfSize, halfSize, halfSize);
mesh.setModelBound(new BoundingBox(center, halfSize, halfSize, halfSize));
block.getVoxelNode().attachChild(mesh);
//////////GRASS///////////////////
if (pool.getLevel().getLevelID() == 0) {
int numOfGrasses = verticesWithGrass.size();
Mesh grassMesh = new Mesh();
MeshData grassMeshData = grassMesh.getMeshData();
FloatBuffer grassVertexBuffer = BufferUtils.createVector3Buffer(numOfGrasses * 4);
IntBuffer grassIndexBuffer = BufferUtils.createIntBuffer(numOfGrasses * 6);
FloatBuffer grassNormalBuffer = BufferUtils.createVector3Buffer(numOfGrasses * 4);
FloatBuffer grassTexCoordBuffer = BufferUtils.createVector2Buffer(numOfGrasses * 4);
//FloatBuffer grassBasePositionBuffer = BufferUtils.createVector2Buffer(numOfGrasses * 4);
for (int i=0, size = verticesWithGrass.size(); i<size; i++) {
int i3 = verticesWithGrass.get(i) * 3;
int i4 = i * 4;
float topY = 2.5f + (float) Math.random();
float bottomY = -0.1f;
float halfEidth = 1.5f + (float) Math.random();
//double alpha = Math.random()*Math.PI;
//float offsetX = (FastMath.sin(alpha) * halfEidth);
//float offsetZ = (float)(FastMath.cos(alpha) * halfEidth);
float offsetX = (float)Math.random()-0.5f;
float offsetZ = (float)Math.random()-0.5f;
grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
grassVertexBuffer.put(vertexBuffer.get(i3 +1) + bottomY);
grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);
grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
grassVertexBuffer.put(vertexBuffer.get(i3 +1) + topY);
grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);
grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
grassVertexBuffer.put(vertexBuffer.get(i3 +1) + topY);
grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);
grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
grassVertexBuffer.put(vertexBuffer.get(i3 +1) + bottomY);
grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);
/*grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
grassVertexBuffer.put(vertexBuffer.get(i3 +1) + bottomY);
grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);
grassVertexBuffer.put(vertexBuffer.get(i3 +0) + offsetX);
grassVertexBuffer.put(vertexBuffer.get(i3 +1) + topY);
grassVertexBuffer.put(vertexBuffer.get(i3 +2) + offsetZ);
grassVertexBuffer.put(vertexBuffer.get(i3 +0) - offsetX);
grassVertexBuffer.put(vertexBuffer.get(i3 +1) + topY);
grassVertexBuffer.put(vertexBuffer.get(i3 +2) - offsetZ);
grassVertexBuffer.put(vertexBuffer.get(i3 +0) - offsetX);
grassVertexBuffer.put(vertexBuffer.get(i3 +1) + bottomY);
grassVertexBuffer.put(vertexBuffer.get(i3 +2) - offsetZ);*/
float nx = normalBuffer.get(i3 +0);
float ny = normalBuffer.get(i3 +1);
float nz = normalBuffer.get(i3 +2);
grassNormalBuffer.put(nx);
grassNormalBuffer.put(ny);
grassNormalBuffer.put(nz);
grassNormalBuffer.put(nx);
grassNormalBuffer.put(ny);
grassNormalBuffer.put(nz);
grassNormalBuffer.put(nx);
grassNormalBuffer.put(ny);
grassNormalBuffer.put(nz);
grassNormalBuffer.put(nx);
grassNormalBuffer.put(ny);
grassNormalBuffer.put(nz);
grassTexCoordBuffer.put(0);
grassTexCoordBuffer.put(0);
grassTexCoordBuffer.put(0);
grassTexCoordBuffer.put(1);
grassTexCoordBuffer.put(1);
grassTexCoordBuffer.put(1);
grassTexCoordBuffer.put(1);
grassTexCoordBuffer.put(0);
grassIndexBuffer.put(i4);
grassIndexBuffer.put(i4+1);
grassIndexBuffer.put(i4+2);
grassIndexBuffer.put(i4);
grassIndexBuffer.put(i4+2);
grassIndexBuffer.put(i4+3);
}
//grassIndexBuffer.rewind();
//colorBuffer.flip();
grassMeshData.setVertexBuffer(grassVertexBuffer);
grassMeshData.setIndexBuffer(grassIndexBuffer);
grassMeshData.setNormalBuffer(grassNormalBuffer);
grassMeshData.setTextureBuffer(grassTexCoordBuffer,0);
grassMesh.setModelBound(new BoundingBox(center, halfSize, halfSize, halfSize));
block.getGrassNode().attachChild(grassMesh);
}
}
}