package appeng.client.render.model; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.ExecutionException; import javax.annotation.Nullable; import javax.vecmath.Matrix4f; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.collect.ImmutableList; import com.mojang.authlib.GameProfile; import org.apache.commons.lang3.tuple.Pair; 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.entity.EntityLivingBase; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.world.World; import net.minecraftforge.common.model.TRSRTransformation; import appeng.api.implementations.items.IBiometricCard; import appeng.api.util.AEColor; import appeng.client.render.cablebus.CubeBuilder; import appeng.core.AELog; class BiometricCardBakedModel implements IBakedModel { private final VertexFormat format; private final IBakedModel baseModel; private final TextureAtlasSprite texture; private final int hash; private final Cache modelCache; private final ImmutableList generalQuads; BiometricCardBakedModel( VertexFormat format, IBakedModel baseModel, TextureAtlasSprite texture ) { this( format, baseModel, texture, 0, createCache() ); } private BiometricCardBakedModel( VertexFormat format, IBakedModel baseModel, TextureAtlasSprite texture, int hash, Cache modelCache ) { this.format = format; this.baseModel = baseModel; this.texture = texture; this.hash = hash; this.generalQuads = ImmutableList.copyOf( this.buildGeneralQuads() ); this.modelCache = modelCache; } private static Cache createCache() { return CacheBuilder.newBuilder() .maximumSize( 100 ) .build(); } @Override public List getQuads( @Nullable IBlockState state, @Nullable EnumFacing side, long rand ) { List quads = this.baseModel.getQuads( state, side, rand ); if( side != null ) { return quads; } List result = new ArrayList<>( quads.size() + this.generalQuads.size() ); result.addAll( quads ); result.addAll( this.generalQuads ); return result; } private List buildGeneralQuads() { CubeBuilder builder = new CubeBuilder( this.format ); builder.setTexture( this.texture ); AEColor col = AEColor.values()[Math.abs( 3 + this.hash ) % AEColor.values().length]; if( this.hash == 0 ) { col = AEColor.BLACK; } for( int x = 0; x < 8; x++ ) { for( int y = 0; y < 6; y++ ) { final boolean isLit; // This makes the border always use the darker color if( x == 0 || y == 0 || x == 7 || y == 5 ) { isLit = false; } else { isLit = ( this.hash & ( 1 << x ) ) != 0 || ( this.hash & ( 1 << y ) ) != 0; } if( isLit ) { builder.setColorRGB( col.mediumVariant ); } else { final float scale = 0.3f / 255.0f; builder.setColorRGB( ( ( col.blackVariant >> 16 ) & 0xff ) * scale, ( ( col.blackVariant >> 8 ) & 0xff ) * scale, ( col.blackVariant & 0xff ) * scale ); } builder.addCube( 4 + x, 6 + y, 7.5f, 4 + x + 1, 6 + y + 1, 8.5f ); } } return builder.getOutput(); } @Override public boolean isAmbientOcclusion() { return this.baseModel.isAmbientOcclusion(); } @Override public boolean isGui3d() { return this.baseModel.isGui3d(); } @Override public boolean isBuiltInRenderer() { return this.baseModel.isBuiltInRenderer(); } @Override public TextureAtlasSprite getParticleTexture() { return this.baseModel.getParticleTexture(); } @Override public ItemCameraTransforms getItemCameraTransforms() { return this.baseModel.getItemCameraTransforms(); } @Override public ItemOverrideList getOverrides() { return new ItemOverrideList( Collections.emptyList() ) { @Override public IBakedModel handleItemState( IBakedModel originalModel, ItemStack stack, World world, EntityLivingBase entity ) { String username = ""; if( stack.getItem() instanceof IBiometricCard ) { final GameProfile gp = ( (IBiometricCard) stack.getItem() ).getProfile( stack ); if( gp != null ) { if( gp.getId() != null ) { username = gp.getId().toString(); } else { username = gp.getName(); } } } final int hash = !username.isEmpty() ? username.hashCode() : 0; // Get hash if( hash == 0 ) { return BiometricCardBakedModel.this; } try { return BiometricCardBakedModel.this.modelCache.get( hash, () -> new BiometricCardBakedModel( BiometricCardBakedModel.this.format, BiometricCardBakedModel.this.baseModel, BiometricCardBakedModel.this.texture, hash, BiometricCardBakedModel.this.modelCache ) ); } catch( ExecutionException e ) { AELog.error( e ); return BiometricCardBakedModel.this; } } }; } @Override public Pair handlePerspective( ItemCameraTransforms.TransformType type ) { // Delegate to the base model if possible if( this.baseModel instanceof IBakedModel ) { IBakedModel pam = this.baseModel; Pair pair = pam.handlePerspective( type ); return Pair.of( this, pair.getValue() ); } return Pair.of( this, TRSRTransformation.identity().getMatrix() ); } }