Сетка в воксельной игре

В настоящее время я работаю над системой рендеринга для моей картографической системы на основе вокселей (аналогично Minecraft). Прямо сейчас я просто пытаюсь визуализировать 1 фрагмент за раз. Чанк состоит из трехмерного массива блоков, каждый блок состоит из 6 граней (север, восток, юг, запад, верх, низ). Все идет нормально.

Когда я искал лучший способ визуализации фрагмента на экране, я наткнулся на технику, называемую жадной сеткой. К сожалению, математика для оптимальной версии мне не подходит, поэтому я внедряю упрощенную версию, называемую наивным жадным зацеплением. Идея довольно проста: для каждой стороны, пройдите через каждый слой чанка и попытайтесь построить максимально возможную сетку, состоящую из одинаковых типов блоков (в этом случае блок кубов размером 10*10 становится 1 сеткой вместо 100). сетка куба). Я делаю это, получая 2d срез куска для каждого слоя для каждой стороны, а затем сравнивая это с маской (простой 2d логический массив), которая отслеживает уже замаскированные блоки. Конечный результат работает довольно хорошо, за исключением того факта, что после установки маски он не будет учитывать воздушные блоки на более низких z-уровнях. Этот кусок не хватает сетки для воздушного блока в середине Как можно видеть, в середине нижней половины куска находится воздушный блок, за которым находится блок грязи. С севера это работает отлично, так как маска не установлена ​​в положение true для положения аэроблока. Когда он попадает на следующий 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) будет абсолютная позиция 0, 0, 9 в чанке с размером 10. getBlockRelativeToSide(EAST, 0, 0, 0) будет слева внизу, позиция первого слоя, если смотреть с востока (абсолютная позиция 9, 0, 9).

0 ответов

Другие вопросы по тегам