diff --git a/gradle.properties b/gradle.properties index 4f893da4a..a80949e4d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,6 +26,7 @@ top_version=1.12-1.4.23-16 cofhcore_version=1.12.2-4.5.2.19 crafttweaker_version=4.1.8.9 inventorytweaks_version=1.63 +ctm_version=0.3.0.15 ######################################################### # Deployment # diff --git a/gradle/scripts/dependencies.gradle b/gradle/scripts/dependencies.gradle index efdca4198..3f48ccdd7 100644 --- a/gradle/scripts/dependencies.gradle +++ b/gradle/scripts/dependencies.gradle @@ -68,7 +68,7 @@ repositories { name 'modmaven' url "https://modmaven.k-4u.nl/" } - + maven { name = "CurseForge" url = "https://minecraft.curseforge.com/api/maven/" @@ -94,7 +94,8 @@ dependencies { compileOnly "cofh:CoFHCore:${cofhcore_version}:deobf" compileOnly "CraftTweaker2:CraftTweaker2-API:${crafttweaker_version}" compileOnly "inventory-tweaks:InventoryTweaks:${inventorytweaks_version}:api" - + compileOnly "team.chisel.ctm:CTM:MC1.12-${ctm_version}" + // at runtime, use the full JEI jar runtime "mezz.jei:jei_${minecraft_version}:${jei_version}" diff --git a/src/main/java/appeng/block/networking/BlockCableBus.java b/src/main/java/appeng/block/networking/BlockCableBus.java index cfeeca70d..ccb53d72c 100644 --- a/src/main/java/appeng/block/networking/BlockCableBus.java +++ b/src/main/java/appeng/block/networking/BlockCableBus.java @@ -61,10 +61,12 @@ import net.minecraftforge.fml.common.registry.GameRegistry; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; -import appeng.api.AEApi; +import appeng.api.parts.IFacadeContainer; +import appeng.api.parts.IFacadePart; import appeng.api.parts.PartItemStack; import appeng.api.parts.SelectedPart; import appeng.api.util.AEColor; +import appeng.api.util.AEPartLocation; import appeng.block.AEBaseTileBlock; import appeng.client.UnlistedProperty; import appeng.client.render.cablebus.CableBusBakedModel; @@ -74,6 +76,7 @@ import appeng.core.AppEng; import appeng.core.sync.network.NetworkHandler; import appeng.core.sync.packets.PacketClick; import appeng.helpers.AEGlassMaterial; +import appeng.integration.abstraction.IAEFacade; import appeng.parts.ICableBusContainer; import appeng.parts.NullCableBusContainer; import appeng.tile.AEBaseTile; @@ -83,7 +86,7 @@ import appeng.tile.networking.TileCableBusTESR; import appeng.util.Platform; -public class BlockCableBus extends AEBaseTileBlock +public class BlockCableBus extends AEBaseTileBlock implements IAEFacade { public static final UnlistedProperty RENDER_STATE_PROPERTY = new UnlistedProperty<>( "cable_bus_render_state", CableBusRenderState.class ); @@ -122,6 +125,8 @@ public class BlockCableBus extends AEBaseTileBlock public IBlockState getExtendedState( IBlockState state, IBlockAccess world, BlockPos pos ) { CableBusRenderState renderState = this.cb( world, pos ).getRenderState(); + renderState.setWorld( world ); + renderState.setPos( pos ); return ( (IExtendedBlockState) state ).withProperty( RENDER_STATE_PROPERTY, renderState ); } @@ -317,13 +322,14 @@ public class BlockCableBus extends AEBaseTileBlock for( int l = 0; l < 4; ++l ) { // Randomly select one of the textures if the cable bus has more than just one possibility here - TextureAtlasSprite texture = Platform.pickRandom( textures ); + final TextureAtlasSprite texture = Platform.pickRandom( textures ); - double d0 = pos.getX() + ( j + 0.5D ) / 4.0D; - double d1 = pos.getY() + ( k + 0.5D ) / 4.0D; - double d2 = pos.getZ() + ( l + 0.5D ) / 4.0D; - ParticleDigging particle = new DestroyFX( world, d0, d1, d2, d0 - pos.getX() - 0.5D, d1 - pos + final double d0 = pos.getX() + ( j + 0.5D ) / 4.0D; + final double d1 = pos.getY() + ( k + 0.5D ) / 4.0D; + final double d2 = pos.getZ() + ( l + 0.5D ) / 4.0D; + final ParticleDigging particle = new DestroyFX( world, d0, d1, d2, d0 - pos.getX() - 0.5D, d1 - pos .getY() - 0.5D, d2 - pos.getZ() - 0.5D, this.getDefaultState() ).setBlockPos( pos ); + particle.setParticleTexture( texture ); effectRenderer.addEffect( particle ); } @@ -356,6 +362,20 @@ public class BlockCableBus extends AEBaseTileBlock return out == null ? NULL_CABLE_BUS : out; } + @Nullable + private IFacadeContainer fc( final IBlockAccess w, final BlockPos pos ) + { + final TileEntity te = w.getTileEntity( pos ); + IFacadeContainer out = null; + + if( te instanceof TileCableBus ) + { + out = ( (TileCableBus) te ).getCableBus().getFacadeContainer(); + } + + return out; + } + @Override public void onBlockClicked( World worldIn, BlockPos pos, EntityPlayer playerIn ) { @@ -436,14 +456,25 @@ public class BlockCableBus extends AEBaseTileBlock @Override public boolean canRenderInLayer( IBlockState state, BlockRenderLayer layer ) { - if( AEApi.instance().partHelper().getCableRenderMode().transparentFacades ) + return true; + } + + @Override + public IBlockState getFacadeState( IBlockAccess world, BlockPos pos, EnumFacing side ) + { + if( side != null ) { - return layer == BlockRenderLayer.CUTOUT || layer == BlockRenderLayer.TRANSLUCENT; - } - else - { - return layer == BlockRenderLayer.CUTOUT; + IFacadeContainer container = this.fc( world, pos ); + if( container != null ) + { + IFacadePart facade = container.getFacade( AEPartLocation.fromFacing( side ) ); + if( facade != null ) + { + return facade.getBlockState(); + } + } } + return world.getBlockState( pos ); } public static Class getNoTesrTile() diff --git a/src/main/java/appeng/client/render/FacadeBakedItemModel.java b/src/main/java/appeng/client/render/FacadeBakedItemModel.java new file mode 100644 index 000000000..fb8d68461 --- /dev/null +++ b/src/main/java/appeng/client/render/FacadeBakedItemModel.java @@ -0,0 +1,86 @@ +/* + * This file is part of Applied Energistics 2. + * Copyright (c) 2013 - 2018, AlgorithmX2, All rights reserved. + * + * Applied Energistics 2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Applied Energistics 2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Applied Energistics 2. If not, see . + */ + +package appeng.client.render; + + +import java.util.ArrayList; +import java.util.Collections; +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.ItemOverrideList; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumFacing; + +import appeng.client.render.cablebus.FacadeBuilder; + + +/** + * This model used the provided FacadeBuilder to "slice" the item quads for the facade provided. + * + * @author covers1624 + */ +public class FacadeBakedItemModel extends DelegateBakedModel +{ + + private final ItemStack textureStack; + private final FacadeBuilder facadeBuilder; + + protected FacadeBakedItemModel( IBakedModel base, ItemStack textureStack, FacadeBuilder facadeBuilder ) + { + super( base ); + this.textureStack = textureStack; + this.facadeBuilder = facadeBuilder; + } + + @Override + public List getQuads( @Nullable IBlockState state, @Nullable EnumFacing side, long rand ) + { + if( side != null ) + { + return Collections.emptyList(); + } + List quads = new ArrayList<>(); + quads.addAll( this.facadeBuilder.buildFacadeItemQuads( this.textureStack, EnumFacing.NORTH ) ); + quads.addAll( this.getBaseModel().getQuads( state, side, rand ) ); + return quads; + } + + @Override + public boolean isGui3d() + { + return false; + } + + @Override + public boolean isBuiltInRenderer() + { + return false; + } + + @Override + public ItemOverrideList getOverrides() + { + return ItemOverrideList.NONE; + } +} diff --git a/src/main/java/appeng/client/render/FacadeDispatcherBakedModel.java b/src/main/java/appeng/client/render/FacadeDispatcherBakedModel.java index 6eac8a2c8..d67fbf535 100644 --- a/src/main/java/appeng/client/render/FacadeDispatcherBakedModel.java +++ b/src/main/java/appeng/client/render/FacadeDispatcherBakedModel.java @@ -34,6 +34,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.world.World; +import appeng.client.render.cablebus.FacadeBuilder; import appeng.items.parts.ItemFacade; @@ -45,11 +46,13 @@ import appeng.items.parts.ItemFacade; public class FacadeDispatcherBakedModel extends DelegateBakedModel { private final VertexFormat format; + private final FacadeBuilder facadeBuilder; - public FacadeDispatcherBakedModel( IBakedModel baseModel, VertexFormat format ) + public FacadeDispatcherBakedModel( IBakedModel baseModel, VertexFormat format, FacadeBuilder facadeBuilder ) { super( baseModel ); this.format = format; + this.facadeBuilder = facadeBuilder; } // This is never used. See the item override list below. @@ -86,11 +89,9 @@ public class FacadeDispatcherBakedModel extends DelegateBakedModel ItemFacade itemFacade = (ItemFacade) stack.getItem(); - IBlockState state = itemFacade.getTextureBlockState( stack ); ItemStack textureItem = itemFacade.getTextureItem( stack ); - return new FacadeWithBlockBakedModel( FacadeDispatcherBakedModel.this - .getBaseModel(), state, textureItem, FacadeDispatcherBakedModel.this.format ); + return new FacadeBakedItemModel( FacadeDispatcherBakedModel.this.getBaseModel(), textureItem, FacadeDispatcherBakedModel.this.facadeBuilder ); } }; } diff --git a/src/main/java/appeng/client/render/FacadeItemModel.java b/src/main/java/appeng/client/render/FacadeItemModel.java index 5ec387d0e..cc05a07d5 100644 --- a/src/main/java/appeng/client/render/FacadeItemModel.java +++ b/src/main/java/appeng/client/render/FacadeItemModel.java @@ -31,6 +31,7 @@ import net.minecraftforge.client.model.IModel; import net.minecraftforge.client.model.ModelLoaderRegistry; import net.minecraftforge.common.model.IModelState; +import appeng.client.render.cablebus.FacadeBuilder; import appeng.core.AppEng; @@ -71,8 +72,9 @@ public class FacadeItemModel implements IModel public IBakedModel bake( IModelState state, VertexFormat format, Function bakedTextureGetter ) { IBakedModel bakedBaseModel = this.getBaseModel().bake( state, format, bakedTextureGetter ); + FacadeBuilder facadeBuilder = new FacadeBuilder(); - return new FacadeDispatcherBakedModel( bakedBaseModel, format ); + return new FacadeDispatcherBakedModel( bakedBaseModel, format, facadeBuilder ); } @Override diff --git a/src/main/java/appeng/client/render/FacadeWithBlockBakedModel.java b/src/main/java/appeng/client/render/FacadeWithBlockBakedModel.java deleted file mode 100644 index 7ca637db7..000000000 --- a/src/main/java/appeng/client/render/FacadeWithBlockBakedModel.java +++ /dev/null @@ -1,106 +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 . - */ - -package appeng.client.render; - - -import java.util.ArrayList; -import java.util.EnumSet; -import java.util.List; - -import javax.annotation.Nullable; - -import net.minecraft.block.state.IBlockState; -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.block.model.BakedQuad; -import net.minecraft.client.renderer.block.model.IBakedModel; -import net.minecraft.client.renderer.block.model.ItemOverrideList; -import net.minecraft.client.renderer.vertex.VertexFormat; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumFacing; - -import appeng.client.render.cablebus.CubeBuilder; -import appeng.client.render.cablebus.FacadeBuilder; - - -/** - * This is the actual baked model that will combine the north face of a given block state - * with the base facade item model to achieve what is then actually rendered on screen. - */ -public class FacadeWithBlockBakedModel extends DelegateBakedModel -{ - private final IBlockState blockState; - - private final IBakedModel textureModel; - - private final VertexFormat format; - - private final ItemStack textureItem; - - public FacadeWithBlockBakedModel( IBakedModel baseModel, IBlockState blockState, ItemStack textureItem, VertexFormat format ) - { - super( baseModel ); - this.blockState = blockState; - this.textureItem = textureItem; - this.textureModel = Minecraft.getMinecraft().getBlockRendererDispatcher().getModelForState( blockState ); - this.format = format; - } - - @Override - public List getQuads( @Nullable IBlockState state, @Nullable EnumFacing side, long rand ) - { - // Only the north side is actually read from the base model for item models - if( side == EnumFacing.NORTH ) - { - List quads = new ArrayList<>( 1 ); - CubeBuilder builder = new CubeBuilder( this.format, quads ); - FacadeBuilder.TextureAtlasAndTint sprite = FacadeBuilder.getSprite( this.textureModel, this.blockState, side, rand ); - if( sprite != null && sprite.getSprite() != null ) - { - if( sprite.getTint() != -1 ) - { - builder.setColor( Minecraft.getMinecraft().getItemColors().colorMultiplier( this.textureItem, sprite.getTint() ) ); - } - builder.setTexture( sprite.getSprite() ); - builder.setDrawFaces( EnumSet.of( EnumFacing.NORTH ) ); - builder.addCube( 0, 0, 0, 16, 16, 16 ); - return quads; - } - } - - return this.getBaseModel().getQuads( state, side, rand ); - } - - @Override - public boolean isGui3d() - { - return false; - } - - @Override - public boolean isBuiltInRenderer() - { - return false; - } - - @Override - public ItemOverrideList getOverrides() - { - return ItemOverrideList.NONE; - } -} diff --git a/src/main/java/appeng/client/render/cablebus/CableBusBakedModel.java b/src/main/java/appeng/client/render/cablebus/CableBusBakedModel.java index ee46d86c9..d458e7b71 100644 --- a/src/main/java/appeng/client/render/cablebus/CableBusBakedModel.java +++ b/src/main/java/appeng/client/render/cablebus/CableBusBakedModel.java @@ -88,8 +88,9 @@ public class CableBusBakedModel implements IBakedModel List quads = new ArrayList<>(); - // The core parts of the cable will only be rendered in the CUTOUT layer. TRANSLUCENT is used only for - // translucent facades further down below. + // The core parts of the cable will only be rendered in the CUTOUT layer. + // Facades will add them selves to what ever the block would be rendered with, + // except when transparent facades are enabled, they are forced to TRANSPARENT. if( layer == BlockRenderLayer.CUTOUT ) { @@ -138,14 +139,7 @@ public class CableBusBakedModel implements IBakedModel } } } - - this.facadeBuilder.addFacades( - layer, - renderState.getFacades(), - renderState.getBoundingBoxes(), - renderState.getAttachments().keySet(), - rand, - quads ); + this.facadeBuilder.buildFacadeQuads( layer, renderState, rand, quads, this.partModels::get ); return quads; } diff --git a/src/main/java/appeng/client/render/cablebus/CableBusModel.java b/src/main/java/appeng/client/render/cablebus/CableBusModel.java index 67c9ef688..56856cd3e 100644 --- a/src/main/java/appeng/client/render/cablebus/CableBusModel.java +++ b/src/main/java/appeng/client/render/cablebus/CableBusModel.java @@ -65,7 +65,6 @@ public class CableBusModel implements IModel { return ImmutableList.builder() .addAll( CableBuilder.getTextures() ) - .addAll( FacadeBuilder.getTextures() ) .build(); } @@ -75,7 +74,7 @@ public class CableBusModel implements IModel Map partModels = this.loadPartModels( state, format, bakedTextureGetter ); CableBuilder cableBuilder = new CableBuilder( format, bakedTextureGetter ); - FacadeBuilder facadeBuilder = new FacadeBuilder( format, bakedTextureGetter ); + FacadeBuilder facadeBuilder = new FacadeBuilder(); // This should normally not be used, but we *have* to provide a particle texture or otherwise damage models will // crash diff --git a/src/main/java/appeng/client/render/cablebus/CableBusRenderState.java b/src/main/java/appeng/client/render/cablebus/CableBusRenderState.java index 03f94b583..28e2f8bf4 100644 --- a/src/main/java/appeng/client/render/cablebus/CableBusRenderState.java +++ b/src/main/java/appeng/client/render/cablebus/CableBusRenderState.java @@ -19,6 +19,7 @@ package appeng.client.render.cablebus; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.EnumMap; import java.util.EnumSet; @@ -27,6 +28,8 @@ import java.util.Objects; import net.minecraft.util.EnumFacing; import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; import appeng.api.parts.IPartModel; import appeng.api.util.AECableType; @@ -69,6 +72,10 @@ public class CableBusRenderState // Contains the facade to use for each side that has a facade attached private EnumMap facades = new EnumMap<>( EnumFacing.class ); + // Used for Facades. + private WeakReference world; + private BlockPos pos; + // Contains the bounding boxes of all parts on the cable bus to allow facades to cut out holes for the parts. This // list is only populated if there are // facades on this cable bus @@ -151,6 +158,26 @@ public class CableBusRenderState return this.facades; } + public IBlockAccess getWorld() + { + return this.world.get(); + } + + public void setWorld( IBlockAccess world ) + { + this.world = new WeakReference<>( world ); + } + + public BlockPos getPos() + { + return this.pos; + } + + public void setPos( BlockPos pos ) + { + this.pos = pos; + } + public List getBoundingBoxes() { return this.boundingBoxes; @@ -196,9 +223,8 @@ public class CableBusRenderState final CableBusRenderState other = (CableBusRenderState) obj; return this.cableColor == other.cableColor && this.cableType == other.cableType && this.coreType == other.coreType && Objects - .equals( this.attachmentConnections, other.attachmentConnections ) && Objects.equals( this.cableBusAdjacent, - other.cableBusAdjacent ) && Objects.equals( this.channelsOnSide, other.channelsOnSide ) && Objects.equals( this.connectionTypes, - other.connectionTypes ) && Objects.equals( this.partFlags, other.partFlags ); + .equals( this.attachmentConnections, other.attachmentConnections ) && Objects.equals( this.cableBusAdjacent, other.cableBusAdjacent ) && Objects + .equals( this.channelsOnSide, other.channelsOnSide ) && Objects.equals( this.connectionTypes, other.connectionTypes ) && Objects + .equals( this.partFlags, other.partFlags ); } - } diff --git a/src/main/java/appeng/client/render/cablebus/FacadeBlockAccess.java b/src/main/java/appeng/client/render/cablebus/FacadeBlockAccess.java new file mode 100644 index 000000000..733f038ef --- /dev/null +++ b/src/main/java/appeng/client/render/cablebus/FacadeBlockAccess.java @@ -0,0 +1,115 @@ +/* + * This file is part of Applied Energistics 2. + * Copyright (c) 2013 - 2018, AlgorithmX2, All rights reserved. + * + * Applied Energistics 2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Applied Energistics 2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Applied Energistics 2. If not, see . + */ + +package appeng.client.render.cablebus; + + +import javax.annotation.Nullable; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.WorldType; +import net.minecraft.world.biome.Biome; + + +/** + * This is used to retrieve the ExtendedState of a block for facade rendering. + * It fakes the block at BlockPos provided as the IBlockState provided. + * + * @author covers1624 + */ +public class FacadeBlockAccess implements IBlockAccess +{ + + private final IBlockAccess world; + private final BlockPos pos; + private final EnumFacing side; + private final IBlockState state; + + public FacadeBlockAccess( IBlockAccess world, BlockPos pos, EnumFacing side, IBlockState state ) + { + this.world = world; + this.pos = pos; + this.side = side; + this.state = state; + } + + @Nullable + @Override + public TileEntity getTileEntity( BlockPos pos ) + { + return this.world.getTileEntity( pos ); + } + + @Override + public int getCombinedLight( BlockPos pos, int lightValue ) + { + return this.world.getCombinedLight( pos, lightValue ); + } + + @Override + public IBlockState getBlockState( BlockPos pos ) + { + if( this.pos == pos ) + { + return this.state; + } + return this.world.getBlockState( pos ); + } + + @Override + public boolean isAirBlock( BlockPos pos ) + { + IBlockState state = this.getBlockState( pos ); + return state.getBlock().isAir( state, this.world, pos ); + } + + @Override + public Biome getBiome( BlockPos pos ) + { + return this.world.getBiome( pos ); + } + + @Override + public int getStrongPower( BlockPos pos, EnumFacing direction ) + { + return this.world.getStrongPower( pos, direction ); + } + + @Override + public WorldType getWorldType() + { + return this.world.getWorldType(); + } + + @Override + public boolean isSideSolid( BlockPos pos, EnumFacing side, boolean _default ) + { + if( pos.getX() < -30000000 || pos.getZ() < -30000000 || pos.getX() >= 30000000 || pos.getZ() >= 30000000 ) + { + return _default; + } + else + { + return this.getBlockState( pos ).isSideSolid( this, pos, side ); + } + } +} diff --git a/src/main/java/appeng/client/render/cablebus/FacadeBuilder.java b/src/main/java/appeng/client/render/cablebus/FacadeBuilder.java index a855f5bb2..1cc8b0989 100644 --- a/src/main/java/appeng/client/render/cablebus/FacadeBuilder.java +++ b/src/main/java/appeng/client/render/cablebus/FacadeBuilder.java @@ -1,6 +1,6 @@ /* * This file is part of Applied Energistics 2. - * Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved. + * Copyright (c) 2013 - 2018, 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 @@ -19,370 +19,372 @@ package appeng.client.render.cablebus; -import java.util.Collection; +import java.util.ArrayList; import java.util.Collections; -import java.util.EnumSet; -import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.function.Function; import javax.annotation.Nullable; -import javax.vecmath.Vector3f; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BlockRendererDispatcher; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.IBakedModel; -import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.color.BlockColors; +import net.minecraft.item.ItemStack; import net.minecraft.util.BlockRenderLayer; import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumFacing.Axis; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; import net.minecraftforge.client.ForgeHooksClient; -import net.minecraftforge.client.MinecraftForgeClient; import appeng.api.AEApi; import appeng.api.util.AEAxisAlignedBB; -import appeng.core.AELog; -import appeng.core.AppEng; +import appeng.parts.misc.PartCableAnchor; +import appeng.thirdparty.codechicken.lib.model.CachedFormat; +import appeng.thirdparty.codechicken.lib.model.Quad; +import appeng.thirdparty.codechicken.lib.model.pipeline.BakedPipeline; +import appeng.thirdparty.codechicken.lib.model.pipeline.transformers.QuadAlphaOverride; +import appeng.thirdparty.codechicken.lib.model.pipeline.transformers.QuadClamper; +import appeng.thirdparty.codechicken.lib.model.pipeline.transformers.QuadCornerKicker; +import appeng.thirdparty.codechicken.lib.model.pipeline.transformers.QuadFaceStripper; +import appeng.thirdparty.codechicken.lib.model.pipeline.transformers.QuadReInterpolator; +import appeng.thirdparty.codechicken.lib.model.pipeline.transformers.QuadTinter; /** - * Handles creating the quads for facades attached to cable busses. + * The FacadeBuilder builds for facades.. + * + * @author covers1624 */ public class FacadeBuilder { - private static final ResourceLocation TEXTURE_FACADE = new ResourceLocation( AppEng.MOD_ID, "parts/cable_anchor" ); + public static double THICK_THICKNESS = 2D / 16D; + public static double THIN_THICKNESS = 1D / 16D; - private final VertexFormat format; + public static final AxisAlignedBB[] THICK_FACADE_BOXES = new AxisAlignedBB[] { + new AxisAlignedBB( 0.0, 0.0, 0.0, 1.0, THICK_THICKNESS, 1.0 ), + new AxisAlignedBB( 0.0, 1.0 - THICK_THICKNESS, 0.0, 1.0, 1.0, 1.0 ), + new AxisAlignedBB( 0.0, 0.0, 0.0, 1.0, 1.0, THICK_THICKNESS ), + new AxisAlignedBB( 0.0, 0.0, 1.0 - THICK_THICKNESS, 1.0, 1.0, 1.0 ), + new AxisAlignedBB( 0.0, 0.0, 0.0, THICK_THICKNESS, 1.0, 1.0 ), + new AxisAlignedBB( 1.0 - THICK_THICKNESS, 0.0, 0.0, 1.0, 1.0, 1.0 ) + }; - private final TextureAtlasSprite facadeTexture; + public static final AxisAlignedBB[] THIN_FACADE_BOXES = new AxisAlignedBB[] { + new AxisAlignedBB( 0.0, 0.0, 0.0, 1.0, THIN_THICKNESS, 1.0 ), + new AxisAlignedBB( 0.0, 1.0 - THIN_THICKNESS, 0.0, 1.0, 1.0, 1.0 ), + new AxisAlignedBB( 0.0, 0.0, 0.0, 1.0, 1.0, THIN_THICKNESS ), + new AxisAlignedBB( 0.0, 0.0, 1.0 - THIN_THICKNESS, 1.0, 1.0, 1.0 ), + new AxisAlignedBB( 0.0, 0.0, 0.0, THIN_THICKNESS, 1.0, 1.0 ), + new AxisAlignedBB( 1.0 - THIN_THICKNESS, 0.0, 0.0, 1.0, 1.0, 1.0 ) + }; - private static final Set warnedFor = new HashSet<>(); + private ThreadLocal pipelines = ThreadLocal.withInitial( () -> BakedPipeline.builder() + // Clamper is responsible for clamping the vertex to the bounds specified. + .addElement( "clamper", QuadClamper.FACTORY ) + // Strips faces if they match a mask. + .addElement( "face_stripper", QuadFaceStripper.FACTORY ) + // Kicks the edge inner corners in, solves Z fighting + .addElement( "corner_kicker", QuadCornerKicker.FACTORY ) + // Re-Interpolates the UV's for the quad. + .addElement( "interp", QuadReInterpolator.FACTORY ) + // Tints the quad if we need it to. Disabled by default. + .addElement( "tinter", QuadTinter.FACTORY, false ) + // Overrides the quad's alpha if we are forcing transparent facades. + .addElement( "transparent", QuadAlphaOverride.FACTORY, false, e -> e.setAlphaOverride( 0x4C / 255F ) ) + .build()// + ); + private ThreadLocal collectors = ThreadLocal.withInitial( Quad::new ); - FacadeBuilder( VertexFormat format, Function bakedTextureGetter ) - { - this.format = format; - this.facadeTexture = bakedTextureGetter.apply( TEXTURE_FACADE ); - } - - static Collection getTextures() - { - return Collections.singletonList( TEXTURE_FACADE ); - } - - void addFacades( BlockRenderLayer layer, Map facadesState, List partBoxes, Set sidesWithParts, long rand, List quads ) + public void buildFacadeQuads( BlockRenderLayer layer, CableBusRenderState renderState, long rand, List quads, Function modelLookup ) { + BakedPipeline pipeline = this.pipelines.get(); + Quad collectorQuad = this.collectors.get(); + boolean transparent = AEApi.instance().partHelper().getCableRenderMode().transparentFacades; + Map facadeStates = renderState.getFacades(); + List partBoxes = renderState.getBoundingBoxes(); + Set sidesWithParts = renderState.getAttachments().keySet(); + IBlockAccess parentWorld = renderState.getWorld(); + BlockPos pos = renderState.getPos(); + BlockColors blockColors = Minecraft.getMinecraft().getBlockColors(); boolean thinFacades = isUseThinFacades( partBoxes ); - CubeBuilder builder = new CubeBuilder( this.format, quads ); - - facadesState.forEach( ( side, state ) -> + for( Entry entry : facadeStates.entrySet() ) { - AxisAlignedBB facadeBox = getFacadeBox( side, thinFacades ); - AEAxisAlignedBB cutOutBox = getCutOutBox( facadeBox, partBoxes ); + EnumFacing side = entry.getKey(); + int sideIndex = side.ordinal(); + FacadeRenderState facadeRenderState = entry.getValue(); boolean renderStilt = !sidesWithParts.contains( side ); + if( layer == BlockRenderLayer.CUTOUT && renderStilt ) + { + for( ResourceLocation part : PartCableAnchor.FACADE_MODELS.getModels() ) + { + IBakedModel partModel = modelLookup.apply( part ); + QuadRotator rotator = new QuadRotator(); + quads.addAll( rotator.rotateQuads( gatherQuads( partModel, null, rand ), side, EnumFacing.UP ) ); + } + } + // If we are forcing transparency and this isn't the Translucent layer. + if( transparent && layer != BlockRenderLayer.TRANSLUCENT ) + { + continue; + } + + IBlockState blockState = facadeRenderState.getSourceBlock(); + // If we aren't forcing transparency let the block decide if it should render. + if( !transparent ) + { + if( !blockState.getBlock().canRenderInLayer( blockState, layer ) ) + { + continue; + } + } + + AxisAlignedBB fullBounds = thinFacades ? THIN_FACADE_BOXES[sideIndex] : THICK_FACADE_BOXES[sideIndex]; + AxisAlignedBB facadeBox = fullBounds; + // If we are a transparent facade, we need to modify out BB. + if( facadeRenderState.isTransparent() ) + { + double offset = thinFacades ? THIN_THICKNESS : THICK_THICKNESS; + AEAxisAlignedBB tmpBB = null; + for( EnumFacing face : EnumFacing.VALUES ) + { + // Only faces that aren't on our axis + if( face.getAxis() != side.getAxis() ) + { + FacadeRenderState otherState = facadeStates.get( face ); + if( otherState != null && !otherState.isTransparent() ) + { + if( tmpBB == null ) + { + tmpBB = AEAxisAlignedBB.fromBounds( facadeBox ); + } + switch( face ) + { + case DOWN: + tmpBB.minY += offset; + break; + case UP: + tmpBB.maxY -= offset; + break; + case NORTH: + tmpBB.minZ += offset; + break; + case SOUTH: + tmpBB.maxZ -= offset; + break; + case WEST: + tmpBB.minX += offset; + break; + case EAST: + tmpBB.maxX -= offset; + break; + default: + throw new RuntimeException( "Switch falloff. " + String.valueOf( face ) ); + } + } + } + } + if( tmpBB != null ) + { + facadeBox = tmpBB.getBoundingBox(); + } + } + + AEAxisAlignedBB cutOutBox = getCutOutBox( facadeBox, partBoxes ); + List holeStrips = getBoxes( facadeBox, cutOutBox, side.getAxis() ); + IBlockAccess facadeAccess = new FacadeBlockAccess( parentWorld, pos, side, blockState ); + + BlockRendererDispatcher dispatcher = Minecraft.getMinecraft().getBlockRendererDispatcher(); try { - this.addFacade( layer, facadesState, side, cutOutBox, thinFacades, renderStilt, rand, builder ); + blockState = blockState.getActualState( facadeAccess, pos ); } - catch( Throwable t ) + catch( Exception ignored ) { - AELog.debug( t ); } - } ); - } - - public static TextureAtlasAndTint getSprite( IBakedModel blockModel, IBlockState state, EnumFacing facing, long rand ) - { - - TextureAtlasAndTint firstFound = null; - BlockRenderLayer orgLayer = MinecraftForgeClient.getRenderLayer(); - - try - { - // Some other mods also distinguish between layers, so we're doing this in a loop from most likely to least - // likely - for( BlockRenderLayer layer : BlockRenderLayer.values() ) + IBakedModel model = dispatcher.getModelForState( blockState ); + try { + blockState = blockState.getBlock().getExtendedState( blockState, facadeAccess, pos ); + } + catch( Exception ignored ) + { + } + List modelQuads = new ArrayList<>(); + // If we are forcing transparent facades, fake the render layer, and grab all quads. + if( transparent ) + { + for( BlockRenderLayer forcedLayer : BlockRenderLayer.values() ) + { + // Check if the block renders on the layer we want to force. + if( blockState.getBlock().canRenderInLayer( blockState, forcedLayer ) ) + { + // Force the layer and gather quads. + ForgeHooksClient.setRenderLayer( forcedLayer ); + modelQuads.addAll( gatherQuads( model, blockState, rand ) ); + } + } + + // Reset. ForgeHooksClient.setRenderLayer( layer ); + } + else + { + modelQuads.addAll( gatherQuads( model, blockState, rand ) ); + } - for( BakedQuad bakedQuad : blockModel.getQuads( state, facing, rand ) ) - { - return new TextureAtlasAndTint( bakedQuad ); - } + // No quads.. Cool, next! + if( modelQuads.isEmpty() ) + { + continue; + } - for( BakedQuad bakedQuad : blockModel.getQuads( state, null, rand ) ) + // Grab out pipeline elements. + QuadClamper clamper = pipeline.getElement( "clamper", QuadClamper.class ); + QuadFaceStripper edgeStripper = pipeline.getElement( "face_stripper", QuadFaceStripper.class ); + QuadTinter tinter = pipeline.getElement( "tinter", QuadTinter.class ); + QuadCornerKicker kicker = pipeline.getElement( "corner_kicker", QuadCornerKicker.class ); + + // Set global element states. + + // calculate the side mask. + int facadeMask = 0; + for( Entry ent : facadeStates.entrySet() ) + { + EnumFacing s = ent.getKey(); + if( s.getAxis() != side.getAxis() ) { - if( firstFound == null ) + FacadeRenderState otherState = ent.getValue(); + if( !otherState.isTransparent() ) { - firstFound = new TextureAtlasAndTint( bakedQuad ); + facadeMask |= 1 << s.ordinal(); } - if( bakedQuad.getFace() == facing ) + } + } + // Setup the edge stripper. + edgeStripper.setBounds( fullBounds ); + edgeStripper.setMask( facadeMask ); + + // Setup the kicker. + kicker.setSide( sideIndex ); + kicker.setFacadeMask( facadeMask ); + kicker.setBox( fullBounds ); + kicker.setThickness( thinFacades ? THIN_THICKNESS : THICK_THICKNESS ); + + for( BakedQuad quad : modelQuads ) + { + // lookup the format in CachedFormat. + CachedFormat format = CachedFormat.lookup( quad.getFormat() ); + // If this quad has a tint index, setup the tinter. + if( quad.hasTintIndex() ) + { + tinter.setTint( blockColors.colorMultiplier( blockState, facadeAccess, pos, quad.getTintIndex() ) ); + } + for( AxisAlignedBB box : holeStrips ) + { + // setup the clamper for this box + clamper.setClampBounds( box ); + // Reset the pipeline, clears all enabled/disabled states. + pipeline.reset( format ); + // Reset out collector. + collectorQuad.reset( format ); + // Enable / disable the optional elements + pipeline.setElementState( "tinter", quad.hasTintIndex() ); + pipeline.setElementState( "transparent", transparent ); + // Prepare the pipeline for a quad. + pipeline.prepare( collectorQuad ); + + // Pipe our quad into the pipeline. + quad.pipe( pipeline ); + // Check if the collector got any data. + if( collectorQuad.full ) { - return new TextureAtlasAndTint( bakedQuad ); + // Add the result. + quads.add( collectorQuad.bake() ); } } } } - catch( Exception e ) - { - if( warnedFor.add( state.getBlock().getRegistryName() ) ) - { - AELog.warn( "Unable to get facade sprite for blockstate %s. Supressing further warnings for this block.", state ); - AELog.debug( e ); - } - } - finally - { - ForgeHooksClient.setRenderLayer( orgLayer ); - } - - // Fall back to the particle texture, if we havent found anything else so far. - if( firstFound == null ) - { - try - { - return new TextureAtlasAndTint( blockModel.getParticleTexture(), -1 ); - } - catch( Exception e ) - { - if( warnedFor.add( state.getBlock().getRegistryName() ) ) - { - AELog.warn( "Unable to get facade sprite particle texture fallback for blockstate %s. Supressing further warnings for this block.", state ); - AELog.debug( e ); - } - } - } - - return firstFound; } - private void addFacade( BlockRenderLayer layer, Map facades, EnumFacing side, AEAxisAlignedBB busBounds, boolean thinFacades, boolean renderStilt, long rand, CubeBuilder builder ) + /** + * This is slow, so should be cached. + * + * @return The model. + */ + public List buildFacadeItemQuads( ItemStack textureItem, EnumFacing side ) { + List facadeQuads = new ArrayList<>(); + IBakedModel model = Minecraft.getMinecraft().getRenderItem().getItemModelWithOverrides( textureItem, null, null ); + List modelQuads = gatherQuads( model, null, 0 ); - FacadeRenderState facadeState = facades.get( side ); - IBlockState blockState = facadeState.getSourceBlock(); + BakedPipeline pipeline = this.pipelines.get(); + Quad collectorQuad = this.collectors.get(); - builder.setDrawFaces( EnumSet.allOf( EnumFacing.class ) ); + // Grab pipeline elements. + QuadClamper clamper = pipeline.getElement( "clamper", QuadClamper.class ); + QuadTinter tinter = pipeline.getElement( "tinter", QuadTinter.class ); - // Reset to no color multiplicator - builder.setColorRGB( 0xFFFFFF ); - builder.useStandardUV(); - - // We only render the stilt if we don't intersect with any part directly, and if there's no part on our side - if( renderStilt && busBounds == null && layer == BlockRenderLayer.CUTOUT ) + for( BakedQuad quad : modelQuads ) { - builder.setTexture( this.facadeTexture ); - switch( side ) + // Lookup the CachedFormat for this quads format. + CachedFormat format = CachedFormat.lookup( quad.getFormat() ); + // Reset the pipeline. + pipeline.reset( format ); + // Reset the collector. + collectorQuad.reset( format ); + // If we have a tint index, setup the tinter and enable it. + if( quad.hasTintIndex() ) { - case DOWN: - builder.addCube( 7, 1, 7, 9, 6, 9 ); - break; - case UP: - builder.addCube( 7, 10, 7, 9, 15, 9 ); - break; - case NORTH: - builder.addCube( 7, 7, 1, 9, 9, 6 ); - break; - case SOUTH: - builder.addCube( 7, 7, 10, 9, 9, 15 ); - break; - case WEST: - builder.addCube( 1, 7, 7, 6, 9, 9 ); - break; - case EAST: - builder.addCube( 10, 7, 7, 15, 9, 9 ); - break; - } - } - - // Do not add the translucent facade in any other layer than translucent - boolean translucent = AEApi.instance().partHelper().getCableRenderMode().transparentFacades; - if( translucent && layer != BlockRenderLayer.TRANSLUCENT ) - { - return; - } - - final float thickness = thinFacades ? 1 : 2; - - BlockRendererDispatcher blockRendererDispatcher = Minecraft.getMinecraft().getBlockRendererDispatcher(); - IBakedModel blockModel = blockRendererDispatcher.getModelForState( blockState ); - - final int color; - if( translucent ) - { - color = 0x4CFFFFFF; - } - else - { - color = 0xFFFFFFFF; - } - - // TODO: Cache this - for( EnumFacing facing : facadeState.getOpenFaces() ) - { - TextureAtlasAndTint spriteAndTint = getSprite( blockModel, blockState, facing, rand ); - if( spriteAndTint != null && spriteAndTint.sprite != null ) - { - // Use the tint color from the item stack here, which is based upon the assumption that the - // model used for the block will use the same meaning for tint indices as the item model does - if( spriteAndTint.tint != -1 ) - { - int tintColor = facadeState.resolveTintColor( spriteAndTint.tint ); - - // Still apply the transparency color - tintColor &= 0xFFFFFF; - tintColor |= color & 0xFF000000; - builder.setColor( tintColor ); - } - else - { - builder.setColor( color ); - } - builder.setTexture( facing, spriteAndTint.sprite ); - } - else - { - builder.setColor( color ); - builder.setTexture( facing, this.facadeTexture ); - } - } - - builder.setDrawFaces( facadeState.getOpenFaces() ); - - AxisAlignedBB primaryBox = getFacadeBox( side, thinFacades ); - - Vector3f min = new Vector3f( (float) primaryBox.minX * 16, (float) primaryBox.minY * 16, (float) primaryBox.minZ * 16 ); - Vector3f max = new Vector3f( (float) primaryBox.maxX * 16, (float) primaryBox.maxY * 16, (float) primaryBox.maxZ * 16 ); - - if( busBounds == null ) - { - // Adjust the facade for neighboring facades so that facade cubes dont overlap with each other - if( side == EnumFacing.NORTH || side == EnumFacing.SOUTH ) - { - if( facades.containsKey( EnumFacing.UP ) ) - { - max.y -= thickness; - } - - if( facades.containsKey( EnumFacing.DOWN ) ) - { - min.y += thickness; - } - } - else if( side == EnumFacing.EAST || side == EnumFacing.WEST ) - { - if( facades.containsKey( EnumFacing.UP ) ) - { - max.y -= thickness; - } - - if( facades.containsKey( EnumFacing.DOWN ) ) - { - min.y += thickness; - } - - if( facades.containsKey( EnumFacing.SOUTH ) ) - { - max.z -= thickness; - } - - if( facades.containsKey( EnumFacing.NORTH ) ) - { - min.z += thickness; - } - } - - builder.addCube( min.x, min.y, min.z, max.x, max.y, max.z ); - } - else - { - Vector3f busMin = new Vector3f( (float) busBounds.minX * 16, (float) busBounds.minY * 16, (float) busBounds.minZ * 16 ); - Vector3f busMax = new Vector3f( (float) busBounds.maxX * 16, (float) busBounds.maxY * 16, (float) busBounds.maxZ * 16 ); - - if( side == EnumFacing.UP || side == EnumFacing.DOWN ) - { - this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, busMax.z, 16.0f, 16.0f, 16.0f ); - this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, 0.0f, 16.0f, 16.0f, busMin.z ); - this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, busMin.z, busMin.x, 16.0f, busMax.z ); - this.renderSegmentBlockCurrentBounds( builder, min, max, busMax.x, 0.0f, busMin.z, 16.0f, 16.0f, busMax.z ); - } - else if( side == EnumFacing.NORTH || side == EnumFacing.SOUTH ) - { - if( facades.get( EnumFacing.UP ) != null ) - { - max.y -= thickness; - } - - if( facades.get( EnumFacing.DOWN ) != null ) - { - min.y += thickness; - } - - this.renderSegmentBlockCurrentBounds( builder, min, max, busMax.x, 0.0f, 0.0f, 16.0f, 16.0f, 16.0f ); - this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, 0.0f, busMin.x, 16.0f, 16.0f ); - this.renderSegmentBlockCurrentBounds( builder, min, max, busMin.x, 0.0f, 0.0f, busMax.x, busMin.y, 16.0f ); - this.renderSegmentBlockCurrentBounds( builder, min, max, busMin.x, busMax.y, 0.0f, busMax.x, 16.0f, 16.0f ); - } - else - { - if( facades.get( EnumFacing.UP ) != null ) - { - max.y -= thickness; - } - - if( facades.get( EnumFacing.DOWN ) != null ) - { - min.y += thickness; - } - - if( facades.get( EnumFacing.SOUTH ) != null ) - { - max.z -= thickness; - } - - if( facades.get( EnumFacing.NORTH ) != null ) - { - min.z += thickness; - } - - this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, busMax.z, 16.0f, 16.0f, 16.0f ); - this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, 0.0f, 16.0f, 16.0f, busMin.z ); - this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, 0.0f, busMin.z, 16.0f, busMin.y, busMax.z ); - this.renderSegmentBlockCurrentBounds( builder, min, max, 0.0f, busMax.y, busMin.z, 16.0f, 16.0f, busMax.z ); + tinter.setTint( Minecraft.getMinecraft().getItemColors().colorMultiplier( textureItem, quad.getTintIndex() ) ); + pipeline.enableElement( "tinter" ); + } + // Disable elements we don't need for items. + pipeline.disableElement( "face_stripper" ); + pipeline.disableElement( "corner_kicker" ); + // Setup the clamper + clamper.setClampBounds( THICK_FACADE_BOXES[side.ordinal()] ); + // Prepare the pipeline. + pipeline.prepare( collectorQuad ); + // Pipe our quad into the pipeline. + quad.pipe( pipeline ); + // Check the collector for data and add the quad if there was. + if( collectorQuad.full ) + { + facadeQuads.add( collectorQuad.bakeUnpacked() ); } } + return facadeQuads; } - private void renderSegmentBlockCurrentBounds( CubeBuilder builder, Vector3f min, Vector3f max, float minX, float minY, float minZ, float maxX, float maxY, float maxZ ) + // Helper to gather all quads from a model into a list. + private static List gatherQuads( IBakedModel model, IBlockState state, long rand ) { - minX = Math.max( min.x, minX ); - minY = Math.max( min.y, minY ); - minZ = Math.max( min.z, minZ ); - maxX = Math.min( max.x, maxX ); - maxY = Math.min( max.y, maxY ); - maxZ = Math.min( max.z, maxZ ); - - // don't draw it if its not at least a pixel wide... - if( maxX - minX >= 1.0 && maxY - minY >= 1.0 && maxZ - minZ >= 1.0 ) + List modelQuads = new ArrayList<>(); + for( EnumFacing face : EnumFacing.VALUES ) { - builder.addCube( minX, minY, minZ, maxX, maxY, maxZ ); + modelQuads.addAll( model.getQuads( state, face, rand ) ); } - + modelQuads.addAll( model.getQuads( state, null, rand ) ); + return modelQuads; } /** * Given the actual facade bounding box, and the bounding boxes of all parts, determine the biggest union of AABB - * that intersect with the - * facade's bounding box. This AABB will need to be "cut out" when the facade is rendered. + * that intersect with the facade's bounding + * box. This AABB will need to be "cut out" when the facade is rendered. */ @Nullable private static AEAxisAlignedBB getCutOutBox( AxisAlignedBB facadeBox, List partBoxes ) @@ -411,8 +413,58 @@ public class FacadeBuilder } /** - * Determines if any of the part's bounding boxes intersects with the outside 2 voxel wide layer. - * If so, we should use thinner facades (1 voxel deep). + * Generates the box segments around the specified hole. If the specified hole is null, a Singleton of the Facade + * box is returned. + * + * @param fb The Facade's box. + * @param hole The hole to 'cut'. + * @param axis The axis the facade is on. + * + * @return The box segments. + */ + private static List getBoxes( AxisAlignedBB fb, AEAxisAlignedBB hole, Axis axis ) + { + if( hole == null ) + { + return Collections.singletonList( fb ); + } + List boxes = new ArrayList<>(); + switch( axis ) + { + case Y: + boxes.add( new AxisAlignedBB( fb.minX, fb.minY, fb.minZ, hole.minX, fb.maxY, fb.maxZ ) ); + boxes.add( new AxisAlignedBB( hole.maxX, fb.minY, fb.minZ, fb.maxX, fb.maxY, fb.maxZ ) ); + + boxes.add( new AxisAlignedBB( hole.minX, fb.minY, fb.minZ, hole.maxX, fb.maxY, hole.minZ ) ); + boxes.add( new AxisAlignedBB( hole.minX, fb.minY, hole.maxZ, hole.maxX, fb.maxY, fb.maxZ ) ); + + break; + case Z: + boxes.add( new AxisAlignedBB( fb.minX, fb.minY, fb.minZ, fb.maxX, hole.minY, fb.maxZ ) ); + boxes.add( new AxisAlignedBB( fb.minX, hole.maxY, fb.minZ, fb.maxX, fb.maxY, fb.maxZ ) ); + + boxes.add( new AxisAlignedBB( fb.minX, hole.minY, fb.minZ, hole.minX, hole.maxY, fb.maxZ ) ); + boxes.add( new AxisAlignedBB( hole.maxX, hole.minY, fb.minZ, fb.maxX, hole.maxY, fb.maxZ ) ); + + break; + case X: + boxes.add( new AxisAlignedBB( fb.minX, fb.minY, fb.minZ, fb.maxX, hole.minY, fb.maxZ ) ); + boxes.add( new AxisAlignedBB( fb.minX, hole.maxY, fb.minZ, fb.maxX, fb.maxY, fb.maxZ ) ); + + boxes.add( new AxisAlignedBB( fb.minX, hole.minY, fb.minZ, fb.maxX, hole.maxY, hole.minZ ) ); + boxes.add( new AxisAlignedBB( fb.minX, hole.minY, hole.maxZ, fb.maxX, hole.maxY, fb.maxZ ) ); + break; + default: + // should never happen. + throw new RuntimeException( "switch falloff. " + String.valueOf( axis ) ); + } + + return boxes; + } + + /** + * Determines if any of the part's bounding boxes intersects with the outside 2 voxel wide layer. If so, we should + * use thinner facades (1 voxel deep). */ private static boolean isUseThinFacades( List partBoxes ) { @@ -436,57 +488,4 @@ public class FacadeBuilder } return false; } - - private static AxisAlignedBB getFacadeBox( EnumFacing side, boolean thinFacades ) - { - double thickness = ( thinFacades ? 1 : 2 ) / 16.0; - - switch( side ) - { - case DOWN: - return new AxisAlignedBB( 0.0, 0.0, 0.0, 1.0, thickness, 1.0 ); - case EAST: - return new AxisAlignedBB( 1.0 - thickness, 0.0, 0.0, 1.0, 1.0, 1.0 ); - case NORTH: - return new AxisAlignedBB( 0.0, 0.0, 0.0, 1.0, 1.0, thickness ); - case SOUTH: - return new AxisAlignedBB( 0.0, 0.0, 1.0 - thickness, 1.0, 1.0, 1.0 ); - case UP: - return new AxisAlignedBB( 0.0, 1.0 - thickness, 0.0, 1.0, 1.0, 1.0 ); - case WEST: - return new AxisAlignedBB( 0.0, 0.0, 0.0, thickness, 1.0, 1.0 ); - default: - throw new IllegalArgumentException( "Unsupported face: " + side ); - } - } - - public static class TextureAtlasAndTint - { - private final TextureAtlasSprite sprite; - private final int tint; - - private TextureAtlasAndTint( BakedQuad quad ) - { - this.sprite = quad.getSprite(); - this.tint = quad.getTintIndex(); - } - - private TextureAtlasAndTint( TextureAtlasSprite sprite, int tint ) - { - this.sprite = sprite; - this.tint = tint; - } - - public TextureAtlasSprite getSprite() - { - return this.sprite; - } - - public int getTint() - { - return this.tint; - } - - } - } diff --git a/src/main/java/appeng/client/render/cablebus/FacadeRenderState.java b/src/main/java/appeng/client/render/cablebus/FacadeRenderState.java index 5e4bbd870..82fb75f48 100644 --- a/src/main/java/appeng/client/render/cablebus/FacadeRenderState.java +++ b/src/main/java/appeng/client/render/cablebus/FacadeRenderState.java @@ -2,12 +2,7 @@ package appeng.client.render.cablebus; -import java.util.EnumSet; - import net.minecraft.block.state.IBlockState; -import net.minecraft.client.Minecraft; -import net.minecraft.item.ItemStack; -import net.minecraft.util.EnumFacing; /** @@ -19,17 +14,12 @@ public class FacadeRenderState // The block state to use for rendering this facade private final IBlockState sourceBlock; - // Which faces of the cube should be rendered for this particular facade - private final EnumSet openFaces; + private final boolean transparent; - // For resolving the tint indices of a facade - private final ItemStack textureItem; - - public FacadeRenderState( IBlockState sourceBlock, EnumSet openFaces, ItemStack textureItem ) + public FacadeRenderState( IBlockState sourceBlock, boolean transparent ) { this.sourceBlock = sourceBlock; - this.openFaces = openFaces; - this.textureItem = textureItem; + this.transparent = transparent; } public IBlockState getSourceBlock() @@ -37,14 +27,8 @@ public class FacadeRenderState return this.sourceBlock; } - public EnumSet getOpenFaces() + public boolean isTransparent() { - return this.openFaces; + return this.transparent; } - - public int resolveTintColor( int tintIndex ) - { - return Minecraft.getMinecraft().getItemColors().colorMultiplier( this.textureItem, tintIndex ); - } - } diff --git a/src/main/java/appeng/integration/abstraction/IAEFacade.java b/src/main/java/appeng/integration/abstraction/IAEFacade.java new file mode 100644 index 000000000..22102d08a --- /dev/null +++ b/src/main/java/appeng/integration/abstraction/IAEFacade.java @@ -0,0 +1,60 @@ +/* + * This file is part of Applied Energistics 2. + * Copyright (c) 2013 - 2018, AlgorithmX2, All rights reserved. + * + * Applied Energistics 2 is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Applied Energistics 2 is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Applied Energistics 2. If not, see . + */ + +package appeng.integration.abstraction; + + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraftforge.fml.common.Optional; + +import team.chisel.ctm.api.IFacade; + + +/** + * Neat abstraction class for All the IFacade interfaces. + * + * @author covers1624 + */ +@Optional.Interface( iface = "team.chisel.ctm.api.IFacade", modid = "ctm-api" ) +public interface IAEFacade extends IFacade +{ + + IBlockState getFacadeState( IBlockAccess world, BlockPos pos, EnumFacing side ); + + @Nonnull + @Override + @Optional.Method( modid = "ctm-api" ) + default IBlockState getFacade( @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nullable EnumFacing side, @Nonnull BlockPos connection ) + { + return getFacadeState( world, pos, side ); + } + + @Nonnull + @Override + @Optional.Method( modid = "ctm-api" ) + default IBlockState getFacade( @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nullable EnumFacing side ) + { + return getFacadeState( world, pos, side ); + } +} diff --git a/src/main/java/appeng/items/parts/ItemFacade.java b/src/main/java/appeng/items/parts/ItemFacade.java index 2a57d2c89..2da863214 100644 --- a/src/main/java/appeng/items/parts/ItemFacade.java +++ b/src/main/java/appeng/items/parts/ItemFacade.java @@ -23,8 +23,6 @@ import java.util.ArrayList; import java.util.List; import net.minecraft.block.Block; -import net.minecraft.block.BlockGlass; -import net.minecraft.block.BlockStainedGlass; import net.minecraft.block.state.IBlockState; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.player.EntityPlayer; @@ -50,7 +48,6 @@ import appeng.api.parts.IAlphaPassItem; import appeng.api.util.AEPartLocation; import appeng.core.AELog; import appeng.core.FacadeConfig; -import appeng.decorative.solid.BlockQuartzOre; import appeng.facade.FacadePart; import appeng.facade.IFacadeItem; import appeng.items.AEBaseItem; @@ -167,8 +164,6 @@ public class ItemFacade extends AEBaseItem implements IFacadeItem, IAlphaPassIte final int metadata = l.getItem().getMetadata( l.getItemDamage() ); final boolean hasTile = b.hasTileEntity( b.getDefaultState() ); - final boolean enableGlass = b instanceof BlockGlass || b instanceof BlockStainedGlass; - final boolean disableOre = b instanceof BlockQuartzOre; // Try to get the block state based on the item stack's meta. If this fails, don't consider it for a facade // This for example fails for Pistons because they hardcoded an invalid meta value in vanilla @@ -183,9 +178,7 @@ public class ItemFacade extends AEBaseItem implements IFacadeItem, IAlphaPassIte return ItemStack.EMPTY; } - final boolean defaultValue = ( b - .isTopSolid( blockState ) && hasSimpleModel( blockState ) && !b.getTickRandomly() && !hasTile && !disableOre ) || enableGlass; - if( FacadeConfig.instance().checkEnabled( b, metadata, defaultValue ) ) + if( blockState.getRenderType() == EnumBlockRenderType.MODEL && !hasTile ) { if( returnItem ) { @@ -260,7 +253,6 @@ public class ItemFacade extends AEBaseItem implements IFacadeItem, IAlphaPassIte } return new ItemStack( baseItem, 1, itemDamage ); - } @Override @@ -292,7 +284,6 @@ public class ItemFacade extends AEBaseItem implements IFacadeItem, IAlphaPassIte AELog.warn( "Block %s has broken getStateFromMeta method for meta %d", block.getRegistryName().toString(), baseItemStack.getItemDamage() ); return Blocks.GLASS.getDefaultState(); } - } public List getFacades() diff --git a/src/main/java/appeng/parts/CableBusContainer.java b/src/main/java/appeng/parts/CableBusContainer.java index 645a58db5..070c55014 100644 --- a/src/main/java/appeng/parts/CableBusContainer.java +++ b/src/main/java/appeng/parts/CableBusContainer.java @@ -1291,79 +1291,10 @@ public class CableBusContainer extends CableBusStorage implements AEMultiTile, I if( blockState != null && textureItem != null ) { - final EnumSet openFaces = this.calculateFaceOpenFaces( side ); - return new FacadeRenderState( blockState, openFaces, textureItem ); + return new FacadeRenderState( blockState, !facade.getBlockState().isOpaqueCube() ); } } return null; } - - private EnumSet calculateFaceOpenFaces( EnumFacing side ) - { - final EnumSet out = EnumSet.of( side, side.getOpposite() ); - final IFacadePart facade = this.getFacade( side.ordinal() ); - final IBlockAccess blockAccess = this.getTile().getWorld(); - final BlockPos pos = this.getTile().getPos(); - - for( final EnumFacing it : EnumFacing.values() ) - { - if( !out.contains( it ) && this.hasAlphaDiff( blockAccess.getTileEntity( pos.offset( it ) ), side, facade ) ) - { - out.add( it ); - } - } - - if( out.contains( EnumFacing.UP ) && ( side.getFrontOffsetX() != 0 || side.getFrontOffsetZ() != 0 ) ) - { - final IFacadePart fp = this.getFacade( EnumFacing.UP.ordinal() ); - if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) - { - out.remove( EnumFacing.UP ); - } - } - - if( out.contains( EnumFacing.DOWN ) && ( side.getFrontOffsetX() != 0 || side.getFrontOffsetZ() != 0 ) ) - { - final IFacadePart fp = this.getFacade( EnumFacing.DOWN.ordinal() ); - if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) - { - out.remove( EnumFacing.DOWN ); - } - } - - if( out.contains( EnumFacing.SOUTH ) && ( side.getFrontOffsetX() != 0 ) ) - { - final IFacadePart fp = this.getFacade( EnumFacing.SOUTH.ordinal() ); - if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) - { - out.remove( EnumFacing.SOUTH ); - } - } - - if( out.contains( EnumFacing.NORTH ) && ( side.getFrontOffsetX() != 0 ) ) - { - final IFacadePart fp = this.getFacade( EnumFacing.NORTH.ordinal() ); - if( fp != null && ( fp.isTransparent() == facade.isTransparent() ) ) - { - out.remove( EnumFacing.NORTH ); - } - } - - return out; - } - - private boolean hasAlphaDiff( final TileEntity tileEntity, final EnumFacing side, final IFacadePart facade ) - { - if( tileEntity instanceof IPartHost ) - { - final IPartHost ph = (IPartHost) tileEntity; - final IFacadePart fp = ph.getFacadeContainer().getFacade( AEPartLocation.fromFacing( side ) ); - - return fp == null || ( fp.isTransparent() != facade.isTransparent() ); - } - - return true; - } - } diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/math/InterpHelper.java b/src/main/java/appeng/thirdparty/codechicken/lib/math/InterpHelper.java new file mode 100644 index 000000000..2f6a72c93 --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/math/InterpHelper.java @@ -0,0 +1,129 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.math; + + +/** + * @author covers1624 + */ +public class InterpHelper +{ + + private float[][] posCache = new float[4][2]; + private float[] valCache = new float[4]; + + private float x0; + private float x1; + private float y0; + private float y1; + + private float rX; + private float rY; + + private int p00; + private int p10; + private int p11; + private int p01; + + /** + * Resets the interp helper with the given quad. Does not care what order the vertices are in. + */ + public void reset( float dx0, float dy0, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3 ) + { + + float[] vec0 = this.posCache[0]; + float[] vec1 = this.posCache[1]; + float[] vec2 = this.posCache[2]; + float[] vec3 = this.posCache[3]; + + vec0[0] = dx0; + vec1[0] = dx1; + vec2[0] = dx2; + vec3[0] = dx3; + + vec0[1] = dy0; + vec1[1] = dy1; + vec2[1] = dy2; + vec3[1] = dy3; + } + + /** + * Call when you are ready to use the InterpHelper. + */ + public void setup() + { + this.p00 = 0;// Bottom Left is always first. + this.x0 = this.posCache[this.p00][0]; + this.y0 = this.posCache[this.p00][1]; + for( int i = 1; i < 4; i++ ) + { + float x = this.posCache[i][0]; + float y = this.posCache[i][1]; + if( this.y0 == y ) + { + this.p10 = i;// Bottom right. + this.x1 = x; + } + else if( this.x0 == x ) + { + this.p01 = i;// Top left. + this.y1 = y; + } + else + { + // Top right. + this.p11 = i; + } + } + } + + /** + * Computes the coefficients for the interpolation. + * + * @param x X interp location. + * @param y Y interp location. + */ + public void locate( float x, float y ) + { + this.rX = ( x - this.x0 ) / ( this.x1 - this.x0 ); + this.rY = ( y - this.y0 ) / ( this.y1 - this.y0 ); + } + + /** + * Interpolates using the already computed coefficients. + * + * @param q0 Value at dx0 dy0 + * @param q1 Value at dx1 dy1 + * @param q2 Value at dx2 dy2 + * @param q3 Value at dx3 dy3 + * + * @return The result. + */ + public float interpolate( float q0, float q1, float q2, float q3 ) + { + this.valCache[0] = q0; + this.valCache[1] = q1; + this.valCache[2] = q2; + this.valCache[3] = q3; + float f0 = ( this.valCache[this.p00] * ( 1 - this.rX ) ) + ( this.valCache[this.p10] * this.rX ); + float f1 = ( this.valCache[this.p01] * ( 1 - this.rX ) ) + ( this.valCache[this.p11] * this.rX ); + + return ( f0 * ( 1 - this.rY ) ) + ( f1 * this.rY ); + } +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/CachedFormat.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/CachedFormat.java new file mode 100644 index 000000000..0c407a6c6 --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/CachedFormat.java @@ -0,0 +1,164 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model; + + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.client.renderer.vertex.VertexFormatElement; + + +/** + * A simple VertexFormat cache. + * This caches the existence of attributes and their indexes. + * + * @author covers1624 + */ +public class CachedFormat +{ + + public static final Map formatCache = new ConcurrentHashMap<>(); + + /** + * Lookup or create the CachedFormat for a given VertexFormat. + * + * @param format The format to lookup. + * + * @return The CachedFormat. + */ + public static CachedFormat lookup( VertexFormat format ) + { + return formatCache.computeIfAbsent( format, CachedFormat::new ); + } + + public VertexFormat format; + + public boolean hasPosition; + public boolean hasNormal; + public boolean hasColor; + public boolean hasUV; + public boolean hasLightMap; + + public int positionIndex = -1; + public int normalIndex = -1; + public int colorIndex = -1; + public int uvIndex = -1; + public int lightMapIndex = -1; + + public int elementCount; + + /** + * Caches the vertex format element indexes for efficiency. + * + * @param format The format. + */ + public CachedFormat( VertexFormat format ) + { + this.format = format; + this.elementCount = format.getElementCount(); + for( int i = 0; i < this.elementCount; i++ ) + { + VertexFormatElement element = format.getElement( i ); + switch( element.getUsage() ) + { + case POSITION: + if( this.hasPosition ) + { + throw new IllegalStateException( "Found 2 position elements.." ); + } + this.hasPosition = true; + this.positionIndex = i; + break; + case NORMAL: + if( this.hasNormal ) + { + throw new IllegalStateException( "Found 2 normal elements.." ); + } + this.hasNormal = true; + this.normalIndex = i; + break; + case COLOR: + if( this.hasColor ) + { + throw new IllegalStateException( "Found 2 color elements.." ); + } + this.hasColor = true; + this.colorIndex = i; + break; + case UV: + if( element.getIndex() == 0 ) + { + if( this.hasUV ) + { + throw new IllegalStateException( "Found 2 UV elements.." ); + } + this.hasUV = true; + this.uvIndex = i; + break; + } + else if( element.getIndex() == 1 ) + { + if( this.hasLightMap ) + { + throw new IllegalStateException( "Found 2 LightMap elements.." ); + } + this.hasLightMap = true; + this.lightMapIndex = i; + break; + } + break; + } + } + } + + @Override + public boolean equals( Object obj ) + { + if( this == obj ) + { + return true; + } + if( !( obj instanceof CachedFormat ) ) + { + return false; + } + CachedFormat other = (CachedFormat) obj; + return other.elementCount == this.elementCount && // + other.positionIndex == this.positionIndex && // + other.normalIndex == this.normalIndex && // + other.colorIndex == this.colorIndex && // + other.uvIndex == this.uvIndex && // + other.lightMapIndex == this.lightMapIndex; + } + + @Override + public int hashCode() + { + int result = 1; + result = 31 * result + this.elementCount; + result = 31 * result + this.positionIndex; + result = 31 * result + this.normalIndex; + result = 31 * result + this.colorIndex; + result = 31 * result + this.uvIndex; + result = 31 * result + this.lightMapIndex; + return result; + } +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/ISmartVertexConsumer.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/ISmartVertexConsumer.java new file mode 100644 index 000000000..b09676f09 --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/ISmartVertexConsumer.java @@ -0,0 +1,42 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model; + + +import net.minecraftforge.client.model.pipeline.IVertexConsumer; + + +/** + * Marks a standard IVertexConsumer as compatible with {@link Quad}. + * + * @author covers1624 + */ +public interface ISmartVertexConsumer extends IVertexConsumer +{ + + /** + * Assumes the data is already completely unpacked. + * You must always copy the data from the quad provided to an internal cache. + * basically: + * this.quad.put(quad); + * + * @param quad The quad to copy data from. + */ + void put( Quad quad ); +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/Quad.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/Quad.java new file mode 100644 index 000000000..05826bea8 --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/Quad.java @@ -0,0 +1,551 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model; + + +import javax.vecmath.Vector3f; + +import net.minecraft.client.renderer.block.model.BakedQuad; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.MathHelper; +import net.minecraftforge.client.model.pipeline.IVertexConsumer; +import net.minecraftforge.client.model.pipeline.IVertexProducer; +import net.minecraftforge.client.model.pipeline.LightUtil; +import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; + +import appeng.thirdparty.codechicken.lib.math.InterpHelper; + + +/** + * A simple easy to manipulate quad format. Can be reset and then used on a different format. + * + * @author covers1624 + */ +public class Quad implements IVertexProducer, ISmartVertexConsumer +{ + + public CachedFormat format; + + public int tintIndex = -1; + // TODO, sometimes this is null because people don't do models properly. + public EnumFacing orientation; + public boolean diffuseLighting = true; + public TextureAtlasSprite sprite; + + public Vertex[] vertices = new Vertex[4]; + public boolean full; + + // Not copied. + private int vertexIndex = 0; + // Cache for normal computation. + private Vector3f v1 = new Vector3f(); + private Vector3f v2 = new Vector3f(); + private Vector3f t = new Vector3f(); + private Vector3f normal = new Vector3f(); + + /** + * Use this if you reset the quad each time you use it. + */ + public Quad() + { + } + + /** + * use this if you want to initialize the quad with a format. + * + * @param format The format. + */ + public Quad( CachedFormat format ) + { + this.format = format; + } + + @Override + public VertexFormat getVertexFormat() + { + return this.format.format; + } + + @Override + public void setQuadTint( int tint ) + { + this.tintIndex = tint; + } + + @Override + public void setQuadOrientation( EnumFacing orientation ) + { + this.orientation = orientation; + } + + @Override + public void setApplyDiffuseLighting( boolean diffuse ) + { + this.diffuseLighting = diffuse; + } + + @Override + public void setTexture( TextureAtlasSprite texture ) + { + this.sprite = texture; + } + + @Override + public void put( int element, float... data ) + { + if( this.full ) + { + throw new RuntimeException( "Unable to add data when full." ); + } + Vertex v = this.vertices[this.vertexIndex]; + if( v == null ) + { + v = new Vertex( this.format ); + this.vertices[this.vertexIndex] = v; + } + System.arraycopy( data, 0, v.raw[element], 0, data.length ); + if( element == ( this.format.elementCount - 1 ) ) + { + this.vertexIndex++; + if( this.vertexIndex == 4 ) + { + this.vertexIndex = 0; + this.full = true; + } + } + } + + @Override + public void put( Quad quad ) + { + this.copyFrom( quad ); + } + + @Override + public void pipe( IVertexConsumer consumer ) + { + if( consumer instanceof ISmartVertexConsumer ) + { + ( (ISmartVertexConsumer) consumer ).put( this ); + } + else + { + consumer.setQuadTint( this.tintIndex ); + consumer.setQuadOrientation( this.orientation ); + consumer.setApplyDiffuseLighting( this.diffuseLighting ); + consumer.setTexture( this.sprite ); + for( Vertex v : this.vertices ) + { + for( int e = 0; e < this.format.elementCount; e++ ) + { + consumer.put( e, v.raw[e] ); + } + } + } + } + + /** + * Used to reset the interpolation values inside the provided helper. + * + * @param helper The helper. + * @param s The axis. side >> 1; + * + * @return The same helper. + */ + public InterpHelper resetInterp( InterpHelper helper, int s ) + { + helper.reset( // + this.vertices[0].dx( s ), this.vertices[0].dy( s ), // + this.vertices[1].dx( s ), this.vertices[1].dy( s ), // + this.vertices[2].dx( s ), this.vertices[2].dy( s ), // + this.vertices[3].dx( s ), this.vertices[3].dy( s ) ); + return helper; + } + + /** + * Clamps the Quad inside the box. + * + * @param bb The box. + */ + public void clamp( AxisAlignedBB bb ) + { + for( Vertex vertex : this.vertices ) + { + float[] vec = vertex.vec; + vec[0] = (float) MathHelper.clamp( vec[0], bb.minX, bb.maxX ); + vec[1] = (float) MathHelper.clamp( vec[1], bb.minY, bb.maxY ); + vec[2] = (float) MathHelper.clamp( vec[2], bb.minZ, bb.maxZ ); + } + + this.v1.set( this.vertices[3].vec ); + this.t.set( this.vertices[1].vec ); + this.v1.sub( this.t ); + + this.v2.set( this.vertices[2].vec ); + this.t.set( this.vertices[0].vec ); + this.v2.sub( this.t ); + + this.normal.cross( this.v2, this.v1 ); + this.normal.normalize(); + + if( this.format.hasNormal ) + { + for( Vertex vertex : this.vertices ) + { + vertex.normal[0] = this.normal.x; + vertex.normal[1] = this.normal.y; + vertex.normal[2] = this.normal.z; + vertex.normal[3] = 0; + } + } + this.orientation = EnumFacing.getFacingFromVector( this.normal.x, this.normal.y, this.normal.z ); + } + + /** + * Used to create a new quad complete copy of this one. + * + * @return The new quad. + */ + public Quad copy() + { + if( !this.full ) + { + throw new RuntimeException( "Only copying full quads is supported." ); + } + Quad quad = new Quad( this.format ); + quad.tintIndex = this.tintIndex; + quad.orientation = this.orientation; + quad.diffuseLighting = this.diffuseLighting; + quad.sprite = this.sprite; + quad.full = true; + for( int i = 0; i < 4; i++ ) + { + quad.vertices[i] = this.vertices[i].copy(); + } + return quad; + } + + /** + * Copies the data inside the given quad to this one. This ignores VertexFormat, please make sure your quads are in + * the same format. + * + * @param quad The Quad to copy from. + * + * @return This quad. + */ + public Quad copyFrom( Quad quad ) + { + this.tintIndex = quad.tintIndex; + this.orientation = quad.orientation; + this.diffuseLighting = quad.diffuseLighting; + this.sprite = quad.sprite; + this.full = quad.full; + for( int v = 0; v < 4; v++ ) + { + for( int e = 0; e < this.format.elementCount; e++ ) + { + System.arraycopy( quad.vertices[v].raw[e], 0, this.vertices[v].raw[e], 0, 4 ); + } + } + return this; + } + + /** + * Reset the quad to the new format. + * + * @param format The new format. + */ + public void reset( CachedFormat format ) + { + this.format = format; + this.tintIndex = -1; + this.orientation = null; + this.diffuseLighting = true; + this.sprite = null; + for( int i = 0; i < this.vertices.length; i++ ) + { + Vertex v = this.vertices[i]; + if( v == null ) + { + this.vertices[i] = v = new Vertex( format ); + } + v.reset( format ); + } + this.vertexIndex = 0; + this.full = false; + } + + /** + * Bakes this Quad to a BakedQuad. + * + * @return The BakedQuad. + */ + public BakedQuad bake() + { + int[] packedData = new int[this.format.format.getNextOffset()]; + for( int v = 0; v < 4; v++ ) + { + for( int e = 0; e < this.format.elementCount; e++ ) + { + LightUtil.pack( this.vertices[v].raw[e], packedData, this.format.format, v, e ); + } + } + return new BakedQuad( packedData, this.tintIndex, this.orientation, this.sprite, this.diffuseLighting, this.format.format ); + } + + /** + * Bakes this quad to an UnpackedBakedQuad. + * + * @return The UnpackedBakedQuad. + */ + public UnpackedBakedQuad bakeUnpacked() + { + UnpackedBakedQuad.Builder quad = new UnpackedBakedQuad.Builder( this.format.format ); + this.pipe( quad ); + return quad.build(); + } + + /** + * A simple vertex format. + */ + public static class Vertex + { + + public CachedFormat format; + + /** + * The raw data. + */ + public float[][] raw; + + // References to the arrays inside raw. + public float[] vec; + public float[] normal; + public float[] color; + public float[] uv; + public float[] lightmap; + + /** + * Create a new Vertex. + * + * @param format The format for the vertex. + */ + public Vertex( CachedFormat format ) + { + this.format = format; + this.raw = new float[format.elementCount][4]; + this.preProcess(); + } + + /** + * Creates a new Vertex using the data inside the other. A copy! + * + * @param other The other. + */ + public Vertex( Vertex other ) + { + this.format = other.format; + this.raw = other.raw.clone(); + for( int v = 0; v < this.format.elementCount; v++ ) + { + this.raw[v] = other.raw[v].clone(); + } + this.preProcess(); + } + + /** + * Pulls references to the individual element's arrays inside raw. Modifying the individual element arrays will + * update raw. + */ + public void preProcess() + { + if( this.format.hasPosition ) + { + this.vec = this.raw[this.format.positionIndex]; + } + if( this.format.hasNormal ) + { + this.normal = this.raw[this.format.normalIndex]; + } + if( this.format.hasColor ) + { + this.color = this.raw[this.format.colorIndex]; + } + if( this.format.hasUV ) + { + this.uv = this.raw[this.format.uvIndex]; + } + if( this.format.hasLightMap ) + { + this.lightmap = this.raw[this.format.lightMapIndex]; + } + } + + /** + * Gets the 2d X coord for the given axis. + * + * @param s The axis. side >> 1 + * + * @return The x coord. + */ + public float dx( int s ) + { + if( s <= 1 ) + { + return this.vec[0]; + } + else + { + return this.vec[2]; + } + } + + /** + * Gets the 2d Y coord for the given axis. + * + * @param s The axis. side >> 1 + * + * @return The y coord. + */ + public float dy( int s ) + { + if( s > 0 ) + { + return this.vec[1]; + } + else + { + return this.vec[2]; + } + } + + /** + * Interpolates the new color values for this Vertex using the others as a reference. + * + * @param interpHelper The InterpHelper to use. + * @param others The other Vertices to use as a template. + * + * @return The same Vertex. + */ + public Vertex interpColorFrom( InterpHelper interpHelper, Vertex[] others ) + { + for( int e = 0; e < 4; e++ ) + { + float p1 = others[0].color[e]; + float p2 = others[1].color[e]; + float p3 = others[2].color[e]; + float p4 = others[3].color[e]; + // Only interpolate if colors are different. + if( p1 != p2 || p2 != p3 || p3 != p4 ) + { + this.color[e] = interpHelper.interpolate( p1, p2, p3, p4 ); + } + } + return this; + } + + /** + * Interpolates the new UV values for this Vertex using the others as a reference. + * + * @param interpHelper The InterpHelper to use. + * @param others The other Vertices to use as a template. + * + * @return The same Vertex. + */ + public Vertex interpUVFrom( InterpHelper interpHelper, Vertex[] others ) + { + for( int e = 0; e < 2; e++ ) + { + float p1 = others[0].uv[e]; + float p2 = others[1].uv[e]; + float p3 = others[2].uv[e]; + float p4 = others[3].uv[e]; + if( p1 != p2 || p2 != p3 || p3 != p4 ) + { + this.uv[e] = interpHelper.interpolate( p1, p2, p3, p4 ); + } + } + return this; + } + + /** + * Interpolates the new LightMap values for this Vertex using the others as a reference. + * + * @param interpHelper The InterpHelper to use. + * @param others The other Vertices to use as a template. + * + * @return The same Vertex. + */ + public Vertex interpLightMapFrom( InterpHelper interpHelper, Vertex[] others ) + { + for( int e = 0; e < 2; e++ ) + { + float p1 = others[0].lightmap[e]; + float p2 = others[1].lightmap[e]; + float p3 = others[2].lightmap[e]; + float p4 = others[3].lightmap[e]; + if( p1 != p2 || p2 != p3 || p3 != p4 ) + { + this.lightmap[e] = interpHelper.interpolate( p1, p2, p3, p4 ); + } + } + return this; + } + + /** + * Copies this Vertex to a new one. + * + * @return The new Vertex. + */ + public Vertex copy() + { + return new Vertex( this ); + } + + /** + * Resets the Vertex to a new format. Expands the raw array if needed. + * + * @param format The format to reset to. + */ + public void reset( CachedFormat format ) + { + // If the format is different and our raw array is smaller, then expand it. + if( !this.format.equals( format ) && format.elementCount > this.raw.length ) + { + this.raw = new float[format.elementCount][4]; + } + this.format = format; + + this.vec = null; + this.normal = null; + this.color = null; + this.uv = null; + this.lightmap = null; + + // for (float[] f : raw) { + // Arrays.fill(f, 0F); + // } + + this.preProcess(); + } + } +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/BakedPipeline.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/BakedPipeline.java new file mode 100644 index 000000000..735373e99 --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/BakedPipeline.java @@ -0,0 +1,436 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model.pipeline; + + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.client.model.pipeline.IVertexConsumer; +import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; + +import appeng.thirdparty.codechicken.lib.model.CachedFormat; +import appeng.thirdparty.codechicken.lib.model.ISmartVertexConsumer; +import appeng.thirdparty.codechicken.lib.model.Quad; + + +/** + * The BakedPipeline! + * Basically this allows us to efficiently transform a BakedQuad, + * the Pipeline has Elements, each element has a name, state and a transformer, + * you can enable and disable elements easily, you can also grab the underlying + * transformer for the element if you need to set its state before rendering. + * + * The BakedPipeline is final once created, you cannot add or remove elements, + * you should not need to add or remove them runtime, enable and disable exist. + * + * You must use the Builder class to construct a BakedPipeline, see {@link #builder} + * + * Transformers run on a mutable state inside each transformer, allowing for easy reuse. + * It is recommended to store your pipeline inside a ThreadLocal because 'minecraft'. + * + * Each Transformer should be smart enough to expand itself for each newly sized VertexFormat it comes across, + * meaning that the internal states for the transformers can be safely shared across VertexFormats, this reduces + * array creations, and generally makes the system as efficient as it is. + * + * To use the system: + * Grab any elements you need to set state data on first, using {@link #getElement(String, Class)} + * transformers should NOT clear their state on pipeline Reset's so set any global data on elements now. + * Assuming you are looping over a set of quads to transform, next you need to {@link #reset} the pipeline, + * Now you should disable / enable any optional elements that are needed, NOTE: Element states are reset when resetting + * the pipeline. + * Now you will need to call {@link #prepare(IVertexConsumer)} on the pipeline, here you will pass your collector, + * usually this is some form of (Unpacked)BakedQuadBuilder, See {@link QuadBuilder} for a simple and fast implementation + * for standard BakedQuads, and {@link UnpackedBakedQuad.Builder} for UnpackedBakedQuads. + * Now final step, simply pipe the quad you want to transform INTO the pipeline 'quad.pipe(pipeline)' + * And that's it! hell, Pipe a pipeline into each other for all i care, the system is efficient enough that there + * would be no performance penalty for doing so. + * + * @author covers1624 + */ +public class BakedPipeline implements ISmartVertexConsumer +{ + + private PipelineElement[] elements; + private Map nameLookup; + private IPipelineConsumer first; + + private Quad unpacker = new Quad(); + + private BakedPipeline( PipelineElement[] elements ) + { + this.elements = elements; + this.nameLookup = Arrays.stream( elements ).collect( Collectors.toMap( e -> e.name, e -> e ) ); + } + + /** + * Used to create a BakedPipeline. + * + * @return The builder. + */ + public static Builder builder() + { + return new Builder(); + } + + /** + * Used to reset the pipeline for the next quad. + * MUST be called between quads. + * + * @param format The format. + */ + public void reset( VertexFormat format ) + { + this.reset( CachedFormat.lookup( format ) ); + } + + /** + * Used to reset the pipeline for the next quad. + * MUST be called between quads. + * + * @param format The format. + */ + public void reset( CachedFormat format ) + { + this.unpacker.reset( format ); + for( PipelineElement element : this.elements ) + { + element.reset( format ); + } + this.first = null; + } + + /** + * Get an element from the pipeline. + * + * @param name The name of the element. + * @param clazz The Class of the element, used to safe cast. + * + * @return The element. + */ + public T getElement( String name, Class clazz ) + { + PipelineElement element = this.nameLookup.get( name ); + if( element != null ) + { + if( !clazz.isAssignableFrom( element.consumer.getClass() ) ) + { + throw new IllegalArgumentException( "Element with name " + name + " is not assignable from reference class." ); + } + return clazz.cast( element.consumer ); + } + throw new IllegalArgumentException( "Element with name " + name + " does not exist." ); + } + + /** + * Used to enable an element on the pipeline with the specified name. + * + * @param name The elements name. + */ + public void enableElement( String name ) + { + this.setElementState( name, true ); + } + + /** + * Used to disable an element on the pipeline with the specified name. + * + * @param name The elements name. + */ + public void disableElement( String name ) + { + this.setElementState( name, false ); + } + + /** + * Used to set the state of an element on the pipeline. + * + * @param name The name of the element. + * @param enabled The state to set it to. + */ + public void setElementState( String name, boolean enabled ) + { + PipelineElement element = this.nameLookup.get( name ); + if( element != null ) + { + element.isEnabled = enabled; + return; + } + throw new IllegalArgumentException( "Element with name " + name + " does not exist." ); + } + + /** + * Call when you are ready to use the pipeline. + * This builds the internal state of the Elements getting things ready to transform. + * + * @param collector The IVertexConsumer that should collect the transformed quad. + */ + public void prepare( IVertexConsumer collector ) + { + IPipelineConsumer next = null; + for( PipelineElement element : this.elements ) + { + if( element.isEnabled ) + { + if( this.first == null ) + { + this.first = element.consumer; + } + else + { + next.setParent( element.consumer ); + } + next = element.consumer; + } + } + next.setParent( collector ); + } + + @Override + public VertexFormat getVertexFormat() + { + this.check(); + return this.first.getVertexFormat(); + } + + @Override + public void setQuadTint( int tint ) + { + this.check(); + this.unpacker.setQuadTint( tint ); + } + + @Override + public void setQuadOrientation( EnumFacing orientation ) + { + this.check(); + this.unpacker.setQuadOrientation( orientation ); + } + + @Override + public void setApplyDiffuseLighting( boolean diffuse ) + { + this.check(); + this.unpacker.setApplyDiffuseLighting( diffuse ); + } + + @Override + public void setTexture( TextureAtlasSprite texture ) + { + this.check(); + this.unpacker.setTexture( texture ); + } + + @Override + public void put( int element, float... data ) + { + this.check(); + this.unpacker.put( element, data ); + if( this.unpacker.full ) + { + this.onFull(); + } + } + + @Override + public void put( Quad quad ) + { + this.check(); + this.unpacker.put( quad ); + } + + private void check() + { + if( this.first == null ) + { + throw new IllegalStateException( "Pipeline used before prepare was called." ); + } + } + + private void onFull() + { + this.first.setInputQuad( this.unpacker ); + this.first.put( this.unpacker ); + } + + /** + * Internal class, used to hold a PipelineElement's state. + */ + public static class PipelineElement + { + + public String name; + public boolean defaultState; + public T consumer; + public boolean isEnabled; + + public void reset( CachedFormat format ) + { + this.isEnabled = this.defaultState; + this.consumer.setParent( null ); + this.consumer.reset( format ); + } + } + + /** + * The builder associated with the BakedPipeline. + * You must create a BakedPipeline with this, + * once created a pipeline cannot be modified, + * modifying should not be needed as you can enable + * and disable elements with ease. + */ + public static class Builder + { + + private LinkedList elements = new LinkedList<>(); + + /** + * Inserts an element to the front of the list, Useful if you have a more complex system + * and each system need to be independent from each other, but this element must be first. + * + * @param name The name to identify this element, used as an identifier when setting state, and retrieving the + * element. + * @param factory The factory used to create the Transformer. + * + * @return The same builder. + */ + public Builder addFirst( String name, IPipelineElementFactory factory ) + { + return this.addFirst( name, factory, true ); + } + + /** + * Inserts an element to the front of the list, Useful if you have a more complex system + * and each system need to be independent from each other, but this element must be first. + * + * @param name The name to identify this element, used as an identifier when setting state, and retrieving the + * element. + * @param factory The factory used to create the Transformer. + * @param defaultState The default state for this element. + * + * @return The same builder. + */ + public Builder addFirst( String name, IPipelineElementFactory factory, boolean defaultState ) + { + return this.addFirst( name, factory, defaultState, e -> + { + } ); + } + + /** + * Inserts an element to the front of the list, Useful if you have a more complex system + * and each system need to be independent from each other, but this element must be first. + * + * @param name The name to identify this element, used as an identifier when setting state, and retrieving the + * element. + * @param factory The factory used to create the Transformer. + * @param defaultState The default state for this element. + * @param defaultsSetter A callback used to set any defaults on the transformer. + * + * @return The same builder. + */ + public Builder addFirst( String name, IPipelineElementFactory factory, boolean defaultState, Consumer defaultsSetter ) + { + PipelineElement element = this.makeElement( name, factory, defaultState ); + defaultsSetter.accept( element.consumer ); + this.elements.addFirst( element ); + return this; + } + + /** + * Adds an element at the end of the transform list, Suitable for 99% of cases. + * + * @param name The name to identify this element, used as an identifier when setting state, and retrieving the + * element. + * @param factory The factory used to create the Transformer. + * + * @return The same builder. + */ + public Builder addElement( String name, IPipelineElementFactory factory ) + { + return this.addElement( name, factory, true ); + } + + /** + * Adds an element at the end of the transform list, Suitable for 99% of cases. + * + * @param name The name to identify this element, used as an identifier when setting state, and retrieving the + * element. + * @param factory The factory used to create the Transformer. + * @param defaultState The default state for this element. + * + * @return The same builder. + */ + public Builder addElement( String name, IPipelineElementFactory factory, boolean defaultState ) + { + return this.addElement( name, factory, defaultState, e -> + { + } ); + } + + /** + * Adds an element at the end of the transform list, Suitable for 99% of cases. + * + * @param name The name to identify this element, used as an identifier when setting state, and retrieving the + * element. + * @param factory The factory used to create the Transformer. + * @param defaultState The default state for this element. + * @param defaultsSetter A callback used to set any defaults on the transformer. + * + * @return The same builder. + */ + public Builder addElement( String name, IPipelineElementFactory factory, boolean defaultState, Consumer defaultsSetter ) + { + PipelineElement element = this.makeElement( name, factory, defaultState ); + defaultsSetter.accept( element.consumer ); + this.elements.add( element ); + return this; + } + + // Internal method, used to construct the PipelineElement class. + private PipelineElement makeElement( String name, IPipelineElementFactory factory, boolean defaultState ) + { + if( this.elements.stream().anyMatch( p -> p.name.equals( name ) ) ) + { + throw new IllegalArgumentException( "Unable to add element with duplicate name: " + name ); + } + PipelineElement element = new PipelineElement<>(); + element.name = name; + element.consumer = factory.create(); + element.defaultState = defaultState; + return element; + } + + /** + * Call this once you are finished to build your BakedPipeline! + * + * @return The new Pipeline. + */ + public BakedPipeline build() + { + return new BakedPipeline( this.elements.toArray( new PipelineElement[0] ) ); + } + } +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/IPipelineConsumer.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/IPipelineConsumer.java new file mode 100644 index 000000000..1eca87fa8 --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/IPipelineConsumer.java @@ -0,0 +1,64 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model.pipeline; + + +import net.minecraftforge.client.model.pipeline.IVertexConsumer; + +import appeng.thirdparty.codechicken.lib.model.CachedFormat; +import appeng.thirdparty.codechicken.lib.model.ISmartVertexConsumer; +import appeng.thirdparty.codechicken.lib.model.Quad; +import appeng.thirdparty.codechicken.lib.model.pipeline.transformers.QuadReInterpolator; + + +/** + * Anything implementing this may be used in the BakedPipeline. + * + * @author covers1624 + */ +public interface IPipelineConsumer extends ISmartVertexConsumer +{ + + /** + * The quad at the start of the transformation. + * This is useful for obtaining the vertex data before any transformations have been applied, + * such as interpolation, See {@link QuadReInterpolator}. + * When overriding this make sure you call setInputQuad on your parent consumer too. + * + * @param quad The quad. + */ + void setInputQuad( Quad quad ); + + /** + * Resets the Consumer to the new format. + * This should resize any internal arrays if needed, ready for the new vertex data. + * + * @param format The format to reset to. + */ + void reset( CachedFormat format ); + + /** + * Sets the parent consumer. + * This consumer may choose to not pipe any data, + * that's fine, but if it does, it MUST pipe the data to the one provided here. + * + * @param parent The parent. + */ + void setParent( IVertexConsumer parent ); +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/IPipelineElementFactory.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/IPipelineElementFactory.java new file mode 100644 index 000000000..d2fec2541 --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/IPipelineElementFactory.java @@ -0,0 +1,30 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model.pipeline; + + +/** + * @author covers1624 + */ +@FunctionalInterface +public interface IPipelineElementFactory +{ + + T create(); +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/QuadTransformer.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/QuadTransformer.java new file mode 100644 index 000000000..4749481c2 --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/QuadTransformer.java @@ -0,0 +1,175 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model.pipeline; + + +import javax.annotation.OverridingMethodsMustInvokeSuper; + +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.vertex.VertexFormat; +import net.minecraft.util.EnumFacing; +import net.minecraftforge.client.model.pipeline.IVertexConsumer; + +import appeng.thirdparty.codechicken.lib.model.CachedFormat; +import appeng.thirdparty.codechicken.lib.model.ISmartVertexConsumer; +import appeng.thirdparty.codechicken.lib.model.Quad; + + +/** + * Base class for a simple QuadTransformer. + * Operates on BakedQuads. + * QuadTransformers can be piped into each other at no performance penalty. + * + * @author covers1624 + */ +public abstract class QuadTransformer implements IVertexConsumer, ISmartVertexConsumer, IPipelineConsumer +{ + + protected CachedFormat format; + protected IVertexConsumer consumer; + protected Quad quad; + + /** + * Used for the BakedPipeline. + */ + protected QuadTransformer() + { + this.quad = new Quad(); + } + + public QuadTransformer( IVertexConsumer consumer ) + { + this( consumer.getVertexFormat(), consumer ); + } + + public QuadTransformer( VertexFormat format, IVertexConsumer consumer ) + { + this( CachedFormat.lookup( format ), consumer ); + } + + public QuadTransformer( CachedFormat format, IVertexConsumer consumer ) + { + this.format = format; + this.consumer = consumer; + this.quad = new Quad( format ); + } + + @Override + @OverridingMethodsMustInvokeSuper + public void reset( CachedFormat format ) + { + this.format = format; + this.quad.reset( format ); + } + + @Override + public void setParent( IVertexConsumer parent ) + { + this.consumer = parent; + } + + @Override + @OverridingMethodsMustInvokeSuper + public void setInputQuad( Quad quad ) + { + if( this.consumer instanceof IPipelineConsumer ) + { + ( (IPipelineConsumer) this.consumer ).setInputQuad( quad ); + } + } + + // @formatter:off + @Override + public VertexFormat getVertexFormat() + { + return this.format.format; + } + + @Override + public void setQuadTint( int tint ) + { + this.quad.setQuadTint( tint ); + } + + @Override + public void setQuadOrientation( EnumFacing orientation ) + { + this.quad.setQuadOrientation( orientation ); + } + + @Override + public void setApplyDiffuseLighting( boolean diffuse ) + { + this.quad.setApplyDiffuseLighting( diffuse ); + } + + @Override + public void setTexture( TextureAtlasSprite texture ) + { + this.quad.setTexture( texture ); + } + // @formatter:on + + @Override + public void put( int element, float... data ) + { + this.quad.put( element, data ); + if( this.quad.full ) + { + this.onFull(); + } + } + + @Override + public void put( Quad quad ) + { + this.quad.put( quad ); + this.onFull(); + } + + /** + * Called to transform the vertices. + * + * @return If the transformer should pipe the quad. + */ + public abstract boolean transform(); + + public void onFull() + { + if( this.transform() ) + { + this.quad.pipe( this.consumer ); + } + } + + // Should be small enough. + private final static double EPSILON = 0.00001; + + public static boolean epsComp( float a, float b ) + { + if( a == b ) + { + return true; + } + else + { + return Math.abs( a - b ) < EPSILON; + } + } +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadAlphaOverride.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadAlphaOverride.java new file mode 100644 index 000000000..37bbeb03a --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadAlphaOverride.java @@ -0,0 +1,71 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model.pipeline.transformers; + + +import net.minecraftforge.client.model.pipeline.IVertexConsumer; + +import appeng.thirdparty.codechicken.lib.model.Quad.Vertex; +import appeng.thirdparty.codechicken.lib.model.pipeline.IPipelineElementFactory; +import appeng.thirdparty.codechicken.lib.model.pipeline.QuadTransformer; + + +/** + * This transformer simply overrides the alpha of the quad. + * Only operates if the format has color. + * + * @author covers1624 + */ +public class QuadAlphaOverride extends QuadTransformer +{ + + public static final IPipelineElementFactory FACTORY = QuadAlphaOverride::new; + + private float alphaOverride; + + QuadAlphaOverride() + { + super(); + } + + public QuadAlphaOverride( IVertexConsumer consumer, float alphaOverride ) + { + super( consumer ); + this.alphaOverride = alphaOverride; + } + + public QuadAlphaOverride setAlphaOverride( float alphaOverride ) + { + this.alphaOverride = alphaOverride; + return this; + } + + @Override + public boolean transform() + { + if( this.format.hasColor ) + { + for( Vertex v : this.quad.vertices ) + { + v.color[3] = this.alphaOverride; + } + } + return true; + } +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadClamper.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadClamper.java new file mode 100644 index 000000000..a460fa4fa --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadClamper.java @@ -0,0 +1,83 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model.pipeline.transformers; + + +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraftforge.client.model.pipeline.IVertexConsumer; + +import appeng.thirdparty.codechicken.lib.model.Quad.Vertex; +import appeng.thirdparty.codechicken.lib.model.pipeline.IPipelineElementFactory; +import appeng.thirdparty.codechicken.lib.model.pipeline.QuadTransformer; + + +/** + * This transformer simply clamps the vertices inside the provided box. + * You probably want to Re-Interpolate the UV's, Color, and Lmap, see {@link QuadReInterpolator} + * + * @author covers1624 + */ +public class QuadClamper extends QuadTransformer +{ + + public static IPipelineElementFactory FACTORY = QuadClamper::new; + + private AxisAlignedBB clampBounds; + + QuadClamper() + { + super(); + } + + public QuadClamper( IVertexConsumer parent, AxisAlignedBB bounds ) + { + super( parent ); + this.clampBounds = bounds; + } + + public void setClampBounds( AxisAlignedBB bounds ) + { + this.clampBounds = bounds; + } + + @Override + public boolean transform() + { + int s = this.quad.orientation.ordinal() >> 1; + + this.quad.clamp( this.clampBounds ); + + // Check if the quad would be invisible and cull it. + Vertex[] vertices = this.quad.vertices; + float x1 = vertices[0].dx( s ); + float x2 = vertices[1].dx( s ); + float x3 = vertices[2].dx( s ); + float x4 = vertices[3].dx( s ); + + float y1 = vertices[0].dy( s ); + float y2 = vertices[1].dy( s ); + float y3 = vertices[2].dy( s ); + float y4 = vertices[3].dy( s ); + + // These comparisons are safe as we are comparing clamped values. + boolean flag1 = x1 == x2 && x2 == x3 && x3 == x4; + boolean flag2 = y1 == y2 && y2 == y3 && y3 == y4; + return !flag1 && !flag2; + } +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadCornerKicker.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadCornerKicker.java new file mode 100644 index 000000000..523cdd75e --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadCornerKicker.java @@ -0,0 +1,219 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model.pipeline.transformers; + + +import static net.minecraft.util.EnumFacing.AxisDirection.NEGATIVE; +import static net.minecraft.util.EnumFacing.AxisDirection.POSITIVE; + +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumFacing.AxisDirection; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.Vec3i; + +import appeng.thirdparty.codechicken.lib.model.Quad.Vertex; +import appeng.thirdparty.codechicken.lib.model.pipeline.IPipelineElementFactory; +import appeng.thirdparty.codechicken.lib.model.pipeline.QuadTransformer; + + +/** + * This transformer is a little complicated. + * Basically a Facade / Cover can use this to 'kick' the edges + * in of quads to fix z-Fighting in the corners. + * Use it by specifying the side of the block you are on, + * the bitmask for where the other Facades / Cover's are, + * the bounding box of the facade, NOT the hole piece, + * and the thickness of your Facade / Cover, this is used + * as the kick amount. + * + * @author covers1624 + */ +public class QuadCornerKicker extends QuadTransformer +{ + + // The factory for pipeline creation. + public static final IPipelineElementFactory FACTORY = QuadCornerKicker::new; + + // Simple horizonal lookups. + public static int[][] horizonals = new int[][] { + // Around Y axis, NSWE. + { 2, 3, 4, 5 }, // + { 2, 3, 4, 5 }, // + + // Around Z axis, DUWE. + { 0, 1, 4, 5 }, // + { 0, 1, 4, 5 }, // + + // Around X axis, DUNS. + { 0, 1, 2, 3 }, // + { 0, 1, 2, 3 } + }; + + private int mySide; + private int facadeMask; + private AxisAlignedBB box; + private double thickness; + + QuadCornerKicker() + { + super(); + } + + /** + * Set's the side this Facade / Cover is attached to. + * + * @param side The side. + */ + public void setSide( int side ) + { + this.mySide = side; + } + + /** + * Sets the bitmask of Facades / Covers in the blockspace. + * This is as simple as, mask = (1 << side) + * + * @param mask The mask. + */ + public void setFacadeMask( int mask ) + { + this.facadeMask = mask; + } + + /** + * Sets the bounding box of the Facade / Cover, + * this should be the full box, not just a piece + * of the hole's 'ring'. + * + * @param box The BoundingBox. + */ + public void setBox( AxisAlignedBB box ) + { + this.box = box; + } + + /** + * Sets the amount to kick the vertex in by, + * this is your facades thickness. + * + * @param thickness The thickness. + */ + public void setThickness( double thickness ) + { + this.thickness = thickness; + } + + @Override + public boolean transform() + { + + int side = this.quad.orientation.ordinal(); + if( side != this.mySide && side != ( this.mySide ^ 1 ) ) + { + for( int hoz : horizonals[this.mySide] ) + { + if( side != hoz && side != ( hoz ^ 1 ) ) + { + if( ( this.facadeMask & ( 1 << hoz ) ) != 0 ) + { + Corner corner = Corner.fromSides( this.mySide ^ 1, side, hoz ); + for( Vertex vertex : this.quad.vertices ) + { + float x = vertex.vec[0]; + float y = vertex.vec[1]; + float z = vertex.vec[2]; + if( epsComp( x, corner.pX( this.box ) ) && epsComp( y, corner.pY( this.box ) ) && epsComp( z, corner.pZ( this.box ) ) ) + { + Vec3i vec = EnumFacing.VALUES[hoz].getDirectionVec(); + x -= vec.getX() * this.thickness; + y -= vec.getY() * this.thickness; + z -= vec.getZ() * this.thickness; + vertex.vec[0] = x; + vertex.vec[1] = y; + vertex.vec[2] = z; + } + } + } + } + } + } + + return true; + } + + public enum Corner + { + + MIN_X_MIN_Y_MIN_Z( NEGATIVE, NEGATIVE, NEGATIVE ), + MIN_X_MIN_Y_MAX_Z( NEGATIVE, NEGATIVE, POSITIVE ), + MIN_X_MAX_Y_MIN_Z( NEGATIVE, POSITIVE, NEGATIVE ), + MIN_X_MAX_Y_MAX_Z( NEGATIVE, POSITIVE, POSITIVE ), + + MAX_X_MIN_Y_MIN_Z( POSITIVE, NEGATIVE, NEGATIVE ), + MAX_X_MIN_Y_MAX_Z( POSITIVE, NEGATIVE, POSITIVE ), + MAX_X_MAX_Y_MIN_Z( POSITIVE, POSITIVE, NEGATIVE ), + MAX_X_MAX_Y_MAX_Z( POSITIVE, POSITIVE, POSITIVE ); + + private AxisDirection xAxis; + private AxisDirection yAxis; + private AxisDirection zAxis; + + private static final int[] sideMask = { 0, 2, 0, 1, 0, 4 }; + + Corner( AxisDirection xAxis, AxisDirection yAxis, AxisDirection zAxis ) + { + this.xAxis = xAxis; + this.yAxis = yAxis; + this.zAxis = zAxis; + } + + /** + * Used to find what corner is at the 3 sides. + * This method assumes you pass in the X axis side, Y axis side, and Z axis side, + * it will NOT complain about an invalid side, you will just get garbage data. + * This method also does not care what order the 3 axes are in. + * + * @param sideA Side one. + * @param sideB Side two. + * @param sideC Side three. + * + * @return The corner at the 3 sides. + */ + public static Corner fromSides( int sideA, int sideB, int sideC ) + { + // <3 Chicken-Bones. + return values()[sideMask[sideA] | sideMask[sideB] | sideMask[sideC]]; + } + + public float pX( AxisAlignedBB box ) + { + return (float) ( this.xAxis == NEGATIVE ? box.minX : box.maxX ); + } + + public float pY( AxisAlignedBB box ) + { + return (float) ( this.yAxis == NEGATIVE ? box.minY : box.maxY ); + } + + public float pZ( AxisAlignedBB box ) + { + return (float) ( this.zAxis == NEGATIVE ? box.minZ : box.maxZ ); + } + } +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadFaceStripper.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadFaceStripper.java new file mode 100644 index 000000000..e9f52ee22 --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadFaceStripper.java @@ -0,0 +1,128 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model.pipeline.transformers; + + +import static net.minecraft.util.EnumFacing.AxisDirection.POSITIVE; + +import net.minecraft.util.EnumFacing.AxisDirection; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraftforge.client.model.pipeline.IVertexConsumer; + +import appeng.thirdparty.codechicken.lib.model.Quad.Vertex; +import appeng.thirdparty.codechicken.lib.model.pipeline.IPipelineElementFactory; +import appeng.thirdparty.codechicken.lib.model.pipeline.QuadTransformer; + + +/** + * This transformer strips quads that are on faces. + * Simply set the bounds for the faces, and the strip mask. + * + * @author covers1624 + */ +public class QuadFaceStripper extends QuadTransformer +{ + + public static final IPipelineElementFactory FACTORY = QuadFaceStripper::new; + + private AxisAlignedBB bounds; + private int mask; + + QuadFaceStripper() + { + super(); + } + + public QuadFaceStripper( IVertexConsumer parent, AxisAlignedBB bounds, int mask ) + { + super( parent ); + this.bounds = bounds; + this.mask = mask; + } + + /** + * The bounds of the faces, + * used as the .. bounds, if all vertices of a quad + * lay on the bounds, it is up for stripping. + * + * @param bounds The bounds. + */ + public void setBounds( AxisAlignedBB bounds ) + { + this.bounds = bounds; + } + + /** + * The mask to strip edges. + * This is an opt in system, + * the mask is simple 'mask = (1 << side)'. + * + * @param mask The mask. + */ + public void setMask( int mask ) + { + this.mask = mask; + } + + @Override + public boolean transform() + { + if( this.mask == 0 ) + { + return true;// No mask, nothing changes. + } + // If the bit for this quad is set, then check if we should strip. + if( ( this.mask & ( 1 << this.quad.orientation.ordinal() ) ) != 0 ) + { + AxisDirection dir = this.quad.orientation.getAxisDirection(); + Vertex[] vertices = this.quad.vertices; + switch( this.quad.orientation.getAxis() ) + { + case X: + { + float bound = (float) ( dir == POSITIVE ? this.bounds.maxX : this.bounds.minX ); + float x1 = vertices[0].vec[0]; + float x2 = vertices[1].vec[0]; + float x3 = vertices[2].vec[0]; + float x4 = vertices[3].vec[0]; + return x1 != x2 || x2 != x3 || x3 != x4 || x4 != bound; + } + case Y: + { + float bound = (float) ( dir == POSITIVE ? this.bounds.maxY : this.bounds.minY ); + float y1 = vertices[0].vec[1]; + float y2 = vertices[1].vec[1]; + float y3 = vertices[2].vec[1]; + float y4 = vertices[3].vec[1]; + return y1 != y2 || y2 != y3 || y3 != y4 || y4 != bound; + } + case Z: + { + float bound = (float) ( dir == POSITIVE ? this.bounds.maxZ : this.bounds.minZ ); + float z1 = vertices[0].vec[2]; + float z2 = vertices[1].vec[2]; + float z3 = vertices[2].vec[2]; + float z4 = vertices[3].vec[2]; + return z1 != z2 || z2 != z3 || z3 != z4 || z4 != bound; + } + } + } + return true; + } +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadReInterpolator.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadReInterpolator.java new file mode 100644 index 000000000..95e52fe3c --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadReInterpolator.java @@ -0,0 +1,92 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model.pipeline.transformers; + + +import appeng.thirdparty.codechicken.lib.math.InterpHelper; +import appeng.thirdparty.codechicken.lib.model.CachedFormat; +import appeng.thirdparty.codechicken.lib.model.Quad; +import appeng.thirdparty.codechicken.lib.model.Quad.Vertex; +import appeng.thirdparty.codechicken.lib.model.pipeline.IPipelineElementFactory; +import appeng.thirdparty.codechicken.lib.model.pipeline.QuadTransformer; + + +/** + * This transformer Re-Interpolates the Color, UV's and LightMaps. + * Use this after all transformations that translate vertices in the pipeline. + * + * This Transformation can only be used in the BakedPipeline. + * + * @author covers1624 + */ +public class QuadReInterpolator extends QuadTransformer +{ + + public static final IPipelineElementFactory FACTORY = QuadReInterpolator::new; + + private Quad interpCache = new Quad(); + private InterpHelper interpHelper = new InterpHelper(); + + QuadReInterpolator() + { + super(); + } + + @Override + public void reset( CachedFormat format ) + { + super.reset( format ); + this.interpCache.reset( format ); + } + + @Override + public void setInputQuad( Quad quad ) + { + super.setInputQuad( quad ); + quad.resetInterp( this.interpHelper, quad.orientation.ordinal() >> 1 ); + } + + @Override + public boolean transform() + { + int s = this.quad.orientation.ordinal() >> 1; + if( this.format.hasColor || this.format.hasUV || this.format.hasLightMap ) + { + this.interpCache.copyFrom( this.quad ); + this.interpHelper.setup(); + for( Vertex v : this.quad.vertices ) + { + this.interpHelper.locate( v.dx( s ), v.dy( s ) ); + if( this.format.hasColor ) + { + v.interpColorFrom( this.interpHelper, this.interpCache.vertices ); + } + if( this.format.hasUV ) + { + v.interpUVFrom( this.interpHelper, this.interpCache.vertices ); + } + if( this.format.hasLightMap ) + { + v.interpLightMapFrom( this.interpHelper, this.interpCache.vertices ); + } + } + } + return true; + } +} diff --git a/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadTinter.java b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadTinter.java new file mode 100644 index 000000000..760fbb6df --- /dev/null +++ b/src/main/java/appeng/thirdparty/codechicken/lib/model/pipeline/transformers/QuadTinter.java @@ -0,0 +1,78 @@ +/* + * This file is part of CodeChickenLib. + * Copyright (c) 2018, covers1624, All rights reserved. + * + * CodeChickenLib 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 2.1 of the License, or + * (at your option) any later version. + * + * CodeChickenLib 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 CodeChickenLib. If not, see . + */ + +package appeng.thirdparty.codechicken.lib.model.pipeline.transformers; + + +import net.minecraftforge.client.model.pipeline.IVertexConsumer; + +import appeng.thirdparty.codechicken.lib.model.Quad.Vertex; +import appeng.thirdparty.codechicken.lib.model.pipeline.IPipelineElementFactory; +import appeng.thirdparty.codechicken.lib.model.pipeline.QuadTransformer; + + +/** + * This transformer tints quads.. + * Feed it the output of BlockColors.colorMultiplier. + * + * @author covers1624 + */ +public class QuadTinter extends QuadTransformer +{ + + public static final IPipelineElementFactory FACTORY = QuadTinter::new; + + private int tint; + + QuadTinter() + { + super(); + } + + public QuadTinter( IVertexConsumer consumer, int tint ) + { + super( consumer ); + this.tint = tint; + } + + public QuadTinter setTint( int tint ) + { + this.tint = tint; + return this; + } + + @Override + public boolean transform() + { + // Nuke tintIndex. + this.quad.tintIndex = -1; + if( this.format.hasColor ) + { + float r = ( this.tint >> 0x10 & 0xFF ) / 255F; + float g = ( this.tint >> 0x08 & 0xFF ) / 255F; + float b = ( this.tint & 0xFF ) / 255F; + for( Vertex v : this.quad.vertices ) + { + v.color[0] *= r; + v.color[1] *= g; + v.color[2] *= b; + } + } + return true; + } +} diff --git a/src/main/resources/assets/appliedenergistics2/models/item/facade_base.json b/src/main/resources/assets/appliedenergistics2/models/item/facade_base.json index 0a9cf92b2..1883437ea 100644 --- a/src/main/resources/assets/appliedenergistics2/models/item/facade_base.json +++ b/src/main/resources/assets/appliedenergistics2/models/item/facade_base.json @@ -23,17 +23,6 @@ "up": { "texture": "#anchor" }, "down": { "texture": "#anchor" } } - }, - { - "from": [ 0, 0, 0 ], - "to": [ 16, 16, 2 ], - "faces": { - "east": { "texture": "#anchor" }, - "south": { "texture": "#anchor" }, - "west": { "texture": "#anchor" }, - "up": { "texture": "#anchor" }, - "down": { "texture": "#anchor" } - } } ] }