jaquadro Posted March 14, 2016 Share Posted March 14, 2016 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. 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. Quote Link to comment Share on other sites More sharing options...
jaquadro Posted March 18, 2016 Author Share Posted March 18, 2016 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. 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. Quote Link to comment Share on other sites More sharing options...
jaquadro Posted March 20, 2016 Author Share Posted March 20, 2016 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. Quote Link to comment Share on other sites More sharing options...
Draco18s Posted March 20, 2016 Share Posted March 20, 2016 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 Quote 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 More sharing options...
jaquadro Posted March 20, 2016 Author Share Posted March 20, 2016 The uncertainty was whether there's an optimization to prevent calling for faces that were removed by shouldSideBeRendered. Though a quick source check suggests that's not the case. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.