First pass of implementing rendering for crafting cubes (#7)

This commit is contained in:
Sebastian Hartte 2016-09-12 01:11:38 +02:00
parent f35a951b09
commit 5b6ed0cfd0
57 changed files with 980 additions and 173 deletions

View File

@ -21,23 +21,64 @@ package appeng.block.crafting;
import java.util.List;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraftforge.common.property.ExtendedBlockState;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import appeng.api.util.AEColor;
import appeng.client.UnlistedProperty;
import appeng.tile.crafting.TileCraftingMonitorTile;
public class BlockCraftingMonitor extends BlockCraftingUnit
{
public static final UnlistedProperty<AEColor> COLOR = new UnlistedProperty<>( "color", AEColor.class );
public static final UnlistedProperty<EnumFacing> FORWARD = new UnlistedProperty<>( "forward", EnumFacing.class );
public BlockCraftingMonitor()
{
super( CraftingUnitType.MONITOR );
this.setTileEntity( TileCraftingMonitorTile.class );
}
@Override
protected BlockStateContainer createBlockState()
{
return new ExtendedBlockState( this, getAEStates(), new IUnlistedProperty[] {
STATE,
COLOR,
FORWARD
} );
}
@Override
public IExtendedBlockState getExtendedState( IBlockState state, IBlockAccess world, BlockPos pos )
{
AEColor color = AEColor.Transparent;
EnumFacing forward = EnumFacing.NORTH;
TileCraftingMonitorTile te = getTileEntity( world, pos );
if( te != null )
{
color = te.getColor();
forward = te.getForward();
}
return super.getExtendedState( state, world, pos ).withProperty( COLOR, color ).withProperty( FORWARD, forward );
}
@Override
@SideOnly( Side.CLIENT )
public void getCheckedSubBlocks( final Item item, final CreativeTabs tabs, final List<ItemStack> itemStacks )

View File

@ -19,12 +19,14 @@
package appeng.block.crafting;
import java.util.EnumSet;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.properties.PropertyBool;
import net.minecraft.block.state.BlockStateContainer;
import net.minecraft.block.state.IBlockState;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
@ -32,10 +34,16 @@ import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.property.ExtendedBlockState;
import net.minecraftforge.common.property.IExtendedBlockState;
import net.minecraftforge.common.property.IUnlistedProperty;
import appeng.api.util.AEPartLocation;
import appeng.block.AEBaseTileBlock;
import appeng.client.UnlistedProperty;
import appeng.client.render.crafting.CraftingCubeState;
import appeng.core.sync.GuiBridge;
import appeng.tile.crafting.TileCraftingTile;
import appeng.util.Platform;
@ -43,8 +51,9 @@ import appeng.util.Platform;
public class BlockCraftingUnit extends AEBaseTileBlock
{
public static final PropertyBool POWERED = PropertyBool.create( "powered" );
public static final PropertyBool FORMED = PropertyBool.create( "formed" );
public static final PropertyBool POWERED = PropertyBool.create( "powered" );
public static final UnlistedProperty<CraftingCubeState> STATE = new UnlistedProperty<>( "state", CraftingCubeState.class );
public final CraftingUnitType type;
@ -61,7 +70,38 @@ public class BlockCraftingUnit extends AEBaseTileBlock
{
return new IProperty[] { POWERED, FORMED };
}
@Override
public IExtendedBlockState getExtendedState( IBlockState state, IBlockAccess world, BlockPos pos )
{
EnumSet<EnumFacing> connections = EnumSet.noneOf( EnumFacing.class );
for( EnumFacing facing : EnumFacing.values() )
{
if( isConnected( world, pos, facing ) )
{
connections.add( facing );
}
}
IExtendedBlockState extState = (IExtendedBlockState) state;
return extState.withProperty( STATE, new CraftingCubeState( connections ) );
}
private boolean isConnected( IBlockAccess world, BlockPos pos, EnumFacing side )
{
BlockPos adjacentPos = pos.offset( side );
return world.getBlockState( adjacentPos ).getBlock() instanceof BlockCraftingUnit;
}
@Override
protected BlockStateContainer createBlockState()
{
return new ExtendedBlockState( this, getAEStates(), new IUnlistedProperty[] { STATE } );
}
@Override
public IBlockState getStateFromMeta( final int meta )
{
@ -71,8 +111,8 @@ public class BlockCraftingUnit extends AEBaseTileBlock
@Override
public int getMetaFromState( final IBlockState state )
{
final boolean p = (boolean) state.getValue( POWERED );
final boolean f = (boolean) state.getValue( FORMED );
boolean p = state.getValue( POWERED );
boolean f = state.getValue( FORMED );
return ( p ? 1 : 0 ) | ( f ? 2 : 0 );
}

View File

@ -61,6 +61,7 @@ import appeng.api.parts.PartItemStack;
import appeng.api.parts.SelectedPart;
import appeng.api.util.AEColor;
import appeng.block.AEBaseTileBlock;
import appeng.client.UnlistedProperty;
import appeng.client.render.cablebus.CableBusBakedModel;
import appeng.client.render.cablebus.CableBusRenderState;
import appeng.core.AEConfig;
@ -81,7 +82,8 @@ import appeng.util.Platform;
public class BlockCableBus extends AEBaseTileBlock
{
public static final CableBusRenderStateProperty RENDER_STATE_PROPERTY = new CableBusRenderStateProperty();
public static final UnlistedProperty<CableBusRenderState> RENDER_STATE_PROPERTY =
new UnlistedProperty<>( "cable_bus_render_state", CableBusRenderState.class );
private static final ICableBusContainer NULL_CABLE_BUS = new NullCableBusContainer();

View File

@ -1,34 +0,0 @@
package appeng.block.networking;
import net.minecraftforge.common.property.IUnlistedProperty;
import appeng.parts.CableBusContainer;
public class CableBusContainerUnlistedProperty implements IUnlistedProperty<CableBusContainer>
{
@Override
public String getName()
{
return "bus";
}
@Override
public boolean isValid( CableBusContainer value )
{
return true;
}
@Override
public Class<CableBusContainer> getType()
{
return CableBusContainer.class;
}
@Override
public String valueToString( CableBusContainer value )
{
return null;
}
}

View File

@ -1,37 +0,0 @@
package appeng.block.networking;
import net.minecraftforge.common.property.IUnlistedProperty;
import appeng.client.render.cablebus.CableBusRenderState;
/**
* An unlisted property for the cable bus block's render state.
*/
public class CableBusRenderStateProperty implements IUnlistedProperty<CableBusRenderState>
{
@Override
public String getName()
{
return "cable_bus_render_state";
}
@Override
public boolean isValid( CableBusRenderState value )
{
return value != null;
}
@Override
public Class<CableBusRenderState> getType()
{
return CableBusRenderState.class;
}
@Override
public String valueToString( CableBusRenderState value )
{
return value.toString();
}
}

View File

@ -38,6 +38,7 @@ import net.minecraftforge.common.property.IUnlistedProperty;
import appeng.api.util.AEPartLocation;
import appeng.block.AEBaseTileBlock;
import appeng.client.UnlistedProperty;
import appeng.core.sync.GuiBridge;
import appeng.tile.storage.TileDrive;
import appeng.util.Platform;
@ -46,7 +47,7 @@ import appeng.util.Platform;
public class BlockDrive extends AEBaseTileBlock
{
public static final DriveSlotsStateProperty SLOTS_STATE = new DriveSlotsStateProperty();
public static final UnlistedProperty<DriveSlotsState> SLOTS_STATE = new UnlistedProperty<>("drive_slots_state", DriveSlotsState.class);
public BlockDrive()
{

View File

@ -1,50 +0,0 @@
/*
* 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 <http://www.gnu.org/licenses/lgpl>.
*/
package appeng.block.storage;
import net.minecraftforge.common.property.IUnlistedProperty;
public class DriveSlotsStateProperty implements IUnlistedProperty<DriveSlotsState>
{
@Override
public String getName()
{
return "drive_slots_state";
}
@Override
public boolean isValid( DriveSlotsState value )
{
return value != null;
}
@Override
public Class<DriveSlotsState> getType()
{
return DriveSlotsState.class;
}
@Override
public String valueToString( DriveSlotsState value )
{
return value.toString();
}
}

View File

@ -13,7 +13,7 @@ import net.minecraftforge.fml.relauncher.Side;
/**
* Registers a custom state mapper for a given block.
*/
public class StateMapperComponent implements InitComponent
public class StateMapperComponent implements PreInitComponent
{
private final Block block;
@ -27,7 +27,7 @@ public class StateMapperComponent implements InitComponent
}
@Override
public void initialize( Side side )
public void preInitialize( Side side )
{
ModelLoader.setCustomStateMapper( block, stateMapper );
if( stateMapper instanceof IResourceManagerReloadListener )

View File

@ -0,0 +1,48 @@
package appeng.client;
import net.minecraftforge.common.property.IUnlistedProperty;
/**
* A generic implementation for {@link IUnlistedProperty}.
* @param <T>
*/
public class UnlistedProperty<T> implements IUnlistedProperty<T>
{
private final String name;
private final Class<T> clazz;
public UnlistedProperty( String name, Class<T> clazz )
{
this.name = name;
this.clazz = clazz;
}
@Override
public String getName()
{
return name;
}
@Override
public boolean isValid( T value )
{
return value != null;
}
@Override
public Class<T> getType()
{
return clazz;
}
@Override
public String valueToString( T value )
{
return value.toString();
}
}

View File

@ -136,10 +136,10 @@ public class CubeBuilder {
v2 = texture.getInterpolatedV( 16 - y2 * 16 );
}
putVertex(builder, face, x2, y2, z1, u1, v2);
putVertex(builder, face, x2, y1, z1, u1, v1);
putVertex(builder, face, x1, y1, z1, u2, v1);
putVertex(builder, face, x1, y2, z1, u2, v2);
putVertex(builder, face, x2, y2, z1, u2, v2);
putVertex(builder, face, x2, y1, z1, u2, v1);
putVertex(builder, face, x1, y1, z1, u1, v1);
putVertex(builder, face, x1, y2, z1, u1, v2);
break;
case SOUTH:
if (customUv == null)
@ -178,10 +178,10 @@ public class CubeBuilder {
v2 = texture.getInterpolatedV( 16 - y2 * 16 );
}
putVertex(builder, face, x2, y2, z1, u1, v2);
putVertex(builder, face, x2, y2, z2, u2, v2);
putVertex(builder, face, x2, y1, z2, u2, v1);
putVertex(builder, face, x2, y1, z1, u1, v1);
putVertex(builder, face, x2, y2, z1, u2, v2);
putVertex(builder, face, x2, y2, z2, u1, v2);
putVertex(builder, face, x2, y1, z2, u1, v1);
putVertex(builder, face, x2, y1, z1, u2, v1);
break;
}

View File

@ -0,0 +1,294 @@
package appeng.client.render.crafting;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import javax.annotation.Nullable;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.block.model.ItemOverrideList;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.common.property.IExtendedBlockState;
import appeng.block.crafting.BlockCraftingUnit;
import appeng.client.render.cablebus.CubeBuilder;
/**
* The base model for baked models used by components of the crafting cube multi-block in it's formed state.
* Primarily this base class handles adding the "ring" that frames the multi-block structure and delegates
* rendering of the "inner" part of each block to the subclasses of this class.
*/
abstract class CraftingCubeBakedModel implements IBakedModel
{
private final VertexFormat format;
private final TextureAtlasSprite ringCorner;
private final TextureAtlasSprite ringHor;
private final TextureAtlasSprite ringVer;
CraftingCubeBakedModel( VertexFormat format, TextureAtlasSprite ringCorner, TextureAtlasSprite ringHor, TextureAtlasSprite ringVer )
{
this.format = format;
this.ringCorner = ringCorner;
this.ringHor = ringHor;
this.ringVer = ringVer;
}
@Override
public List<BakedQuad> getQuads( @Nullable IBlockState state, @Nullable EnumFacing side, long rand )
{
if( side == null )
{
return Collections.emptyList(); // No generic quads for this model
}
EnumSet<EnumFacing> connections = getConnections( state );
List<BakedQuad> quads = new ArrayList<>();
CubeBuilder builder = new CubeBuilder( format, quads );
builder.setDrawFaces( EnumSet.of( side ) );
// Add the quads for the ring that frames the entire multi-block structure
addRing( builder, side, connections );
// Calculate the bounds of the "inner" block that is framed by the border drawn above
float x2 = connections.contains( EnumFacing.EAST ) ? 16 : 13.01f;
float x1 = connections.contains( EnumFacing.WEST ) ? 0 : 2.99f;
float y2 = connections.contains( EnumFacing.UP ) ? 16 : 13.01f;
float y1 = connections.contains( EnumFacing.DOWN ) ? 0 : 2.99f;
float z2 = connections.contains( EnumFacing.SOUTH ) ? 16 : 13.01f;
float z1 = connections.contains( EnumFacing.NORTH ) ? 0 : 2.99f;
// On the axis of the side that we're currently drawing, extend the dimensions
// out to the outer face of the block
switch( side )
{
case DOWN:
case UP:
y1 = 0;
y2 = 16;
break;
case NORTH:
case SOUTH:
z1 = 0;
z2 = 16;
break;
case WEST:
case EAST:
x1 = 0;
x2 = 16;
break;
}
addInnerCube( side, state, builder, x1, y1, z1, x2, y2, z2 );
return quads;
}
private void addRing( CubeBuilder builder, @Nullable EnumFacing side, EnumSet<EnumFacing> connections )
{
// Fill in the corners
builder.setTexture( ringCorner );
addCornerCap( builder, connections, side, EnumFacing.UP, EnumFacing.EAST, EnumFacing.NORTH );
addCornerCap( builder, connections, side, EnumFacing.UP, EnumFacing.EAST, EnumFacing.SOUTH );
addCornerCap( builder, connections, side, EnumFacing.UP, EnumFacing.WEST, EnumFacing.NORTH );
addCornerCap( builder, connections, side, EnumFacing.UP, EnumFacing.WEST, EnumFacing.SOUTH );
addCornerCap( builder, connections, side, EnumFacing.DOWN, EnumFacing.EAST, EnumFacing.NORTH );
addCornerCap( builder, connections, side, EnumFacing.DOWN, EnumFacing.EAST, EnumFacing.SOUTH );
addCornerCap( builder, connections, side, EnumFacing.DOWN, EnumFacing.WEST, EnumFacing.NORTH );
addCornerCap( builder, connections, side, EnumFacing.DOWN, EnumFacing.WEST, EnumFacing.SOUTH );
// Fill in the remaining stripes of the face
for( EnumFacing a : EnumFacing.values() )
{
if( a == side || a == side.getOpposite() )
{
continue;
}
// Select the horizontal or vertical ring texture depending on which side we're filling in
if( ( side.getAxis() != EnumFacing.Axis.Y ) && ( a == EnumFacing.NORTH || a == EnumFacing.EAST || a == EnumFacing.WEST || a == EnumFacing.SOUTH ) )
{
builder.setTexture( ringVer );
}
else if( side.getAxis() == EnumFacing.Axis.Y && ( a == EnumFacing.EAST || a == EnumFacing.WEST ) )
{
builder.setTexture( ringVer );
}
else
{
builder.setTexture( ringHor );
}
// If there's an adjacent crafting cube block on side a, then the core of the block already extends
// fully to this side. So only bother drawing the stripe, if there's no connection.
if( !connections.contains( a ) )
{
// Note that since we're drawing something that "looks" 2-dimensional,
// two of the following will always be 0 and 16.
float x1 = 0, y1 = 0, z1 = 0, x2 = 16, y2 = 16, z2 = 16;
switch( a )
{
case DOWN:
y1 = 0;
y2 = 3;
break;
case UP:
y1 = 13.0f;
y2 = 16;
break;
case WEST:
x1 = 0;
x2 = 3;
break;
case EAST:
x1 = 13;
x2 = 16;
break;
case NORTH:
z1 = 0;
z2 = 3;
break;
case SOUTH:
z1 = 13;
z2 = 16;
break;
}
// Constraint the stripe in the two directions perpendicular to a in case there has been a corner
// drawn in those directions. Since a corner is drawn if the three touching faces dont have adjacent
// crafting cube blocks, we'd have to check for a, side, and the perpendicular direction. But in this
// block, we've already checked for side (due to face culling) and a (see above).
EnumFacing perpendicular = a.rotateAround( side.getAxis() );
for ( EnumFacing cornerCandidate: EnumSet.of(perpendicular, perpendicular.getOpposite()))
{
if( !connections.contains( cornerCandidate ) )
{
// There's a cap in this direction
switch( cornerCandidate )
{
case DOWN:
y1 = 3;
break;
case UP:
y2 = 13;
break;
case NORTH:
z1 = 3;
break;
case SOUTH:
z2 = 13;
break;
case WEST:
x1 = 3;
break;
case EAST:
x2 = 13;
break;
}
}
}
builder.addCube( x1, y1, z1, x2, y2, z2 );
}
}
}
/**
* Adds a 3x3x3 corner cap to the cube builder if there are no adjacent crafting cubes on that corner.
*/
private void addCornerCap( CubeBuilder builder, EnumSet<EnumFacing> connections, EnumFacing side, EnumFacing down, EnumFacing west, EnumFacing north )
{
if( connections.contains( down ) || connections.contains( west ) || connections.contains( north ) )
{
return;
}
// Only add faces for sides that can actually be seen (the outside of the cube)
if( side != down && side != west && side != north )
{
return;
}
float x1 = (west == EnumFacing.WEST ? 0 : 13);
float y1 = (down == EnumFacing.DOWN ? 0 : 13);
float z1 = (north == EnumFacing.NORTH ? 0 : 13);
float x2 = (west == EnumFacing.WEST ? 3 : 16);
float y2 = (down == EnumFacing.DOWN ? 3 : 16);
float z2 = (north == EnumFacing.NORTH ? 3 : 16);
builder.addCube( x1, y1, z1, x2, y2, z2 );
}
// Retrieve the cube connection state from the block state
// If none is present, just assume there are no adjacent crafting cube blocks
private static EnumSet<EnumFacing> getConnections( @Nullable IBlockState state )
{
if( !( state instanceof IExtendedBlockState ) )
{
return EnumSet.noneOf( EnumFacing.class );
}
IExtendedBlockState extState = (IExtendedBlockState) state;
CraftingCubeState cubeState = extState.getValue( BlockCraftingUnit.STATE );
if( cubeState == null )
{
return EnumSet.noneOf( EnumFacing.class );
}
return cubeState.getConnections();
}
protected abstract void addInnerCube( EnumFacing facing, IBlockState state, CubeBuilder builder, float x1, float y1, float z1, float x2, float y2, float z2 );
@Override
public boolean isAmbientOcclusion()
{
return false;
}
@Override
public boolean isGui3d()
{
return false;
}
@Override
public boolean isBuiltInRenderer()
{
return false;
}
@Override
public TextureAtlasSprite getParticleTexture()
{
return null;
}
@Override
public ItemCameraTransforms getItemCameraTransforms()
{
return ItemCameraTransforms.DEFAULT;
}
@Override
public ItemOverrideList getOverrides()
{
return ItemOverrideList.NONE;
}
}

View File

@ -0,0 +1,137 @@
package appeng.client.render.crafting;
import java.util.Collection;
import java.util.Collections;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.common.model.IModelState;
import net.minecraftforge.common.model.TRSRTransformation;
import appeng.block.crafting.BlockCraftingUnit;
import appeng.core.AppEng;
/**
* The built-in model for the connected texture crafting cube.
*/
class CraftingCubeModel implements IModel
{
private final static ResourceLocation RING_CORNER = texture( "ring_corner" );
private final static ResourceLocation RING_SIDE_HOR = texture( "ring_side_hor" );
private final static ResourceLocation RING_SIDE_VER = texture( "ring_side_ver" );
private final static ResourceLocation UNIT_BASE = texture( "unit_base" );
private final static ResourceLocation LIGHT_BASE = texture( "light_base" );
private final static ResourceLocation ACCELERATOR_LIGHT = texture( "accelerator_light" );
private final static ResourceLocation STORAGE_1K_LIGHT = texture( "storage_1k_light" );
private final static ResourceLocation STORAGE_4K_LIGHT = texture( "storage_4k_light" );
private final static ResourceLocation STORAGE_16K_LIGHT = texture( "storage_16k_light" );
private final static ResourceLocation STORAGE_64K_LIGHT = texture( "storage_64k_light" );
private final static ResourceLocation MONITOR_BASE = texture( "monitor_base" );
private final static ResourceLocation MONITOR_LIGHT_DARK = texture( "monitor_light_dark" );
private final static ResourceLocation MONITOR_LIGHT_MEDIUM = texture( "monitor_light_medium" );
private final static ResourceLocation MONITOR_LIGHT_BRIGHT = texture( "monitor_light_bright" );
private final BlockCraftingUnit.CraftingUnitType type;
CraftingCubeModel( BlockCraftingUnit.CraftingUnitType type )
{
this.type = type;
}
@Override
public Collection<ResourceLocation> getDependencies()
{
return Collections.emptyList();
}
@Override
public Collection<ResourceLocation> getTextures()
{
return ImmutableList.of(
RING_CORNER,
RING_SIDE_HOR,
RING_SIDE_VER,
UNIT_BASE,
LIGHT_BASE,
ACCELERATOR_LIGHT,
STORAGE_1K_LIGHT,
STORAGE_4K_LIGHT,
STORAGE_16K_LIGHT,
STORAGE_64K_LIGHT,
MONITOR_BASE,
MONITOR_LIGHT_DARK,
MONITOR_LIGHT_MEDIUM,
MONITOR_LIGHT_BRIGHT
);
}
@Override
public IBakedModel bake( IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter )
{
// Retrieve our textures and pass them on to the baked model
TextureAtlasSprite ringCorner = bakedTextureGetter.apply( RING_CORNER );
TextureAtlasSprite ringSideHor = bakedTextureGetter.apply( RING_SIDE_HOR );
TextureAtlasSprite ringSideVer = bakedTextureGetter.apply( RING_SIDE_VER );
switch( type )
{
case UNIT:
return new UnitBakedModel( format, ringCorner, ringSideHor, ringSideVer, bakedTextureGetter.apply( UNIT_BASE ) );
case ACCELERATOR:
case STORAGE_1K:
case STORAGE_4K:
case STORAGE_16K:
case STORAGE_64K:
return new LightBakedModel( format, ringCorner, ringSideHor, ringSideVer, bakedTextureGetter.apply( LIGHT_BASE ), getLightTexture( bakedTextureGetter, type ) );
case MONITOR:
return new MonitorBakedModel( format, ringCorner, ringSideHor, ringSideVer,
bakedTextureGetter.apply( UNIT_BASE ),
bakedTextureGetter.apply( MONITOR_BASE ),
bakedTextureGetter.apply( MONITOR_LIGHT_DARK ),
bakedTextureGetter.apply( MONITOR_LIGHT_MEDIUM ),
bakedTextureGetter.apply( MONITOR_LIGHT_BRIGHT )
);
default:
throw new IllegalArgumentException( "Unsupported crafting unit type: " + type );
}
}
private static TextureAtlasSprite getLightTexture( Function<ResourceLocation, TextureAtlasSprite> textureGetter, BlockCraftingUnit.CraftingUnitType type )
{
switch( type )
{
case ACCELERATOR:
return textureGetter.apply( ACCELERATOR_LIGHT );
case STORAGE_1K:
return textureGetter.apply( STORAGE_1K_LIGHT );
case STORAGE_4K:
return textureGetter.apply( STORAGE_4K_LIGHT );
case STORAGE_16K:
return textureGetter.apply( STORAGE_16K_LIGHT );
case STORAGE_64K:
return textureGetter.apply( STORAGE_64K_LIGHT );
default:
throw new IllegalArgumentException( "Crafting unit type " + type + " does not use a light texture." );
}
}
@Override
public IModelState getDefaultState()
{
return TRSRTransformation.identity();
}
private static ResourceLocation texture( String name )
{
return new ResourceLocation( AppEng.MOD_ID, "blocks/crafting/" + name );
}
}

View File

@ -0,0 +1,73 @@
package appeng.client.render.crafting;
import java.util.HashMap;
import java.util.Map;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.util.ResourceLocation;
import appeng.block.crafting.BlockCraftingUnit;
import appeng.bootstrap.BlockRenderingCustomizer;
import appeng.bootstrap.IBlockRendering;
import appeng.bootstrap.IItemRendering;
import appeng.core.AppEng;
/**
* Rendering customization for the crafting cube.
*/
public class CraftingCubeRendering extends BlockRenderingCustomizer
{
private final String registryName;
private final BlockCraftingUnit.CraftingUnitType type;
public CraftingCubeRendering( String registryName, BlockCraftingUnit.CraftingUnitType type )
{
this.registryName = registryName;
this.type = type;
}
@Override
public void customize( IBlockRendering rendering, IItemRendering itemRendering )
{
ResourceLocation baseName = new ResourceLocation( AppEng.MOD_ID, registryName );
// Disable auto-rotation
rendering.modelCustomizer( ( loc, model ) -> model );
// This is the standard blockstate model
ModelResourceLocation defaultModel = new ModelResourceLocation( baseName, "normal" );
// This is the built-in model
String builtInName = "models/block/crafting/" + registryName + "/builtin";
ModelResourceLocation builtInModelName = new ModelResourceLocation( new ResourceLocation( AppEng.MOD_ID, builtInName ), "normal" );
rendering.builtInModel( builtInName, new CraftingCubeModel( type ) );
rendering.stateMapper( block -> mapState( block, defaultModel, builtInModelName ) );
}
private Map<IBlockState, ModelResourceLocation> mapState( Block block, ModelResourceLocation defaultModel, ModelResourceLocation formedModel )
{
Map<IBlockState, ModelResourceLocation> result = new HashMap<>();
for( IBlockState state : block.getBlockState().getValidStates() )
{
if( state.getValue( BlockCraftingUnit.FORMED ) )
{
// Always use the builtin model if the multiblock is formed
result.put( state, formedModel );
}
else
{
// Use the default model
result.put( state, defaultModel );
}
}
return result;
}
}

View File

@ -0,0 +1,27 @@
package appeng.client.render.crafting;
import java.util.EnumSet;
import net.minecraft.util.EnumFacing;
/**
* Transports the rendering state for a block of a crafting cube.
*/
public final class CraftingCubeState
{
// Contains information on which sides of the block are connected to other parts of a formed crafting cube
private final EnumSet<EnumFacing> connections;
public CraftingCubeState( EnumSet<EnumFacing> connections )
{
this.connections = connections;
}
public EnumSet<EnumFacing> getConnections()
{
return connections;
}
}

View File

@ -0,0 +1,42 @@
package appeng.client.render.crafting;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
import appeng.block.crafting.BlockCraftingUnit;
import appeng.client.render.cablebus.CubeBuilder;
/**
* Crafting cube baked model that adds a full-bright light texture on top of a normal base texture onto the inner cube.
* The light texture is only drawn fullbright if the multiblock is currently powered.
*/
class LightBakedModel extends CraftingCubeBakedModel
{
private final TextureAtlasSprite baseTexture;
private final TextureAtlasSprite lightTexture;
LightBakedModel( VertexFormat format, TextureAtlasSprite ringCorner, TextureAtlasSprite ringHor, TextureAtlasSprite ringVer, TextureAtlasSprite baseTexture, TextureAtlasSprite lightTexture )
{
super( format, ringCorner, ringHor, ringVer );
this.baseTexture = baseTexture;
this.lightTexture = lightTexture;
}
@Override
protected void addInnerCube( EnumFacing facing, IBlockState state, CubeBuilder builder, float x1, float y1, float z1, float x2, float y2, float z2 )
{
builder.setTexture( baseTexture );
builder.addCube( x1, y1, z1, x2, y2, z2 );
boolean powered = state.getValue( BlockCraftingUnit.POWERED );
builder.setRenderFullBright( powered );
builder.setTexture( lightTexture );
builder.addCube( x1, y1, z1, x2, y2, z2 );
}
}

View File

@ -0,0 +1,116 @@
package appeng.client.render.crafting;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.common.property.IExtendedBlockState;
import appeng.api.util.AEColor;
import appeng.block.crafting.BlockCraftingMonitor;
import appeng.client.render.cablebus.CubeBuilder;
/**
* The baked model for the crafting monitor. Please note that this model doesn't handle the item being displayed. That is handled by a TESR.
* Instead, this model adds 3 layered light textures using the [dark|medium|bright] color variants of the attached bus color. The textures
* are full-bright if the cube is powered.
*/
class MonitorBakedModel extends CraftingCubeBakedModel
{
private final TextureAtlasSprite chassisTexture;
private final TextureAtlasSprite baseTexture;
private final TextureAtlasSprite lightDarkTexture;
private final TextureAtlasSprite lightMediumTexture;
private final TextureAtlasSprite lightBrightTexture;
MonitorBakedModel( VertexFormat format,
TextureAtlasSprite ringCorner,
TextureAtlasSprite ringHor,
TextureAtlasSprite ringVer,
TextureAtlasSprite chassisTexture,
TextureAtlasSprite baseTexture,
TextureAtlasSprite lightDarkTexture,
TextureAtlasSprite lightMediumTexture,
TextureAtlasSprite lightBrightTexture )
{
super( format, ringCorner, ringHor, ringVer );
this.chassisTexture = chassisTexture;
this.baseTexture = baseTexture;
this.lightDarkTexture = lightDarkTexture;
this.lightMediumTexture = lightMediumTexture;
this.lightBrightTexture = lightBrightTexture;
}
@Override
protected void addInnerCube( EnumFacing side, IBlockState state, CubeBuilder builder, float x1, float y1, float z1, float x2, float y2, float z2 )
{
EnumFacing forward = getForward( state );
// For sides other than the front, use the chassis texture
if( side != forward )
{
builder.setTexture( chassisTexture );
builder.addCube( x1, y1, z1, x2, y2, z2 );
return;
}
builder.setTexture( baseTexture );
builder.addCube( x1, y1, z1, x2, y2, z2 );
// Now add the three layered light textures
AEColor color = getColor( state );
boolean powered = state.getValue( BlockCraftingMonitor.POWERED );
builder.setRenderFullBright( powered );
builder.setColorRGB( color.whiteVariant );
builder.setTexture( lightBrightTexture );
builder.addCube( x1, y1, z1, x2, y2, z2 );
builder.setColorRGB( color.mediumVariant );
builder.setTexture( lightMediumTexture );
builder.addCube( x1, y1, z1, x2, y2, z2 );
builder.setColorRGB( color.blackVariant );
builder.setTexture( lightDarkTexture );
builder.addCube( x1, y1, z1, x2, y2, z2 );
}
private static AEColor getColor( IBlockState state )
{
if( state instanceof IExtendedBlockState )
{
IExtendedBlockState extState = (IExtendedBlockState) state;
AEColor color = extState.getValue( BlockCraftingMonitor.COLOR );
if( color != null )
{
return color;
}
}
return AEColor.Transparent;
}
private static EnumFacing getForward( IBlockState state )
{
if( state instanceof IExtendedBlockState )
{
IExtendedBlockState extState = (IExtendedBlockState) state;
EnumFacing forward = extState.getValue( BlockCraftingMonitor.FORWARD );
if( forward != null )
{
return forward;
}
}
return EnumFacing.NORTH;
}
}

View File

@ -0,0 +1,32 @@
package appeng.client.render.crafting;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.util.EnumFacing;
import appeng.client.render.cablebus.CubeBuilder;
/**
* A simple crafting unit model that uses an un-lit texture for the inner block.
*/
class UnitBakedModel extends CraftingCubeBakedModel
{
private final TextureAtlasSprite unitTexture;
UnitBakedModel( VertexFormat format, TextureAtlasSprite ringCorner, TextureAtlasSprite ringHor, TextureAtlasSprite ringVer, TextureAtlasSprite unitTexture )
{
super( format, ringCorner, ringHor, ringVer );
this.unitTexture = unitTexture;
}
@Override
protected void addInnerCube( EnumFacing facing, IBlockState state, CubeBuilder builder, float x1, float y1, float z1, float x2, float y2, float z2 )
{
builder.setTexture( unitTexture );
builder.addCube( x1, y1, z1, x2, y2, z2 );
}
}

View File

@ -83,6 +83,7 @@ import appeng.bootstrap.BlockRenderingCustomizer;
import appeng.bootstrap.FeatureFactory;
import appeng.bootstrap.IBlockRendering;
import appeng.bootstrap.IItemRendering;
import appeng.client.render.crafting.CraftingCubeRendering;
import appeng.client.render.model.GlassModel;
import appeng.core.AppEng;
import appeng.core.features.AEFeature;
@ -321,28 +322,35 @@ public final class ApiBlocks implements IBlocks
FeatureFactory crafting = registry.features( AEFeature.CraftingCPU );
this.craftingUnit = crafting.block( "crafting_unit", () -> new BlockCraftingUnit( CraftingUnitType.UNIT ) )
.rendering( new CraftingCubeRendering( "crafting_unit", CraftingUnitType.UNIT ) )
.useCustomItemModel()
.build();
this.craftingAccelerator = crafting.block( "crafting_accelerator", () -> new BlockCraftingUnit( CraftingUnitType.ACCELERATOR ) )
.rendering( new CraftingCubeRendering( "crafting_accelerator", CraftingUnitType.ACCELERATOR ) )
.useCustomItemModel()
.build();
this.craftingStorage1k = crafting.block( "crafting_storage_1k", () -> new BlockCraftingStorage( CraftingUnitType.STORAGE_1K ) )
.item( ItemCraftingStorage::new )
.rendering( new CraftingCubeRendering( "crafting_storage_1k", CraftingUnitType.STORAGE_1K ) )
.useCustomItemModel()
.build();
this.craftingStorage4k = crafting.block( "crafting_storage_4k", () -> new BlockCraftingStorage( CraftingUnitType.STORAGE_4K ) )
.item( ItemCraftingStorage::new )
.rendering( new CraftingCubeRendering( "crafting_storage_4k", CraftingUnitType.STORAGE_4K ) )
.useCustomItemModel()
.build();
this.craftingStorage16k = crafting.block( "crafting_storage_16k", () -> new BlockCraftingStorage( CraftingUnitType.STORAGE_16K ) )
.item( ItemCraftingStorage::new )
.rendering( new CraftingCubeRendering( "crafting_storage_16k", CraftingUnitType.STORAGE_16K ) )
.useCustomItemModel()
.build();
this.craftingStorage64k = crafting.block( "crafting_storage_64k", () -> new BlockCraftingStorage( CraftingUnitType.STORAGE_64K ) )
.item( ItemCraftingStorage::new )
.rendering( new CraftingCubeRendering( "crafting_storage_64k", CraftingUnitType.STORAGE_64K ) )
.useCustomItemModel()
.build();
this.craftingMonitor = crafting.block( "crafting_monitor", BlockCraftingMonitor::new )
.rendering( new CraftingCubeRendering( "crafting_monitor", CraftingUnitType.MONITOR ) )
.useCustomItemModel()
.build();

View File

@ -465,6 +465,10 @@ public final class CraftingCPUCluster implements IAECluster, ICraftingCPU
private TileCraftingTile getCore()
{
if( this.machineSrc == null )
{
return null;
}
return (TileCraftingTile) this.machineSrc.via;
}

View File

@ -158,11 +158,18 @@ public class TileCraftingTile extends AENetworkTile implements IAEMultiBlock, IP
}
final IBlockState current = this.worldObj.getBlockState( this.pos );
final IBlockState newState = current.withProperty( BlockCraftingUnit.POWERED, power ).withProperty( BlockCraftingUnit.FORMED, formed );
if( current != newState )
// The tile might try to update while being destroyed
if( current.getBlock() instanceof BlockCraftingUnit )
{
this.worldObj.setBlockState( this.pos, newState );
final IBlockState newState = current.withProperty( BlockCraftingUnit.POWERED, power ).withProperty( BlockCraftingUnit.FORMED, formed );
if( current != newState )
{
// Not using flag 2 here (only send to clients, prevent block update) will cause infinite loops
// In case there is an inconsistency in the crafting clusters.
this.worldObj.setBlockState( this.pos, newState, 2 );
}
}
if( updateFormed )

View File

@ -0,0 +1,5 @@
{
"variants": {
"normal": { "model": "appliedenergistics2:crafting/accelerator" }
}
}

View File

@ -0,0 +1,5 @@
{
"variants": {
"normal": { "model": "appliedenergistics2:crafting/monitor" }
}
}

View File

@ -0,0 +1,5 @@
{
"variants": {
"normal": { "model": "appliedenergistics2:crafting/storage_16k" }
}
}

View File

@ -0,0 +1,5 @@
{
"variants": {
"normal": { "model": "appliedenergistics2:crafting/storage_1k" }
}
}

View File

@ -0,0 +1,5 @@
{
"variants": {
"normal": { "model": "appliedenergistics2:crafting/storage_4k" }
}
}

View File

@ -0,0 +1,5 @@
{
"variants": {
"normal": { "model": "appliedenergistics2:crafting/storage_64k" }
}
}

View File

@ -0,0 +1,5 @@
{
"variants": {
"normal": { "model": "appliedenergistics2:crafting/unit" }
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/accelerator"
}
}

View File

@ -0,0 +1,11 @@
{
"parent": "block/cube",
"textures": {
"north": "appliedenergistics2:blocks/crafting/monitor",
"east": "appliedenergistics2:blocks/crafting/unit",
"west": "appliedenergistics2:blocks/crafting/unit",
"south": "appliedenergistics2:blocks/crafting/unit",
"up": "appliedenergistics2:blocks/crafting/unit",
"down": "appliedenergistics2:blocks/crafting/unit"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/storage_16k"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/storage_1k"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/storage_4k"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/storage_64k"
}
}

View File

@ -0,0 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/unit"
}
}

View File

@ -1,6 +1,3 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/accelerator"
}
"parent": "appliedenergistics2:block/crafting/accelerator"
}

View File

@ -1,11 +1,3 @@
{
"parent": "block/cube",
"textures": {
"north": "appliedenergistics2:blocks/crafting/monitor",
"east": "appliedenergistics2:blocks/crafting/unit",
"west": "appliedenergistics2:blocks/crafting/unit",
"south": "appliedenergistics2:blocks/crafting/unit",
"up": "appliedenergistics2:blocks/crafting/unit",
"down": "appliedenergistics2:blocks/crafting/unit"
}
"parent": "appliedenergistics2:block/crafting/monitor"
}

View File

@ -1,6 +1,3 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/storage_16k"
}
"parent": "appliedenergistics2:block/crafting/storage_16k"
}

View File

@ -1,6 +1,3 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/storage_1k"
}
"parent": "appliedenergistics2:block/crafting/storage_1k"
}

View File

@ -1,6 +1,3 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/storage_4k"
}
"parent": "appliedenergistics2:block/crafting/storage_4k"
}

View File

@ -1,6 +1,3 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/storage_64k"
}
"parent": "appliedenergistics2:block/crafting/storage_64k"
}

View File

@ -1,6 +1,3 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/crafting/unit"
}
"parent": "appliedenergistics2:block/crafting/unit"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 B