Более сложный динамический рендер с .obj моделью и анимацией

Перевод Более сложный динамический рендер с .obj моделью и анимацией

Версия(и) Minecraft
1.12.х
Источник
https://wiki.mcjty.eu/modding/index.php?title=Render_Block_TESR_/_OBJ-1.12
Это более продвинутый учебник, в котором представлены различные новые концепции. Сначала вы увидите, как создать TileEntity, который должен синхронизировать данные с сервера на клиент. Вы также увидите, как мы можем использовать статическую .obj модель, так и анимированную динамическую .obj модель. И, наконец, вы увидите, как рендерить любой предмет прямо в мире. Для этого нам необходимо реализовать TESR ("TileEntitySpecialRenderer"). Конечные результат будет выглядят вот как-то так. Вы сможете так же вставлять и удалять предметы в блоке, щелкнув правой кнопкой мыши по этому самому блоку:
74jddr9.png


Для начала убедитесь что вы добавили в ваш ClientProxy.preInit () следующий код, который гарантировал бы работу ․օbj загрузчика:
ClientProxy.java:
    public static class ClientProxy extends CommonProxy {
      
        @Override
        public void preInit(FMLPreInitializationEvent e) {
          
            super.preInit(e);

            OBJLoader.INSTANCE.addDomain(MODID);

            ...
        }
    }

Затем добавляем блок:
PedestalBlock.java:
public class PedestalBlock extends Block implements ITileEntityProvider {

    //Для рендера
    public static final IProperty<Boolean> IS_HANDLES = PropertyBool.create("is_handles");

    public PedestalBlock() {
        super(Material.ROCK);
        setUnlocalizedName(ModTut.MODID + ".pedestalblock");
        setRegistryName("pedestalblock");
        setDefaultState(blockState.getBaseState().withProperty(IS_HANDLES, false));
    }

    @Override
    public IBlockState getActualState(IBlockState state, IBlockAccess worldIn, BlockPos pos) {
        return state.withProperty(IS_HANDLES, false);
    }

    @Override
    protected BlockStateContainer createBlockState() {
        return new BlockStateContainer(this, IS_HANDLES);
    }

    @Override
    public IBlockState getStateFromMeta(int meta) {
        return getDefaultState();
    }

    @Override
    public int getMetaFromState(IBlockState state) {
        return 0;
    }

    @SideOnly(Side.CLIENT)
    public void initModel() {
        ModelLoader.setCustomModelResourceLocation(Item.getItemFromBlock(this), 0, new ModelResourceLocation(getRegistryName(), "inventory"));
        //Связать TESR с TileEntity
        ClientRegistry.bindTileEntitySpecialRenderer(PedestalTileEntity.class, new PedestalTESR());
    }

    @Override
    public TileEntity createNewTileEntity(World worldIn, int meta) {
        return new PedestalTileEntity();
    }

    @Override
    @SideOnly(Side.CLIENT)
    public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess worldIn, BlockPos pos, EnumFacing side) {
        return false;
    }

    @Override
    public boolean isBlockNormalCube(IBlockState blockState) {
        return false;
    }

    @Override
    public boolean isOpaqueCube(IBlockState blockState) {
        return false;
    }

    private PedestalTileEntity getTE(World world, BlockPos pos) {
        return (PedestalTileEntity) world.getTileEntity(pos);
    }

    @Override
    public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player,
            EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) {
        if (!world.isRemote) {
            PedestalTileEntity te = getTE(world, pos);
            if (te.getStack().isEmpty()) {
                if (!player.getHeldItem(hand).isEmpty()) {
                    //Ели на пьедестале нет предмета, а игрок держит предмет. Мы перемещаем этот предмет на пьедестал
                    te.setStack(player.getHeldItem(hand));
                    player.inventory.setInventorySlotContents(player.inventory.currentItem, ItemStack.EMPTY);
                    //Убедитесь, что клиентская сторона знает об изменениях в инвентаре игрока
                    player.openContainer.detectAndSendChanges();
                }
            } else {
                //В пьедестале есть стек. В этом случае мы удаляем его и пытаемся положить его в инвентарь игрока, если есть место
                ItemStack stack = te.getStack();
                te.setStack(ItemStack.EMPTY);
                if (!player.inventory.addItemStackToInventory(stack)) {
                    //Если в инвентаре нет места, выкидываем предмет в мир
                    EntityItem entityItem = new EntityItem(world, pos.getX(), pos.getY()+1, pos.getZ(), stack);
                    world.spawnEntity(entityItem);
                } else {
                    player.openContainer.detectAndSendChanges();
                }
            }
        }

        //Возвращаем true на клиенте, чтобы убедиться, что игра знает, что мы обработали этот блок, и не надо его пытаться разместить на клиенте.
        return true;
    }
}

Затем работаем над TileEntity. Обратите внимание на дополнительный код, который мы используем для синхронизации вашего TileEntity с клиентом при необходимости:
PedestalTileEntity.java:
public class PedestalTileEntity extends TileEntity {

    private ItemStack stack = ItemStack.EMPTY;

    public ItemStack getStack() {
        return stack;
    }

    public void setStack(ItemStack stack) {
        this.stack = stack;
        markDirty();
        if (world != null) {
            IBlockState state = worldObj.getBlockState(getPos());
            world.notifyBlockUpdate(getPos(), state, state, 3);
        }
    }

    @Override
    public NBTTagCompound getUpdateTag() {
        //getUpdateTag () вызывается всякий раз, когда данные чанка отправляются на сторону клиента.
        //В противоположность этому имееться getUpdatePacket () который вызывается, когда сама TileEntity хочет синхронизироваться с клиентом.
        //Во многих случаях можно отправить ту же информацию через getUpdateTag (), или getUpdatePacket ().
        return writeToNBT(new NBTTagCompound());
    }

    @Override
    public SPacketUpdateTileEntity getUpdatePacket() {
        // Подготовить пакет для синхронизации TileEntity с клиентом.
        //Поскольку нам нужно только синхронизировать стек, и это все, что у нас есть, мы просто напишем здесь весь NBT.
        //Если у нас имееться сложный TileEntity, которому не нужно иметь всю информацию о клиенте, вы можете написать более оптимальный NBT здесь.
        NBTTagCompound nbtTag = new NBTTagCompound();
        this.writeToNBT(nbtTag);
        return new SPacketUpdateTileEntity(getPos(), 1, nbtTag);
    }

    @Override
    public void onDataPacket(NetworkManager net, SPacketUpdateTileEntity packet) {
        //Здесь мы получаем пакет с сервера и записываем его в вашу клиентскую часть TileEntity
        this.readFromNBT(packet.getNbtCompound());
    }

    @Override
    public void readFromNBT(NBTTagCompound compound) {
        super.readFromNBT(compound);
        if (compound.hasKey("item")) {
            stack = new ItemStack(compound.getCompoundTag("item"));
        } else {
            stack = ItemStack.EMPTY;
        }
    }

    @Override
    public NBTTagCompound writeToNBT(NBTTagCompound compound) {
        super.writeToNBT(compound);
        if (!stack.isEmpty()) {
            NBTTagCompound tagCompound = new NBTTagCompound();
            stack.writeToNBT(tagCompound);
            compound.setTag("item", tagCompound);
        }
        return compound;
    }
}

Наконец у нас есть работающий TESR. Обратите внимание, что статическая модель здесь не отображается. Это обрабатывается автоматически. Нам нужно только сделать анимирующие рукояти и предмет:
PedestalTESR.java:
@SideOnly(Side.CLIENT)
public class PedestalTESR extends TileEntitySpecialRenderer<PedestalTileEntity> {

    @Override
    public void render(PedestalTileEntity te, double x, double y, double z, float partialTicks, int destroyStage, float alpha) {
        GlStateManager.pushAttrib();
        GlStateManager.pushMatrix();

        //Перемести на расположение вашего TileEntity
        GlStateManager.translate(x, y, z);
        GlStateManager.disableRescaleNormal();

        //Рендеринг вращающихся ручек
        renderHandles(te);

        //рендер предмета на пьедестале
        renderItem(te);

        GlStateManager.popMatrix();
        GlStateManager.popAttrib();
    }

    private void renderHandles(PedestalTileEntity te) {
        GlStateManager.pushMatrix();

        GlStateManager.translate(.5, 0, .5);
        long angle = (System.currentTimeMillis() / 10) % 360;
        GlStateManager.rotate(angle, 0, 1, 0);

        RenderHelper.disableStandardItemLighting();
        this.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
        if (Minecraft.isAmbientOcclusionEnabled()) {
            GlStateManager.shadeModel(GL11.GL_SMOOTH);
        } else {
            GlStateManager.shadeModel(GL11.GL_FLAT);
        }

        World world = te.getWorld();
        // Перевести обратно в локальный вид координат, так что бы мы могли провести здесь рендер
        GlStateManager.translate(-te.getPos().getX(), -te.getPos().getY(), -te.getPos().getZ());

        Tessellator tessellator = Tessellator.getInstance();
        BufferBuilder bufferBuilder = tessellator.getBuffer();
        bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);

        IBlockState state = ModBlocks.pedestalBlock.getDefaultState().withProperty(PedestalBlock.IS_HANDLES, true);
        BlockRendererDispatcher dispatcher = Minecraft.getMinecraft().getBlockRendererDispatcher();
        IBakedModel model = dispatcher.getModelForState(state);
        dispatcher.getBlockModelRenderer().renderModel(world, model, state, te.getPos(), bufferBuilder, true);
        tessellator.draw();

        RenderHelper.enableStandardItemLighting();
        GlStateManager.popMatrix();
    }

    private void renderItem(PedestalTileEntity te) {
        ItemStack stack = te.getStack();
        if (!stack.isEmpty()) {
            RenderHelper.enableStandardItemLighting();
            GlStateManager.enableLighting();
            GlStateManager.pushMatrix();
            //Переместить в центр блока и на 0,9 пунктов выше
            GlStateManager.translate(.5, .9, .5);
            GlStateManager.scale(.4f, .4f, .4f);

            Minecraft.getMinecraft().getRenderItem().renderItem(stack, ItemCameraTransforms.TransformType.NONE);

            GlStateManager.popMatrix();
        }
    }

}

И, наконец, нам нужен .json файл состояния блока:
modelblock.json:
{
  "forge_marker": 1,
  "defaults": {
    "custom": { "flip-v": true }
  },
  "variants": {
    "is_handles=false": { "model": "modtut:pedestal.obj" },
    "is_handles=true": { "model": "modtut:pedestalhandles.obj" },
    "inventory": { "model": "modtut:pedestal.obj" }
  }
}
Автор
Garik
Просмотры
3,988
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок

Другие ресурсы пользователя Garik

Сверху