From 2de184244535eb60cd828565bf7fb875f21bb653 Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Wed, 21 Sep 2016 00:11:03 +0200 Subject: [PATCH] Implemented facade rendering on the cable bus. --- .../java/appeng/api/parts/IFacadePart.java | 23 +- .../block/networking/BlockCableBus.java | 14 + .../render/cablebus/CableBusBakedModel.java | 14 +- .../client/render/cablebus/CableBusModel.java | 10 +- .../render/cablebus/CableBusRenderState.java | 19 + .../client/render/cablebus/CubeBuilder.java | 5 + .../client/render/cablebus/FacadeBuilder.java | 376 ++++++++++++++++++ .../render/cablebus/FacadeRenderState.java | 38 ++ src/main/java/appeng/facade/FacadePart.java | 59 +-- .../java/appeng/parts/CableBusContainer.java | 103 ++++- src/main/java/appeng/util/Platform.java | 22 - 11 files changed, 619 insertions(+), 64 deletions(-) create mode 100644 src/main/java/appeng/client/render/cablebus/FacadeBuilder.java create mode 100644 src/main/java/appeng/client/render/cablebus/FacadeRenderState.java diff --git a/src/api/java/appeng/api/parts/IFacadePart.java b/src/api/java/appeng/api/parts/IFacadePart.java index 7984d1c8..cbddd62a 100644 --- a/src/api/java/appeng/api/parts/IFacadePart.java +++ b/src/api/java/appeng/api/parts/IFacadePart.java @@ -24,10 +24,12 @@ package appeng.api.parts; +import javax.annotation.Nullable; + +import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; -import net.minecraft.util.math.AxisAlignedBB; import appeng.api.util.AEPartLocation; @@ -58,18 +60,23 @@ public interface IFacadePart */ AEPartLocation getSide(); - /** - * @return the box for the face of the facade - */ - AxisAlignedBB getPrimaryBox(); - Item getItem(); int getItemDamage(); boolean notAEFacade(); - void setThinFacades( boolean useThinFacades ); - boolean isTransparent(); + + /** + * The item that this facade masquerades as. + */ + @Nullable + ItemStack getTextureItem(); + + /** + * @return The block state used for rendering. + */ + IBlockState getBlockState(); + } \ No newline at end of file diff --git a/src/main/java/appeng/block/networking/BlockCableBus.java b/src/main/java/appeng/block/networking/BlockCableBus.java index 80509e81..144b3dcc 100644 --- a/src/main/java/appeng/block/networking/BlockCableBus.java +++ b/src/main/java/appeng/block/networking/BlockCableBus.java @@ -58,6 +58,7 @@ import net.minecraftforge.fml.common.registry.GameRegistry; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; +import appeng.api.AEApi; import appeng.api.parts.IPartHost; import appeng.api.parts.PartItemStack; import appeng.api.parts.SelectedPart; @@ -405,6 +406,19 @@ public class BlockCableBus extends AEBaseTileBlock ClientRegistry.bindTileEntitySpecialRenderer( BlockCableBus.getTesrTile(), new CableBusTESR() ); } + @Override + public boolean canRenderInLayer( IBlockState state, BlockRenderLayer layer ) + { + if( AEApi.instance().partHelper().getCableRenderMode().transparentFacades ) + { + return layer == BlockRenderLayer.TRANSLUCENT; + } + else + { + return layer == BlockRenderLayer.CUTOUT; + } + } + public static Class getNoTesrTile() { return noTesrTile; diff --git a/src/main/java/appeng/client/render/cablebus/CableBusBakedModel.java b/src/main/java/appeng/client/render/cablebus/CableBusBakedModel.java index e436ae83..94aa3f15 100644 --- a/src/main/java/appeng/client/render/cablebus/CableBusBakedModel.java +++ b/src/main/java/appeng/client/render/cablebus/CableBusBakedModel.java @@ -26,7 +26,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; - import javax.annotation.Nullable; import net.minecraft.block.state.IBlockState; @@ -49,11 +48,14 @@ public class CableBusBakedModel implements IBakedModel private final CableBuilder cableBuilder; + private final FacadeBuilder facadeBuilder; + private final Map partModels; - CableBusBakedModel( CableBuilder cableBuilder, Map partModels ) + CableBusBakedModel( CableBuilder cableBuilder, FacadeBuilder facadeBuilder, Map partModels ) { this.cableBuilder = cableBuilder; + this.facadeBuilder = facadeBuilder; this.partModels = partModels; } @@ -98,6 +100,14 @@ public class CableBusBakedModel implements IBakedModel } } + facadeBuilder.addFacades( + renderState.getFacades(), + renderState.getBoundingBoxes(), + renderState.getAttachments().keySet(), + rand, + quads + ); + return quads; } diff --git a/src/main/java/appeng/client/render/cablebus/CableBusModel.java b/src/main/java/appeng/client/render/cablebus/CableBusModel.java index 474b683d..0722f18b 100644 --- a/src/main/java/appeng/client/render/cablebus/CableBusModel.java +++ b/src/main/java/appeng/client/render/cablebus/CableBusModel.java @@ -23,6 +23,7 @@ import java.util.Collection; import java.util.Map; import com.google.common.base.Function; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import net.minecraft.client.renderer.block.model.IBakedModel; @@ -61,7 +62,10 @@ public class CableBusModel implements IModel @Override public Collection getTextures() { - return CableBuilder.getTextures(); + return ImmutableList.builder() + .addAll( CableBuilder.getTextures() ) + .addAll( FacadeBuilder.getTextures() ) + .build(); } @Override @@ -71,7 +75,9 @@ public class CableBusModel implements IModel Map partModels = loadPartModels( state, format, bakedTextureGetter ); CableBuilder cableBuilder = new CableBuilder( format, bakedTextureGetter ); - return new CableBusBakedModel( cableBuilder, partModels ); + FacadeBuilder facadeBuilder = new FacadeBuilder( format, bakedTextureGetter ); + + return new CableBusBakedModel( cableBuilder, facadeBuilder, partModels ); } private Map loadPartModels( IModelState state, VertexFormat format, Function bakedTextureGetter ) diff --git a/src/main/java/appeng/client/render/cablebus/CableBusRenderState.java b/src/main/java/appeng/client/render/cablebus/CableBusRenderState.java index 555560a7..ae3276ab 100644 --- a/src/main/java/appeng/client/render/cablebus/CableBusRenderState.java +++ b/src/main/java/appeng/client/render/cablebus/CableBusRenderState.java @@ -19,12 +19,14 @@ package appeng.client.render.cablebus; +import java.util.ArrayList; import java.util.EnumMap; import java.util.EnumSet; import java.util.List; import net.minecraft.util.EnumFacing; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.AxisAlignedBB; import appeng.api.util.AECableType; import appeng.api.util.AEColor; @@ -63,6 +65,13 @@ public class CableBusRenderState // For each attachment, this contains the distance from the edge until which a cable connection should be drawn private EnumMap attachmentConnections = new EnumMap<>( EnumFacing.class ); + // Contains the facade to use for each side that has a facade attached + private EnumMap facades = new EnumMap<>( EnumFacing.class ); + + // Contains the bounding boxes of all parts on the cable bus to allow facades to cut out holes for the parts. This list is only populated if there are + // facades on this cable bus + private List boundingBoxes = new ArrayList<>(); + public CableCoreType getCoreType() { return coreType; @@ -133,4 +142,14 @@ public class CableBusRenderState return attachmentConnections; } + public EnumMap getFacades() + { + return facades; + } + + public List getBoundingBoxes() + { + return boundingBoxes; + } + } diff --git a/src/main/java/appeng/client/render/cablebus/CubeBuilder.java b/src/main/java/appeng/client/render/cablebus/CubeBuilder.java index ac0c047a..70b633ec 100644 --- a/src/main/java/appeng/client/render/cablebus/CubeBuilder.java +++ b/src/main/java/appeng/client/render/cablebus/CubeBuilder.java @@ -411,6 +411,11 @@ public class CubeBuilder textures.put( EnumFacing.WEST, west ); } + public void setTexture( EnumFacing facing, TextureAtlasSprite sprite ) + { + textures.put( facing, sprite ); + } + public void setDrawFaces( EnumSet drawFaces ) { this.drawFaces = drawFaces; diff --git a/src/main/java/appeng/client/render/cablebus/FacadeBuilder.java b/src/main/java/appeng/client/render/cablebus/FacadeBuilder.java new file mode 100644 index 00000000..e63accb4 --- /dev/null +++ b/src/main/java/appeng/client/render/cablebus/FacadeBuilder.java @@ -0,0 +1,376 @@ +/* + * This file is part of Applied Energistics 2. + * Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved. + * + * Applied Energistics 2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Applied Energistics 2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Applied Energistics 2. If not, see . + */ + +package appeng.client.render.cablebus; + + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.annotation.Nullable; +import javax.vecmath.Vector3f; + +import com.google.common.base.Function; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BlockRendererDispatcher; +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.block.model.IBakedModel; +import net.minecraft.client.renderer.color.BlockColors; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.AxisAlignedBB; + +import appeng.api.AEApi; +import appeng.api.util.AEAxisAlignedBB; +import appeng.core.AELog; +import appeng.core.AppEng; + + +/** + * Handles creating the quads for facades attached to cable busses. + */ +class FacadeBuilder +{ + + static final ResourceLocation TEXTURE_FACADE = new ResourceLocation( AppEng.MOD_ID, "parts/cable_anchor" ); + + private final BlockColors blockColors = Minecraft.getMinecraft().getBlockColors(); + + private final VertexFormat format; + + private final TextureAtlasSprite facadeTexture; + private final BlockRendererDispatcher blockRendererDispatcher = Minecraft.getMinecraft().getBlockRendererDispatcher(); + + FacadeBuilder( VertexFormat format, Function bakedTextureGetter ) + { + this.format = format; + this.facadeTexture = bakedTextureGetter.apply( TEXTURE_FACADE ); + } + + static Collection getTextures() + { + return Collections.singletonList( TEXTURE_FACADE ); + } + + void addFacades( Map facadesState, List partBoxes, Set sidesWithParts, long rand, List quads ) + { + boolean thinFacades = isUseThinFacades( partBoxes ); + + CubeBuilder builder = new CubeBuilder( format, quads ); + + facadesState.forEach( ( side, textureItem ) -> + { + AxisAlignedBB facadeBox = getFacadeBox( side, thinFacades ); + AEAxisAlignedBB cutOutBox = getCutOutBox( facadeBox, partBoxes ); + boolean renderStilt = !sidesWithParts.contains( side ); + + try + { + addFacade( facadesState, side, cutOutBox, thinFacades, renderStilt, rand, builder ); + } + catch( Throwable t ) + { + AELog.debug( t ); + } + } ); + } + + private void addFacade( Map facades, EnumFacing side, AEAxisAlignedBB busBounds, boolean thinFacades, boolean renderStilt, long rand, CubeBuilder builder ) + { + + FacadeRenderState facadeState = facades.get( side ); + IBlockState blockState = facadeState.getSourceBlock(); + + builder.setDrawFaces( EnumSet.allOf( EnumFacing.class ) ); + + // We only render the stilt if we don't intersect with any part directly, and if there's no part on our side + if( renderStilt && busBounds == null ) + { + builder.setTexture( facadeTexture ); + switch( side ) + { + case DOWN: + builder.addCube( 7, 1, 7, 9, 6, 9 ); + break; + case UP: + builder.addCube( 7, 10, 7, 9, 15, 9 ); + break; + case NORTH: + builder.addCube( 7, 7, 1, 9, 9, 6 ); + break; + case SOUTH: + builder.addCube( 7, 7, 10, 9, 9, 15 ); + break; + case WEST: + builder.addCube( 1, 7, 7, 6, 9, 9 ); + break; + case EAST: + builder.addCube( 10, 7, 7, 15, 9, 9 ); + break; + } + } + + final float thickness = thinFacades ? 1 : 2; + + IBakedModel blockModel = blockRendererDispatcher.getModelForState( blockState ); + + if( AEApi.instance().partHelper().getCableRenderMode().transparentFacades ) + { + // TODO rbw.setOpacity( 0.3f ); + } + + int color = 0xffffff; + try + { + blockColors.func_189991_a( blockState ); + } + catch( final Throwable ignored ) + { + } + + builder.setColorRGB( color ); + + // TODO: Cache this + for( EnumFacing facing : facadeState.getOpenFaces() ) + { + List quads = blockModel.getQuads( blockState, facing, rand ); + for( BakedQuad quad : quads ) + { + builder.setTexture( quad.getSprite() ); + } + } + + builder.setDrawFaces( facadeState.getOpenFaces() ); + + AxisAlignedBB primaryBox = getFacadeBox( side, thinFacades ); + + Vector3f min = new Vector3f( + (float) primaryBox.minX * 16, + (float) primaryBox.minY * 16, + (float) primaryBox.minZ * 16 + ); + Vector3f max = new Vector3f( + (float) primaryBox.maxX * 16, + (float) primaryBox.maxY * 16, + (float) primaryBox.maxZ * 16 + ); + + if( busBounds == null ) + { + // Adjust the facade for neighboring facades so that facade cubes dont overlap with each other + if( side == EnumFacing.NORTH || side == EnumFacing.SOUTH ) + { + if( facades.containsKey( EnumFacing.UP ) ) + { + max.y -= thickness; + } + + if( facades.containsKey( EnumFacing.DOWN ) ) + { + min.y += thickness; + } + } + else if( side == EnumFacing.EAST || side == EnumFacing.WEST ) + { + if( facades.containsKey( EnumFacing.UP ) ) + { + max.y -= thickness; + } + + if( facades.containsKey( EnumFacing.DOWN ) ) + { + min.y += thickness; + } + + if( facades.containsKey( EnumFacing.SOUTH ) ) + { + max.z -= thickness; + } + + if( facades.containsKey( EnumFacing.NORTH ) ) + { + min.z += thickness; + } + } + + builder.addCube( min.x, min.y, min.z, max.x, max.y, max.z ); + } + else + { + Vector3f busMin = new Vector3f( (float) busBounds.minX * 16, (float) busBounds.minY * 16, (float) busBounds.minZ * 16 ); + Vector3f busMax = new Vector3f( (float) busBounds.maxX * 16, (float) busBounds.maxY * 16, (float) busBounds.maxZ * 16 ); + + if( side == EnumFacing.UP || side == EnumFacing.DOWN ) + { + this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, busMax.z, 16.0f, 16.0f, 16.0f ); + this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, 0.0f, 16.0f, 16.0f, busMin.z ); + this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, busMin.z, busMin.x, 16.0f, busMax.z ); + this.renderSegmentBlockCurrentBounds( builder, min, max, busMax.x, 0.0f, busMin.z, 16.0f, 16.0f, busMax.z ); + } + else if( side == EnumFacing.NORTH || side == EnumFacing.SOUTH ) + { + if( facades.get( EnumFacing.UP ) != null ) + { + max.y -= thickness; + } + + if( facades.get( EnumFacing.DOWN ) != null ) + { + min.y += thickness; + } + + this.renderSegmentBlockCurrentBounds( builder, min, max, busMax.x, 0.0f, 0.0f, 16.0f, 16.0f, 16.0f ); + this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, 0.0f, busMin.x, 16.0f, 16.0f ); + this.renderSegmentBlockCurrentBounds( builder, min, max, busMin.x, 0.0f, 0.0f, busMax.x, busMin.y, 16.0f ); + this.renderSegmentBlockCurrentBounds( builder, min, max, busMin.x, busMax.y, 0.0f, busMax.x, 16.0f, 16.0f ); + } + else + { + if( facades.get( EnumFacing.UP ) != null ) + { + max.y -= thickness; + } + + if( facades.get( EnumFacing.DOWN ) != null ) + { + min.y += thickness; + } + + if( facades.get( EnumFacing.SOUTH ) != null ) + { + max.z -= thickness; + } + + if( facades.get( EnumFacing.NORTH ) != null ) + { + min.z += thickness; + } + + this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, busMax.z, 16.0f, 16.0f, 16.0f ); + this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, 0.0f, 16.0f, 16.0f, busMin.z ); + this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, busMin.z, 16.0f, busMin.y, busMax.z ); + this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, busMax.y, busMin.z, 16.0f, 16.0f, busMax.z ); + } + } + } + + private void renderSegmentBlockCurrentBounds( CubeBuilder builder, Vector3f min, Vector3f max, + float minX, float minY, float minZ, float maxX, float maxY, float maxZ ) + { + minX = Math.max( min.x, minX ); + minY = Math.max( min.y, minY ); + minZ = Math.max( min.z, minZ ); + maxX = Math.min( max.x, maxX ); + maxY = Math.min( max.y, maxY ); + maxZ = Math.min( max.z, maxZ ); + + // don't draw it if its not at least a pixel wide... + if( maxX - minX >= 1.0 && maxY - minY >= 1.0 && maxZ - minZ >= 1.0 ) + { + builder.addCube( minX, minY, minZ, maxX, maxY, maxZ ); + } + + } + + /** + * Given the actual facade bounding box, and the bounding boxes of all parts, determine the biggest union of AABB that intersect with the + * facade's bounding box. This AABB will need to be "cut out" when the facade is rendered. + */ + @Nullable + private static AEAxisAlignedBB getCutOutBox( AxisAlignedBB facadeBox, List partBoxes ) + { + AEAxisAlignedBB b = null; + for( AxisAlignedBB bb : partBoxes ) + { + if( bb.intersectsWith( facadeBox ) ) + { + if( b == null ) + { + b = AEAxisAlignedBB.fromBounds( bb ); + } + else + { + b.maxX = Math.max( b.maxX, bb.maxX ); + b.maxY = Math.max( b.maxY, bb.maxY ); + b.maxZ = Math.max( b.maxZ, bb.maxZ ); + b.minX = Math.min( b.minX, bb.minX ); + b.minY = Math.min( b.minY, bb.minY ); + b.minZ = Math.min( b.minZ, bb.minZ ); + } + } + } + return b; + } + + /** + * Determines if any of the part's bounding boxes intersects with the outside 2 voxel wide layer. + * If so, we should use thinner facades (1 voxel deep). + */ + private static boolean isUseThinFacades( List partBoxes ) + { + final double min = 2.0 / 16.0; + final double max = 14.0 / 16.0; + + for( AxisAlignedBB bb : partBoxes ) + { + int o = 0; + o += bb.maxX > max ? 1 : 0; + o += bb.maxY > max ? 1 : 0; + o += bb.maxZ > max ? 1 : 0; + o += bb.minX < min ? 1 : 0; + o += bb.minY < min ? 1 : 0; + o += bb.minZ < min ? 1 : 0; + + if( o >= 2 ) + { + return true; + } + } + return false; + } + + private static AxisAlignedBB getFacadeBox( EnumFacing side, boolean thinFacades ) + { + int thickness = thinFacades ? 1 : 2; + + switch( side ) + { + case DOWN: + return new AxisAlignedBB( 0.0, 0.0, 0.0, 1.0, ( thickness ) / 16.0, 1.0 ); + case EAST: + return new AxisAlignedBB( ( 16.0 - thickness ) / 16.0, 0.0, 0.0, 1.0, 1.0, 1.0 ); + case NORTH: + return new AxisAlignedBB( 0.0, 0.0, 0.0, 1.0, 1.0, ( thickness ) / 16.0 ); + case SOUTH: + return new AxisAlignedBB( 0.0, 0.0, ( 16.0 - thickness ) / 16.0, 1.0, 1.0, 1.0 ); + case UP: + return new AxisAlignedBB( 0.0, ( 16.0 - thickness ) / 16.0, 0.0, 1.0, 1.0, 1.0 ); + case WEST: + return new AxisAlignedBB( 0.0, 0.0, 0.0, ( thickness ) / 16.0, 1.0, 1.0 ); + default: + throw new IllegalArgumentException( "Unsupported face: " + side ); + } + } +} diff --git a/src/main/java/appeng/client/render/cablebus/FacadeRenderState.java b/src/main/java/appeng/client/render/cablebus/FacadeRenderState.java new file mode 100644 index 00000000..80eb0f72 --- /dev/null +++ b/src/main/java/appeng/client/render/cablebus/FacadeRenderState.java @@ -0,0 +1,38 @@ +package appeng.client.render.cablebus; + + +import java.util.EnumSet; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.EnumFacing; + + +/** + * Captures the state required to render a facade properly. + */ +public class FacadeRenderState +{ + + // The block state to use for rendering this facade + private final IBlockState sourceBlock; + + // Which faces of the cube should be rendered for this particular facade + private final EnumSet openFaces; + + public FacadeRenderState( IBlockState sourceBlock, EnumSet openFaces ) + { + this.sourceBlock = sourceBlock; + this.openFaces = openFaces; + } + + public IBlockState getSourceBlock() + { + return sourceBlock; + } + + public EnumSet getOpenFaces() + { + return openFaces; + } + +} diff --git a/src/main/java/appeng/facade/FacadePart.java b/src/main/java/appeng/facade/FacadePart.java index d32bbc62..bf46dadf 100644 --- a/src/main/java/appeng/facade/FacadePart.java +++ b/src/main/java/appeng/facade/FacadePart.java @@ -22,21 +22,18 @@ package appeng.facade; import javax.annotation.Nullable; import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; -import net.minecraft.util.math.AxisAlignedBB; import appeng.api.AEApi; import appeng.api.parts.IBoxProvider; import appeng.api.parts.IFacadePart; import appeng.api.parts.IPartCollisionHelper; import appeng.api.util.AEPartLocation; -import appeng.integration.IntegrationRegistry; -import appeng.integration.IntegrationType; -import appeng.integration.abstraction.IBuildCraftTransport; -import appeng.util.Platform; public class FacadePart implements IFacadePart, IBoxProvider @@ -44,7 +41,6 @@ public class FacadePart implements IFacadePart, IBoxProvider private final ItemStack facade; private final AEPartLocation side; - private int thickness = 2; public FacadePart( final ItemStack facade, final AEPartLocation side ) { @@ -89,16 +85,10 @@ public class FacadePart implements IFacadePart, IBoxProvider return this.side; } - @Override - public AxisAlignedBB getPrimaryBox() - { - return Platform.getPrimaryBox( this.side, this.thickness ); - } - @Override public Item getItem() { - final ItemStack is = this.getTexture(); + final ItemStack is = this.getTextureItem(); if( is == null ) { return null; @@ -109,7 +99,7 @@ public class FacadePart implements IFacadePart, IBoxProvider @Override public int getItemDamage() { - final ItemStack is = this.getTexture(); + final ItemStack is = this.getTextureItem(); if( is == null ) { return 0; @@ -123,12 +113,6 @@ public class FacadePart implements IFacadePart, IBoxProvider return !( this.facade.getItem() instanceof IFacadeItem ); } - @Override - public void setThinFacades( final boolean useThinFacades ) - { - this.thickness = useThinFacades ? 1 : 2; - } - @Override public boolean isTransparent() { @@ -137,14 +121,15 @@ public class FacadePart implements IFacadePart, IBoxProvider return true; } - final ItemStack is = this.getTexture(); + final ItemStack is = this.getTextureItem(); final Block blk = Block.getBlockFromItem( is.getItem() ); return !blk.isOpaqueCube( blk.getDefaultState() ); } @Nullable - private ItemStack getTexture() + @Override + public ItemStack getTextureItem() { final Item maybeFacade = this.facade.getItem(); @@ -155,16 +140,34 @@ public class FacadePart implements IFacadePart, IBoxProvider return facade.getTextureItem( this.facade ); } - else if( IntegrationRegistry.INSTANCE.isEnabled( IntegrationType.BuildCraftTransport ) ) - { - final IBuildCraftTransport bc = (IBuildCraftTransport) IntegrationRegistry.INSTANCE.getInstance( IntegrationType.BuildCraftTransport ); - - return bc.getTextureForFacade( this.facade ); - } return null; } + @Override + public IBlockState getBlockState() + { + ItemStack itemStack = getTextureItem(); + + if( !(itemStack.getItem() instanceof ItemBlock ) ) + { + return null; + } + + ItemBlock itemBlock = (ItemBlock) itemStack.getItem(); + + // Try to get the block state based on the item stack's meta. If this fails, don't consider it for a facade + // This for example fails for Pistons because they hardcoded an invalid meta value in vanilla + try + { + return itemBlock.getBlock().getStateFromMeta( itemStack.getItemDamage() ); + } + catch( Exception e ) + { + return null; + } + } + @Override public void getBoxes( final IPartCollisionHelper bch ) { diff --git a/src/main/java/appeng/parts/CableBusContainer.java b/src/main/java/appeng/parts/CableBusContainer.java index c905158c..e2be73f1 100644 --- a/src/main/java/appeng/parts/CableBusContainer.java +++ b/src/main/java/appeng/parts/CableBusContainer.java @@ -25,11 +25,11 @@ import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.Set; - import javax.annotation.Nullable; import io.netty.buffer.ByteBuf; +import net.minecraft.block.state.IBlockState; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; @@ -42,6 +42,7 @@ import net.minecraft.util.EnumHand; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; +import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import appeng.api.AEApi; @@ -65,6 +66,7 @@ import appeng.api.util.AEPartLocation; import appeng.api.util.DimensionalCoord; import appeng.client.render.cablebus.CableBusRenderState; import appeng.client.render.cablebus.CableCoreType; +import appeng.client.render.cablebus.FacadeRenderState; import appeng.core.AELog; import appeng.facade.FacadeContainer; import appeng.helpers.AEMultiTile; @@ -1155,7 +1157,6 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I @Override public CableBusRenderState getRenderState() { - // TODO: Inspect whether this is a problem. PartCable is the only implementor of IPartCable, which is not part of the public API PartCable cable = (PartCable) getCenter(); CableBusRenderState renderState = new CableBusRenderState(); @@ -1210,8 +1211,16 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I } } + // Determine attachments and facades for( EnumFacing facing : EnumFacing.values() ) { + + FacadeRenderState facadeState = getFacadeRenderState( facing ); + if ( facadeState != null ) + { + renderState.getFacades().put( facing, facadeState ); + } + IPart part = getPart( facing ); if( part == null ) @@ -1219,6 +1228,11 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I continue; } + // This will add the part's bounding boxes to the render state, which is required for facades + AEPartLocation loc = AEPartLocation.fromFacing( facing ); + IPartCollisionHelper bch = new BusCollisionHelper( renderState.getBoundingBoxes(), loc, null, true ); + part.getBoxes( bch ); + if( part instanceof IGridHost ) { // Some attachments want a thicker cable than glass, account for that @@ -1242,4 +1256,89 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I return renderState; } + private FacadeRenderState getFacadeRenderState( EnumFacing side ) { + // Store the "masqueraded" itemstack for the given side, if there is a facade + IFacadePart facade = getFacade( side.ordinal() ); + if( facade != null ) + { + + IBlockState blockState = facade.getBlockState(); + if( blockState != null ) + { + EnumSet openFaces = calculateFaceOpenFaces( side ); + return new FacadeRenderState( blockState, openFaces ); + } + } + + return null; + } + + private EnumSet calculateFaceOpenFaces( EnumFacing side ) + { + final EnumSet out = EnumSet.of( side, side.getOpposite() ); + final IFacadePart facade = getFacade( side.ordinal() ); + + IBlockAccess blockAccess = getTile().getWorld(); + BlockPos pos = getTile().getPos(); + for( final EnumFacing it : EnumFacing.values() ) + { + if( !out.contains( it ) && this.hasAlphaDiff( blockAccess.getTileEntity( pos.offset( it ) ), side, facade ) ) + { + out.add( it ); + } + } + + if( out.contains( EnumFacing.UP ) && ( side.getFrontOffsetX() != 0 || side.getFrontOffsetZ() != 0 ) ) + { + final IFacadePart fp = getFacade( EnumFacing.UP.ordinal() ); + if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) + { + out.remove( EnumFacing.UP ); + } + } + + if( out.contains( EnumFacing.DOWN ) && ( side.getFrontOffsetX() != 0 || side.getFrontOffsetZ() != 0 ) ) + { + final IFacadePart fp = getFacade( EnumFacing.DOWN.ordinal() ); + if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) + { + out.remove( EnumFacing.DOWN ); + } + } + + if( out.contains( EnumFacing.SOUTH ) && ( side.getFrontOffsetX() != 0 ) ) + { + final IFacadePart fp = getFacade( EnumFacing.SOUTH.ordinal() ); + if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) + { + out.remove( EnumFacing.SOUTH ); + } + } + + if( out.contains( EnumFacing.NORTH ) && ( side.getFrontOffsetX() != 0 ) ) + { + final IFacadePart fp = getFacade( EnumFacing.NORTH.ordinal() ); + if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) + { + out.remove( EnumFacing.NORTH ); + } + } + + return out; + } + + private boolean hasAlphaDiff( final TileEntity tileEntity, final EnumFacing side, final IFacadePart facade ) + { + if( tileEntity instanceof IPartHost ) + { + final IPartHost ph = (IPartHost) tileEntity; + final IFacadePart fp = ph.getFacadeContainer().getFacade( AEPartLocation.fromFacing( side ) ); + + return fp == null || ( fp.isTransparent() != facade.isTransparent() ); + } + + return true; + } + + } diff --git a/src/main/java/appeng/util/Platform.java b/src/main/java/appeng/util/Platform.java index 453c354d..b481ba30 100644 --- a/src/main/java/appeng/util/Platform.java +++ b/src/main/java/appeng/util/Platform.java @@ -2155,28 +2155,6 @@ public class Platform } } - public static AxisAlignedBB getPrimaryBox( final AEPartLocation side, final int facadeThickness ) - { - switch( side ) - { - case DOWN: - return new AxisAlignedBB( 0.0, 0.0, 0.0, 1.0, ( facadeThickness ) / 16.0, 1.0 ); - case EAST: - return new AxisAlignedBB( ( 16.0 - facadeThickness ) / 16.0, 0.0, 0.0, 1.0, 1.0, 1.0 ); - case NORTH: - return new AxisAlignedBB( 0.0, 0.0, 0.0, 1.0, 1.0, ( facadeThickness ) / 16.0 ); - case SOUTH: - return new AxisAlignedBB( 0.0, 0.0, ( 16.0 - facadeThickness ) / 16.0, 1.0, 1.0, 1.0 ); - case UP: - return new AxisAlignedBB( 0.0, ( 16.0 - facadeThickness ) / 16.0, 0.0, 1.0, 1.0, 1.0 ); - case WEST: - return new AxisAlignedBB( 0.0, 0.0, 0.0, ( facadeThickness ) / 16.0, 1.0, 1.0 ); - default: - break; - } - return new AxisAlignedBB( 0, 0, 0, 1, 1, 1 ); - } - public static float getEyeOffset( final EntityPlayer player ) { assert player.worldObj.isRemote : "Valid only on client";