我目前正在研究基于体素的绘图系统的渲染系统(类似于Minecraft)。现在,我只想尝试一次渲染一个块。块由块的3d数组组成,每个块由6个面(北,东,南,西,顶部,底部)组成。到目前为止一切都很好。
在研究在屏幕上渲染块的最佳方法时,我遇到了一种称为贪婪网格的技术。不幸的是,最佳版本的数学超出了我的范围,因此我正在实现一个名为naive greedy meshing的简化版本。这个想法非常简单:对于每一方,遍历块的每一层,并尝试构造由相同块类型组成的最大可能网格(在这种情况下,10 * 10块立方体变为1个网格,而不是100个立方体网格)。我这样做是通过为每一层获得每层的2d切片,然后将其与掩码(简单的2d布尔数组)进行比较,该掩码跟踪已经屏蔽的块。最终结果很有效,除了一旦设置了掩模之后,它不会考虑较低z级别的空气阻塞。 可以看出,在大块的下半部分的中间有一个空气挡块,后面有一个污垢块。从北方来看,这完美地起作用,因为对于空气挡块的位置,掩模没有设置为真。当它到达下一个z级(它后面的那个)时,它会为掩码中的缺失块创建一个网格。然而,从东侧,整个面具填充在第一层(下半部分有岩石,上半部分有空气,总共2个四边形)所以一旦它与空气块达到z级别,它将被忽略,因为方面已经掩盖了。
如何绕过这个问题,哪一方可能已经正确地进行了网格划分,但是该层后面还有空格,还需要网格划分?我不能简单地清除每个z级别的蒙版,因为这会导致不可见的图层被网格化。
区块代码:
public class Block {
private BlockType type;
private BlockFace[] faces;
public BlockType getType() {
return type;
}
public BlockFace[] getFaces() {
return faces;
}
public Block(BlockType type) {
this.type = type;
this.faces = new BlockFace[6];
initFaces();
}
Block(Block block) {
this(block.getType());
}
private void initFaces() {
int idx=0;
for(BlockSide side : BlockSide.values()) {
this.faces[idx] = new BlockFace(type, side);
idx++;
}
}
}
块的代码:
public class Chunk {
public static int CHUNK_SIZE = 10;
public static int BLOCK_SIZE = 1;
private Block[][][] blocks;
public Chunk() {
this.blocks = new Block[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE];
}
public void fillChunk(Block block) {
for(int x=0; x<CHUNK_SIZE; x++) {
for(int y=0; y<CHUNK_SIZE; y++) {
for(int z=0; z<CHUNK_SIZE; z++) {
blocks[x][y][z] = new Block(block);
}
}
}
}
public void setBlock(Block block, int x, int y, int z) {
blocks[x][y][z] = new Block(block);
}
public Block getBlock(int x, int y, int z) {
return this.blocks[x][y][z];
}
public Block getBlockRelativeToSide(BlockSide side, int x, int y, int z) {
switch(side) {
case NORTH:
return getBlock(CHUNK_SIZE-1-x, y, z);
case SOUTH:
return getBlock(x, y, CHUNK_SIZE-1-z);
case EAST:
return getBlock(CHUNK_SIZE-1-z, y, CHUNK_SIZE-1-x);
case WEST:
return getBlock(z, y, x);
case TOP:
return getBlock(x, CHUNK_SIZE-1-z, CHUNK_SIZE-1-y);
case BOTTOM:
return getBlock(x, z, y);
}
return null;
}
public void print(int y) {
System.out.println("Printing for level:" + y);
for(int z=0; z<CHUNK_SIZE; z++) {
String row = "";
for(int x=0; x<CHUNK_SIZE; x++) {
switch(blocks[x][y][z].getType()) {
case AIR:
row += "[ ]";
break;
case DIRT:
row+= "[D]";
break;
case ROCK:
row+= "[R]";
break;
}
}
System.out.println(row);
}
}
}
注意:方法getBlockRelativeToSide()
允许您指定边,x,y和z坐标。这种方法可以确保您始终以从左到右的方式处理x,从下到上处理y,从近到远处理深度。因此getBlockRelativeToSide(SOUTH, 0, 0, 0)
将是大小为10的块中的绝对位置0,0,9。getBlockRelativeToSide(EAST, 0, 0, 0)
将是左下角,从东方看到的第一层位置(绝对位置9,0,9) )。