在体素游戏中进行网格划分

时间:2018-03-01 19:39:01

标签: java 3d mesh voxel

我目前正在研究基于体素的绘图系统的渲染系统(类似于Minecraft)。现在,我只想尝试一次渲染一个块。块由块的3d数组组成,每个块由6个面(北,东,南,西,顶部,底部)组成。到目前为止一切都很好。

在研究在屏幕上渲染块的最佳方法时,我遇到了一种称为贪婪网格的技术。不幸的是,最佳版本的数学超出了我的范围,因此我正在实现一个名为naive greedy meshing的简化版本。这个想法非常简单:对于每一方,遍历块的每一层,并尝试构造由相同块类型组成的最大可能网格(在这种情况下,10 * 10块立方体变为1个网格,而不是100个立方体网格)。我这样做是通过为每一层获得每层的2d切片,然后将其与掩码(简单的2d布尔数组)进行比较,该掩码跟踪已经屏蔽的块。最终结果很有效,除了一旦设置了掩模之后,它不会考虑较低z级别的空气阻塞。 This chunk is missing meshes for the air block in the middle 可以看出,在大块的下半部分的中间有一个空气挡块,后面有一个污垢块。从北方来看,这完美地起作用,因为对于空气挡块的位置,掩模没有设置为真。当它到达下一个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) )。

0 个答案:

没有答案