ISimpleBlockRenderingHandler и obj-модели.

398
4
7
Обычно для отрисовки моделей используют TileEntitySpecialRenderer, но при этом блок рисуется в каждом кадре заново, что снижает производительность.
Конечно, TileEntitySpecialRenderer хорошо подходит для анимированных моделек, вроде сундука, но как быть со статичными?

Встроенные в фордж загрузчики приспособлены для TileEntitySpecialRenderer, но некоторые их используют так: https://gist.github.com/RainWarrior/9573985
Некоторые делают собственные загрузчики: https://github.com/rezemika/Mers-et-Oceans/blob/master/forge/mcp/src/minecraft/mods/mersetoceans/client/RenderHuitre.java

Основная проблема в том, что в obj-моделях стороны могут быть не квадратами. Можно ли заставить blender все полигоны составлять только из квадратов, а не из треугольников?
Может кто-нибудь предложить наиболее простой и удобный способ отрисовки obj-моделей с использованием ISimpleBlockRenderingHandler ?
 
Может можно заставить blender так делать, потом преобразовывать из obj в формат, понятный тесселятору.
А можно в коде на этапе инициализации строить квады из треугольников.
Проверяем каждые два треугольника:
 if (2 вершины совпадают)
   if (треугольники в одной плоскости)
     if (результируемый квад выпуклый)
       добавить новый квад, убрать оба треугольника

По поводу проверки принадлежности двух треугольников одной плоскости:
KBnQEps.png

Надеюсь, понятно. Мне лень просто переводить было.

Есть другой путь. Это вроде полегче будет.
Имеется 4 точки от двух треугольников (2 вершины смежные, две уникальные), ABCD.
Найти смешанное произведение векторов AB, AC, AD. Если оно равно 0, то 4 точки лежат в одной плоскости -> 2 треугольника тоже.
 
А можно и не ипаццо и рисовать сразу треугольники/ставить две вершины квада в одной точке.
 
В выходные ничего не сделал.
Сегодня ко мне пришла немного бредовая идея - может для рисования обычных блоков использовать модельки? Было бы довольно удобно. Но проблема с освещением.
На основе рендера обычного блока при отключенном сглаживании освещения написал такое: http://pastebin.com/5rzxvQr9
Результат:
1) http://itmages.ru/image/view/2068412/9182fd20 Сглаживание освещения отключено.
2) http://itmages.ru/image/view/2068418/86587370 Сглаживание освещения включено.

Если сделать так http://pastebin.com/hNUq8L0L (по примеру TileEntitySpecialRenderer), то выходит такое: http://itmages.ru/image/view/2068453/9ec99f72

Вопрос: где находится код, который отвечающий за затемнение боковых частей моделей, рисуемых через TileEntitySpecialRenderer?

Посмотрел код сглаживания освещения блоков и стало совсем печально - там около тысячи строк, причем деобфусцированных лишь частично.
Вроде бы в CodeChickenLib есть BlockRenderer, может быть получится использовать его.
 
В CodeChickenLib неплохой рендерер и загрузчик obj-моделей.
Имеется сглаживание освещения (ambient occlusion):
h_1417026349_7963252_cc07e2e49c.png

Код:
public class RenderCPU implements ISimpleBlockRenderingHandler {
    public ResourceLocation texture = new ResourceLocation("nedocomputers", "textures/blocks/cpu.png");
    public CCModel modelInv;
    public CCModel[] models;
    
    private RenderCPU() {}
    
    public RenderCPU(IIcon icon) {
        NedoComputers.renderCPUID = RenderingRegistry.getNextAvailableRenderId();
        ResourceLocation model_loc = new ResourceLocation("nedocomputers", "models/cpu.obj");
        CCModel model = CCModel.parseObjModels(model_loc, GL11.GL_QUADS, new SwapYZ()).get("block");
        model.apply(new Rotation(Math.PI, 0, 1, 0));
        model.apply(new Rotation(Math.PI/2, 1, 0, 0));
        modelInv = model.copy();
        model.apply(new IconTransformation(icon));
        models = new CCModel[6];
        models[0] = null;
        models[1] = null;
        models[2] = model.copy().apply(new Rotation(Math.PI, 0, 1, 0));        // North.
        models[3] = model;                                                    // South.
        models[4] = model.copy().apply(new Rotation(-Math.PI/2, 0, 1, 0));    // West.
        models[5] = model.copy().apply(new Rotation(Math.PI/2, 0, 1, 0));    // East.
    }

    @Override
    public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) {
        CCRenderState.reset();
        CCRenderState.useNormals = true;
        CCRenderState.pullLightmap();
        CCRenderState.changeTexture(texture);
        CCRenderState.startDrawing();
        modelInv.render(new Scale(1));
        CCRenderState.draw();
    }

    @Override
    public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks renderer) {
        int meta = world.getBlockMetadata(x, y, z);
        CCRenderState.reset();
        CCRenderState.lightMatrix.locate(world, x, y, z);
        models[meta].render(new Translation(x + 0.5, y + 0.5, z + 0.5), CCRenderState.lightMatrix);
        return true;
    }

    @Override
    public boolean shouldRender3DInInventory(int modelId) {
        return true;
    }

    @Override
    public int getRenderId() {
        return NedoComputers.renderCPUID;
    }
}
Похоже, что ambient occlusion на моделях работает точно так же, как и в рендере блоков майнкрафта (renderStandardBlockWithAmbientOcclusion в классе RenderBlocks).
Казалось бы, что все хорошо, но
h_1417026396_9094532_d322e8009c.png

Такая ерунда (затемнение) проявляется при z = -17...-20 и isOpaqueCube() == true (в блоке).
 
Конечно, для такого блока можно обойтись без модельки, но с моделькой удобнее получается. Без модельки пришлось бы рисовать по 4 текстуры верха и низа, повернутых в разные стороны. И немного универсальнее с моделькой получается.
Нагрузка от такого решения увеличивается слабо, ведь рисуется не через TileEntitySpecialRenderer.
 
Ага, рекомендую, хорошая вещь, базарю.
 
Проблема с затемнением блоков решилась - я сделал модель немного выступающую за границы блока, аналогично стандартному рендеру блоков.
И написал нечто.
Код:
public class NedoBlock extends Block {
    public ResourceLocation texture_loc;
    public int renderID;
    
    NedoBlock(String texture, String model) {
        super(Material.iron);
        setHardness(3.0F);
        setCreativeTab(CreativeTabs.tabRedstone);
        setBlockTextureName("nedocomputers:" + texture);
        texture_loc = new ResourceLocation("nedocomputers", "textures/blocks/" + texture + ".png");
        renderID = RenderingRegistry.getNextAvailableRenderId();
        NedoComputers.proxy.registerBlockHandler(new NedoBlockRenderer(this, model));
        isBlockContainer = this instanceof ITileEntityProvider;
    }

    @Override
    public int getRenderType() {
        return renderID;
    }

    public int[] getRotation(IBlockAccess world, int x, int y, int z) {
        ForgeDirection block_dir = ForgeDirection.getOrientation(world.getBlockMetadata(x, y, z));
        int result[] = {0, 0, 0};
        switch (ForgeDirection.getOrientation(world.getBlockMetadata(x, y, z))) {
            case DOWN:
                result[0] = 3; result[1] = 0; result[2] = 0;
                break;
            case UP:
                result[0] = 1; result[1] = 0; result[2] = 0;
                break;
            case NORTH:
                result[0] = 0; result[1] = 2; result[2] = 0;
                break;
            case SOUTH:
                result[0] = 0; result[1] = 0; result[2] = 0;
                break;
            case WEST:
                result[0] = 0; result[1] = 3; result[2] = 0;
                break;
            case EAST:
                result[0] = 0; result[1] = 1; result[2] = 0;
                break;
            default:
                break;
        }
        return result;
    }

    @Override
    public boolean onBlockEventReceived(World world, int x, int y, int z, int p_149696_5_, int p_149696_6_) {
        TileEntity tileentity = world.getTileEntity(x, y, z);
        return tileentity != null ? tileentity.receiveClientEvent(p_149696_5_, p_149696_6_) : false;
    }

    public class NedoBlockRenderer implements ISimpleBlockRenderingHandler {
        public CCModel model;
        public CCModel models[][][];
        public int renderID;
        public ResourceLocation texture;
        
        NedoBlockRenderer(NedoBlock block, String model_name) {
            renderID = block.getRenderType();
            texture = block.texture_loc;
            ResourceLocation model_loc = new ResourceLocation("nedocomputers", model_name);
            CCModel m = CCModel.parseObjModels(model_loc, GL11.GL_QUADS, new SwapYZ()).get("block");
            m.apply(new Rotation(Math.PI, 0, 1, 0));
            m.apply(new Rotation(Math.PI/2, 1, 0, 0));
            model = m.copy();
            models = new CCModel[4][4][4];
            for (int k = 0; k < 4; k++) {
                for (int j = 0; j < 4; j++) {
                    for (int i = 0; i < 4; i++) {
                        models[i][j][k] = model.copy()
                                .apply(new Rotation(i * Math.PI/2, 1, 0, 0))
                                .apply(new Rotation(j * Math.PI/2, 0, 1, 0))
                                .apply(new Rotation(k * Math.PI/2, 0, 0, 1));
                    }
                }
            }
        }

        @Override
        public void renderInventoryBlock(Block block, int metadata, int modelId, RenderBlocks renderer) {
            CCRenderState.reset();
            CCRenderState.useNormals = true;
            CCRenderState.pullLightmap();
            CCRenderState.changeTexture(texture);
            CCRenderState.startDrawing();
            model.render(new Scale(1));
            CCRenderState.draw();
        }

        @Override
        public boolean renderWorldBlock(IBlockAccess world, int x, int y,
                int z, Block block, int modelId, RenderBlocks renderer) {
            CCRenderState.reset();
            CCRenderState.lightMatrix.locate(world, x, y, z);
            int rot[] = ((NedoBlock)block).getRotation(world, x, y, z);
            models[rot[0]][rot[1]][rot[2]].render(
                    new Translation(x + 0.5, y + 0.5, z + 0.5),
                    CCRenderState.lightMatrix,
                    new IconTransformation(block.getIcon(0, 0)));
            return true;
        }

        @Override
        public boolean shouldRender3DInInventory(int modelId) {
            return true;
        }

        @Override
        public int getRenderId() {
            return renderID;
        }
    }
}
В результате, для создания блока с моделькой нужно всего лишь наследовать NedoBlock, и в конструктор вставить super("текстура", "моделька");
Это я написал нормально или так писать не стоит?
 
Назад
Сверху