Restored glass model that implements the uv shift.

Added a way to register built-in models to support this.
This commit is contained in:
Sebastian Hartte 2016-08-26 12:35:48 +02:00
parent ea6c892ec0
commit c6b9926d7f
45 changed files with 834 additions and 107 deletions

View File

@ -1,6 +1,8 @@
package appeng.bootstrap;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import net.minecraft.block.Block;
@ -9,6 +11,7 @@ import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.renderer.block.statemap.IStateMapper;
import net.minecraft.client.renderer.color.IBlockColor;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@ -34,6 +37,9 @@ class BlockRendering implements IBlockRendering
@SideOnly( Side.CLIENT )
private IStateMapper stateMapper;
@SideOnly( Side.CLIENT )
private Map<String, IModel> builtInModels = new HashMap<>();
@SideOnly( Side.CLIENT )
public IBlockRendering modelCustomizer( BiFunction<ModelResourceLocation, IBakedModel, IBakedModel> customizer )
{
@ -57,6 +63,13 @@ class BlockRendering implements IBlockRendering
return this;
}
@Override
public IBlockRendering builtInModel( String name, IModel model )
{
this.builtInModels.put( name, model );
return this;
}
@SideOnly( Side.CLIENT )
@Override
public IBlockRendering stateMapper( IStateMapper mapper )
@ -65,7 +78,7 @@ class BlockRendering implements IBlockRendering
return this;
}
void apply( FeatureFactory registry, Block block, Class<?> tileEntityClass )
void apply( FeatureFactory factory, Block block, Class<?> tileEntityClass )
{
if( tesr != null )
{
@ -73,27 +86,29 @@ class BlockRendering implements IBlockRendering
{
throw new IllegalStateException( "Tried to register a TESR for " + block + " even though no tile entity has been specified." );
}
registry.addBootstrapComponent( new TesrComponent( tileEntityClass, tesr ) );
factory.addBootstrapComponent( new TesrComponent( tileEntityClass, tesr ) );
}
if( modelCustomizer != null )
{
registry.modelOverrideComponent.addOverride( block.getRegistryName().getResourcePath(), modelCustomizer );
factory.modelOverrideComponent.addOverride( block.getRegistryName().getResourcePath(), modelCustomizer );
}
else if ( block instanceof AEBaseTileBlock )
{
// This is a default rotating model if the base-block uses an AE tile entity which exposes UP/FRONT as extended props
registry.modelOverrideComponent.addOverride( block.getRegistryName().getResourcePath(), ( l, m ) -> new CachingRotatingBakedModel( m ) );
factory.modelOverrideComponent.addOverride( block.getRegistryName().getResourcePath(), ( l, m ) -> new CachingRotatingBakedModel( m ) );
}
builtInModels.forEach( factory::addBuiltInModel );
if( blockColor != null )
{
registry.addBootstrapComponent( new BlockColorComponent( block, blockColor ) );
factory.addBootstrapComponent( new BlockColorComponent( block, blockColor ) );
}
if( stateMapper != null )
{
registry.addBootstrapComponent( new StateMapperComponent( block, stateMapper ) );
factory.addBootstrapComponent( new StateMapperComponent( block, stateMapper ) );
}
}
}

View File

@ -7,12 +7,14 @@ import java.util.function.Supplier;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import appeng.api.definitions.IItemDefinition;
import appeng.api.util.AEColor;
import appeng.api.util.AEColoredItemDefinition;
import appeng.bootstrap.components.BuiltInModelComponent;
import appeng.bootstrap.components.InitComponent;
import appeng.bootstrap.components.ModelOverrideComponent;
import appeng.bootstrap.components.PostInitComponent;
@ -34,6 +36,9 @@ public class FeatureFactory
@SideOnly( Side.CLIENT )
ModelOverrideComponent modelOverrideComponent;
@SideOnly( Side.CLIENT )
private BuiltInModelComponent builtInModelComponent;
public FeatureFactory()
{
this.defaultFeatures = new AEFeature[] { AEFeature.Core };
@ -42,7 +47,10 @@ public class FeatureFactory
if( Platform.isClient() )
{
modelOverrideComponent = new ModelOverrideComponent();
this.bootstrapComponents.add( modelOverrideComponent );
bootstrapComponents.add( modelOverrideComponent );
builtInModelComponent = new BuiltInModelComponent();
bootstrapComponents.add( builtInModelComponent );
}
}
@ -53,6 +61,7 @@ public class FeatureFactory
if( Platform.isClient() )
{
this.modelOverrideComponent = parent.modelOverrideComponent;
this.builtInModelComponent = parent.builtInModelComponent;
}
}
@ -108,9 +117,14 @@ public class FeatureFactory
this.bootstrapComponents.add( component );
}
@SideOnly( Side.CLIENT )
void addBuiltInModel( String path, IModel model )
{
builtInModelComponent.addModel( path, model );
}
public List<IBootstrapComponent> getBootstrapComponents()
{
return bootstrapComponents;
}
}

View File

@ -7,8 +7,8 @@ import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.renderer.block.statemap.IStateMapper;
import net.minecraft.client.renderer.color.IBlockColor;
import net.minecraft.client.renderer.color.IItemColor;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@ -31,4 +31,10 @@ public interface IBlockRendering
@SideOnly( Side.CLIENT )
IBlockRendering tesr( TileEntitySpecialRenderer<?> tesr );
/**
* Registers a built-in model under the given resource path.
*/
@SideOnly( Side.CLIENT )
IBlockRendering builtInModel( String name, IModel model );
}

View File

@ -8,6 +8,7 @@ import net.minecraft.client.renderer.ItemMeshDefinition;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.client.renderer.color.IItemColor;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@ -66,4 +67,11 @@ public interface IItemRendering
*/
@SideOnly( Side.CLIENT )
IItemRendering color( IItemColor itemColor );
/**
* Registers a built-in model under the given resource path.
*/
@SideOnly( Side.CLIENT )
IItemRendering builtInModel( String name, IModel model );
}

View File

@ -18,6 +18,7 @@ import net.minecraft.client.renderer.color.IItemColor;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@ -42,6 +43,9 @@ class ItemRendering implements IItemRendering
@SideOnly( Side.CLIENT )
private Set<ResourceLocation> variants = new HashSet<>();
@SideOnly( Side.CLIENT )
private Map<String, IModel> builtInModels = new HashMap<>();
@Override
@SideOnly( Side.CLIENT )
public IItemRendering meshDefinition( ItemMeshDefinition meshDefinition )
@ -73,6 +77,13 @@ class ItemRendering implements IItemRendering
return this;
}
@Override
public IItemRendering builtInModel( String name, IModel model )
{
this.builtInModels.put( name, model );
return this;
}
void apply( FeatureFactory factory, Item item )
{
if( this.itemMeshDefinition != null )
@ -110,6 +121,8 @@ class ItemRendering implements IItemRendering
factory.addBootstrapComponent( new ItemModelComponent( item, ImmutableMap.of( 0, model ) ) );
}
builtInModels.forEach( factory::addBuiltInModel );
if( !resources.isEmpty() )
{
factory.addBootstrapComponent( new ItemVariantsComponent( item, resources ) );

View File

@ -0,0 +1,39 @@
package appeng.bootstrap.components;
import java.util.HashMap;
import java.util.Map;
import com.google.common.base.Preconditions;
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import appeng.client.render.model.BuiltInModelLoader;
@SideOnly( Side.CLIENT )
public class BuiltInModelComponent implements PreInitComponent
{
private final Map<String, IModel> builtInModels = new HashMap<>();
private boolean hasInitialized = false;
public void addModel( String path, IModel model )
{
Preconditions.checkState( !hasInitialized );
builtInModels.put( path, model );
}
@Override
public void preInitialize( Side side )
{
hasInitialized = true;
BuiltInModelLoader loader = new BuiltInModelLoader( builtInModels );
ModelLoaderRegistry.registerLoader( loader );
}
}

View File

@ -0,0 +1,58 @@
package appeng.client.render.model;
import java.util.Map;
import com.google.common.collect.ImmutableMap;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.IResourceManagerReloadListener;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.client.model.ICustomModelLoader;
import net.minecraftforge.client.model.IModel;
import appeng.core.AppEng;
/**
* Manages built-in models.
*/
public class BuiltInModelLoader implements ICustomModelLoader
{
private final Map<String, IModel> builtInModels;
public BuiltInModelLoader( Map<String, IModel> builtInModels )
{
this.builtInModels = ImmutableMap.copyOf( builtInModels );
}
@Override
public boolean accepts( ResourceLocation modelLocation )
{
if( !modelLocation.getResourceDomain().equals( AppEng.MOD_ID ) )
{
return false;
}
return builtInModels.containsKey( modelLocation.getResourcePath() );
}
@Override
public IModel loadModel( ResourceLocation modelLocation ) throws Exception
{
return builtInModels.get( modelLocation.getResourcePath() );
}
@Override
public void onResourceManagerReload( IResourceManager resourceManager )
{
for( IModel model : builtInModels.values() )
{
if( model instanceof IResourceManagerReloadListener )
{
( (IResourceManagerReloadListener) model ).onResourceManagerReload( resourceManager );
}
}
}
}

View File

@ -0,0 +1,314 @@
/*
* 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.client.render.model;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import java.util.stream.IntStream;
import javax.annotation.Nullable;
import com.google.common.base.Function;
import com.google.common.base.Strings;
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.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad;
import net.minecraftforge.common.property.IExtendedBlockState;
import appeng.decorative.solid.BlockQuartzGlass;
import appeng.decorative.solid.GlassState;
class GlassBakedModel implements IBakedModel
{
private static final byte[][][] OFFSETS = generateOffsets();
// Alternating textures based on position
static final ResourceLocation TEXTURE_A = new ResourceLocation( "appliedenergistics2:blocks/glass/quartz_glass_a" );
static final ResourceLocation TEXTURE_B = new ResourceLocation( "appliedenergistics2:blocks/glass/quartz_glass_b" );
static final ResourceLocation TEXTURE_C = new ResourceLocation( "appliedenergistics2:blocks/glass/quartz_glass_c" );
static final ResourceLocation TEXTURE_D = new ResourceLocation( "appliedenergistics2:blocks/glass/quartz_glass_d" );
// Frame texture
static final ResourceLocation[] TEXTURES_FRAME = generateTexturesFrame();
// Generates the required textures for the frame
private static ResourceLocation[] generateTexturesFrame()
{
return IntStream.range( 1, 16 )
.mapToObj( Integer::toBinaryString )
.map( s -> Strings.padStart( s, 4, '0' ) )
.map( s -> new ResourceLocation( "appliedenergistics2:blocks/glass/quartz_glass_frame" + s ) )
.toArray( ResourceLocation[]::new );
}
private final TextureAtlasSprite[] glassTextures;
private final TextureAtlasSprite[] frameTextures;
private final VertexFormat vertexFormat;
public GlassBakedModel( VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter )
{
this.glassTextures = new TextureAtlasSprite[] {
bakedTextureGetter.apply( TEXTURE_A ),
bakedTextureGetter.apply( TEXTURE_B ),
bakedTextureGetter.apply( TEXTURE_C ),
bakedTextureGetter.apply( TEXTURE_D )
};
this.vertexFormat = format;
// The first frame texture would be empty, so we simply leave it set to null here
this.frameTextures = new TextureAtlasSprite[16];
for( int i = 0; i < TEXTURES_FRAME.length; i++ )
{
this.frameTextures[1 + i] = bakedTextureGetter.apply( TEXTURES_FRAME[i] );
}
}
@Override
public List<BakedQuad> getQuads( @Nullable IBlockState state, @Nullable EnumFacing side, long rand )
{
if( !( state instanceof IExtendedBlockState ) || side == null )
{
return Collections.emptyList();
}
IExtendedBlockState extState = (IExtendedBlockState) state;
GlassState glassState = extState.getValue( BlockQuartzGlass.GLASS_STATE );
final int cx = Math.abs( glassState.getX() % 10 );
final int cy = Math.abs( glassState.getY() % 10 );
final int cz = Math.abs( glassState.getZ() % 10 );
int u = OFFSETS[cx][cy][cz] % 4;
int v = OFFSETS[9 - cx][9 - cy][9 - cz] % 4;
int texIdx = Math.abs( ( OFFSETS[cx][cy][cz] + ( glassState.getX() + glassState.getY() + glassState.getZ() ) ) % 4 );
if( texIdx < 2 )
{
u /= 2;
v /= 2;
}
TextureAtlasSprite glassTexture = glassTextures[texIdx];
// Render the glass side
List<BakedQuad> quads = new ArrayList<>( 5 ); // At most 5
List<Vec3d> corners = RenderHelper.getFaceCorners( side );
quads.add( createQuad( side, corners, glassTexture, u, v ) );
/*
This needs some explanation:
The bit-field contains 4-bits, one for each direction that a frame may be drawn.
Converted to a number, the bit-field is then used as an index into the list of
frame textures, which have been created in such a way that their filenames
indicate, in which directions they contain borders.
i.e. bitmask = 0101 means a border should be drawn up and down (in terms of u,v space).
Converted to a number, this bitmask is 5. So the texture at index 5 is used.
That texture had "0101" in its filename to indicate this.
*/
int edgeBitmask = makeBitmask( glassState, side );
TextureAtlasSprite sideSprite = frameTextures[edgeBitmask];
if( sideSprite != null )
{
quads.add( createQuad( side, corners, sideSprite, 0, 0 ) );
}
return quads;
}
/**
* Creates the bitmask that indicates, in which directions (in terms of u,v space) a border should be drawn.
*/
private static int makeBitmask( GlassState state, EnumFacing side )
{
switch( side )
{
case DOWN:
return makeBitmask( state, EnumFacing.SOUTH, EnumFacing.EAST, EnumFacing.NORTH, EnumFacing.WEST );
case UP:
return makeBitmask( state, EnumFacing.SOUTH, EnumFacing.WEST, EnumFacing.NORTH, EnumFacing.EAST );
case NORTH:
return makeBitmask( state, EnumFacing.UP, EnumFacing.WEST, EnumFacing.DOWN, EnumFacing.EAST );
case SOUTH:
return makeBitmask( state, EnumFacing.UP, EnumFacing.EAST, EnumFacing.DOWN, EnumFacing.WEST );
case WEST:
return makeBitmask( state, EnumFacing.UP, EnumFacing.SOUTH, EnumFacing.DOWN, EnumFacing.NORTH );
case EAST:
return makeBitmask( state, EnumFacing.UP, EnumFacing.NORTH, EnumFacing.DOWN, EnumFacing.SOUTH );
default:
throw new IllegalArgumentException( "Unsupported side!" );
}
}
private static int makeBitmask( GlassState state, EnumFacing up, EnumFacing right, EnumFacing down, EnumFacing left )
{
int bitmask = 0;
if( !state.isFlushWith( up ) )
{
bitmask |= 1;
}
if( !state.isFlushWith( right ) )
{
bitmask |= 2;
}
if( !state.isFlushWith( down ) )
{
bitmask |= 4;
}
if( !state.isFlushWith( left ) )
{
bitmask |= 8;
}
return bitmask;
}
private BakedQuad createQuad( EnumFacing side, List<Vec3d> corners, TextureAtlasSprite sprite, float uOffset, float vOffset )
{
return createQuad( side, corners.get( 0 ), corners.get( 1 ), corners.get( 2 ), corners.get( 3 ), sprite, uOffset, vOffset );
}
private BakedQuad createQuad( EnumFacing side, Vec3d c1, Vec3d c2, Vec3d c3, Vec3d c4, TextureAtlasSprite sprite, float uOffset, float vOffset )
{
Vec3d normal = new Vec3d( side.getDirectionVec() );
// Apply the u,v shift.
// This mirrors the logic from OffsetIcon from 1.7
float u1 = MathHelper.clamp_float( 0 - uOffset, 0, 16 );
float u2 = MathHelper.clamp_float( 16 - uOffset, 0, 16 );
float v1 = MathHelper.clamp_float( 0 - vOffset, 0, 16 );
float v2 = MathHelper.clamp_float( 16 - vOffset, 0, 16 );
UnpackedBakedQuad.Builder builder = new UnpackedBakedQuad.Builder( vertexFormat );
builder.setTexture( sprite );
putVertex( builder, normal, c1.xCoord, c1.yCoord, c1.zCoord, sprite, u1, v1 );
putVertex( builder, normal, c2.xCoord, c2.yCoord, c2.zCoord, sprite, u1, v2 );
putVertex( builder, normal, c3.xCoord, c3.yCoord, c3.zCoord, sprite, u2, v2 );
putVertex( builder, normal, c4.xCoord, c4.yCoord, c4.zCoord, sprite, u2, v1 );
return builder.build();
}
/*
This method is as complicated as it is, because the order in which we push data into the vertexbuffer actually has to be precisely the order
in which the vertex elements had been declared in the vertex format.
*/
private void putVertex( UnpackedBakedQuad.Builder builder, Vec3d normal, double x, double y, double z, TextureAtlasSprite sprite, float u, float v )
{
for( int e = 0; e < vertexFormat.getElementCount(); e++ )
{
switch( vertexFormat.getElement( e ).getUsage() )
{
case POSITION:
builder.put( e, (float) x, (float) y, (float) z, 1.0f );
break;
case COLOR:
builder.put( e, 1.0f, 1.0f, 1.0f, 1.0f );
break;
case UV:
if( vertexFormat.getElement( e ).getIndex() == 0 )
{
u = sprite.getInterpolatedU( u );
v = sprite.getInterpolatedV( v );
builder.put( e, u, v, 0f, 1f );
break;
}
case NORMAL:
builder.put( e, (float) normal.xCoord, (float) normal.yCoord, (float) normal.zCoord, 0f );
break;
default:
builder.put( e );
break;
}
}
}
@Override
public ItemOverrideList getOverrides()
{
return ItemOverrideList.NONE;
}
@Override
public boolean isAmbientOcclusion()
{
return false;
}
@Override
public boolean isGui3d()
{
return false;
}
@Override
public boolean isBuiltInRenderer()
{
return false;
}
@Override
public TextureAtlasSprite getParticleTexture()
{
return frameTextures[frameTextures.length - 1];
}
@Override
public ItemCameraTransforms getItemCameraTransforms()
{
return ItemCameraTransforms.DEFAULT;
}
private static byte[][][] generateOffsets()
{
final Random r = new Random( 924 );
final byte[][][] offset = new byte[10][10][10];
for( int x = 0; x < 10; x++ )
{
for( int y = 0; y < 10; y++ )
{
r.nextBytes( offset[x][y] );
}
}
return offset;
}
}

View File

@ -0,0 +1,70 @@
/*
* 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.client.render.model;
import java.util.Collection;
import java.util.Collections;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
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;
/**
* Model class for the connected texture glass model.
*/
public class GlassModel implements IModel
{
@Override
public Collection<ResourceLocation> getDependencies()
{
return Collections.emptySet();
}
@Override
public Collection<ResourceLocation> getTextures()
{
return ImmutableSet.<ResourceLocation>builder()
.add( GlassBakedModel.TEXTURE_A, GlassBakedModel.TEXTURE_B, GlassBakedModel.TEXTURE_C, GlassBakedModel.TEXTURE_D )
.add( GlassBakedModel.TEXTURES_FRAME )
.build();
}
@Override
public IBakedModel bake( IModelState state, VertexFormat format, Function<ResourceLocation, TextureAtlasSprite> bakedTextureGetter )
{
return new GlassBakedModel( format, bakedTextureGetter );
}
@Override
public IModelState getDefaultState()
{
return TRSRTransformation.identity();
}
}

View File

@ -0,0 +1,107 @@
/*
* 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.client.render.model;
import java.util.EnumMap;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.Vec3d;
final class RenderHelper
{
private static EnumMap<EnumFacing, List<Vec3d>> cornersForFacing = generateCornersForFacings();
private RenderHelper() {
}
static List<Vec3d> getFaceCorners( EnumFacing side )
{
return cornersForFacing.get( side );
}
private static EnumMap<EnumFacing, List<Vec3d>> generateCornersForFacings()
{
EnumMap<EnumFacing, List<Vec3d>> result = new EnumMap<>( EnumFacing.class );
for( EnumFacing facing : EnumFacing.values() )
{
List<Vec3d> corners;
float offset = ( facing.getAxisDirection() == EnumFacing.AxisDirection.NEGATIVE ) ? 0 : 1;
switch( facing.getAxis() )
{
default:
case X:
corners = Lists.newArrayList(
new Vec3d( offset, 1, 1 ),
new Vec3d( offset, 0, 1 ),
new Vec3d( offset, 0, 0 ),
new Vec3d( offset, 1, 0 )
);
break;
case Y:
corners = Lists.newArrayList(
new Vec3d( 1, offset, 1 ),
new Vec3d( 1, offset, 0 ),
new Vec3d( 0, offset, 0 ),
new Vec3d( 0, offset, 1 )
);
break;
case Z:
corners = Lists.newArrayList(
new Vec3d( 0, 1, offset ),
new Vec3d( 0, 0, offset ),
new Vec3d( 1, 0, offset ),
new Vec3d( 1, 1, offset )
);
break;
}
if (facing.getAxisDirection() == EnumFacing.AxisDirection.NEGATIVE) {
corners = Lists.reverse( corners );
}
result.put( facing, ImmutableList.copyOf( corners ) );
}
return result;
}
private static Vec3d adjust( Vec3d vec, EnumFacing.Axis axis, double delta )
{
switch( axis )
{
default:
case X:
return new Vec3d( vec.xCoord + delta, vec.yCoord, vec.zCoord );
case Y:
return new Vec3d( vec.xCoord, vec.yCoord + delta, vec.zCoord );
case Z:
return new Vec3d( vec.xCoord, vec.yCoord, vec.zCoord + delta );
}
}
}

View File

@ -46,8 +46,8 @@ import appeng.block.misc.BlockInscriber;
import appeng.block.misc.BlockInterface;
import appeng.block.misc.BlockLightDetector;
import appeng.block.misc.BlockPaint;
import appeng.block.misc.BlockQuartzGrowthAccelerator;
import appeng.block.misc.BlockQuartzFixture;
import appeng.block.misc.BlockQuartzGrowthAccelerator;
import appeng.block.misc.BlockSecurityStation;
import appeng.block.misc.BlockSkyCompass;
import appeng.block.misc.BlockTinyTNT;
@ -77,6 +77,7 @@ import appeng.bootstrap.BlockRenderingCustomizer;
import appeng.bootstrap.FeatureFactory;
import appeng.bootstrap.IBlockRendering;
import appeng.bootstrap.IItemRendering;
import appeng.client.render.model.GlassModel;
import appeng.core.AppEng;
import appeng.core.features.AEFeature;
import appeng.debug.BlockChunkloader;
@ -201,6 +202,15 @@ public final class ApiBlocks implements IBlocks
this.chiseledQuartzBlock = deco.block( "chiseled_quartz_block", BlockChiseledQuartz::new ).build();
this.quartzGlass = deco.block( "quartz_glass", BlockQuartzGlass::new )
.useCustomItemModel()
.rendering( new BlockRenderingCustomizer()
{
@Override
@SideOnly( Side.CLIENT )
public void customize( IBlockRendering rendering, IItemRendering itemRendering )
{
rendering.builtInModel( "models/block/builtin/quartz_glass", new GlassModel() );
}
} )
.build();
this.quartzVibrantGlass = deco.block( "quartz_vibrant_glass", BlockQuartzLamp::new )
.addFeatures( AEFeature.DecorativeLights )

View File

@ -19,27 +19,29 @@
package appeng.decorative.solid;
import java.util.EnumSet;
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.util.BlockRenderLayer;
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 appeng.block.AEBaseBlock;
import appeng.helpers.AEGlassMaterial;
public class BlockQuartzGlass extends AEBaseBlock
{
public static final PropertyBool[] props = { PropertyBool.create( "down" ), PropertyBool.create( "up" ), PropertyBool.create( "north" ), PropertyBool.create( "south" ), PropertyBool.create( "west" ), PropertyBool.create( "east" ) };
private static boolean isGlassBlock( IBlockAccess world, BlockPos pos, EnumFacing facing )
{
return world.getBlockState( pos.offset( facing ) ).getBlock() instanceof BlockQuartzGlass;
}
// This unlisted property is used to determine the actual block that should be rendered
public static final UnlistedGlassStateProperty GLASS_STATE = new UnlistedGlassStateProperty();
public BlockQuartzGlass()
{
@ -49,25 +51,37 @@ public class BlockQuartzGlass extends AEBaseBlock
}
@Override
protected IProperty[] getAEStates()
protected BlockStateContainer createBlockState()
{
return props;
IProperty[] listedProperties = new IProperty[0];
IUnlistedProperty[] unlistedProperties = new IUnlistedProperty[] { GLASS_STATE };
return new ExtendedBlockState( this, listedProperties, unlistedProperties );
}
@Override
public int getMetaFromState( IBlockState state )
public IBlockState getExtendedState( IBlockState state, IBlockAccess world, BlockPos pos )
{
return 0;
}
@Override
public IBlockState getActualState( IBlockState state, IBlockAccess world, BlockPos pos )
{
EnumSet<EnumFacing> flushWith = EnumSet.noneOf( EnumFacing.class );
// Test every direction for another glass block
for( EnumFacing facing : EnumFacing.values() )
{
state = state.withProperty( props[facing.ordinal()], isGlassBlock( world, pos, facing ) );
if( isGlassBlock( world, pos, facing ) )
{
flushWith.add( facing );
}
}
return state;
GlassState glassState = new GlassState( pos.getX(), pos.getY(), pos.getZ(), flushWith );
IExtendedBlockState extState = (IExtendedBlockState) state;
return extState.withProperty( GLASS_STATE, glassState );
}
private static boolean isGlassBlock( IBlockAccess world, BlockPos pos, EnumFacing facing )
{
return world.getBlockState( pos.offset( facing ) ).getBlock() instanceof BlockQuartzGlass;
}
@Override
@ -79,7 +93,17 @@ public class BlockQuartzGlass extends AEBaseBlock
@Override
public boolean shouldSideBeRendered( final IBlockState state, final IBlockAccess w, final BlockPos pos, final EnumFacing side )
{
return !isGlassBlock( w, pos, side ) && super.shouldSideBeRendered( state, w, pos, side );
BlockPos adjacentPos = pos.offset( side );
final Material mat = w.getBlockState( adjacentPos ).getBlock().getMaterial( state );
if( mat == Material.GLASS || mat == AEGlassMaterial.INSTANCE )
{
if( w.getBlockState( adjacentPos ).getBlock().getRenderType( state ) == this.getRenderType( state ) )
{
return false;
}
}
return super.shouldSideBeRendered( state, w, pos, side );
}
@Override
@ -87,5 +111,4 @@ public class BlockQuartzGlass extends AEBaseBlock
{
return false;
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.decorative.solid;
import java.util.EnumSet;
import net.minecraft.util.EnumFacing;
/**
* Immutable (and thus thread-safe) class that encapsulates the rendering state required for a connected texture
* glass block.
*/
public final class GlassState
{
private final int x;
private final int y;
private final int z;
private final EnumSet<EnumFacing> flushWith = EnumSet.noneOf( EnumFacing.class );
public GlassState( int x, int y, int z, EnumSet<EnumFacing> flushWith )
{
this.x = x;
this.y = y;
this.z = z;
this.flushWith.addAll( flushWith );
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
public int getZ()
{
return z;
}
public boolean isFlushWith( EnumFacing side )
{
return flushWith.contains( side );
}
}

View File

@ -0,0 +1,54 @@
/*
* 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.decorative.solid;
import net.minecraftforge.common.property.IUnlistedProperty;
/**
* This unlisted property is used to transport the connected texture state into our model class.
*/
public class UnlistedGlassStateProperty implements IUnlistedProperty<GlassState>
{
@Override
public String getName()
{
return "glass_state";
}
@Override
public boolean isValid( GlassState value )
{
return true;
}
@Override
public Class<GlassState> getType()
{
return GlassState.class;
}
@Override
public String valueToString( GlassState value )
{
return null;
}
}

View File

@ -1,31 +1,7 @@
{
"multipart": [
{
"apply": [{ "model": "appliedenergistics2:quartz_glass_1" }, { "model": "appliedenergistics2:quartz_glass_2" }, { "model": "appliedenergistics2:quartz_glass_3" }, { "model": "appliedenergistics2:quartz_glass_4" }]
},
{
"when": { "down": "false" },
"apply": { "model": "appliedenergistics2:quartz_glass_frame", "x": 90 }
},
{
"when": { "up": "false" },
"apply": { "model": "appliedenergistics2:quartz_glass_frame", "x": -90 }
},
{
"when": { "north": "false" },
"apply": { "model": "appliedenergistics2:quartz_glass_frame"}
},
{
"when": { "south": "false" },
"apply": { "model": "appliedenergistics2:quartz_glass_frame", "y": 180 }
},
{
"when": { "west": "false" },
"apply": { "model": "appliedenergistics2:quartz_glass_frame", "y": -90 }
},
{
"when": { "east": "false" },
"apply": { "model": "appliedenergistics2:quartz_glass_frame", "y": 90 }
"variants": {
"normal": {
"model": "appliedenergistics2:builtin/quartz_glass"
}
]
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,18 +0,0 @@
{
"textures": {
"0": "appliedenergistics2:blocks/quartz_glass_frame"
},
"elements": [
{
"name": "Frame",
"from": [ 0.0, 0.0, 0.0 ],
"to": [ 16.0, 16.0, 1.0 ],
"faces": {
"east": { "texture": "#0", "uv": [ 0.0, 0.0, 1.0, 16.0 ], "cullface": "east" },
"west": { "texture": "#0", "uv": [ 0.0, 0.0, 1.0, 16.0 ], "cullface": "west" },
"up": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 1.0 ], "cullface": "up" },
"down": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 1.0 ], "cullface": "down" }
}
}
]
}

View File

@ -1,6 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/quartz_glass"
"all": "appliedenergistics2:items/quartz_glass"
}
}

View File

@ -1,6 +1,6 @@
{
"parent": "block/cube_all",
"textures": {
"all": "appliedenergistics2:blocks/quartz_glass"
"all": "appliedenergistics2:items/quartz_glass"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@ -1,5 +0,0 @@
{
"animation": {
"frametime": 100
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B