mirror of
https://github.com/GTNewHorizons/Chisel.git
synced 2026-11-12 08:17:48 +00:00
Merge CTMLib
This commit is contained in:
1 parent
83cd16061f
commit
e9e63d6fc5
14 files changed
+1739
-2
No files matched your search
@@ -0,0 +1,313 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import static team.chisel.ctmlib.Dir.BOTTOM;
|
||||
import static team.chisel.ctmlib.Dir.BOTTOM_LEFT;
|
||||
import static team.chisel.ctmlib.Dir.BOTTOM_RIGHT;
|
||||
import static team.chisel.ctmlib.Dir.LEFT;
|
||||
import static team.chisel.ctmlib.Dir.RIGHT;
|
||||
import static team.chisel.ctmlib.Dir.TOP;
|
||||
import static team.chisel.ctmlib.Dir.TOP_LEFT;
|
||||
import static team.chisel.ctmlib.Dir.TOP_RIGHT;
|
||||
|
||||
import java.util.EnumMap;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import com.cricketcraft.chisel.api.IConnectable;
|
||||
import com.cricketcraft.chisel.api.IFacade;
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import gnu.trove.map.TIntObjectMap;
|
||||
import gnu.trove.map.hash.TIntObjectHashMap;
|
||||
|
||||
//@formatter:off
|
||||
/**
|
||||
* The CTM renderer will draw the block's FACE using by assembling 4 quadrants from the 5 available block
|
||||
* textures. The normal Texture.png is the blocks "unconnected" texture, and is used when CTM is disabled or the block
|
||||
* has nothing to connect to. This texture has all of the outside corner quadrants The texture-ctm.png contains the
|
||||
* rest of the quadrants.
|
||||
* <pre><blockquote>
|
||||
* ┌─────────────────┐ ┌────────────────────────────────┐
|
||||
* │ texture.png │ │ texture-ctm.png │
|
||||
* │ ╔══════╤══════╗ │ │ ──────┼────── ║ ─────┼───── ║ │
|
||||
* │ ║ │ ║ │ │ │ │ │║ │ ║ │
|
||||
* │ ║ 16 │ 17 ║ │ │ │ 0 │ 1 │║ 2 │ 3 ║ │
|
||||
* │ ╟──────┼──────╢ │ │ ┼──────┼──────┼╟──────┼──────╢ │
|
||||
* │ ║ │ ║ │ │ │ │ │║ │ ║ │
|
||||
* │ ║ 18 │ 19 ║ │ │ │ 4 │ 5 │║ 6 │ 7 ║ │
|
||||
* │ ╚══════╧══════╝ │ │ ──────┼────── ║ ─────┼───── ║ │
|
||||
* └─────────────────┘ │ ═══════╤═══════╝ ─────┼───── ╚ │
|
||||
* │ │ │ ││ │ │ │
|
||||
* │ │ 8 │ 9 ││ 10 │ 11 │ │
|
||||
* │ ┼──────┼──────┼┼──────┼──────┼ │
|
||||
* │ │ │ ││ │ │ │
|
||||
* │ │ 12 │ 13 ││ 14 │ 15 │ │
|
||||
* │ ═══════╧═══════╗ ─────┼───── ╔ │
|
||||
* └────────────────────────────────┘
|
||||
* </blockquote></pre>
|
||||
* combining { 18, 13, 9, 16 }, we can generate a texture connected to the right!
|
||||
* <pre><blockquote>
|
||||
* ╔══════╤═══════
|
||||
* ║ │ │
|
||||
* ║ 16 │ 9 │
|
||||
* ╟──────┼──────┼
|
||||
* ║ │ │
|
||||
* ║ 18 │ 13 │
|
||||
* ╚══════╧═══════
|
||||
* </blockquote></pre>
|
||||
*
|
||||
* combining { 18, 13, 11, 2 }, we can generate a texture, in the shape of an L (connected to the right, and up
|
||||
* <pre><blockquote>
|
||||
* ║ ─────┼───── ╚
|
||||
* ║ │ │
|
||||
* ║ 2 │ 11 │
|
||||
* ╟──────┼──────┼
|
||||
* ║ │ │
|
||||
* ║ 18 │ 13 │
|
||||
* ╚══════╧═══════
|
||||
* </blockquote></pre>
|
||||
*
|
||||
* HAVE FUN!
|
||||
* -CptRageToaster-
|
||||
*/
|
||||
//@formatter:on
|
||||
public class CTM {
|
||||
|
||||
/** Some hardcoded offset values for the different corner indeces */
|
||||
protected static int[] submapOffsets = { 4, 5, 1, 0 };
|
||||
/** For use via the Chisel 2 config only, altering this could cause unintended behavior */
|
||||
public static boolean disableObscuredFaceCheckConfig = false;
|
||||
|
||||
public Optional<Boolean> disableObscuredFaceCheck = Optional.absent();
|
||||
|
||||
protected TIntObjectMap<Dir[]> submapMap = new TIntObjectHashMap<Dir[]>();
|
||||
protected EnumMap<Dir, Boolean> connectionMap = Maps.newEnumMap(Dir.class);
|
||||
|
||||
protected CTM() {
|
||||
for (Dir dir : Dir.VALUES) {
|
||||
connectionMap.put(dir, false);
|
||||
}
|
||||
|
||||
// Mapping the different corner indeces to their respective dirs
|
||||
submapMap.put(0, new Dir[] { BOTTOM, LEFT, BOTTOM_LEFT });
|
||||
submapMap.put(1, new Dir[] { BOTTOM, RIGHT, BOTTOM_RIGHT });
|
||||
submapMap.put(2, new Dir[] { TOP, RIGHT, TOP_RIGHT });
|
||||
submapMap.put(3, new Dir[] { TOP, LEFT, TOP_LEFT });
|
||||
}
|
||||
|
||||
public static CTM getInstance() {
|
||||
return new CTM();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The indeces of the typical 4x4 submap to use for the given face at the given location.
|
||||
*
|
||||
* Indeces are in counter-clockwise order starting at bottom left.
|
||||
*/
|
||||
public int[] getSubmapIndices(IBlockAccess world, int x, int y, int z, int side) {
|
||||
int[] ret = new int[] { 18, 19, 17, 16 };
|
||||
|
||||
if (world == null) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
Block block = world.getBlock(x, y, z);
|
||||
int meta = world.getBlockMetadata(x, y, z);
|
||||
|
||||
buildConnectionMap(world, x, y, z, side, block, meta);
|
||||
|
||||
// Map connections to submap indeces
|
||||
for (int i = 0; i < 4; i++) {
|
||||
fillSubmaps(ret, i);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the connection map and stores it in this CTM instance. The {@link #connected(Dir)},
|
||||
* {@link #connectedAnd(Dir...)}, and {@link #connectedOr(Dir...)} methods can be used to access it.
|
||||
*/
|
||||
public void buildConnectionMap(IBlockAccess world, int x, int y, int z, int side, Block block, int meta) {
|
||||
for (Dir dir : Dir.VALUES) {
|
||||
connectionMap.put(dir, dir.isConnected(this, world, x, y, z, side, block, meta));
|
||||
}
|
||||
}
|
||||
|
||||
private void fillSubmaps(int[] ret, int idx) {
|
||||
Dir[] dirs = submapMap.get(idx);
|
||||
if (connectedOr(dirs[0], dirs[1])) {
|
||||
if (connectedAnd(dirs)) {
|
||||
// If all dirs are connected, we use the fully connected face,
|
||||
// the base offset value.
|
||||
ret[idx] = submapOffsets[idx];
|
||||
} else {
|
||||
// This is a bit magic-y, but basically the array is ordered so
|
||||
// the first dir requires an offset of 2, and the second dir
|
||||
// requires an offset of 8, plus the initial offset for the
|
||||
// corner.
|
||||
ret[idx] = submapOffsets[idx] + (connected(dirs[0]) ? 2 : 0) + (connected(dirs[1]) ? 8 : 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dir The direction to check connection in.
|
||||
* @return True if the cached connectionMap holds a connection in this {@link Dir direction}.
|
||||
*/
|
||||
public boolean connected(Dir dir) {
|
||||
return connectionMap.get(dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dirs The directions to check connection in.
|
||||
* @return True if the cached connectionMap holds a connection in <i><b>all</b></i> the given {@link Dir
|
||||
* directions}.
|
||||
*/
|
||||
public boolean connectedAnd(Dir... dirs) {
|
||||
for (Dir dir : dirs) {
|
||||
if (!connected(dir)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dirs The directions to check connection in.
|
||||
* @return True if the cached connectionMap holds a connection in <i><b>one of</b></i> the given {@link Dir
|
||||
* directions}.
|
||||
*/
|
||||
private boolean connectedOr(Dir... dirs) {
|
||||
for (Dir dir : dirs) {
|
||||
if (connected(dir)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple check for if the given block can connect to the given direction on the given side.
|
||||
*
|
||||
* @param world
|
||||
* @param x The x coordinate of the block to check <i>against</i>. This is not the position of your block.
|
||||
* @param y The y coordinate of the block to check <i>against</i>. This is not the position of your block.
|
||||
* @param z The z coordinate of the block to check <i>against</i>. This is not the position of your block.
|
||||
* @param side The side of the block to check for connection status. This is <i>not</i> the direction to check in.
|
||||
* @param block The block to check against for connection.
|
||||
* @param meta The metadata to check against for connection.
|
||||
* @return True if the given block can connect to the given location on the given side.
|
||||
*/
|
||||
public boolean isConnected(IBlockAccess world, int x, int y, int z, int side, Block block, int meta) {
|
||||
ForgeDirection dir = ForgeDirection.VALID_DIRECTIONS[side];
|
||||
return isConnected(world, x, y, z, dir, block, meta);
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple check for if the given block can connect to the given direction on the given side.
|
||||
*
|
||||
* @param world
|
||||
* @param x The x coordinate of the block to check <i>against</i>. This is not the position of your block.
|
||||
* @param y The y coordinate of the block to check <i>against</i>. This is not the position of your block.
|
||||
* @param z The z coordinate of the block to check <i>against</i>. This is not the position of your block.
|
||||
* @param dir The {@link ForgeDirection side} of the block to check for connection status. This is <i>not</i> the
|
||||
* direction to check in.
|
||||
* @param block The block to check against for connection.
|
||||
* @param meta The metadata to check against for connection.
|
||||
* @return True if the given block can connect to the given location on the given side.
|
||||
*/
|
||||
public boolean isConnected(IBlockAccess world, int x, int y, int z, ForgeDirection dir, Block block, int meta) {
|
||||
|
||||
if (CTMLib.chiselLoaded() && connectionBlocked(world, x, y, z, dir.ordinal())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int x2 = x + dir.offsetX;
|
||||
int y2 = y + dir.offsetY;
|
||||
int z2 = z + dir.offsetZ;
|
||||
|
||||
boolean disableObscured = disableObscuredFaceCheck.or(disableObscuredFaceCheckConfig);
|
||||
|
||||
Block con = getBlockOrFacade(world, x, y, z, dir.ordinal());
|
||||
Block obscuring = disableObscured ? null : getBlockOrFacade(world, x2, y2, z2, dir.ordinal());
|
||||
|
||||
// no block or a bad API user
|
||||
if (con == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean ret = con.equals(block) && getBlockOrFacadeMetadata(world, x, y, z, dir.ordinal()) == meta;
|
||||
|
||||
// no block obscuring this face
|
||||
if (obscuring == null) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// check that we aren't already connected outwards from this side
|
||||
ret &= !(obscuring.equals(block) && getBlockOrFacadeMetadata(world, x2, y2, z2, dir.ordinal()) == meta);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private boolean connectionBlocked(IBlockAccess world, int x, int y, int z, int side) {
|
||||
Block block = world.getBlock(x, y, z);
|
||||
if (block instanceof IConnectable) {
|
||||
return !((IConnectable) block).canConnectCTM(world, x, y, z, side);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to access metadata from both the world and {@link IFacade} blocks in the world.
|
||||
*
|
||||
* @param world
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
* @param side The side to check for. -1 for unknown.
|
||||
* @return The metadata at this position in the world, or inside an {@link IFacade} block.
|
||||
*/
|
||||
public int getBlockOrFacadeMetadata(IBlockAccess world, int x, int y, int z, int side) {
|
||||
Block blk = world.getBlock(x, y, z);
|
||||
if (CTMLib.chiselLoaded()) {
|
||||
return getFacadeMeta(blk, world, x, y, z, side);
|
||||
}
|
||||
return world.getBlockMetadata(x, y, z);
|
||||
}
|
||||
|
||||
private int getFacadeMeta(Block blk, IBlockAccess world, int x, int y, int z, int side) {
|
||||
if (blk instanceof IFacade) {
|
||||
return ((IFacade) blk).getFacadeMetadata(world, x, y, z, side);
|
||||
}
|
||||
return world.getBlockMetadata(x, y, z);
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility method to access a block from both the world and {@link IFacade} blocks in the world.
|
||||
*
|
||||
* @param world
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
* @param side The side to check for. -1 for unknown.
|
||||
* @return The block at this position in the world, or inside an {@link IFacade} block.
|
||||
*/
|
||||
public Block getBlockOrFacade(IBlockAccess world, int x, int y, int z, int side) {
|
||||
Block blk = world.getBlock(x, y, z);
|
||||
if (CTMLib.chiselLoaded()) {
|
||||
return getFacade(blk, world, x, y, z, side);
|
||||
}
|
||||
return blk;
|
||||
}
|
||||
|
||||
private Block getFacade(Block blk, IBlockAccess world, int x, int y, int z, int side) {
|
||||
if (blk instanceof IFacade) {
|
||||
blk = ((IFacade) blk).getFacade(world, x, y, z, side);
|
||||
}
|
||||
return blk;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Convenience implementation of ICMBlock. This does everything you should need for basic CTM.
|
||||
* <p>
|
||||
* This class does <i>not</i> handle metadata-dependent textures.
|
||||
*/
|
||||
public class CTMBlock extends Block implements ICTMBlock<ISubmapManager> {
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
private SubmapManagerCTM manager;
|
||||
|
||||
@Getter
|
||||
private final String modid;
|
||||
@Getter
|
||||
private final String texturePath;
|
||||
|
||||
private final int renderId;
|
||||
|
||||
public CTMBlock(String modid, String texturePath, int renderId) {
|
||||
super(Material.rock);
|
||||
this.modid = modid;
|
||||
this.texturePath = texturePath;
|
||||
this.renderId = renderId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void registerBlockIcons(IIconRegister icon) {
|
||||
manager = new SubmapManagerCTM(texturePath);
|
||||
manager.registerIcons(modid, this, icon);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public IIcon getIcon(IBlockAccess world, int x, int y, int z, int side) {
|
||||
return manager.getIcon(world, x, y, z, side);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public IIcon getIcon(int side, int meta) {
|
||||
return manager.getIcon(side, meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public int getRenderType() {
|
||||
return renderId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISubmapManager getManager(IBlockAccess world, int x, int y, int z, int meta) {
|
||||
return manager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ISubmapManager getManager(int meta) {
|
||||
return manager;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import cpw.mods.fml.common.ModAPIManager;
|
||||
|
||||
public class CTMLib {
|
||||
|
||||
public static final String VERSION = "GRADLETOKEN_VERSION";
|
||||
|
||||
private static boolean chiselPresent, chiselInitialized;
|
||||
|
||||
public static boolean chiselLoaded() {
|
||||
if (!chiselInitialized) {
|
||||
chiselInitialized = true;
|
||||
chiselPresent = ModAPIManager.INSTANCE.hasAPI("ChiselAPI");
|
||||
}
|
||||
return chiselPresent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.crash.CrashReport;
|
||||
import net.minecraft.crash.CrashReportCategory;
|
||||
import net.minecraft.util.ReportedException;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
|
||||
/**
|
||||
* A convenience base class for CTM blocks. Any block using this must implement {@link ICTMBlock}.
|
||||
*/
|
||||
public class CTMRenderer implements ISimpleBlockRenderingHandler {
|
||||
|
||||
private int renderId;
|
||||
|
||||
public CTMRenderer(int renderId) {
|
||||
this.renderId = renderId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderInventoryBlock(Block block, int metadata, int modelID, RenderBlocks renderer) {
|
||||
GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
|
||||
RenderBlocks rb = getContext(
|
||||
renderer,
|
||||
block,
|
||||
Minecraft.getMinecraft().theWorld,
|
||||
((ICTMBlock<?>) block).getManager(metadata),
|
||||
metadata);
|
||||
Drawing.drawBlock(block, metadata, rb);
|
||||
GL11.glTranslatef(0.5F, 0.5F, 0.5F);
|
||||
rb.unlockBlockBounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId,
|
||||
RenderBlocks rendererOld) {
|
||||
try {
|
||||
int meta = world.getBlockMetadata(x, y, z);
|
||||
RenderBlocks rb = getContext(
|
||||
rendererOld,
|
||||
block,
|
||||
world,
|
||||
((ICTMBlock<?>) block).getManager(world, x, y, z, meta),
|
||||
meta);
|
||||
boolean ret = rb.renderStandardBlock(block, x, y, z);
|
||||
rb.unlockBlockBounds();
|
||||
return ret;
|
||||
} catch (Throwable t) {
|
||||
CrashReport crashreport = CrashReport.makeCrashReport(t, "Rendering CTM Block");
|
||||
CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being rendered");
|
||||
crashreportcategory.addCrashSection("Block name", GameRegistry.findUniqueIdentifierFor(block));
|
||||
crashreportcategory.addCrashSection("Block metadata", world.getBlockMetadata(x, y, z));
|
||||
throw new ReportedException(crashreport);
|
||||
}
|
||||
}
|
||||
|
||||
protected RenderBlocks getContext(RenderBlocks rendererOld, Block block, IBlockAccess world, ISubmapManager manager,
|
||||
int meta) {
|
||||
if (!rendererOld.hasOverrideBlockTexture() && manager != null) {
|
||||
RenderBlocks rb = manager.createRenderContext(rendererOld, block, world);
|
||||
if (rb != null && rb != rendererOld) {
|
||||
rb.blockAccess = world;
|
||||
if (rendererOld.lockBlockBounds) {
|
||||
rb.overrideBlockBounds(
|
||||
rendererOld.renderMinX,
|
||||
rendererOld.renderMinY,
|
||||
rendererOld.renderMinZ,
|
||||
rendererOld.renderMaxX,
|
||||
rendererOld.renderMaxY,
|
||||
rendererOld.renderMaxZ);
|
||||
}
|
||||
if (rb instanceof RenderBlocksCTM) {
|
||||
RenderBlocksCTM rbctm = (RenderBlocksCTM) rb;
|
||||
rbctm.manager = rbctm.manager == null ? manager : rbctm.manager;
|
||||
rbctm.rendererOld = rbctm.rendererOld == null ? rendererOld : rbctm.rendererOld;
|
||||
}
|
||||
return rb;
|
||||
}
|
||||
}
|
||||
return rendererOld;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRender3DInInventory(int renderId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRenderId() {
|
||||
return renderId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import static net.minecraftforge.common.util.ForgeDirection.DOWN;
|
||||
import static net.minecraftforge.common.util.ForgeDirection.EAST;
|
||||
import static net.minecraftforge.common.util.ForgeDirection.SOUTH;
|
||||
import static net.minecraftforge.common.util.ForgeDirection.UP;
|
||||
import static net.minecraftforge.common.util.ForgeDirection.WEST;
|
||||
import static net.minecraftforge.common.util.ForgeDirection.getOrientation;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
/**
|
||||
* Think of this class as a "Two dimensional ForgeDirection, with diagonals".
|
||||
* <p>
|
||||
* It represents the eight different directions a face of a block can connect with CTM, and contains the logic for
|
||||
* determining if a block is indeed connected in that direction.
|
||||
* <p>
|
||||
* Note that, for example, {@link #TOP_RIGHT} does not mean connected to the {@link #TOP} and {@link #RIGHT}, but
|
||||
* connected in the diagonal direction represented by {@link #TOP_RIGHT}. This is used for inner corner rendering.
|
||||
*/
|
||||
public enum Dir {
|
||||
|
||||
// @formatter:off
|
||||
TOP(UP),
|
||||
TOP_RIGHT(UP, EAST),
|
||||
RIGHT(EAST),
|
||||
BOTTOM_RIGHT(DOWN, EAST),
|
||||
BOTTOM(DOWN),
|
||||
BOTTOM_LEFT(DOWN, WEST),
|
||||
LEFT(WEST),
|
||||
TOP_LEFT(UP, WEST);
|
||||
// @formatter:on
|
||||
|
||||
/**
|
||||
* All values of this enum, used to prevent unnecessary allocation via {@link #values()}.
|
||||
*/
|
||||
public static final Dir[] VALUES = values();
|
||||
private static final ForgeDirection NORMAL = SOUTH;
|
||||
|
||||
private ForgeDirection[] dirs;
|
||||
|
||||
private Dir(ForgeDirection... dirs) {
|
||||
this.dirs = dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds if this block is connected for the given side in this Dir.
|
||||
*
|
||||
* @param inst The CTM instance to use for logic.
|
||||
* @param world The world the block is in.
|
||||
* @param x The x coordinate of your block.
|
||||
* @param y The y coordinate of your block.
|
||||
* @param z The z coordinate of your block.
|
||||
* @param sideIdx The side index of the current face. This equivalent to {@link ForgeDirection#ordinal()}
|
||||
* @param block The block being rendered.
|
||||
* @param meta The metadata of the block.
|
||||
* @return True if the block is connected in the given Dir, false otherwise.
|
||||
*/
|
||||
public boolean isConnected(CTM inst, IBlockAccess world, int x, int y, int z, int sideIdx, Block block, int meta) {
|
||||
ForgeDirection side = getOrientation(sideIdx);
|
||||
ForgeDirection[] dirs = getNormalizedDirs(side);
|
||||
for (ForgeDirection dir : dirs) {
|
||||
x += dir.offsetX;
|
||||
y += dir.offsetY;
|
||||
z += dir.offsetZ;
|
||||
}
|
||||
return inst.isConnected(world, x, y, z, side, block, meta);
|
||||
}
|
||||
|
||||
private ForgeDirection[] getNormalizedDirs(ForgeDirection normal) {
|
||||
if (normal == NORMAL) {
|
||||
return dirs;
|
||||
} else if (normal == NORMAL.getOpposite()) {
|
||||
// If this is the opposite direction of the default normal, we
|
||||
// need to mirror the dirs
|
||||
// A mirror version does not affect y+ and y- so we ignore those
|
||||
ForgeDirection[] ret = new ForgeDirection[dirs.length];
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = dirs[i].offsetY != 0 ? dirs[i] : dirs[i].getOpposite();
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
ForgeDirection axis = null;
|
||||
// Next, we need different a different rotation axis depending
|
||||
// on if this is up/down or not
|
||||
if (normal.offsetY == 0) {
|
||||
// If it is not up/down, pick either the left or right-hand
|
||||
// rotation
|
||||
axis = normal == NORMAL.getRotation(UP) ? UP : DOWN;
|
||||
} else {
|
||||
// If it is up/down, pick either the up or down rotation.
|
||||
axis = normal == UP ? NORMAL.getRotation(DOWN) : NORMAL.getRotation(UP);
|
||||
}
|
||||
ForgeDirection[] ret = new ForgeDirection[dirs.length];
|
||||
// Finally apply all the rotations
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = dirs[i].getRotation(axis);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.IIcon;
|
||||
|
||||
public class Drawing {
|
||||
|
||||
public static void drawBlock(Block block, IIcon icon, RenderBlocks renderer) {
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, -1.0F, 0.0F);
|
||||
renderer.renderFaceYNeg(block, 0.0D, 0.0D, 0.0D, icon);
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, 1.0F, 0.0F);
|
||||
renderer.renderFaceYPos(block, 0.0D, 0.0D, 0.0D, icon);
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, 0.0F, -1.0F);
|
||||
renderer.renderFaceZNeg(block, 0.0D, 0.0D, 0.0D, icon);
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, 0.0F, 1.0F);
|
||||
renderer.renderFaceZPos(block, 0.0D, 0.0D, 0.0D, icon);
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(-1.0F, 0.0F, 0.0F);
|
||||
renderer.renderFaceXNeg(block, 0.0D, 0.0D, 0.0D, icon);
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(1.0F, 0.0F, 0.0F);
|
||||
renderer.renderFaceXPos(block, 0.0D, 0.0D, 0.0D, icon);
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
public static void drawBlock(Block block, int meta, RenderBlocks renderer) {
|
||||
Tessellator tessellator = Tessellator.instance;
|
||||
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, -1.0F, 0.0F);
|
||||
renderer.renderFaceYNeg(block, 0.0D, 0.0D, 0.0D, block.getIcon(0, meta));
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, 1.0F, 0.0F);
|
||||
renderer.renderFaceYPos(block, 0.0D, 0.0D, 0.0D, block.getIcon(1, meta));
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, 0.0F, -1.0F);
|
||||
renderer.renderFaceZNeg(block, 0.0D, 0.0D, 0.0D, block.getIcon(2, meta));
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(0.0F, 0.0F, 1.0F);
|
||||
renderer.renderFaceZPos(block, 0.0D, 0.0D, 0.0D, block.getIcon(3, meta));
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(-1.0F, 0.0F, 0.0F);
|
||||
renderer.renderFaceXNeg(block, 0.0D, 0.0D, 0.0D, block.getIcon(4, meta));
|
||||
tessellator.draw();
|
||||
tessellator.startDrawingQuads();
|
||||
tessellator.setNormal(1.0F, 0.0F, 0.0F);
|
||||
renderer.renderFaceXPos(block, 0.0D, 0.0D, 0.0D, block.getIcon(5, meta));
|
||||
tessellator.draw();
|
||||
}
|
||||
|
||||
public static void renderAllFaces(RenderBlocks renderer, Block block, int x, int y, int z, IIcon icon) {
|
||||
renderer.renderFaceYNeg(block, x, y, z, icon);
|
||||
renderer.renderFaceYPos(block, x, y, z, icon);
|
||||
renderer.renderFaceZNeg(block, x, y, z, icon);
|
||||
renderer.renderFaceZPos(block, x, y, z, icon);
|
||||
renderer.renderFaceXNeg(block, x, y, z, icon);
|
||||
renderer.renderFaceXPos(block, x, y, z, icon);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
public interface ICTMBlock<T extends ISubmapManager> {
|
||||
|
||||
/**
|
||||
* Gets the {@link ISubmapManager} for this block in the world.
|
||||
*/
|
||||
T getManager(IBlockAccess world, int x, int y, int z, int meta);
|
||||
|
||||
/**
|
||||
* Gets the {@link ISubmapManager} for this block in item form.
|
||||
*
|
||||
* @param meta The meta (damage) of the stack.
|
||||
*/
|
||||
T getManager(int meta);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import net.minecraft.util.IIcon;
|
||||
|
||||
public interface ISubmap {
|
||||
|
||||
/**
|
||||
* The width, in icons, of the submap.
|
||||
*/
|
||||
int getWidth();
|
||||
|
||||
/**
|
||||
* The height, in icons, of the submap.
|
||||
*/
|
||||
int getHeight();
|
||||
|
||||
IIcon getSubIcon(int x, int y);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public interface ISubmapManager {
|
||||
|
||||
/**
|
||||
* Gets the icon for item rendering, based on side and item metadata.
|
||||
*
|
||||
* @param side Side of the ItemBlock.
|
||||
* @param meta Metadata of the ItemBlock.
|
||||
* @return The IIcon for rendering.
|
||||
*/
|
||||
IIcon getIcon(int side, int meta);
|
||||
|
||||
/**
|
||||
* Gets the icon for world rendering, based on world position and side.
|
||||
*/
|
||||
IIcon getIcon(IBlockAccess world, int x, int y, int z, int side);
|
||||
|
||||
/**
|
||||
* Hook for registering any custom icons you might need in this class. Will be called during the normal block
|
||||
* method.
|
||||
*
|
||||
* @param modName The mod ID this submap was registered to.
|
||||
* @param block The block being registered for.
|
||||
* @param register The IIconRegister instance.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
void registerIcons(String modName, Block block, IIconRegister register);
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
RenderBlocks createRenderContext(RenderBlocks rendererOld, Block block, IBlockAccess world);
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
void preRenderSide(RenderBlocks renderer, IBlockAccess world, int x, int y, int z, ForgeDirection side);
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
void postRenderSide(RenderBlocks renderer, IBlockAccess world, int x, int y, int z, ForgeDirection side);
|
||||
}
|
||||
@@ -0,0 +1,560 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XNEG_LB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XNEG_LT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XNEG_RB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XNEG_RT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XPOS_LB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XPOS_LT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XPOS_RB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.XPOS_RT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YNEG_LB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YNEG_LT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YNEG_RB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YNEG_RT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YPOS_LB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YPOS_LT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YPOS_RB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.YPOS_RT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZNEG_LB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZNEG_LT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZNEG_RB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZNEG_RT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZPOS_LB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZPOS_LT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZPOS_RB;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.ZPOS_RT;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.X;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XY;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XYZ;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XY_HALF;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XY_HALF_Z;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XZ;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XZ_HALF;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.XZ_HALF_Y;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.X_HALF;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.X_HALF_Y;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.X_HALF_YZ;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.X_HALF_Z;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Y;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.YZ;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.YZ_HALF;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.YZ_HALF_X;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Y_HALF;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Y_HALF_X;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Y_HALF_XZ;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Y_HALF_Z;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Z;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.ZERO;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Z_HALF;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Z_HALF_X;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Z_HALF_XY;
|
||||
import static team.chisel.ctmlib.RenderBlocksCTM.Vert.Z_HALF_Y;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.Tessellator;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class RenderBlocksCTM extends RenderBlocks {
|
||||
|
||||
/**
|
||||
* An enum for all possible 26 sub-side vertices.
|
||||
*
|
||||
* The naming scheme is as follows:
|
||||
*
|
||||
* ZERO is a special case, it is the absolute min point of the block. X, Y, Z, or any combination means that the
|
||||
* axes listed in the name are at 1. X, Y, Z, or any combination followed by HALF means that those axes are at 0.5.
|
||||
* X, Y, Z, or any combination after a HALF means those axes are at 1.
|
||||
*/
|
||||
protected enum Vert {
|
||||
|
||||
// @formatter:off
|
||||
ZERO(0, 0, 0),
|
||||
XYZ(1, 1, 1),
|
||||
X(1, 0, 0),
|
||||
Y(0, 1, 0),
|
||||
Z(0, 0, 1),
|
||||
XY(1, 1, 0),
|
||||
YZ(0, 1, 1),
|
||||
XZ(1, 0, 1),
|
||||
X_HALF(0.5, 0, 0),
|
||||
Y_HALF(0, 0.5, 0),
|
||||
Z_HALF(0, 0, 0.5),
|
||||
XY_HALF(0.5, 0.5, 0),
|
||||
YZ_HALF(0, 0.5, 0.5),
|
||||
XZ_HALF(0.5, 0, 0.5),
|
||||
X_HALF_Y(0.5, 1, 0),
|
||||
X_HALF_Z(0.5, 0, 1),
|
||||
Y_HALF_X(1, 0.5, 0),
|
||||
Y_HALF_Z(0, 0.5, 1),
|
||||
Z_HALF_X(1, 0, 0.5),
|
||||
Z_HALF_Y(0, 1, 0.5),
|
||||
X_HALF_YZ(0.5, 1, 1),
|
||||
Y_HALF_XZ(1, 0.5, 1),
|
||||
Z_HALF_XY(1, 1, 0.5),
|
||||
XY_HALF_Z(0.5, 0.5, 1),
|
||||
YZ_HALF_X(1, 0.5, 0.5),
|
||||
XZ_HALF_Y(0.5, 1, 0.5);
|
||||
// @formatter:on
|
||||
|
||||
private double x, y, z;
|
||||
|
||||
private Vert(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
private static double u, v, xDiff, yDiff, zDiff, uDiff, vDiff;
|
||||
|
||||
void render(RenderBlocksCTM inst, ForgeDirection normal, int cacheID) {
|
||||
if (inst.enableAO) {
|
||||
Tessellator.instance
|
||||
.setColorOpaque_F(inst.redCache[cacheID], inst.grnCache[cacheID], inst.bluCache[cacheID]);
|
||||
Tessellator.instance.setBrightness(inst.lightingCache[cacheID]);
|
||||
}
|
||||
|
||||
u = cacheID == 1 || cacheID == 2 ? inst.maxU : inst.minU;
|
||||
v = cacheID < 2 ? inst.maxV : inst.minV;
|
||||
|
||||
uDiff = inst.maxU - inst.minU;
|
||||
vDiff = inst.maxV - inst.minV;
|
||||
|
||||
if (inst.renderMinX + inst.renderMinY + inst.renderMinZ != 0
|
||||
|| inst.renderMaxX + inst.renderMaxY + inst.renderMaxZ != 3) {
|
||||
boolean uMin = u == inst.minU;
|
||||
boolean vMin = v == inst.minV;
|
||||
|
||||
xDiff = inst.renderMaxX - inst.renderMinX;
|
||||
yDiff = inst.renderMaxY - inst.renderMinY;
|
||||
zDiff = inst.renderMaxZ - inst.renderMinZ;
|
||||
|
||||
if (normal.offsetY != 0) {
|
||||
uDiff *= uMin ? inst.renderMinX : 1 - inst.renderMaxX;
|
||||
vDiff *= vMin ? inst.renderMinZ : 1 - inst.renderMaxZ;
|
||||
} else if (normal.offsetX != 0) {
|
||||
uDiff *= uMin ? inst.renderMinZ : 1 - inst.renderMaxZ;
|
||||
vDiff *= vMin ? inst.renderMinY : 1 - inst.renderMaxY;
|
||||
} else if (normal.offsetZ != 0) {
|
||||
uDiff *= uMin ? inst.renderMinX : 1 - inst.renderMaxX;
|
||||
vDiff *= vMin ? inst.renderMinY : 1 - inst.renderMaxY;
|
||||
}
|
||||
u = u == inst.minU ? inst.minU + uDiff : inst.maxU - uDiff;
|
||||
|
||||
v = v == inst.minV ? inst.minV + vDiff : inst.maxV - vDiff;
|
||||
} else {
|
||||
xDiff = yDiff = zDiff = 1;
|
||||
}
|
||||
|
||||
Tessellator.instance.addVertexWithUV(
|
||||
inst.renderMinX + (x * xDiff),
|
||||
inst.renderMinY + (y * yDiff),
|
||||
inst.renderMinZ + (z * zDiff),
|
||||
u,
|
||||
v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Each side is divided into 4 sub-sides. LB(left bottom), RB(right bottom), LT(right top), and RT(right top).
|
||||
*
|
||||
* Each sub-side contains 4 {@link Vert} objects representing its position on the block.
|
||||
*/
|
||||
protected enum SubSide {
|
||||
|
||||
// @formatter:off
|
||||
XNEG_LB(ZERO, Z_HALF, YZ_HALF, Y_HALF), XNEG_RB(Z_HALF, Z, Y_HALF_Z, YZ_HALF), XNEG_RT(YZ_HALF, Y_HALF_Z, YZ, Z_HALF_Y), XNEG_LT(Y_HALF, YZ_HALF, Z_HALF_Y, Y),
|
||||
XPOS_LB(XZ, Z_HALF_X, YZ_HALF_X, Y_HALF_XZ), XPOS_RB(Z_HALF_X, X, Y_HALF_X, YZ_HALF_X), XPOS_RT(YZ_HALF_X, Y_HALF_X, XY, Z_HALF_XY), XPOS_LT(Y_HALF_XZ, YZ_HALF_X, Z_HALF_XY, XYZ),
|
||||
|
||||
ZNEG_LB(X, X_HALF, XY_HALF, Y_HALF_X), ZNEG_RB(X_HALF, ZERO, Y_HALF, XY_HALF), ZNEG_RT(XY_HALF, Y_HALF, Y, X_HALF_Y), ZNEG_LT(Y_HALF_X, XY_HALF, X_HALF_Y, XY),
|
||||
ZPOS_LB(Z, X_HALF_Z, XY_HALF_Z, Y_HALF_Z), ZPOS_RB(X_HALF_Z, XZ, Y_HALF_XZ, XY_HALF_Z), ZPOS_RT(XY_HALF_Z, Y_HALF_XZ, XYZ, X_HALF_YZ), ZPOS_LT(Y_HALF_Z, XY_HALF_Z, X_HALF_YZ, YZ),
|
||||
|
||||
YNEG_LB(ZERO, X_HALF, XZ_HALF, Z_HALF), YNEG_RB(X_HALF, X, Z_HALF_X, XZ_HALF), YNEG_RT(XZ_HALF, Z_HALF_X, XZ, X_HALF_Z), YNEG_LT(Z_HALF, XZ_HALF, X_HALF_Z, Z),
|
||||
YPOS_LB(YZ, X_HALF_YZ, XZ_HALF_Y, Z_HALF_Y), YPOS_RB(X_HALF_YZ, XYZ, Z_HALF_XY, XZ_HALF_Y), YPOS_RT(XZ_HALF_Y, Z_HALF_XY, XY, X_HALF_Y), YPOS_LT(Z_HALF_Y, XZ_HALF_Y, X_HALF_Y, Y);
|
||||
// @formatter:on
|
||||
private Vert xmin, xmax, ymin, ymax;
|
||||
private ForgeDirection normal;
|
||||
|
||||
SubSide(Vert xmin, Vert ymin, Vert ymax, Vert xmax) {
|
||||
this.xmin = xmin;
|
||||
this.ymin = ymin;
|
||||
this.ymax = ymax;
|
||||
this.xmax = xmax;
|
||||
this.normal = calcNormal();
|
||||
}
|
||||
|
||||
private ForgeDirection calcNormal() {
|
||||
double xTot = xmin.x + xmax.x + ymin.x + ymax.x;
|
||||
double yTot = xmin.y + xmax.y + ymin.y + ymax.y;
|
||||
double zTot = xmin.z + xmax.z + ymin.z + ymax.z;
|
||||
if (xTot % 4 == 0) {
|
||||
return xTot > 0 ? ForgeDirection.EAST : ForgeDirection.WEST;
|
||||
} else if (yTot % 4 == 0) {
|
||||
return yTot > 0 ? ForgeDirection.UP : ForgeDirection.DOWN;
|
||||
} else if (zTot % 4 == 0) {
|
||||
return zTot > 0 ? ForgeDirection.SOUTH : ForgeDirection.NORTH;
|
||||
}
|
||||
return ForgeDirection.UNKNOWN;
|
||||
}
|
||||
|
||||
void render(RenderBlocksCTM inst) {
|
||||
xmin.render(inst, normal, 0);
|
||||
ymin.render(inst, normal, 1);
|
||||
ymax.render(inst, normal, 2);
|
||||
xmax.render(inst, normal, 3);
|
||||
}
|
||||
}
|
||||
|
||||
public CTM ctm = CTM.getInstance();
|
||||
|
||||
// globals added to save the JVM some trouble. No need to constantly create
|
||||
// and destroy ints if we don't have to
|
||||
protected int blockLightBitChannel = 0;
|
||||
protected int redBitChannel = 0;
|
||||
protected int greenBitChannel = 0;
|
||||
protected int blueBitChannel = 0;
|
||||
protected int sunlightBitChannel = 0;
|
||||
|
||||
public RenderBlocksCTM() {
|
||||
super();
|
||||
renderMaxX = renderMaxY = renderMaxZ = 1;
|
||||
}
|
||||
|
||||
protected Tessellator tessellator = Tessellator.instance;
|
||||
protected double minU, maxU;
|
||||
protected double minV, maxV;
|
||||
protected int[] lightingCache = new int[4];
|
||||
protected float[] redCache = new float[4];
|
||||
protected float[] grnCache = new float[4];
|
||||
protected float[] bluCache = new float[4];
|
||||
public TextureSubmap submap;
|
||||
public TextureSubmap submapSmall;
|
||||
public RenderBlocks rendererOld;
|
||||
public ISubmapManager manager;
|
||||
|
||||
protected int[][] lightmap = new int[3][3];
|
||||
protected float[][] redmap = new float[3][3];
|
||||
protected float[][] grnmap = new float[3][3];
|
||||
protected float[][] blumap = new float[3][3];
|
||||
|
||||
protected int bx, by, bz, meta;
|
||||
|
||||
@Getter
|
||||
protected boolean inWorld = false;
|
||||
|
||||
@Override
|
||||
public boolean renderStandardBlock(Block block, int x, int y, int z) {
|
||||
bx = x;
|
||||
by = y;
|
||||
bz = z;
|
||||
meta = Minecraft.getMinecraft().theWorld.getBlockMetadata(x, y, z);
|
||||
|
||||
Tessellator.instance.setColorOpaque_F(1.0F, 1.0F, 1.0F);
|
||||
Tessellator.instance.addTranslation(x, y, z);
|
||||
if (rendererOld != null && rendererOld.hasOverrideBlockTexture()) {
|
||||
setOverrideBlockTexture(rendererOld.overrideBlockTexture);
|
||||
}
|
||||
inWorld = true;
|
||||
boolean res = super.renderStandardBlock(block, x, y, z);
|
||||
inWorld = false;
|
||||
Tessellator.instance.addTranslation(-x, -y, -z);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* @formatter:off
|
||||
* This method fills a 3x3 grid of light values based on the four corners, by averaging them together.
|
||||
*
|
||||
* 2 TL x TR
|
||||
*
|
||||
* 1 x x x
|
||||
*
|
||||
* 0 BL x BR
|
||||
* 0 1 2
|
||||
*
|
||||
* Note: Variable names mean nothing... don't touch
|
||||
*
|
||||
* ._.
|
||||
*
|
||||
* shakes fist in anger
|
||||
*/
|
||||
// @formatter:on
|
||||
protected void fillLightmap(int bottomLeft, int bottomRight, int topRight, int topLeft) {
|
||||
lightmap[0][0] = bottomLeft;
|
||||
lightmap[2][0] = bottomRight;
|
||||
lightmap[2][2] = topRight;
|
||||
lightmap[0][2] = topLeft;
|
||||
|
||||
lightmap[1][0] = avg(bottomLeft, bottomRight);
|
||||
lightmap[2][1] = avg(bottomRight, topRight);
|
||||
lightmap[1][2] = avg(topRight, topLeft);
|
||||
lightmap[0][1] = avg(topLeft, bottomLeft);
|
||||
|
||||
lightmap[1][1] = avg(bottomLeft, bottomRight, topRight, topLeft);
|
||||
}
|
||||
|
||||
protected void fillColormap(float bottomLeft, float bottomRight, float topRight, float topLeft, float[][] map) {
|
||||
map[0][0] = bottomLeft;
|
||||
map[2][0] = bottomRight;
|
||||
map[2][2] = topRight;
|
||||
map[0][2] = topLeft;
|
||||
|
||||
map[1][0] = (bottomLeft + bottomRight) / 2.0F;
|
||||
map[2][1] = (bottomRight + topRight) / 2.0F;
|
||||
map[1][2] = (topRight + topLeft) / 2.0F;
|
||||
map[0][1] = (topLeft + bottomLeft) / 2.0F;
|
||||
|
||||
map[1][1] = (bottomLeft + bottomRight + topRight + topLeft) / 4.0F;
|
||||
}
|
||||
|
||||
protected void getLight(int x, int y) {
|
||||
lightingCache[0] = lightmap[0 + x][0 + y];
|
||||
lightingCache[1] = lightmap[1 + x][0 + y];
|
||||
lightingCache[2] = lightmap[1 + x][1 + y];
|
||||
lightingCache[3] = lightmap[0 + x][1 + y];
|
||||
|
||||
redCache[0] = redmap[0 + x][0 + y];
|
||||
redCache[1] = redmap[1 + x][0 + y];
|
||||
redCache[2] = redmap[1 + x][1 + y];
|
||||
redCache[3] = redmap[0 + x][1 + y];
|
||||
|
||||
grnCache[0] = grnmap[0 + x][0 + y];
|
||||
grnCache[1] = grnmap[1 + x][0 + y];
|
||||
grnCache[2] = grnmap[1 + x][1 + y];
|
||||
grnCache[3] = grnmap[0 + x][1 + y];
|
||||
|
||||
bluCache[0] = blumap[0 + x][0 + y];
|
||||
bluCache[1] = blumap[1 + x][0 + y];
|
||||
bluCache[2] = blumap[1 + x][1 + y];
|
||||
bluCache[3] = blumap[0 + x][1 + y];
|
||||
}
|
||||
|
||||
/**
|
||||
* This works around a bug in CLC atm
|
||||
*/
|
||||
private int avg(int... lightVals) {
|
||||
blockLightBitChannel = 0;
|
||||
redBitChannel = 0;
|
||||
greenBitChannel = 0;
|
||||
blueBitChannel = 0;
|
||||
sunlightBitChannel = 0;
|
||||
|
||||
for (int light : lightVals) {
|
||||
blockLightBitChannel += (light & 0xFF);
|
||||
redBitChannel += (light & 0xF00);
|
||||
greenBitChannel += (light & 0xF000);
|
||||
blueBitChannel += (light & 0xF0000);
|
||||
sunlightBitChannel += (light & 0xF00000);
|
||||
}
|
||||
|
||||
return ((blockLightBitChannel / lightVals.length) & 0xFF) | ((redBitChannel / lightVals.length) & 0xF00)
|
||||
| ((greenBitChannel / lightVals.length) & 0xF000)
|
||||
| ((blueBitChannel / lightVals.length) & 0xF0000)
|
||||
| ((sunlightBitChannel / lightVals.length) & 0xF00000);
|
||||
}
|
||||
|
||||
protected void side(Block block, SubSide side, int iconIndex) {
|
||||
|
||||
IIcon icon;
|
||||
TextureSubmap map;
|
||||
if (iconIndex >= 16) {
|
||||
iconIndex -= 16;
|
||||
map = submapSmall;
|
||||
} else {
|
||||
map = submap;
|
||||
}
|
||||
|
||||
if (map == null) {
|
||||
icon = getBlockIconFromSideAndMetadata(block, side.normal.ordinal(), meta);
|
||||
} else {
|
||||
int x = iconIndex % map.getWidth();
|
||||
int y = iconIndex / map.getHeight();
|
||||
icon = map.getSubIcon(x, y);
|
||||
}
|
||||
|
||||
double umax = icon.getMaxU();
|
||||
double umin = icon.getMinU();
|
||||
double vmax = icon.getMaxV();
|
||||
double vmin = icon.getMinV();
|
||||
|
||||
minU = umin;
|
||||
maxU = umax;
|
||||
minV = vmin;
|
||||
maxV = vmax;
|
||||
|
||||
// uCache[0] = umin;
|
||||
// uCache[1] = umax;
|
||||
// uCache[2] = umax;
|
||||
// uCache[3] = umin;
|
||||
//
|
||||
// vCache[0] = vmax;
|
||||
// vCache[1] = vmax;
|
||||
// vCache[2] = vmin;
|
||||
// vCache[3] = vmin;
|
||||
|
||||
side.render(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
pre(ForgeDirection.WEST);
|
||||
if (!inWorld || hasOverrideBlockTexture() || submap == null) {
|
||||
super.renderFaceXNeg(block, 0, 0, 0, icon);
|
||||
} else {
|
||||
int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 4);
|
||||
|
||||
fillLightmap(brightnessBottomRight, brightnessTopRight, brightnessTopLeft, brightnessBottomLeft);
|
||||
fillColormap(colorRedBottomRight, colorRedTopRight, colorRedTopLeft, colorRedBottomLeft, redmap);
|
||||
fillColormap(colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, colorGreenBottomLeft, grnmap);
|
||||
fillColormap(colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, colorBlueBottomLeft, blumap);
|
||||
|
||||
getLight(0, 0);
|
||||
side(block, XNEG_LB, tex[0]);
|
||||
getLight(1, 0);
|
||||
side(block, XNEG_RB, tex[1]);
|
||||
getLight(1, 1);
|
||||
side(block, XNEG_RT, tex[2]);
|
||||
getLight(0, 1);
|
||||
side(block, XNEG_LT, tex[3]);
|
||||
}
|
||||
post(ForgeDirection.WEST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
pre(ForgeDirection.EAST);
|
||||
if (!inWorld || hasOverrideBlockTexture() || submap == null) {
|
||||
super.renderFaceXPos(block, 0, 0, 0, icon);
|
||||
} else {
|
||||
int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 5);
|
||||
|
||||
fillLightmap(brightnessTopLeft, brightnessBottomLeft, brightnessBottomRight, brightnessTopRight);
|
||||
fillColormap(colorRedTopLeft, colorRedBottomLeft, colorRedBottomRight, colorRedTopRight, redmap);
|
||||
fillColormap(colorGreenTopLeft, colorGreenBottomLeft, colorGreenBottomRight, colorGreenTopRight, grnmap);
|
||||
fillColormap(colorBlueTopLeft, colorBlueBottomLeft, colorBlueBottomRight, colorBlueTopRight, blumap);
|
||||
|
||||
getLight(0, 0);
|
||||
side(block, XPOS_LB, tex[0]);
|
||||
getLight(1, 0);
|
||||
side(block, XPOS_RB, tex[1]);
|
||||
getLight(1, 1);
|
||||
side(block, XPOS_RT, tex[2]);
|
||||
getLight(0, 1);
|
||||
side(block, XPOS_LT, tex[3]);
|
||||
}
|
||||
post(ForgeDirection.EAST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
pre(ForgeDirection.NORTH);
|
||||
if (!inWorld || hasOverrideBlockTexture() || submap == null) {
|
||||
super.renderFaceZNeg(block, 0, 0, 0, icon);
|
||||
} else {
|
||||
int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 2);
|
||||
|
||||
fillLightmap(brightnessBottomRight, brightnessTopRight, brightnessTopLeft, brightnessBottomLeft);
|
||||
fillColormap(colorRedBottomRight, colorRedTopRight, colorRedTopLeft, colorRedBottomLeft, redmap);
|
||||
fillColormap(colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, colorGreenBottomLeft, grnmap);
|
||||
fillColormap(colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, colorBlueBottomLeft, blumap);
|
||||
|
||||
getLight(0, 0);
|
||||
side(block, ZNEG_LB, tex[0]);
|
||||
getLight(1, 0);
|
||||
side(block, ZNEG_RB, tex[1]);
|
||||
getLight(1, 1);
|
||||
side(block, ZNEG_RT, tex[2]);
|
||||
getLight(0, 1);
|
||||
side(block, ZNEG_LT, tex[3]);
|
||||
}
|
||||
post(ForgeDirection.NORTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
pre(ForgeDirection.SOUTH);
|
||||
if (!inWorld || hasOverrideBlockTexture() || submap == null) {
|
||||
super.renderFaceZPos(block, 0, 0, 0, icon);
|
||||
} else {
|
||||
int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 3);
|
||||
|
||||
fillLightmap(brightnessBottomLeft, brightnessBottomRight, brightnessTopRight, brightnessTopLeft);
|
||||
fillColormap(colorRedBottomLeft, colorRedBottomRight, colorRedTopRight, colorRedTopLeft, redmap);
|
||||
fillColormap(colorGreenBottomLeft, colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, grnmap);
|
||||
fillColormap(colorBlueBottomLeft, colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, blumap);
|
||||
|
||||
getLight(0, 0);
|
||||
side(block, ZPOS_LB, tex[0]);
|
||||
getLight(1, 0);
|
||||
side(block, ZPOS_RB, tex[1]);
|
||||
getLight(1, 1);
|
||||
side(block, ZPOS_RT, tex[2]);
|
||||
getLight(0, 1);
|
||||
side(block, ZPOS_LT, tex[3]);
|
||||
}
|
||||
post(ForgeDirection.SOUTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
pre(ForgeDirection.DOWN);
|
||||
if (!inWorld || hasOverrideBlockTexture() || submap == null) {
|
||||
super.renderFaceYNeg(block, 0, 0, 0, icon);
|
||||
} else {
|
||||
int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 0);
|
||||
|
||||
fillLightmap(brightnessBottomLeft, brightnessBottomRight, brightnessTopRight, brightnessTopLeft);
|
||||
fillColormap(colorRedBottomLeft, colorRedBottomRight, colorRedTopRight, colorRedTopLeft, redmap);
|
||||
fillColormap(colorGreenBottomLeft, colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, grnmap);
|
||||
fillColormap(colorBlueBottomLeft, colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, blumap);
|
||||
|
||||
getLight(0, 0);
|
||||
side(block, YNEG_LB, tex[0]);
|
||||
getLight(1, 0);
|
||||
side(block, YNEG_RB, tex[1]);
|
||||
getLight(1, 1);
|
||||
side(block, YNEG_RT, tex[2]);
|
||||
getLight(0, 1);
|
||||
side(block, YNEG_LT, tex[3]);
|
||||
}
|
||||
post(ForgeDirection.DOWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
pre(ForgeDirection.UP);
|
||||
if (!inWorld || hasOverrideBlockTexture() || submap == null) {
|
||||
super.renderFaceYPos(block, 0, 0, 0, icon);
|
||||
} else {
|
||||
int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 1);
|
||||
|
||||
fillLightmap(brightnessTopRight, brightnessTopLeft, brightnessBottomLeft, brightnessBottomRight);
|
||||
fillColormap(colorRedTopRight, colorRedTopLeft, colorRedBottomLeft, colorRedBottomRight, redmap);
|
||||
fillColormap(colorGreenTopRight, colorGreenTopLeft, colorGreenBottomLeft, colorGreenBottomRight, grnmap);
|
||||
fillColormap(colorBlueTopRight, colorBlueTopLeft, colorBlueBottomLeft, colorBlueBottomRight, blumap);
|
||||
|
||||
getLight(0, 0);
|
||||
side(block, YPOS_LB, tex[0]);
|
||||
getLight(1, 0);
|
||||
side(block, YPOS_RB, tex[1]);
|
||||
getLight(1, 1);
|
||||
side(block, YPOS_RT, tex[2]);
|
||||
getLight(0, 1);
|
||||
side(block, YPOS_LT, tex[3]);
|
||||
}
|
||||
post(ForgeDirection.UP);
|
||||
}
|
||||
|
||||
protected void pre(ForgeDirection face) {
|
||||
manager.preRenderSide(this, blockAccess, bx, by, bz, face);
|
||||
}
|
||||
|
||||
protected void post(ForgeDirection face) {
|
||||
manager.postRenderSide(this, blockAccess, bx, by, bz, face);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
|
||||
import com.cricketcraft.chisel.api.rendering.TextureType;
|
||||
|
||||
/**
|
||||
* Used by {@link TextureType#CTMV}
|
||||
*/
|
||||
public class RenderBlocksColumn extends RenderBlocks {
|
||||
|
||||
public TextureSubmap submap;
|
||||
public IIcon iconTop;
|
||||
private boolean inWorld;
|
||||
|
||||
private IIcon sides[] = new IIcon[6];
|
||||
private CTM ctm = CTM.getInstance();
|
||||
|
||||
public RenderBlocksColumn() {
|
||||
super();
|
||||
}
|
||||
|
||||
boolean connected(IBlockAccess world, int x, int y, int z, Block block, int meta) {
|
||||
Block inWorld = ctm.getBlockOrFacade(world, x, y, z, -1);
|
||||
return inWorld != null && inWorld.equals(block) && ctm.getBlockOrFacadeMetadata(world, x, y, z, -1) == meta;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean renderStandardBlock(Block block, int x, int y, int z) {
|
||||
int metadata = blockAccess.getBlockMetadata(x, y, z);
|
||||
inWorld = true;
|
||||
|
||||
boolean yp = connected(blockAccess, x, y + 1, z, block, metadata);
|
||||
boolean yn = connected(blockAccess, x, y - 1, z, block, metadata);
|
||||
|
||||
if (yp || yn) {
|
||||
sides[0] = iconTop;
|
||||
sides[1] = iconTop;
|
||||
|
||||
if (yp && yn) sides[2] = submap.getSubIcon(0, 1);
|
||||
else if (yp) sides[2] = submap.getSubIcon(1, 1);
|
||||
else sides[2] = submap.getSubIcon(1, 0);
|
||||
|
||||
sides[3] = sides[4] = sides[5] = sides[2];
|
||||
} else {
|
||||
boolean xp = connected(blockAccess, x + 1, y, z, block, metadata);
|
||||
boolean xn = connected(blockAccess, x - 1, y, z, block, metadata);
|
||||
|
||||
if (xp && (connected(blockAccess, x + 1, y + 1, z, block, metadata)
|
||||
|| connected(blockAccess, x + 1, y - 1, z, block, metadata))) xp = false;
|
||||
if (xn && (connected(blockAccess, x - 1, y + 1, z, block, metadata)
|
||||
|| connected(blockAccess, x - 1, y - 1, z, block, metadata))) xn = false;
|
||||
|
||||
if (xp || xn) {
|
||||
uvRotateEast = 2;
|
||||
uvRotateWest = 1;
|
||||
uvRotateTop = 1;
|
||||
uvRotateBottom = 1;
|
||||
|
||||
sides[4] = iconTop;
|
||||
sides[5] = iconTop;
|
||||
|
||||
// for some reason along the x axis the bottom texture on the ends is backwards. This flips it around.
|
||||
if (xp && xn) {
|
||||
sides[1] = submap.getSubIcon(0, 1);
|
||||
sides[0] = sides[1];
|
||||
} else if (xp) {
|
||||
sides[1] = submap.getSubIcon(1, 1);
|
||||
sides[0] = submap.getSubIcon(1, 0);
|
||||
} else {
|
||||
sides[1] = submap.getSubIcon(1, 0);
|
||||
sides[0] = submap.getSubIcon(1, 1);
|
||||
}
|
||||
sides[2] = sides[3] = sides[1];
|
||||
} else {
|
||||
boolean zp = connected(blockAccess, x, y, z + 1, block, metadata);
|
||||
boolean zn = connected(blockAccess, x, y, z - 1, block, metadata);
|
||||
|
||||
if (zp && (connected(blockAccess, x, y + 1, z + 1, block, metadata)
|
||||
|| connected(blockAccess, x, y - 1, z + 1, block, metadata))) zp = false;
|
||||
if (zp && (connected(blockAccess, x + 1, y, z + 1, block, metadata)
|
||||
|| connected(blockAccess, x - 1, y, z + 1, block, metadata))) zp = false;
|
||||
if (zn && (connected(blockAccess, x, y + 1, z - 1, block, metadata)
|
||||
|| connected(blockAccess, x, y - 1, z - 1, block, metadata))) zn = false;
|
||||
if (zn && (connected(blockAccess, x + 1, y, z - 1, block, metadata)
|
||||
|| connected(blockAccess, x - 1, y, z - 1, block, metadata))) zn = false;
|
||||
|
||||
if (zp || zn) {
|
||||
uvRotateSouth = 1;
|
||||
uvRotateNorth = 2;
|
||||
|
||||
sides[2] = iconTop;
|
||||
sides[3] = iconTop;
|
||||
|
||||
if (zp && zn) sides[0] = submap.getSubIcon(0, 1);
|
||||
else if (zp) sides[0] = submap.getSubIcon(1, 0);
|
||||
else sides[0] = submap.getSubIcon(1, 1);
|
||||
|
||||
sides[1] = sides[4] = sides[5] = sides[0];
|
||||
} else {
|
||||
sides[0] = sides[1] = iconTop;
|
||||
sides[2] = sides[3] = sides[4] = sides[5] = submap.getSubIcon(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean flag = super.renderStandardBlock(block, x, y, z);
|
||||
|
||||
uvRotateSouth = 0;
|
||||
uvRotateEast = 0;
|
||||
uvRotateWest = 0;
|
||||
uvRotateNorth = 0;
|
||||
uvRotateTop = 0;
|
||||
uvRotateBottom = 0;
|
||||
|
||||
inWorld = false;
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceXNeg(block, x, y, z, getIcon(icon, 4));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceXPos(block, x, y, z, getIcon(icon, 5));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceZNeg(block, x, y, z, getIcon(icon, 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceZPos(block, x, y, z, getIcon(icon, 3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceYNeg(block, x, y, z, getIcon(icon, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
|
||||
super.renderFaceYPos(block, x, y, z, getIcon(icon, 1));
|
||||
}
|
||||
|
||||
private IIcon getIcon(IIcon defaultIcon, int side) {
|
||||
return sides[side] == null || !inWorld ? defaultIcon : sides[side];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.client.renderer.texture.IIconRegister;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* A convenience implementation of {@link ISubmapManager} which does the standard CTM behavior.
|
||||
*/
|
||||
public class SubmapManagerCTM implements ISubmapManager {
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
private static RenderBlocksCTM rb;
|
||||
|
||||
@Getter
|
||||
private TextureSubmap submap, submapSmall;
|
||||
@Getter
|
||||
private final String textureName;
|
||||
|
||||
/**
|
||||
* @param textureName The path to the texture, excluding the mod ID (this is passed into
|
||||
* {@link #registerIcons(String, Block, IIconRegister)}).
|
||||
* <p>
|
||||
* The CTM texture will be this path, plus {@code "-ctm"}.
|
||||
*/
|
||||
public SubmapManagerCTM(String textureName) {
|
||||
this.textureName = textureName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIcon getIcon(int side, int meta) {
|
||||
return submapSmall.getBaseIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IIcon getIcon(IBlockAccess world, int x, int y, int z, int side) {
|
||||
return getIcon(side, world.getBlockMetadata(x, y, z));
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void registerIcons(String modName, Block block, IIconRegister register) {
|
||||
submap = new TextureSubmap(register.registerIcon(modName + ":" + textureName + "-ctm"), 4, 4);
|
||||
submapSmall = new TextureSubmap(register.registerIcon(modName + ":" + textureName), 2, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public RenderBlocks createRenderContext(RenderBlocks rendererOld, Block block, IBlockAccess world) {
|
||||
if (rb == null) {
|
||||
rb = new RenderBlocksCTM();
|
||||
}
|
||||
rb.setRenderBoundsFromBlock(block);
|
||||
rb.submap = submap;
|
||||
rb.submapSmall = submapSmall;
|
||||
return rb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preRenderSide(RenderBlocks renderer, IBlockAccess world, int x, int y, int z, ForgeDirection side) {}
|
||||
|
||||
@Override
|
||||
public void postRenderSide(RenderBlocks renderer, IBlockAccess world, int x, int y, int z, ForgeDirection side) {}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package team.chisel.ctmlib;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraftforge.client.event.TextureStitchEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import lombok.experimental.Delegate;
|
||||
|
||||
/**
|
||||
* This class is used to split up a large IIcon into smaller "submapped" icons used for CTM and other texture
|
||||
* manipulation.
|
||||
*/
|
||||
public class TextureSubmap implements IIcon, ISubmap {
|
||||
|
||||
private static List<TextureSubmap> submaps = Lists.newArrayList();
|
||||
private static TextureSubmap dummy = new TextureSubmap(null, 0, 0);
|
||||
static {
|
||||
MinecraftForge.EVENT_BUS.register(dummy);
|
||||
}
|
||||
|
||||
private int width, height;
|
||||
@Delegate
|
||||
private IIcon baseIcon;
|
||||
|
||||
protected IIcon[][] icons;
|
||||
|
||||
/**
|
||||
* Construct a new submap. A submap is not required to be square, but it is required to be rectangular.
|
||||
*
|
||||
* @param baseIcon The IIcon to submap.
|
||||
* @param width The width of the map, in icons.
|
||||
* @param height The height of the map, in icons.
|
||||
*/
|
||||
public TextureSubmap(IIcon baseIcon, int width, int height) {
|
||||
this.baseIcon = baseIcon;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.icons = new IIcon[width][height];
|
||||
submaps.add(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* The large "base" icon used for the submap.
|
||||
*/
|
||||
public IIcon getBaseIcon() {
|
||||
return baseIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all the icons in this submap. This is an expensive operation as it first clones the entire 2D array.
|
||||
*
|
||||
* @return All icons in this submap.
|
||||
*/
|
||||
public IIcon[][] getAllIcons() {
|
||||
IIcon[][] ret = ArrayUtils.clone(icons);
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = ArrayUtils.clone(ret[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* ==== ISubmap ==== */
|
||||
|
||||
/**
|
||||
* Finds and returns the sub-icon at the given coordinates. For example, if you have a 3 by 3 submap, calling
|
||||
* {@code getSubIcon(1, 1)} will return the center subicon.
|
||||
*/
|
||||
@Override
|
||||
public IIcon getSubIcon(int x, int y) {
|
||||
x = x % icons.length;
|
||||
return icons[x][y % icons[x].length];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
/* ==== Internal Stitching Logic ==== */
|
||||
|
||||
/**
|
||||
* For internal use only, this is used to create the virtual "subicons" used in the map.
|
||||
*/
|
||||
@SubscribeEvent
|
||||
public final void TexturesStitched(TextureStitchEvent.Post event) {
|
||||
for (TextureSubmap ts : submaps) {
|
||||
ts.texturesStitched();
|
||||
}
|
||||
}
|
||||
|
||||
public void texturesStitched() {
|
||||
for (int x = 0; x < width; x++) {
|
||||
for (int y = 0; y < height; y++) {
|
||||
icons[x][y] = new TextureVirtual(getBaseIcon(), width, height, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class TextureVirtual implements IIcon {
|
||||
|
||||
private int width, height;
|
||||
private float umin, umax, vmin, vmax;
|
||||
private IIcon parentIcon;
|
||||
|
||||
private TextureVirtual(IIcon parent, int w, int h, int x, int y) {
|
||||
parentIcon = parent;
|
||||
|
||||
umin = parentIcon.getInterpolatedU(16.0 * (x) / w);
|
||||
umax = parentIcon.getInterpolatedU(16.0 * (x + 1) / w);
|
||||
vmin = parentIcon.getInterpolatedV(16.0 * (y) / h);
|
||||
vmax = parentIcon.getInterpolatedV(16.0 * (y + 1) / h);
|
||||
|
||||
width = parentIcon.getIconWidth();
|
||||
height = parentIcon.getIconHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getMinU() {
|
||||
return umin;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getMaxU() {
|
||||
return umax;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getInterpolatedU(double d0) {
|
||||
return (float) (umin + (umax - umin) * d0 / 16.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getMinV() {
|
||||
return vmin;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getMaxV() {
|
||||
return vmax;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float getInterpolatedV(double d0) {
|
||||
return (float) (vmin + (vmax - vmin) * d0 / 16.0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public String getIconName() {
|
||||
return parentIcon.getIconName();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public int getIconWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public int getIconHeight() {
|
||||
return height;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user