From d48e7e1f6dede05b345fbaeee33fecf714d4b39f Mon Sep 17 00:00:00 2001 From: Sebastian Hartte Date: Thu, 15 Sep 2016 01:26:53 +0200 Subject: [PATCH] Implemented spatial pylon rendering. --- .../block/spatial/BlockSpatialPylon.java | 42 ++ .../client/render/cablebus/CubeBuilder.java | 600 +++++++++++------- .../spatial/SpatialPylonBakedModel.java | 245 +++++++ .../render/spatial/SpatialPylonModel.java | 65 ++ .../render/spatial/SpatialPylonRendering.java | 41 ++ .../spatial/SpatialPylonStateProperty.java | 38 ++ .../spatial/SpatialPylonTextureType.java | 15 + .../core/api/definitions/ApiBlocks.java | 2 + 8 files changed, 831 insertions(+), 217 deletions(-) create mode 100644 src/main/java/appeng/client/render/spatial/SpatialPylonBakedModel.java create mode 100644 src/main/java/appeng/client/render/spatial/SpatialPylonModel.java create mode 100644 src/main/java/appeng/client/render/spatial/SpatialPylonRendering.java create mode 100644 src/main/java/appeng/client/render/spatial/SpatialPylonStateProperty.java create mode 100644 src/main/java/appeng/client/render/spatial/SpatialPylonTextureType.java diff --git a/src/main/java/appeng/block/spatial/BlockSpatialPylon.java b/src/main/java/appeng/block/spatial/BlockSpatialPylon.java index b4fd9a11..dc49453b 100644 --- a/src/main/java/appeng/block/spatial/BlockSpatialPylon.java +++ b/src/main/java/appeng/block/spatial/BlockSpatialPylon.java @@ -20,24 +20,47 @@ package appeng.block.spatial; import net.minecraft.block.Block; +import net.minecraft.block.state.BlockStateContainer; import net.minecraft.block.state.IBlockState; +import net.minecraft.util.BlockRenderLayer; 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.block.AEBaseTileBlock; +import appeng.client.render.spatial.SpatialPylonStateProperty; import appeng.helpers.AEGlassMaterial; import appeng.tile.spatial.TileSpatialPylon; + public class BlockSpatialPylon extends AEBaseTileBlock { + public static final SpatialPylonStateProperty STATE = new SpatialPylonStateProperty(); + public BlockSpatialPylon() { super( AEGlassMaterial.INSTANCE ); this.setTileEntity( TileSpatialPylon.class ); } + @Override + protected BlockStateContainer createBlockState() + { + return new ExtendedBlockState( this, getAEStates(), new IUnlistedProperty[] { STATE } ); + } + + @Override + public IBlockState getExtendedState( IBlockState state, IBlockAccess world, BlockPos pos ) + { + IExtendedBlockState extState = (IExtendedBlockState) state; + + return extState.withProperty( STATE, getDisplayState( world, pos ) ); + } + @Override public void neighborChanged( final IBlockState state, final World w, final BlockPos pos, final Block neighborBlock ) { @@ -58,4 +81,23 @@ public class BlockSpatialPylon extends AEBaseTileBlock } return super.getLightValue( state, w, pos ); } + + private int getDisplayState( IBlockAccess world, BlockPos pos ) + { + TileSpatialPylon te = getTileEntity( world, pos ); + + if( te == null ) + { + return 0; + } + + return te.getDisplayBits(); + } + + @Override + public BlockRenderLayer getBlockLayer() + { + return BlockRenderLayer.CUTOUT; + } + } diff --git a/src/main/java/appeng/client/render/cablebus/CubeBuilder.java b/src/main/java/appeng/client/render/cablebus/CubeBuilder.java index 2f3312ba..968df6fb 100644 --- a/src/main/java/appeng/client/render/cablebus/CubeBuilder.java +++ b/src/main/java/appeng/client/render/cablebus/CubeBuilder.java @@ -7,6 +7,8 @@ import java.util.EnumSet; import java.util.List; import javax.vecmath.Vector4f; +import com.google.common.base.Preconditions; + import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; @@ -19,257 +21,421 @@ import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; /** * Builds the quads for a cube. */ -public class CubeBuilder { +public class CubeBuilder +{ - private VertexFormat format; + private VertexFormat format; - private final List output; + private final List output; - private final EnumMap textures = new EnumMap<>(EnumFacing.class); + private final EnumMap textures = new EnumMap<>( EnumFacing.class ); - private EnumSet drawFaces = EnumSet.allOf(EnumFacing.class); + private EnumSet drawFaces = EnumSet.allOf( EnumFacing.class ); - private final EnumMap customUv = new EnumMap<>(EnumFacing.class); + private final EnumMap customUv = new EnumMap<>( EnumFacing.class ); - private int color = 0xFFFFFFFF; + private byte[] uvRotations = new byte[EnumFacing.values().length]; - private boolean renderFullBright; + private int color = 0xFFFFFFFF; - public CubeBuilder(VertexFormat format, List output) { - this.output = output; - this.format = format; - } + private boolean renderFullBright; - public CubeBuilder(VertexFormat format) { - this(format, new ArrayList<>(6)); - } + public CubeBuilder( VertexFormat format, List output ) + { + this.output = output; + this.format = format; + } - public void addCube(float x1, float y1, float z1, float x2, float y2, float z2) { - x1 /= 16.0f; - y1 /= 16.0f; - z1 /= 16.0f; - x2 /= 16.0f; - y2 /= 16.0f; - z2 /= 16.0f; + public CubeBuilder( VertexFormat format ) + { + this( format, new ArrayList<>( 6 ) ); + } - // If brightness is forced to specific values, extend the vertex format to contain the multi-texturing lightmap offset - VertexFormat savedFormat = null; - if (renderFullBright) { - savedFormat = format; - format = new VertexFormat(savedFormat); - if (!format.getElements().contains(DefaultVertexFormats.TEX_2S)) { - format.addElement(DefaultVertexFormats.TEX_2S); - } - } + public void addCube( float x1, float y1, float z1, float x2, float y2, float z2 ) + { + x1 /= 16.0f; + y1 /= 16.0f; + z1 /= 16.0f; + x2 /= 16.0f; + y2 /= 16.0f; + z2 /= 16.0f; - for (EnumFacing face : drawFaces) { - putFace(face, x1, y1, z1, x2, y2, z2); - } + // If brightness is forced to specific values, extend the vertex format to contain the multi-texturing lightmap offset + VertexFormat savedFormat = null; + if( renderFullBright ) + { + savedFormat = format; + format = new VertexFormat( savedFormat ); + if( !format.getElements().contains( DefaultVertexFormats.TEX_2S ) ) + { + format.addElement( DefaultVertexFormats.TEX_2S ); + } + } - // Restore old format - if (savedFormat != null) { - format = savedFormat; - } - } + for( EnumFacing face : drawFaces ) + { + putFace( face, x1, y1, z1, x2, y2, z2 ); + } - private void putFace(EnumFacing face, - float x1, float y1, float z1, - float x2, float y2, float z2 - ) { + // Restore old format + if( savedFormat != null ) + { + format = savedFormat; + } + } - TextureAtlasSprite texture = textures.get(face); + private static final class UvVector + { + float u1; + float u2; + float v1; + float v2; + } - UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder(format); - builder.setTexture(texture); - builder.setQuadOrientation(face); + private void putFace( EnumFacing face, + float x1, float y1, float z1, + float x2, float y2, float z2 + ) + { - float u1 = 0; - float v1 = 0; - float u2 = 0; - float v2 = 0; + TextureAtlasSprite texture = textures.get( face ); - // The user might have set specific UV coordinates for this face - Vector4f customUv = this.customUv.get( face ); - if( customUv != null ) - { - u1 = texture.getInterpolatedU( customUv.x ); - v1 = texture.getInterpolatedV( customUv.y ); - u2 = texture.getInterpolatedU( customUv.z ); - v2 = texture.getInterpolatedV( customUv.w ); - } + UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder( format ); + builder.setTexture( texture ); + builder.setQuadOrientation( face ); - switch (face) { - case DOWN: - if (customUv == null) - { - u1 = texture.getInterpolatedU( x1 * 16 ); - v1 = texture.getInterpolatedV( z1 * 16 ); - u2 = texture.getInterpolatedU( x2 * 16 ); - v2 = texture.getInterpolatedV( z2 * 16 ); - } + UvVector uv = new UvVector(); - putVertex(builder, face, x2, y1, z1, u2, v1); - putVertex(builder, face, x2, y1, z2, u2, v2); - putVertex(builder, face, x1, y1, z2, u1, v2); - putVertex(builder, face, x1, y1, z1, u1, v1); - break; - case UP: - if (customUv == null) - { - u1 = texture.getInterpolatedU( x1 * 16 ); - v1 = texture.getInterpolatedV( z1 * 16 ); - u2 = texture.getInterpolatedU( x2 * 16 ); - v2 = texture.getInterpolatedV( z2 * 16 ); - } + // The user might have set specific UV coordinates for this face + Vector4f customUv = this.customUv.get( face ); + if( customUv != null ) + { + uv.u1 = texture.getInterpolatedU( customUv.x ); + uv.v1 = texture.getInterpolatedV( customUv.y ); + uv.u2 = texture.getInterpolatedU( customUv.z ); + uv.v2 = texture.getInterpolatedV( customUv.w ); + } + else + { + uv = getDefaultUv( face, texture, x1, y1, z1, x2, y2, z2 ); + } - putVertex(builder, face, x1, y2, z1, u1, v1); - putVertex(builder, face, x1, y2, z2, u1, v2); - putVertex(builder, face, x2, y2, z2, u2, v2); - putVertex(builder, face, x2, y2, z1, u2, v1); - break; - case NORTH: - if (customUv == null) - { - u1 = texture.getInterpolatedU( x1 * 16 ); - v1 = texture.getInterpolatedV( 16 - y1 * 16 ); - u2 = texture.getInterpolatedU( x2 * 16 ); - v2 = texture.getInterpolatedV( 16 - y2 * 16 ); - } + switch( face ) + { + case DOWN: + putVertexTR( builder, face, x2, y1, z1, uv ); + putVertexBR( builder, face, x2, y1, z2, uv ); + putVertexBL( builder, face, x1, y1, z2, uv ); + putVertexTL( builder, face, x1, y1, z1, uv ); + break; + case UP: + putVertexTL( builder, face, x1, y2, z1, uv ); + putVertexBL( builder, face, x1, y2, z2, uv ); + putVertexBR( builder, face, x2, y2, z2, uv ); + putVertexTR( builder, face, x2, y2, z1, uv ); + break; + case NORTH: + putVertexBR( builder, face, x2, y2, z1, uv ); + putVertexTR( builder, face, x2, y1, z1, uv ); + putVertexTL( builder, face, x1, y1, z1, uv ); + putVertexBL( builder, face, x1, y2, z1, uv ); + break; + case SOUTH: + putVertexBL( builder, face, x1, y2, z2, uv ); + putVertexTL( builder, face, x1, y1, z2, uv ); + putVertexTR( builder, face, x2, y1, z2, uv ); + putVertexBR( builder, face, x2, y2, z2, uv ); + break; + case WEST: + putVertexTL( builder, face, x1, y1, z1, uv ); + putVertexTR( builder, face, x1, y1, z2, uv ); + putVertexBR( builder, face, x1, y2, z2, uv ); + putVertexBL( builder, face, x1, y2, z1, uv ); + break; + case EAST: + putVertexBR( builder, face, x2, y2, z1, uv ); + putVertexBL( builder, face, x2, y2, z2, uv ); + putVertexTL( builder, face, x2, y1, z2, uv ); + putVertexTR( builder, face, x2, y1, z1, uv ); + break; + } - 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) - { - u1 = texture.getInterpolatedU( x1 * 16 ); - v1 = texture.getInterpolatedV( 16 - y1 * 16 ); - u2 = texture.getInterpolatedU( x2 * 16 ); - v2 = texture.getInterpolatedV( 16 - y2 * 16 ); - } + int[] vertexData = builder.build().getVertexData(); + output.add( new BakedQuad( vertexData, -1, face, texture, true, format ) ); + } - putVertex(builder, face, x1, y2, z2, u1, v2); - putVertex(builder, face, x1, y1, z2, u1, v1); - putVertex(builder, face, x2, y1, z2, u2, v1); - putVertex(builder, face, x2, y2, z2, u2, v2); - break; - case WEST: - if (customUv == null) - { - u1 = texture.getInterpolatedU( z1 * 16 ); - v1 = texture.getInterpolatedV( 16 - y1 * 16 ); - u2 = texture.getInterpolatedU( z2 * 16 ); - v2 = texture.getInterpolatedV( 16 - y2 * 16 ); - } + private UvVector getDefaultUv( EnumFacing face, TextureAtlasSprite texture, + float x1, float y1, float z1, + float x2, float y2, float z2 ) + { - putVertex(builder, face, x1, y1, z1, u1, v1); - putVertex(builder, face, x1, y1, z2, u2, v1); - putVertex(builder, face, x1, y2, z2, u2, v2); - putVertex(builder, face, x1, y2, z1, u1, v2); - break; - case EAST: - if (customUv == null) - { - u1 = texture.getInterpolatedU( z2 * 16 ); - v1 = texture.getInterpolatedV( 16 - y1 * 16 ); - u2 = texture.getInterpolatedU( z1 * 16 ); - v2 = texture.getInterpolatedV( 16 - y2 * 16 ); - } + UvVector uv = new UvVector(); - 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; - } + switch( face ) + { + case DOWN: + uv.u1 = texture.getInterpolatedU( x1 * 16 ); + uv.v1 = texture.getInterpolatedV( z1 * 16 ); + uv.u2 = texture.getInterpolatedU( x2 * 16 ); + uv.v2 = texture.getInterpolatedV( z2 * 16 ); + break; + case UP: + uv.u1 = texture.getInterpolatedU( x1 * 16 ); + uv.v1 = texture.getInterpolatedV( z1 * 16 ); + uv.u2 = texture.getInterpolatedU( x2 * 16 ); + uv.v2 = texture.getInterpolatedV( z2 * 16 ); + break; + case NORTH: + uv.u1 = texture.getInterpolatedU( x1 * 16 ); + uv.v1 = texture.getInterpolatedV( 16 - y1 * 16 ); + uv.u2 = texture.getInterpolatedU( x2 * 16 ); + uv.v2 = texture.getInterpolatedV( 16 - y2 * 16 ); + break; + case SOUTH: + uv.u1 = texture.getInterpolatedU( x1 * 16 ); + uv.v1 = texture.getInterpolatedV( 16 - y1 * 16 ); + uv.u2 = texture.getInterpolatedU( x2 * 16 ); + uv.v2 = texture.getInterpolatedV( 16 - y2 * 16 ); + break; + case WEST: + uv.u1 = texture.getInterpolatedU( z1 * 16 ); + uv.v1 = texture.getInterpolatedV( 16 - y1 * 16 ); + uv.u2 = texture.getInterpolatedU( z2 * 16 ); + uv.v2 = texture.getInterpolatedV( 16 - y2 * 16 ); + break; + case EAST: + uv.u1 = texture.getInterpolatedU( z2 * 16 ); + uv.v1 = texture.getInterpolatedV( 16 - y1 * 16 ); + uv.u2 = texture.getInterpolatedU( z1 * 16 ); + uv.v2 = texture.getInterpolatedV( 16 - y2 * 16 ); + break; + } - int[] vertexData = builder.build().getVertexData(); - output.add(new BakedQuad(vertexData, -1, face, texture, true, format)); - } + return uv; + } - private void putVertex(UnpackedBakedQuad.Builder builder, EnumFacing face, float x, float y, float z, float u, float v) { - VertexFormat format = builder.getVertexFormat(); + // uv.u1, uv.v1 + private void putVertexTL( UnpackedBakedQuad.Builder builder, EnumFacing face, float x, float y, float z, UvVector uv ) + { + float u, v; - for (int i = 0; i < format.getElementCount(); i++) { - VertexFormatElement e = format.getElement(i); - switch (e.getUsage()) { - case POSITION: - builder.put(i, x, y, z); - break; - case NORMAL: - builder.put(i, - face.getFrontOffsetX(), - face.getFrontOffsetY(), - face.getFrontOffsetZ()); - break; - case COLOR: - // Color format is RGBA - float r = (color >> 16 & 0xFF) / 255f; - float g = (color >> 8 & 0xFF) / 255f; - float b = (color & 0xFF) / 255f; - float a = (color >> 24 & 0xFF) / 255f; - builder.put(i, r, g, b, a); - break; - case UV: - if (e.getIndex() == 0) { - builder.put(i, u, v); - } else { - // Force Brightness to 15, this is for full bright mode - // this vertex element will only be present in that case - final float lightMapU = (float) (15 * 0x20) / 0xFFFF; - final float lightMapV = (float) (15 * 0x20) / 0xFFFF; - builder.put(i, lightMapU, lightMapV); - } - break; - default: - builder.put(i); - break; - } - } - } + switch (uvRotations[face.ordinal()]) { + default: + case 0: + u = uv.u1; + v = uv.v1; + break; + case 1: // 90° clockwise + u = uv.u1; + v = uv.v2; + break; + case 2: // 180° clockwise + u = uv.u2; + v = uv.v2; + break; + case 3: // 270° clockwise + u = uv.u2; + v = uv.v1; + break; + } - public void setTexture(TextureAtlasSprite texture) { - for (EnumFacing face : EnumFacing.values()) { - textures.put(face, texture); - } - } + putVertex(builder, face, x, y, z, u, v ); + } - public void setTextures(TextureAtlasSprite up, TextureAtlasSprite down, TextureAtlasSprite north, TextureAtlasSprite south, TextureAtlasSprite east, TextureAtlasSprite west) { - textures.put(EnumFacing.UP, up); - textures.put(EnumFacing.DOWN, down); - textures.put(EnumFacing.NORTH, north); - textures.put(EnumFacing.SOUTH, south); - textures.put(EnumFacing.EAST, east); - textures.put(EnumFacing.WEST, west); - } + // uv.u2, uv.v1 + private void putVertexTR( UnpackedBakedQuad.Builder builder, EnumFacing face, float x, float y, float z, UvVector uv ) + { + float u; + float v; - public void setDrawFaces(EnumSet drawFaces) { - this.drawFaces = drawFaces; - } + switch (uvRotations[face.ordinal()]) { + default: + case 0: + u = uv.u2; + v = uv.v1; + break; + case 1: // 90° clockwise + u = uv.u1; + v = uv.v1; + break; + case 2: // 180° clockwise + u = uv.u1; + v = uv.v2; + break; + case 3: // 270° clockwise + u = uv.u2; + v = uv.v2; + break; + } + putVertex(builder, face, x, y, z, u, v ); + } - public void setColor(int color) { - this.color = color; - } + // uv.u2, uv.v2 + private void putVertexBR( UnpackedBakedQuad.Builder builder, EnumFacing face, float x, float y, float z, UvVector uv ) + { - /** - * Sets the vertex color for future vertices to the given RGB value, and forces the alpha component to 255. - */ - public void setColorRGB(int color) { - setColor(color | 0xFF000000); - } + float u; + float v; - public void setRenderFullBright(boolean renderFullBright) { - this.renderFullBright = renderFullBright; - } + switch (uvRotations[face.ordinal()]) { + default: + case 0: + u = uv.u2; + v = uv.v2; + break; + case 1: // 90° clockwise + u = uv.u2; + v = uv.v1; + break; + case 2: // 180° clockwise + u = uv.u1; + v = uv.v1; + break; + case 3: // 270° clockwise + u = uv.u1; + v = uv.v2; + break; + } - public void setCustomUv( EnumFacing facing, float u1, float v1, float u2, float v2 ) - { - customUv.put( facing, new Vector4f( u1, v1, u2, v2 ) ); - } + putVertex(builder, face, x, y, z, u, v ); + } - public List getOutput() { - return output; - } + // uv.u1, uv.v2 + private void putVertexBL( UnpackedBakedQuad.Builder builder, EnumFacing face, float x, float y, float z, UvVector uv ) + { + + float u; + float v; + + switch (uvRotations[face.ordinal()]) { + default: + case 0: + u = uv.u1; + v = uv.v2; + break; + case 1: // 90° clockwise + u = uv.u2; + v = uv.v2; + break; + case 2: // 180° clockwise + u = uv.u2; + v = uv.v1; + break; + case 3: // 270° clockwise + u = uv.u1; + v = uv.v1; + break; + } + + putVertex(builder, face, x, y, z, u, v ); + } + + private void putVertex( UnpackedBakedQuad.Builder builder, EnumFacing face, float x, float y, float z, float u, float v ) + { + VertexFormat format = builder.getVertexFormat(); + + for( int i = 0; i < format.getElementCount(); i++ ) + { + VertexFormatElement e = format.getElement( i ); + switch( e.getUsage() ) + { + case POSITION: + builder.put( i, x, y, z ); + break; + case NORMAL: + builder.put( i, + face.getFrontOffsetX(), + face.getFrontOffsetY(), + face.getFrontOffsetZ() ); + break; + case COLOR: + // Color format is RGBA + float r = ( color >> 16 & 0xFF ) / 255f; + float g = ( color >> 8 & 0xFF ) / 255f; + float b = ( color & 0xFF ) / 255f; + float a = ( color >> 24 & 0xFF ) / 255f; + builder.put( i, r, g, b, a ); + break; + case UV: + if( e.getIndex() == 0 ) + { + builder.put( i, u, v ); + } + else + { + // Force Brightness to 15, this is for full bright mode + // this vertex element will only be present in that case + final float lightMapU = (float) ( 15 * 0x20 ) / 0xFFFF; + final float lightMapV = (float) ( 15 * 0x20 ) / 0xFFFF; + builder.put( i, lightMapU, lightMapV ); + } + break; + default: + builder.put( i ); + break; + } + } + } + + public void setTexture( TextureAtlasSprite texture ) + { + for( EnumFacing face : EnumFacing.values() ) + { + textures.put( face, texture ); + } + } + + public void setTextures( TextureAtlasSprite up, TextureAtlasSprite down, TextureAtlasSprite north, TextureAtlasSprite south, TextureAtlasSprite east, TextureAtlasSprite west ) + { + textures.put( EnumFacing.UP, up ); + textures.put( EnumFacing.DOWN, down ); + textures.put( EnumFacing.NORTH, north ); + textures.put( EnumFacing.SOUTH, south ); + textures.put( EnumFacing.EAST, east ); + textures.put( EnumFacing.WEST, west ); + } + + public void setDrawFaces( EnumSet drawFaces ) + { + this.drawFaces = drawFaces; + } + + public void setColor( int color ) + { + this.color = color; + } + + /** + * Sets the vertex color for future vertices to the given RGB value, and forces the alpha component to 255. + */ + public void setColorRGB( int color ) + { + setColor( color | 0xFF000000 ); + } + + public void setRenderFullBright( boolean renderFullBright ) + { + this.renderFullBright = renderFullBright; + } + + public void setCustomUv( EnumFacing facing, float u1, float v1, float u2, float v2 ) + { + customUv.put( facing, new Vector4f( u1, v1, u2, v2 ) ); + } + + public void setUvRotation( EnumFacing facing, int rotation ) + { + if ( rotation == 2 ) { + rotation = 3; + } else if ( rotation == 3 ) { + rotation = 2; + } + Preconditions.checkArgument( rotation >= 0 && rotation <= 3, "rotation" ); + uvRotations[facing.ordinal()] = (byte) rotation; + } + + public List getOutput() + { + return output; + } } diff --git a/src/main/java/appeng/client/render/spatial/SpatialPylonBakedModel.java b/src/main/java/appeng/client/render/spatial/SpatialPylonBakedModel.java new file mode 100644 index 00000000..ded96fda --- /dev/null +++ b/src/main/java/appeng/client/render/spatial/SpatialPylonBakedModel.java @@ -0,0 +1,245 @@ +package appeng.client.render.spatial; + + +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +import com.google.common.collect.ImmutableMap; + +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.spatial.BlockSpatialPylon; +import appeng.client.render.cablebus.CubeBuilder; +import appeng.tile.spatial.TileSpatialPylon; + + +/** + * The baked model that will be used for rendering the spatial pylon. + */ +class SpatialPylonBakedModel implements IBakedModel +{ + + private final Map textures; + + private final VertexFormat format; + + SpatialPylonBakedModel( VertexFormat format, Map textures ) + { + this.textures = ImmutableMap.copyOf( textures ); + this.format = format; + } + + @Override + public List getQuads( @Nullable IBlockState state, @Nullable EnumFacing side, long rand ) + { + int flags = getFlags( state ); + + CubeBuilder builder = new CubeBuilder( format ); + + if( flags != 0 ) + { + EnumFacing ori = null; + int displayAxis = flags & TileSpatialPylon.DISPLAY_Z; + if( displayAxis == TileSpatialPylon.DISPLAY_X ) + { + ori = EnumFacing.EAST; + + if( ( flags & TileSpatialPylon.DISPLAY_MIDDLE ) == TileSpatialPylon.DISPLAY_END_MAX ) + { + builder.setUvRotation( EnumFacing.SOUTH, 1 ); + builder.setUvRotation( EnumFacing.NORTH, 1 ); + builder.setUvRotation( EnumFacing.UP, 2 ); + builder.setUvRotation( EnumFacing.DOWN, 1 ); + } + else if( ( flags & TileSpatialPylon.DISPLAY_MIDDLE ) == TileSpatialPylon.DISPLAY_END_MIN ) + { + builder.setUvRotation( EnumFacing.SOUTH, 2 ); + builder.setUvRotation( EnumFacing.NORTH, 2 ); + builder.setUvRotation( EnumFacing.UP, 1 ); + builder.setUvRotation( EnumFacing.DOWN, 2 ); + } + else + { + builder.setUvRotation( EnumFacing.SOUTH, 1 ); + builder.setUvRotation( EnumFacing.NORTH, 1 ); + builder.setUvRotation( EnumFacing.UP, 1 ); + builder.setUvRotation( EnumFacing.DOWN, 1 ); + } + } + + else if( displayAxis == TileSpatialPylon.DISPLAY_Y ) + { + ori = EnumFacing.UP; + if( ( flags & TileSpatialPylon.DISPLAY_MIDDLE ) == TileSpatialPylon.DISPLAY_END_MAX ) + { + builder.setUvRotation( EnumFacing.NORTH, 3 ); + builder.setUvRotation( EnumFacing.SOUTH, 3 ); + builder.setUvRotation( EnumFacing.EAST, 3 ); + builder.setUvRotation( EnumFacing.WEST, 3 ); + } + } + + else if( displayAxis == TileSpatialPylon.DISPLAY_Z ) + { + ori = EnumFacing.NORTH; + if( ( flags & TileSpatialPylon.DISPLAY_MIDDLE ) == TileSpatialPylon.DISPLAY_END_MAX ) + { + builder.setUvRotation( EnumFacing.EAST, 2 ); + builder.setUvRotation( EnumFacing.WEST, 1 ); + } + else if( ( flags & TileSpatialPylon.DISPLAY_MIDDLE ) == TileSpatialPylon.DISPLAY_END_MIN ) + { + builder.setUvRotation( EnumFacing.EAST, 1 ); + builder.setUvRotation( EnumFacing.WEST, 2 ); + builder.setUvRotation( EnumFacing.UP, 3 ); + builder.setUvRotation( EnumFacing.DOWN, 3 ); + } + else + { + builder.setUvRotation( EnumFacing.EAST, 1 ); + builder.setUvRotation( EnumFacing.WEST, 2 ); + } + } + + builder.setTextures( + textures.get( getTextureTypeFromSideOutside( flags, ori, EnumFacing.UP ) ), + textures.get( getTextureTypeFromSideOutside( flags, ori, EnumFacing.DOWN ) ), + textures.get( getTextureTypeFromSideOutside( flags, ori, EnumFacing.NORTH ) ), + textures.get( getTextureTypeFromSideOutside( flags, ori, EnumFacing.SOUTH ) ), + textures.get( getTextureTypeFromSideOutside( flags, ori, EnumFacing.EAST ) ), + textures.get( getTextureTypeFromSideOutside( flags, ori, EnumFacing.WEST ) ) + ); + builder.addCube( 0, 0, 0, 16, 16, 16 ); + + if( ( flags & TileSpatialPylon.DISPLAY_POWERED_ENABLED ) == TileSpatialPylon.DISPLAY_POWERED_ENABLED ) + { + builder.setRenderFullBright( true ); + } + + builder.setTextures( + textures.get( getTextureTypeFromSideInside( flags, ori, EnumFacing.UP ) ), + textures.get( getTextureTypeFromSideInside( flags, ori, EnumFacing.DOWN ) ), + textures.get( getTextureTypeFromSideInside( flags, ori, EnumFacing.NORTH ) ), + textures.get( getTextureTypeFromSideInside( flags, ori, EnumFacing.SOUTH ) ), + textures.get( getTextureTypeFromSideInside( flags, ori, EnumFacing.EAST ) ), + textures.get( getTextureTypeFromSideInside( flags, ori, EnumFacing.WEST ) ) + ); + builder.addCube( 0, 0, 0, 16, 16, 16 ); + } + else + { + builder.setTexture( textures.get( SpatialPylonTextureType.BASE ) ); + builder.addCube( 0, 0, 0, 16, 16, 16 ); + + builder.setTexture( textures.get( SpatialPylonTextureType.DIM ) ); + builder.addCube( 0, 0, 0, 16, 16, 16 ); + } + + return builder.getOutput(); + } + + private int getFlags( IBlockState state ) + { + if( !( state instanceof IExtendedBlockState ) ) + { + return 0; + } + + IExtendedBlockState extState = (IExtendedBlockState) state; + + return extState.getValue( BlockSpatialPylon.STATE ); + } + + private static SpatialPylonTextureType getTextureTypeFromSideOutside( int flags, EnumFacing ori, EnumFacing dir ) + { + if( ori == dir || ori.getOpposite() == dir ) + { + return SpatialPylonTextureType.BASE; + } + + if( ( flags & TileSpatialPylon.DISPLAY_MIDDLE ) == TileSpatialPylon.DISPLAY_MIDDLE ) + { + return SpatialPylonTextureType.BASE_SPANNED; + } + else if( ( flags & TileSpatialPylon.DISPLAY_MIDDLE ) == TileSpatialPylon.DISPLAY_END_MIN ) + { + return SpatialPylonTextureType.BASE_END; + } + else if( ( flags & TileSpatialPylon.DISPLAY_MIDDLE ) == TileSpatialPylon.DISPLAY_END_MAX ) + { + return SpatialPylonTextureType.BASE_END; + } + + return SpatialPylonTextureType.BASE; + } + + private static SpatialPylonTextureType getTextureTypeFromSideInside( int flags, EnumFacing ori, EnumFacing dir ) + { + final boolean good = ( flags & TileSpatialPylon.DISPLAY_ENABLED ) == TileSpatialPylon.DISPLAY_ENABLED; + + if( ori == dir || ori.getOpposite() == dir ) + { + return good ? SpatialPylonTextureType.DIM : SpatialPylonTextureType.RED; + } + + if( ( flags & TileSpatialPylon.DISPLAY_MIDDLE ) == TileSpatialPylon.DISPLAY_MIDDLE ) + { + return good ? SpatialPylonTextureType.DIM_SPANNED : SpatialPylonTextureType.RED_SPANNED; + } + else if( ( flags & TileSpatialPylon.DISPLAY_MIDDLE ) == TileSpatialPylon.DISPLAY_END_MIN ) + { + return good ? SpatialPylonTextureType.DIM_END : SpatialPylonTextureType.RED_END; + } + else if( ( flags & TileSpatialPylon.DISPLAY_MIDDLE ) == TileSpatialPylon.DISPLAY_END_MAX ) + { + return good ? SpatialPylonTextureType.DIM_END : SpatialPylonTextureType.RED_END; + } + + return SpatialPylonTextureType.BASE; + } + + @Override + public boolean isAmbientOcclusion() + { + return true; + } + + @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; + } +} diff --git a/src/main/java/appeng/client/render/spatial/SpatialPylonModel.java b/src/main/java/appeng/client/render/spatial/SpatialPylonModel.java new file mode 100644 index 00000000..a692a59d --- /dev/null +++ b/src/main/java/appeng/client/render/spatial/SpatialPylonModel.java @@ -0,0 +1,65 @@ +package appeng.client.render.spatial; + + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.Map; +import java.util.stream.Collectors; + +import com.google.common.base.Function; + +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.core.AppEng; + + +class SpatialPylonModel implements IModel +{ + + @Override + public Collection getDependencies() + { + return Collections.emptyList(); + } + + @Override + public Collection getTextures() + { + return Arrays.stream( SpatialPylonTextureType.values() ) + .map( SpatialPylonModel::getTexturePath ) + .collect( Collectors.toList() ); + } + + @Override + public IBakedModel bake( IModelState state, VertexFormat format, Function bakedTextureGetter ) + { + Map textures = new EnumMap<>( SpatialPylonTextureType.class ); + + for( SpatialPylonTextureType type : SpatialPylonTextureType.values() ) + { + ResourceLocation loc = getTexturePath( type ); + textures.put( type, bakedTextureGetter.apply( loc ) ); + } + + return new SpatialPylonBakedModel( format, textures ); + } + + @Override + public IModelState getDefaultState() + { + return TRSRTransformation.identity(); + } + + private static ResourceLocation getTexturePath( SpatialPylonTextureType type ) + { + return new ResourceLocation( AppEng.MOD_ID, "blocks/spatial_pylon/" + type.name().toLowerCase() ); + } +} diff --git a/src/main/java/appeng/client/render/spatial/SpatialPylonRendering.java b/src/main/java/appeng/client/render/spatial/SpatialPylonRendering.java new file mode 100644 index 00000000..549bb100 --- /dev/null +++ b/src/main/java/appeng/client/render/spatial/SpatialPylonRendering.java @@ -0,0 +1,41 @@ +package appeng.client.render.spatial; + + +import java.util.Map; + +import com.google.common.collect.ImmutableMap; + +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 net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; + +import appeng.bootstrap.BlockRenderingCustomizer; +import appeng.bootstrap.IBlockRendering; +import appeng.bootstrap.IItemRendering; +import appeng.core.AppEng; + + +public class SpatialPylonRendering extends BlockRenderingCustomizer +{ + + private static final ResourceLocation MODEL_ID = new ResourceLocation( AppEng.MOD_ID, "models/blocks/spatial_pylon/builtin" ); + + @Override + @SideOnly( Side.CLIENT ) + public void customize( IBlockRendering rendering, IItemRendering itemRendering ) + { + rendering.builtInModel( MODEL_ID.getResourcePath(), new SpatialPylonModel() ); + rendering.stateMapper( this::mapState ); + } + + private Map mapState( Block block ) + { + return ImmutableMap.of( + block.getDefaultState(), new ModelResourceLocation( MODEL_ID, "normal" ) + ); + } + +} diff --git a/src/main/java/appeng/client/render/spatial/SpatialPylonStateProperty.java b/src/main/java/appeng/client/render/spatial/SpatialPylonStateProperty.java new file mode 100644 index 00000000..e754c5da --- /dev/null +++ b/src/main/java/appeng/client/render/spatial/SpatialPylonStateProperty.java @@ -0,0 +1,38 @@ +package appeng.client.render.spatial; + + +import net.minecraftforge.common.property.IUnlistedProperty; + + +/** + * Models the rendering state of the spatial pylon, which is largely determined by the state of neighboring tiles. + */ +public class SpatialPylonStateProperty implements IUnlistedProperty +{ + + @Override + public String getName() + { + return "spatial_state"; + } + + @Override + public boolean isValid( Integer value ) + { + int val = value; + // The lower 6 bits are used + return ( val & ~0x3F ) == 0; + } + + @Override + public Class getType() + { + return Integer.class; + } + + @Override + public String valueToString( Integer value ) + { + return value.toString(); + } +} diff --git a/src/main/java/appeng/client/render/spatial/SpatialPylonTextureType.java b/src/main/java/appeng/client/render/spatial/SpatialPylonTextureType.java new file mode 100644 index 00000000..31ef355d --- /dev/null +++ b/src/main/java/appeng/client/render/spatial/SpatialPylonTextureType.java @@ -0,0 +1,15 @@ +package appeng.client.render.spatial; + + +enum SpatialPylonTextureType +{ + BASE, + BASE_END, + BASE_SPANNED, + DIM, + DIM_END, + DIM_SPANNED, + RED, + RED_END, + RED_SPANNED +} diff --git a/src/main/java/appeng/core/api/definitions/ApiBlocks.java b/src/main/java/appeng/core/api/definitions/ApiBlocks.java index b284fca3..6898bbd7 100644 --- a/src/main/java/appeng/core/api/definitions/ApiBlocks.java +++ b/src/main/java/appeng/core/api/definitions/ApiBlocks.java @@ -84,6 +84,7 @@ import appeng.bootstrap.IBlockRendering; import appeng.bootstrap.IItemRendering; import appeng.client.render.crafting.CraftingCubeRendering; import appeng.client.render.model.GlassModel; +import appeng.client.render.spatial.SpatialPylonRendering; import appeng.core.AppEng; import appeng.core.features.AEFeature; import appeng.core.features.registries.PartModels; @@ -280,6 +281,7 @@ public final class ApiBlocks implements IBlocks this.spatialPylon = registry.block( "spatial_pylon", BlockSpatialPylon::new ) .features( AEFeature.SpatialIO ) .useCustomItemModel() + .rendering( new SpatialPylonRendering() ) .build(); this.spatialIOPort = registry.block( "spatial_ioport", BlockSpatialIOPort::new ).features( AEFeature.SpatialIO ).build(); this.controller = registry.block( "controller", BlockController::new )