Jump to content

[1.8.9] [Solved] Trouble rendering translucent quad


jaquadro

Recommended Posts

I'm working on a dynamic-rendered IFlexibleBakedModel, which has soild and translucent parts.  The solid rendering is working great, but the translucent quads are rendering strangely.

 

pat8fCo.png

KbJujcv.png

 

The texture of the black quad is a an all-black image with a linear alpha channel from 0-255.  The gradient sort of shows up on the edge, but it's like the whole range got compressed and so most of it is opaque.

 

The translucent quads are being rendered during the TRANSLUCENT pass.  I've verified the texture itself is being processed by the texture loader with sane alpha bytes.  I've tried returning quads in the BLOCK and ITEM vertex formats.  I've cross-referenced baked quads with vanilla blocks like stained glass, and the color/normal/tintIndex bits are all the same.  I'm not sure where else to look for problems.  Any ideas?  Where else can I look?

 

Relevant class files:

BlockFramingTable.java

 

FramingTableModel.java (The IFlexibleBakedModel)

FramingTableRegister.java (Registering the model with various blockstates)

CommonFramingRenderer.java (Render instructions)

 

ChamRender.java (Render system)

ChamRenderLL.java (Low-level tesselation and baking)

ChamRenderState.java (Render state)

IconRegistry.java (Texture loading)

 

public class BlockFramingTable extends BlockContainer
{
    @Override
    public boolean isOpaqueCube () {
        return false;
    }

    @Override
    public boolean isFullCube () {
        return false;
    }

    @Override
    public boolean shouldSideBeRendered (IBlockAccess worldIn, BlockPos pos, EnumFacing side) {
        return true;
    }

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

    @Override
    @SideOnly(Side.CLIENT)
    public EnumWorldBlockLayer getBlockLayer () {
        return EnumWorldBlockLayer.TRANSLUCENT;
    }

    @Override
    public boolean canRenderInLayer (EnumWorldBlockLayer layer) {
        return layer == EnumWorldBlockLayer.SOLID || layer == EnumWorldBlockLayer.TRANSLUCENT;
    }

    @Override
    public IBlockState getStateFromMeta (int meta) {
        EnumFacing side = EnumFacing.getFront(meta & 0x7);
        if (side.getAxis() == EnumFacing.Axis.Y)
            side = EnumFacing.NORTH;

        return getDefaultState().withProperty(RIGHT_SIDE, (meta & 0x8) == 0).withProperty(FACING, side);
    }

    @Override
    public int getMetaFromState (IBlockState state) {
        return (isRightBlock(state) ? 0x8 : 0) | getDirection(state).getIndex();
    }

    @Override
    protected BlockState createBlockState () {
        return new ExtendedBlockState(this, new IProperty[] { RIGHT_SIDE, FACING }, new IUnlistedProperty[] { TILE });
    }
}

 

public class FramingTableModel implements IFlexibleBakedModel {
    private static final List<BakedQuad> EMPTY = new ArrayList<BakedQuad>(0);

    private CommonFramingRenderer renderer;
    private IBlockState blockState;

    private TextureAtlasSprite iconBase;
    private TextureAtlasSprite iconTrim;
    private TextureAtlasSprite iconOverlayLeft;
    private TextureAtlasSprite iconOverlayRight;

    public FramingTableModel (IBlockState state) {
        renderer = new CommonFramingRenderer(ChamRender.instance);
        blockState = state;

        iconBase = Chameleon.instance.iconRegistry.getIcon(StorageDrawers.proxy.iconBaseOak);
        iconTrim = Chameleon.instance.iconRegistry.getIcon(StorageDrawers.proxy.iconTrimOak);
        iconOverlayLeft = Chameleon.instance.iconRegistry.getIcon(StorageDrawers.proxy.iconOverlayFramingTableLeft);
        iconOverlayRight = Chameleon.instance.iconRegistry.getIcon(StorageDrawers.proxy.iconOverlayFramingTableRight);
    }

    @Override
    public VertexFormat getFormat () {
        return DefaultVertexFormats.BLOCK;
    }

    @Override
    public List<BakedQuad> getFaceQuads (EnumFacing facing) {
        if (MinecraftForgeClient.getRenderLayer() != EnumWorldBlockLayer.SOLID && MinecraftForgeClient.getRenderLayer() != EnumWorldBlockLayer.TRANSLUCENT)
            return EMPTY;

        ChamRender.instance.startBaking(getFormat());
        ChamRender.instance.state.setRotateTransform(ChamRender.ZPOS, blockState.getValue(BlockFramingTable.FACING).getIndex());

        if (MinecraftForgeClient.getRenderLayer() == EnumWorldBlockLayer.SOLID) {
            if (blockState.getValue(BlockFramingTable.RIGHT_SIDE))
                renderer.renderRight(null, blockState, iconBase, iconTrim, EnumQuadGroup.FACE);
            else
                renderer.renderLeft(null, blockState, iconBase, iconTrim, EnumQuadGroup.FACE);
        }
        else if (MinecraftForgeClient.getRenderLayer() == EnumWorldBlockLayer.TRANSLUCENT) {
            if (blockState.getValue(BlockFramingTable.RIGHT_SIDE))
                renderer.renderOverlayRight(null, blockState, iconOverlayRight, EnumQuadGroup.FACE);
            else
                renderer.renderOverlayLeft(null, blockState, iconOverlayLeft, EnumQuadGroup.FACE);
        }

        ChamRender.instance.state.clearRotateTransform();
        return ChamRender.instance.stopBaking();
    }

    @Override
    public List<BakedQuad> getGeneralQuads () {
        if (MinecraftForgeClient.getRenderLayer() != EnumWorldBlockLayer.SOLID)
            return EMPTY;

        ChamRender.instance.startBaking(getFormat());
        ChamRender.instance.state.setRotateTransform(ChamRender.ZPOS, blockState.getValue(BlockFramingTable.FACING).getIndex());

        if (blockState.getValue(BlockFramingTable.RIGHT_SIDE))
            renderer.renderRight(null, blockState, iconBase, iconTrim, EnumQuadGroup.GENERAL);
        else
            renderer.renderLeft(null, blockState, iconBase, iconTrim, EnumQuadGroup.GENERAL);

        ChamRender.instance.state.clearRotateTransform();
        return ChamRender.instance.stopBaking();
    }

    @Override
    public boolean isAmbientOcclusion () {
        return true;
    }

    @Override
    public boolean isGui3d () {
        return true;
    }

    @Override
    public boolean isBuiltInRenderer () {
        return false;
    }

    @Override
    public TextureAtlasSprite getParticleTexture () {
        return iconBase;
    }

    @Override
    public ItemCameraTransforms getItemCameraTransforms () {
        return null;
    }
}

 

Note I'm not using an ISmartBlockModel because I don't need to respond to extra state information.  Yet anyway.  A lot of this is an exercise to prepare for rendering "framed drawers", which can take on the textures of arbitrary blocks, and require semi-transparent shading to be applied to give the rest of the block functional definition.

Link to comment
Share on other sites

I can see I haven't attracted any deep rendering experts yet.  I'm not really any closer to solving this, but I did finish the item rendering path for this block.  The path is basically identical, but for item rendering there's only a single pass available for rendering solids and transparencies.  In this case, it works perfectly.

 

Dtuv94e.png

 

Code has been moved around a bit, but the main block and item rendering code share the same file:

 

public class FramingTableModel extends BlockModel
{
    public static class Register extends DefaultRegister
    {
        public static final ResourceLocation iconBaseOak = new ResourceLocation(StorageDrawers.MOD_ID + ":blocks/base/base_oak");
        public static final ResourceLocation iconTrimOak = new ResourceLocation(StorageDrawers.MOD_ID + ":blocks/base/trim_oak");
        public static final ResourceLocation iconOverlayLeft = new ResourceLocation(StorageDrawers.MOD_ID + ":blocks/overlay/shading_worktable_left");
        public static final ResourceLocation iconOverlayRight = new ResourceLocation(StorageDrawers.MOD_ID + ":blocks/overlay/shading_worktable_right");

        public Register () {
            super(ModBlocks.framingTable);
        }

        @Override
        public List<IBlockState> getBlockStates () {
            List<IBlockState> states = new ArrayList<IBlockState>();

            for (EnumFacing dir : EnumFacing.HORIZONTALS) {
                for (Boolean side : new Boolean[] { false, true })
                    states.add(ModBlocks.framingTable.getDefaultState().withProperty(BlockFramingTable.FACING, dir).withProperty(BlockFramingTable.RIGHT_SIDE, side));
            }

            return states;
        }

        @Override
        public List<ResourceLocation> getTextureResources () {
            return Arrays.asList(iconBaseOak, iconTrimOak, iconOverlayLeft, iconOverlayRight);
        }

        @Override
        public IBakedModel getModel (IBlockState state) {
            return new FramingTableModel(state);
        }

        @Override
        public IBakedModel getModel (ItemStack stack) {
            return new FramingTableModel.ItemModel(stack);
        }
    }

    private static final List<BakedQuad> EMPTY = new ArrayList<BakedQuad>(0);

    protected final CommonFramingRenderer renderer;
    protected final IBlockState blockState;

    protected final TextureAtlasSprite iconBase;
    protected final TextureAtlasSprite iconTrim;
    protected final TextureAtlasSprite iconOverlayLeft;
    protected final TextureAtlasSprite iconOverlayRight;

    public FramingTableModel (IBlockState state) {
        renderer = new CommonFramingRenderer(ChamRender.instance);
        blockState = state;

        iconBase = Chameleon.instance.iconRegistry.getIcon(Register.iconBaseOak);
        iconTrim = Chameleon.instance.iconRegistry.getIcon(Register.iconTrimOak);
        iconOverlayLeft = Chameleon.instance.iconRegistry.getIcon(Register.iconOverlayLeft);
        iconOverlayRight = Chameleon.instance.iconRegistry.getIcon(Register.iconOverlayRight);
    }

    @Override
    public List<BakedQuad> getFaceQuads (EnumFacing facing) {
        if (MinecraftForgeClient.getRenderLayer() != EnumWorldBlockLayer.SOLID && MinecraftForgeClient.getRenderLayer() != EnumWorldBlockLayer.TRANSLUCENT)
            return EMPTY;

        ChamRender.instance.startBaking(getFormat());
        ChamRender.instance.state.setRotateTransform(ChamRender.ZPOS, blockState.getValue(BlockFramingTable.FACING).getIndex());

        renderFaceQuads();

        ChamRender.instance.state.clearRotateTransform();
        return ChamRender.instance.stopBaking();
    }

    @Override
    public List<BakedQuad> getGeneralQuads () {
        if (MinecraftForgeClient.getRenderLayer() != EnumWorldBlockLayer.SOLID)
            return EMPTY;

        ChamRender.instance.startBaking(getFormat());
        ChamRender.instance.state.setRotateTransform(ChamRender.ZPOS, blockState.getValue(BlockFramingTable.FACING).getIndex());

        renderGeneralQuads();

        ChamRender.instance.state.clearRotateTransform();
        return ChamRender.instance.stopBaking();
    }

    @Override
    public TextureAtlasSprite getParticleTexture () {
        return iconBase;
    }

    protected void renderFaceQuads () {
        if (MinecraftForgeClient.getRenderLayer() == EnumWorldBlockLayer.SOLID) {
            if (blockState.getValue(BlockFramingTable.RIGHT_SIDE))
                renderer.renderRight(null, blockState, BlockPos.ORIGIN, iconBase, iconTrim, EnumQuadGroup.FACE);
            else
                renderer.renderLeft(null, blockState, BlockPos.ORIGIN, iconBase, iconTrim, EnumQuadGroup.FACE);
        }
        else if (MinecraftForgeClient.getRenderLayer() == EnumWorldBlockLayer.TRANSLUCENT) {
            if (blockState.getValue(BlockFramingTable.RIGHT_SIDE))
                renderer.renderOverlayRight(null, blockState, BlockPos.ORIGIN, iconOverlayRight, EnumQuadGroup.FACE);
            else
                renderer.renderOverlayLeft(null, blockState, BlockPos.ORIGIN, iconOverlayLeft, EnumQuadGroup.FACE);
        }
    }

    protected void renderGeneralQuads () {
        if (blockState.getValue(BlockFramingTable.RIGHT_SIDE))
            renderer.renderRight(null, blockState, BlockPos.ORIGIN, iconBase, iconTrim, EnumQuadGroup.GENERAL);
        else
            renderer.renderLeft(null, blockState, BlockPos.ORIGIN, iconBase, iconTrim, EnumQuadGroup.GENERAL);
    }

    @SuppressWarnings("deprecation")
    public static class ItemModel extends FramingTableModel
    {
        private static final ItemTransformVec3f transformDefault = new ItemTransformVec3f(new Vector3f(0, 0, 0), new Vector3f(-.15f, 0, 0), new Vector3f(.65f, .65f, .65f));
        private static final ItemTransformVec3f transformThirdPerson = new ItemTransformVec3f(new Vector3f(10, 0, 180), new Vector3f(.2f, .1f, -.15f), new Vector3f(.3f, .3f, .3f));
        private static final ItemCameraTransforms transform = new ItemCameraTransforms(transformThirdPerson,
            transformDefault, transformDefault, transformDefault, transformDefault, transformDefault);

        public ItemModel (ItemStack stack) {
            super(ModBlocks.framingTable.getStateFromMeta(stack.getMetadata()));
        }

        @Override
        protected void renderFaceQuads () {
            renderer.renderRight(null, blockState, BlockPos.ORIGIN, iconBase, iconTrim, EnumQuadGroup.FACE);
            renderer.renderLeft(null, blockState, BlockPos.ORIGIN.east(), iconBase, iconTrim, EnumQuadGroup.FACE);

            renderer.renderOverlayRight(null, blockState, BlockPos.ORIGIN, iconOverlayRight, EnumQuadGroup.FACE);
            renderer.renderOverlayLeft(null, blockState, BlockPos.ORIGIN.east(), iconOverlayLeft, EnumQuadGroup.FACE);
        }

        @Override
        protected void renderGeneralQuads () {
            renderer.renderRight(null, blockState, BlockPos.ORIGIN, iconBase, iconTrim, EnumQuadGroup.GENERAL);
            renderer.renderLeft(null, blockState, BlockPos.ORIGIN.east(), iconBase, iconTrim, EnumQuadGroup.GENERAL);
        }

        @Override
        public ItemCameraTransforms getItemCameraTransforms () {
            return transform;
        }
    }
}

 

It FEELS like a problem in how the rendering is setup for the chunk rendering pass, but since vanilla blocks render fine, I'm wary of pointing my finger in that direction.  The next best thing is there is an additional step I need to do somewhere, but I have no idea what it is.

Link to comment
Share on other sites

Alright, this is solved now.  I slowly came to realize the solution while porting to 1.9 and dealing with the quad interface changes.

 

getFaceQuads takes a direction, so the function is getting called (up to?) 6 times to render a block.  I was rendering the full model on each of those calls.  This obviously caused the translucency to stack and give near blackness.  A little strange that it didn't also occur on the item render.

 

As usual, the problem was stupidity.

Link to comment
Share on other sites

getFaceQuads takes a direction, so the function is getting called (up to?) 6 times to render a block.

 

Yeah.

 

Once for each face.

 

Up

Down

North

South

East

West

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.