connectionMap = Maps.newEnumMap(Dir.class);
81 |
82 | protected CTM() {
83 | for (Dir dir : Dir.VALUES) {
84 | connectionMap.put(dir, false);
85 | }
86 |
87 | // Mapping the different corner indeces to their respective dirs
88 | submapMap.put(0, new Dir[] { BOTTOM, LEFT, BOTTOM_LEFT });
89 | submapMap.put(1, new Dir[] { BOTTOM, RIGHT, BOTTOM_RIGHT });
90 | submapMap.put(2, new Dir[] { TOP, RIGHT, TOP_RIGHT });
91 | submapMap.put(3, new Dir[] { TOP, LEFT, TOP_LEFT });
92 | }
93 |
94 | public static CTM getInstance() {
95 | return new CTM();
96 | }
97 |
98 | /**
99 | * @return The indeces of the typical 4x4 submap to use for the given face at the given location.
100 | *
101 | * Indeces are in counter-clockwise order starting at bottom left.
102 | */
103 | public int[] getSubmapIndices(IBlockAccess world, int x, int y, int z, int side) {
104 | int[] ret = new int[] { 18, 19, 17, 16 };
105 |
106 | if (world == null) {
107 | return ret;
108 | }
109 |
110 | Block block = world.getBlock(x, y, z);
111 | int meta = world.getBlockMetadata(x, y, z);
112 |
113 | buildConnectionMap(world, x, y, z, side, block, meta);
114 |
115 | // Map connections to submap indeces
116 | for (int i = 0; i < 4; i++) {
117 | fillSubmaps(ret, i);
118 | }
119 |
120 | return ret;
121 | }
122 |
123 | /**
124 | * 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.
125 | */
126 | public void buildConnectionMap(IBlockAccess world, int x, int y, int z, int side, Block block, int meta) {
127 | for (Dir dir : Dir.VALUES) {
128 | connectionMap.put(dir, dir.isConnected(this, world, x, y, z, side, block, meta));
129 | }
130 | }
131 |
132 | private void fillSubmaps(int[] ret, int idx) {
133 | Dir[] dirs = submapMap.get(idx);
134 | if (connectedOr(dirs[0], dirs[1])) {
135 | if (connectedAnd(dirs)) {
136 | // If all dirs are connected, we use the fully connected face,
137 | // the base offset value.
138 | ret[idx] = submapOffsets[idx];
139 | } else {
140 | // This is a bit magic-y, but basically the array is ordered so
141 | // the first dir requires an offset of 2, and the second dir
142 | // requires an offset of 8, plus the initial offset for the
143 | // corner.
144 | ret[idx] = submapOffsets[idx] + (connected(dirs[0]) ? 2 : 0) + (connected(dirs[1]) ? 8 : 0);
145 | }
146 | }
147 | }
148 |
149 | /**
150 | * @param dir
151 | * The direction to check connection in.
152 | * @return True if the cached connectionMap holds a connection in this {@link Dir direction}.
153 | */
154 | public boolean connected(Dir dir) {
155 | return connectionMap.get(dir);
156 | }
157 |
158 | /**
159 | * @param dirs
160 | * The directions to check connection in.
161 | * @return True if the cached connectionMap holds a connection in all the given {@link Dir directions}.
162 | */
163 | public boolean connectedAnd(Dir... dirs) {
164 | for (Dir dir : dirs) {
165 | if (!connected(dir)) {
166 | return false;
167 | }
168 | }
169 | return true;
170 | }
171 |
172 | /**
173 | * @param dirs
174 | * The directions to check connection in.
175 | * @return True if the cached connectionMap holds a connection in one of the given {@link Dir directions}.
176 | */
177 | private boolean connectedOr(Dir... dirs) {
178 | for (Dir dir : dirs) {
179 | if (connected(dir)) {
180 | return true;
181 | }
182 | }
183 | return false;
184 | }
185 |
186 | /**
187 | * A simple check for if the given block can connect to the given direction on the given side.
188 | *
189 | * @param world
190 | * @param x
191 | * The x coordinate of the block to check against. This is not the position of your block.
192 | * @param y
193 | * The y coordinate of the block to check against. This is not the position of your block.
194 | * @param z
195 | * The z coordinate of the block to check against. This is not the position of your block.
196 | * @param side
197 | * The side of the block to check for connection status. This is not the direction to check in.
198 | * @param block
199 | * The block to check against for connection.
200 | * @param meta
201 | * The metadata to check against for connection.
202 | * @return True if the given block can connect to the given location on the given side.
203 | */
204 | public boolean isConnected(IBlockAccess world, int x, int y, int z, int side, Block block, int meta) {
205 | ForgeDirection dir = ForgeDirection.VALID_DIRECTIONS[side];
206 | return isConnected(world, x, y, z, dir, block, meta);
207 | }
208 |
209 | /**
210 | * A simple check for if the given block can connect to the given direction on the given side.
211 | *
212 | * @param world
213 | * @param x
214 | * The x coordinate of the block to check against. This is not the position of your block.
215 | * @param y
216 | * The y coordinate of the block to check against. This is not the position of your block.
217 | * @param z
218 | * The z coordinate of the block to check against. This is not the position of your block.
219 | * @param dir
220 | * The {@link ForgeDirection side} of the block to check for connection status. This is not the direction to check in.
221 | * @param block
222 | * The block to check against for connection.
223 | * @param meta
224 | * The metadata to check against for connection.
225 | * @return True if the given block can connect to the given location on the given side.
226 | */
227 | public boolean isConnected(IBlockAccess world, int x, int y, int z, ForgeDirection dir, Block block, int meta) {
228 |
229 | if (CTMLib.chiselLoaded() && connectionBlocked(world, x, y, z, dir.ordinal())) {
230 | return false;
231 | }
232 |
233 | int x2 = x + dir.offsetX;
234 | int y2 = y + dir.offsetY;
235 | int z2 = z + dir.offsetZ;
236 |
237 | boolean disableObscured = disableObscuredFaceCheck.or(disableObscuredFaceCheckConfig);
238 |
239 | Block con = getBlockOrFacade(world, x, y, z, dir.ordinal());
240 | Block obscuring = disableObscured ? null : getBlockOrFacade(world, x2, y2, z2, dir.ordinal());
241 |
242 | // no block or a bad API user
243 | if (con == null) {
244 | return false;
245 | }
246 |
247 | boolean ret = con.equals(block) && getBlockOrFacadeMetadata(world, x, y, z, dir.ordinal()) == meta;
248 |
249 | // no block obscuring this face
250 | if (obscuring == null) {
251 | return ret;
252 | }
253 |
254 | // check that we aren't already connected outwards from this side
255 | ret &= !(obscuring.equals(block) && getBlockOrFacadeMetadata(world, x2, y2, z2, dir.ordinal()) == meta);
256 |
257 | return ret;
258 | }
259 |
260 | private boolean connectionBlocked(IBlockAccess world, int x, int y, int z, int side) {
261 | Block block = world.getBlock(x, y, z);
262 | if (block instanceof IConnectable) {
263 | return !((IConnectable) block).canConnectCTM(world, x, y, z, side);
264 | }
265 | return false;
266 | }
267 |
268 | /**
269 | * A utility method to access metadata from both the world and {@link IFacade} blocks in the world.
270 | *
271 | * @param world
272 | * @param x
273 | * @param y
274 | * @param z
275 | * @param side
276 | * The side to check for. -1 for unknown.
277 | * @return The metadata at this position in the world, or inside an {@link IFacade} block.
278 | */
279 | public int getBlockOrFacadeMetadata(IBlockAccess world, int x, int y, int z, int side) {
280 | Block blk = world.getBlock(x, y, z);
281 | if (CTMLib.chiselLoaded()) {
282 | return getFacadeMeta(blk, world, x, y, z, side);
283 | }
284 | return world.getBlockMetadata(x, y, z);
285 | }
286 |
287 | private int getFacadeMeta(Block blk, IBlockAccess world, int x, int y, int z, int side) {
288 | if (blk instanceof IFacade) {
289 | return ((IFacade) blk).getFacadeMetadata(world, x, y, z, side);
290 | }
291 | return world.getBlockMetadata(x, y, z);
292 | }
293 |
294 | /**
295 | * A utility method to access a block from both the world and {@link IFacade} blocks in the world.
296 | *
297 | * @param world
298 | * @param x
299 | * @param y
300 | * @param z
301 | * @param side
302 | * The side to check for. -1 for unknown.
303 | * @return The block at this position in the world, or inside an {@link IFacade} block.
304 | */
305 | public Block getBlockOrFacade(IBlockAccess world, int x, int y, int z, int side) {
306 | Block blk = world.getBlock(x, y, z);
307 | if (CTMLib.chiselLoaded()) {
308 | return getFacade(blk, world, x, y, z, side);
309 | }
310 | return blk;
311 | }
312 |
313 | private Block getFacade(Block blk, IBlockAccess world, int x, int y, int z, int side) {
314 | if (blk instanceof IFacade) {
315 | blk = ((IFacade) blk).getFacade(world, x, y, z, side);
316 | }
317 | return blk;
318 | }
319 | }
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/CTMBlock.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import lombok.Getter;
4 | import net.minecraft.block.Block;
5 | import net.minecraft.block.material.Material;
6 | import net.minecraft.client.renderer.texture.IIconRegister;
7 | import net.minecraft.util.IIcon;
8 | import net.minecraft.world.IBlockAccess;
9 | import cpw.mods.fml.relauncher.Side;
10 | import cpw.mods.fml.relauncher.SideOnly;
11 |
12 | /**
13 | * Convenience implementation of ICMBlock. This does everything you should need for basic CTM.
14 | *
15 | * This class does not handle metadata-dependent textures.
16 | */
17 | public class CTMBlock extends Block implements ICTMBlock {
18 |
19 | @SideOnly(Side.CLIENT)
20 | private SubmapManagerCTM manager;
21 |
22 | @Getter
23 | private final String modid;
24 | @Getter
25 | private final String texturePath;
26 |
27 | private final int renderId;
28 |
29 | public CTMBlock(String modid, String texturePath, int renderId) {
30 | super(Material.rock);
31 | this.modid = modid;
32 | this.texturePath = texturePath;
33 | this.renderId = renderId;
34 | }
35 |
36 | @Override
37 | @SideOnly(Side.CLIENT)
38 | public void registerBlockIcons(IIconRegister icon) {
39 | manager = new SubmapManagerCTM(texturePath);
40 | manager.registerIcons(modid, this, icon);
41 | }
42 |
43 | @Override
44 | @SideOnly(Side.CLIENT)
45 | public IIcon getIcon(IBlockAccess world, int x, int y, int z, int side) {
46 | return manager.getIcon(world, x, y, z, side);
47 | }
48 |
49 | @Override
50 | @SideOnly(Side.CLIENT)
51 | public IIcon getIcon(int side, int meta) {
52 | return manager.getIcon(side, meta);
53 | }
54 |
55 | @Override
56 | @SideOnly(Side.CLIENT)
57 | public int getRenderType() {
58 | return renderId;
59 | }
60 |
61 | @Override
62 | public ISubmapManager getManager(IBlockAccess world, int x, int y, int z, int meta) {
63 | return manager;
64 | }
65 |
66 | @Override
67 | public ISubmapManager getManager(int meta) {
68 | return manager;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/CTMLib.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import cpw.mods.fml.common.ModAPIManager;
4 |
5 | public class CTMLib {
6 |
7 | public static final String VERSION = "@VERSION@";
8 |
9 | private static boolean chiselPresent, chiselInitialized;
10 |
11 | public static boolean chiselLoaded() {
12 | if (!chiselInitialized) {
13 | chiselInitialized = true;
14 | chiselPresent = ModAPIManager.INSTANCE.hasAPI("ChiselAPI");
15 | }
16 | return chiselPresent;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/CTMRenderer.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import net.minecraft.block.Block;
4 | import net.minecraft.client.Minecraft;
5 | import net.minecraft.client.renderer.RenderBlocks;
6 | import net.minecraft.crash.CrashReport;
7 | import net.minecraft.crash.CrashReportCategory;
8 | import net.minecraft.util.ReportedException;
9 | import net.minecraft.world.IBlockAccess;
10 |
11 | import org.lwjgl.opengl.GL11;
12 |
13 | import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
14 | import cpw.mods.fml.common.registry.GameRegistry;
15 |
16 | /**
17 | * A convenience base class for CTM blocks. Any block using this must implement {@link ICTMBlock}.
18 | */
19 | public class CTMRenderer implements ISimpleBlockRenderingHandler {
20 |
21 | private int renderId;
22 |
23 | public CTMRenderer(int renderId) {
24 | this.renderId = renderId;
25 | }
26 |
27 | @Override
28 | public void renderInventoryBlock(Block block, int metadata, int modelID, RenderBlocks renderer) {
29 | GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
30 | RenderBlocks rb = getContext(renderer, block, Minecraft.getMinecraft().theWorld, ((ICTMBlock>)block).getManager(metadata), metadata);
31 | Drawing.drawBlock(block, metadata, rb);
32 | GL11.glTranslatef(0.5F, 0.5F, 0.5F);
33 | rb.unlockBlockBounds();
34 | }
35 |
36 | @Override
37 | public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z, Block block, int modelId, RenderBlocks rendererOld) {
38 | try {
39 | int meta = world.getBlockMetadata(x, y, z);
40 | RenderBlocks rb = getContext(rendererOld, block, world, ((ICTMBlock>) block).getManager(world, x, y, z, meta), meta);
41 | boolean ret = rb.renderStandardBlock(block, x, y, z);
42 | rb.unlockBlockBounds();
43 | return ret;
44 | } catch (Throwable t) {
45 | CrashReport crashreport = CrashReport.makeCrashReport(t, "Rendering CTM Block");
46 | CrashReportCategory crashreportcategory = crashreport.makeCategory("Block being rendered");
47 | crashreportcategory.addCrashSection("Block name", GameRegistry.findUniqueIdentifierFor(block));
48 | crashreportcategory.addCrashSection("Block metadata", world.getBlockMetadata(x, y, z));
49 | throw new ReportedException(crashreport);
50 | }
51 | }
52 |
53 | protected RenderBlocks getContext(RenderBlocks rendererOld, Block block, IBlockAccess world, ISubmapManager manager, int meta) {
54 | if (!rendererOld.hasOverrideBlockTexture() && manager != null) {
55 | RenderBlocks rb = manager.createRenderContext(rendererOld, block, world);
56 | if (rb != null && rb != rendererOld) {
57 | rb.blockAccess = world;
58 | if (rendererOld.lockBlockBounds) {
59 | rb.overrideBlockBounds(rendererOld.renderMinX, rendererOld.renderMinY, rendererOld.renderMinZ, rendererOld.renderMaxX, rendererOld.renderMaxY, rendererOld.renderMaxZ);
60 | }
61 | if (rb instanceof RenderBlocksCTM) {
62 | RenderBlocksCTM rbctm = (RenderBlocksCTM) rb;
63 | rbctm.manager = rbctm.manager == null ? manager : rbctm.manager;
64 | rbctm.rendererOld = rbctm.rendererOld == null ? rendererOld : rbctm.rendererOld;
65 | }
66 | return rb;
67 | }
68 | }
69 | return rendererOld;
70 | }
71 |
72 | @Override
73 | public boolean shouldRender3DInInventory(int renderId) {
74 | return true;
75 | }
76 |
77 | @Override
78 | public int getRenderId() {
79 | return renderId;
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/Dir.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import net.minecraft.block.Block;
4 | import net.minecraft.world.IBlockAccess;
5 | import net.minecraftforge.common.util.ForgeDirection;
6 |
7 | import static net.minecraftforge.common.util.ForgeDirection.*;
8 |
9 | /**
10 | * Think of this class as a "Two dimensional ForgeDirection, with diagonals".
11 | *
12 | * 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.
13 | *
14 | * 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
15 | * for inner corner rendering.
16 | */
17 | public enum Dir {
18 | // @formatter:off
19 | TOP(UP),
20 | TOP_RIGHT(UP, EAST),
21 | RIGHT(EAST),
22 | BOTTOM_RIGHT(DOWN, EAST),
23 | BOTTOM(DOWN),
24 | BOTTOM_LEFT(DOWN, WEST),
25 | LEFT(WEST),
26 | TOP_LEFT(UP, WEST);
27 | // @formatter:on
28 |
29 | /**
30 | * All values of this enum, used to prevent unnecessary allocation via {@link #values()}.
31 | */
32 | public static final Dir[] VALUES = values();
33 | private static final ForgeDirection NORMAL = SOUTH;
34 |
35 | private ForgeDirection[] dirs;
36 |
37 | private Dir(ForgeDirection... dirs) {
38 | this.dirs = dirs;
39 | }
40 |
41 | /**
42 | * Finds if this block is connected for the given side in this Dir.
43 | *
44 | * @param inst
45 | * The CTM instance to use for logic.
46 | * @param world
47 | * The world the block is in.
48 | * @param x
49 | * The x coordinate of your block.
50 | * @param y
51 | * The y coordinate of your block.
52 | * @param z
53 | * The z coordinate of your block.
54 | * @param sideIdx
55 | * The side index of the current face. This equivalent to {@link ForgeDirection#ordinal()}
56 | * @param block
57 | * The block being rendered.
58 | * @param meta
59 | * The metadata of the block.
60 | * @return True if the block is connected in the given Dir, false otherwise.
61 | */
62 | public boolean isConnected(CTM inst, IBlockAccess world, int x, int y, int z, int sideIdx, Block block, int meta) {
63 | ForgeDirection side = getOrientation(sideIdx);
64 | ForgeDirection[] dirs = getNormalizedDirs(side);
65 | for (ForgeDirection dir : dirs) {
66 | x += dir.offsetX;
67 | y += dir.offsetY;
68 | z += dir.offsetZ;
69 | }
70 | return inst.isConnected(world, x, y, z, side, block, meta);
71 | }
72 |
73 | private ForgeDirection[] getNormalizedDirs(ForgeDirection normal) {
74 | if (normal == NORMAL) {
75 | return dirs;
76 | } else if (normal == NORMAL.getOpposite()) {
77 | // If this is the opposite direction of the default normal, we
78 | // need to mirror the dirs
79 | // A mirror version does not affect y+ and y- so we ignore those
80 | ForgeDirection[] ret = new ForgeDirection[dirs.length];
81 | for (int i = 0; i < ret.length; i++) {
82 | ret[i] = dirs[i].offsetY != 0 ? dirs[i] : dirs[i].getOpposite();
83 | }
84 | return ret;
85 | } else {
86 | ForgeDirection axis = null;
87 | // Next, we need different a different rotation axis depending
88 | // on if this is up/down or not
89 | if (normal.offsetY == 0) {
90 | // If it is not up/down, pick either the left or right-hand
91 | // rotation
92 | axis = normal == NORMAL.getRotation(UP) ? UP : DOWN;
93 | } else {
94 | // If it is up/down, pick either the up or down rotation.
95 | axis = normal == UP ? NORMAL.getRotation(DOWN) : NORMAL.getRotation(UP);
96 | }
97 | ForgeDirection[] ret = new ForgeDirection[dirs.length];
98 | // Finally apply all the rotations
99 | for (int i = 0; i < ret.length; i++) {
100 | ret[i] = dirs[i].getRotation(axis);
101 | }
102 | return ret;
103 | }
104 | }
105 | }
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/Drawing.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import net.minecraft.block.Block;
4 | import net.minecraft.client.renderer.RenderBlocks;
5 | import net.minecraft.client.renderer.Tessellator;
6 | import net.minecraft.util.IIcon;
7 |
8 | public class Drawing {
9 |
10 | public static void drawBlock(Block block, IIcon icon, RenderBlocks renderer) {
11 | Tessellator tessellator = Tessellator.instance;
12 |
13 | tessellator.startDrawingQuads();
14 | tessellator.setNormal(0.0F, -1.0F, 0.0F);
15 | renderer.renderFaceYNeg(block, 0.0D, 0.0D, 0.0D, icon);
16 | tessellator.draw();
17 | tessellator.startDrawingQuads();
18 | tessellator.setNormal(0.0F, 1.0F, 0.0F);
19 | renderer.renderFaceYPos(block, 0.0D, 0.0D, 0.0D, icon);
20 | tessellator.draw();
21 | tessellator.startDrawingQuads();
22 | tessellator.setNormal(0.0F, 0.0F, -1.0F);
23 | renderer.renderFaceZNeg(block, 0.0D, 0.0D, 0.0D, icon);
24 | tessellator.draw();
25 | tessellator.startDrawingQuads();
26 | tessellator.setNormal(0.0F, 0.0F, 1.0F);
27 | renderer.renderFaceZPos(block, 0.0D, 0.0D, 0.0D, icon);
28 | tessellator.draw();
29 | tessellator.startDrawingQuads();
30 | tessellator.setNormal(-1.0F, 0.0F, 0.0F);
31 | renderer.renderFaceXNeg(block, 0.0D, 0.0D, 0.0D, icon);
32 | tessellator.draw();
33 | tessellator.startDrawingQuads();
34 | tessellator.setNormal(1.0F, 0.0F, 0.0F);
35 | renderer.renderFaceXPos(block, 0.0D, 0.0D, 0.0D, icon);
36 | tessellator.draw();
37 | }
38 |
39 | public static void drawBlock(Block block, int meta, RenderBlocks renderer) {
40 | Tessellator tessellator = Tessellator.instance;
41 |
42 | tessellator.startDrawingQuads();
43 | tessellator.setNormal(0.0F, -1.0F, 0.0F);
44 | renderer.renderFaceYNeg(block, 0.0D, 0.0D, 0.0D, block.getIcon(0, meta));
45 | tessellator.draw();
46 | tessellator.startDrawingQuads();
47 | tessellator.setNormal(0.0F, 1.0F, 0.0F);
48 | renderer.renderFaceYPos(block, 0.0D, 0.0D, 0.0D, block.getIcon(1, meta));
49 | tessellator.draw();
50 | tessellator.startDrawingQuads();
51 | tessellator.setNormal(0.0F, 0.0F, -1.0F);
52 | renderer.renderFaceZNeg(block, 0.0D, 0.0D, 0.0D, block.getIcon(2, meta));
53 | tessellator.draw();
54 | tessellator.startDrawingQuads();
55 | tessellator.setNormal(0.0F, 0.0F, 1.0F);
56 | renderer.renderFaceZPos(block, 0.0D, 0.0D, 0.0D, block.getIcon(3, meta));
57 | tessellator.draw();
58 | tessellator.startDrawingQuads();
59 | tessellator.setNormal(-1.0F, 0.0F, 0.0F);
60 | renderer.renderFaceXNeg(block, 0.0D, 0.0D, 0.0D, block.getIcon(4, meta));
61 | tessellator.draw();
62 | tessellator.startDrawingQuads();
63 | tessellator.setNormal(1.0F, 0.0F, 0.0F);
64 | renderer.renderFaceXPos(block, 0.0D, 0.0D, 0.0D, block.getIcon(5, meta));
65 | tessellator.draw();
66 | }
67 |
68 | public static void renderAllFaces(RenderBlocks renderer, Block block, int x, int y, int z, IIcon icon) {
69 | renderer.renderFaceYNeg(block, x, y, z, icon);
70 | renderer.renderFaceYPos(block, x, y, z, icon);
71 | renderer.renderFaceZNeg(block, x, y, z, icon);
72 | renderer.renderFaceZPos(block, x, y, z, icon);
73 | renderer.renderFaceXNeg(block, x, y, z, icon);
74 | renderer.renderFaceXPos(block, x, y, z, icon);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/ICTMBlock.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import net.minecraft.world.IBlockAccess;
4 |
5 | public interface ICTMBlock {
6 |
7 | /**
8 | * Gets the {@link ISubmapManager} for this block in the world.
9 | */
10 | T getManager(IBlockAccess world, int x, int y, int z, int meta);
11 |
12 | /**
13 | * Gets the {@link ISubmapManager} for this block in item form.
14 | *
15 | * @param meta
16 | * The meta (damage) of the stack.
17 | */
18 | T getManager(int meta);
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/ISubmap.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import net.minecraft.util.IIcon;
4 |
5 | public interface ISubmap {
6 |
7 | /**
8 | * The width, in icons, of the submap.
9 | */
10 | int getWidth();
11 |
12 | /**
13 | * The height, in icons, of the submap.
14 | */
15 | int getHeight();
16 |
17 | IIcon getSubIcon(int x, int y);
18 |
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/ISubmapManager.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import cpw.mods.fml.relauncher.Side;
4 | import cpw.mods.fml.relauncher.SideOnly;
5 | import net.minecraft.block.Block;
6 | import net.minecraft.client.renderer.RenderBlocks;
7 | import net.minecraft.client.renderer.texture.IIconRegister;
8 | import net.minecraft.util.IIcon;
9 | import net.minecraft.world.IBlockAccess;
10 | import net.minecraftforge.common.util.ForgeDirection;
11 |
12 | public interface ISubmapManager {
13 |
14 | /**
15 | * Gets the icon for item rendering, based on side and item metadata.
16 | *
17 | * @param side
18 | * Side of the ItemBlock.
19 | * @param meta
20 | * Metadata of the ItemBlock.
21 | * @return The IIcon for rendering.
22 | */
23 | IIcon getIcon(int side, int meta);
24 |
25 | /**
26 | * Gets the icon for world rendering, based on world position and side.
27 | */
28 | IIcon getIcon(IBlockAccess world, int x, int y, int z, int side);
29 |
30 | /**
31 | * Hook for registering any custom icons you might need in this class. Will be called during the normal block method.
32 | *
33 | * @param modName
34 | * The mod ID this submap was registered to.
35 | * @param block
36 | * The block being registered for.
37 | * @param register
38 | * The IIconRegister instance.
39 | */
40 | @SideOnly(Side.CLIENT)
41 | void registerIcons(String modName, Block block, IIconRegister register);
42 |
43 | @SideOnly(Side.CLIENT)
44 | RenderBlocks createRenderContext(RenderBlocks rendererOld, Block block, IBlockAccess world);
45 |
46 | @SideOnly(Side.CLIENT)
47 | void preRenderSide(RenderBlocks renderer, IBlockAccess world, int x, int y, int z, ForgeDirection side);
48 |
49 | @SideOnly(Side.CLIENT)
50 | void postRenderSide(RenderBlocks renderer, IBlockAccess world, int x, int y, int z, ForgeDirection side);
51 | }
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/RenderBlocksCTM.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import static team.chisel.ctmlib.RenderBlocksCTM.SubSide.*;
4 | import static team.chisel.ctmlib.RenderBlocksCTM.Vert.*;
5 |
6 | import lombok.Getter;
7 | import net.minecraft.block.Block;
8 | import net.minecraft.client.Minecraft;
9 | import net.minecraft.client.renderer.RenderBlocks;
10 | import net.minecraft.client.renderer.Tessellator;
11 | import net.minecraft.util.IIcon;
12 | import net.minecraftforge.common.util.ForgeDirection;
13 |
14 | public class RenderBlocksCTM extends RenderBlocks {
15 |
16 | /**
17 | * An enum for all possible 26 sub-side vertices.
18 | *
19 | * The naming scheme is as follows:
20 | *
21 | * 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
22 | * means that those axes are at 0.5. X, Y, Z, or any combination after a HALF means those axes are at 1.
23 | */
24 | protected enum Vert {
25 | // @formatter:off
26 | ZERO(0, 0, 0),
27 | XYZ(1, 1, 1),
28 | X(1, 0, 0),
29 | Y(0, 1, 0),
30 | Z(0, 0, 1),
31 | XY(1, 1, 0),
32 | YZ(0, 1, 1),
33 | XZ(1, 0, 1),
34 | X_HALF(0.5, 0, 0),
35 | Y_HALF(0, 0.5, 0),
36 | Z_HALF(0, 0, 0.5),
37 | XY_HALF(0.5, 0.5, 0),
38 | YZ_HALF(0, 0.5, 0.5),
39 | XZ_HALF(0.5, 0, 0.5),
40 | X_HALF_Y(0.5, 1, 0),
41 | X_HALF_Z(0.5, 0, 1),
42 | Y_HALF_X(1, 0.5, 0),
43 | Y_HALF_Z(0, 0.5, 1),
44 | Z_HALF_X(1, 0, 0.5),
45 | Z_HALF_Y(0, 1, 0.5),
46 | X_HALF_YZ(0.5, 1, 1),
47 | Y_HALF_XZ(1, 0.5, 1),
48 | Z_HALF_XY(1, 1, 0.5),
49 | XY_HALF_Z(0.5, 0.5, 1),
50 | YZ_HALF_X(1, 0.5, 0.5),
51 | XZ_HALF_Y(0.5, 1, 0.5);
52 | // @formatter:on
53 |
54 | private double x, y, z;
55 |
56 | private Vert(double x, double y, double z) {
57 | this.x = x;
58 | this.y = y;
59 | this.z = z;
60 | }
61 |
62 | private static double u, v, xDiff, yDiff, zDiff, uDiff, vDiff;
63 |
64 | void render(RenderBlocksCTM inst, ForgeDirection normal, int cacheID) {
65 | if (inst.enableAO) {
66 | inst.tessellator.setColorOpaque_F(inst.redCache[cacheID], inst.grnCache[cacheID], inst.bluCache[cacheID]);
67 | inst.tessellator.setBrightness(inst.lightingCache[cacheID]);
68 | }
69 |
70 | u = cacheID == 1 || cacheID == 2 ? inst.maxU : inst.minU;
71 | v = cacheID < 2 ? inst.maxV : inst.minV;
72 |
73 | uDiff = inst.maxU - inst.minU;
74 | vDiff = inst.maxV - inst.minV;
75 |
76 | if (inst.renderMinX + inst.renderMinY + inst.renderMinZ != 0 || inst.renderMaxX + inst.renderMaxY + inst.renderMaxZ != 3) {
77 | boolean uMin = u == inst.minU;
78 | boolean vMin = v == inst.minV;
79 |
80 | xDiff = inst.renderMaxX - inst.renderMinX;
81 | yDiff = inst.renderMaxY - inst.renderMinY;
82 | zDiff = inst.renderMaxZ - inst.renderMinZ;
83 |
84 | if (normal.offsetY != 0) {
85 | uDiff *= uMin ? inst.renderMinX : 1 - inst.renderMaxX;
86 | vDiff *= vMin ? inst.renderMinZ : 1 - inst.renderMaxZ;
87 | } else if (normal.offsetX != 0) {
88 | uDiff *= uMin ? inst.renderMinZ : 1 - inst.renderMaxZ;
89 | vDiff *= vMin ? inst.renderMinY : 1 - inst.renderMaxY;
90 | } else if (normal.offsetZ != 0) {
91 | uDiff *= uMin ? inst.renderMinX : 1 - inst.renderMaxX;
92 | vDiff *= vMin ? inst.renderMinY : 1 - inst.renderMaxY;
93 | }
94 | u = u == inst.minU ? inst.minU + uDiff : inst.maxU - uDiff;
95 |
96 | v = v == inst.minV ? inst.minV + vDiff : inst.maxV - vDiff;
97 | } else {
98 | xDiff = yDiff = zDiff = 1;
99 | }
100 |
101 | inst.tessellator.addVertexWithUV(inst.renderMinX + (x * xDiff), inst.renderMinY + (y * yDiff), inst.renderMinZ + (z * zDiff), u, v);
102 | }
103 | }
104 |
105 | /**
106 | * Each side is divided into 4 sub-sides. LB(left bottom), RB(right bottom), LT(right top), and RT(right top).
107 | *
108 | * Each sub-side contains 4 {@link Vert} objects representing its position on the block.
109 | */
110 | protected enum SubSide {
111 | // @formatter:off
112 | 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),
113 | 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),
114 |
115 | 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),
116 | 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),
117 |
118 | 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),
119 | 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);
120 | // @formatter:on
121 | private Vert xmin, xmax, ymin, ymax;
122 | private ForgeDirection normal;
123 |
124 | SubSide(Vert xmin, Vert ymin, Vert ymax, Vert xmax) {
125 | this.xmin = xmin;
126 | this.ymin = ymin;
127 | this.ymax = ymax;
128 | this.xmax = xmax;
129 | this.normal = calcNormal();
130 | }
131 |
132 | private ForgeDirection calcNormal() {
133 | double xTot = xmin.x + xmax.x + ymin.x + ymax.x;
134 | double yTot = xmin.y + xmax.y + ymin.y + ymax.y;
135 | double zTot = xmin.z + xmax.z + ymin.z + ymax.z;
136 | if (xTot % 4 == 0) {
137 | return xTot > 0 ? ForgeDirection.EAST : ForgeDirection.WEST;
138 | } else if (yTot % 4 == 0) {
139 | return yTot > 0 ? ForgeDirection.UP : ForgeDirection.DOWN;
140 | } else if (zTot % 4 == 0) {
141 | return zTot > 0 ? ForgeDirection.SOUTH : ForgeDirection.NORTH;
142 | }
143 | return ForgeDirection.UNKNOWN;
144 | }
145 |
146 | void render(RenderBlocksCTM inst) {
147 | xmin.render(inst, normal, 0);
148 | ymin.render(inst, normal, 1);
149 | ymax.render(inst, normal, 2);
150 | xmax.render(inst, normal, 3);
151 | }
152 | }
153 |
154 | public CTM ctm = CTM.getInstance();
155 |
156 | // globals added to save the JVM some trouble. No need to constantly create
157 | // and destroy ints if we don't have to
158 | protected int blockLightBitChannel = 0;
159 | protected int redBitChannel = 0;
160 | protected int greenBitChannel = 0;
161 | protected int blueBitChannel = 0;
162 | protected int sunlightBitChannel = 0;
163 |
164 | public RenderBlocksCTM() {
165 | super();
166 | renderMaxX = renderMaxY = renderMaxZ = 1;
167 | }
168 |
169 | protected Tessellator tessellator = Tessellator.instance;
170 | protected double minU, maxU;
171 | protected double minV, maxV;
172 | protected int[] lightingCache = new int[4];
173 | protected float[] redCache = new float[4];
174 | protected float[] grnCache = new float[4];
175 | protected float[] bluCache = new float[4];
176 | public TextureSubmap submap;
177 | public TextureSubmap submapSmall;
178 | public RenderBlocks rendererOld;
179 | public ISubmapManager manager;
180 |
181 | protected int[][] lightmap = new int[3][3];
182 | protected float[][] redmap = new float[3][3];
183 | protected float[][] grnmap = new float[3][3];
184 | protected float[][] blumap = new float[3][3];
185 |
186 | protected int bx, by, bz, meta;
187 |
188 | @Getter
189 | protected boolean inWorld = false;
190 |
191 | @Override
192 | public boolean renderStandardBlock(Block block, int x, int y, int z) {
193 | bx = x;
194 | by = y;
195 | bz = z;
196 | meta = Minecraft.getMinecraft().theWorld.getBlockMetadata(x, y, z);
197 |
198 | tessellator.setColorOpaque_F(1.0F, 1.0F, 1.0F);
199 | tessellator.addTranslation(x, y, z);
200 | if (rendererOld != null && rendererOld.hasOverrideBlockTexture()) {
201 | setOverrideBlockTexture(rendererOld.overrideBlockTexture);
202 | }
203 | inWorld = true;
204 | boolean res = super.renderStandardBlock(block, x, y, z);
205 | inWorld = false;
206 | tessellator.addTranslation(-x, -y, -z);
207 |
208 | return res;
209 | }
210 |
211 | /* @formatter:off
212 | * This method fills a 3x3 grid of light values based on the four corners, by averaging them together.
213 | *
214 | * 2 TL x TR
215 | *
216 | * 1 x x x
217 | *
218 | * 0 BL x BR
219 | * 0 1 2
220 | *
221 | * Note: Variable names mean nothing... don't touch
222 | *
223 | * ._.
224 | *
225 | * shakes fist in anger
226 | */
227 | // @formatter:on
228 | protected void fillLightmap(int bottomLeft, int bottomRight, int topRight, int topLeft) {
229 | lightmap[0][0] = bottomLeft;
230 | lightmap[2][0] = bottomRight;
231 | lightmap[2][2] = topRight;
232 | lightmap[0][2] = topLeft;
233 |
234 | lightmap[1][0] = avg(bottomLeft, bottomRight);
235 | lightmap[2][1] = avg(bottomRight, topRight);
236 | lightmap[1][2] = avg(topRight, topLeft);
237 | lightmap[0][1] = avg(topLeft, bottomLeft);
238 |
239 | lightmap[1][1] = avg(bottomLeft, bottomRight, topRight, topLeft);
240 | }
241 |
242 | protected void fillColormap(float bottomLeft, float bottomRight, float topRight, float topLeft, float[][] map) {
243 | map[0][0] = bottomLeft;
244 | map[2][0] = bottomRight;
245 | map[2][2] = topRight;
246 | map[0][2] = topLeft;
247 |
248 | map[1][0] = (bottomLeft + bottomRight) / 2.0F;
249 | map[2][1] = (bottomRight + topRight) / 2.0F;
250 | map[1][2] = (topRight + topLeft) / 2.0F;
251 | map[0][1] = (topLeft + bottomLeft) / 2.0F;
252 |
253 | map[1][1] = (bottomLeft + bottomRight + topRight + topLeft) / 4.0F;
254 | }
255 |
256 | protected void getLight(int x, int y) {
257 | lightingCache[0] = lightmap[0 + x][0 + y];
258 | lightingCache[1] = lightmap[1 + x][0 + y];
259 | lightingCache[2] = lightmap[1 + x][1 + y];
260 | lightingCache[3] = lightmap[0 + x][1 + y];
261 |
262 | redCache[0] = redmap[0 + x][0 + y];
263 | redCache[1] = redmap[1 + x][0 + y];
264 | redCache[2] = redmap[1 + x][1 + y];
265 | redCache[3] = redmap[0 + x][1 + y];
266 |
267 | grnCache[0] = grnmap[0 + x][0 + y];
268 | grnCache[1] = grnmap[1 + x][0 + y];
269 | grnCache[2] = grnmap[1 + x][1 + y];
270 | grnCache[3] = grnmap[0 + x][1 + y];
271 |
272 | bluCache[0] = blumap[0 + x][0 + y];
273 | bluCache[1] = blumap[1 + x][0 + y];
274 | bluCache[2] = blumap[1 + x][1 + y];
275 | bluCache[3] = blumap[0 + x][1 + y];
276 | }
277 |
278 | /**
279 | * This works around a bug in CLC atm
280 | */
281 | private int avg(int... lightVals) {
282 | blockLightBitChannel = 0;
283 | redBitChannel = 0;
284 | greenBitChannel = 0;
285 | blueBitChannel = 0;
286 | sunlightBitChannel = 0;
287 |
288 | for (int light : lightVals) {
289 | blockLightBitChannel += (light & 0xFF);
290 | redBitChannel += (light & 0xF00);
291 | greenBitChannel += (light & 0xF000);
292 | blueBitChannel += (light & 0xF0000);
293 | sunlightBitChannel += (light & 0xF00000);
294 | }
295 |
296 | return ((blockLightBitChannel / lightVals.length) & 0xFF) | ((redBitChannel / lightVals.length) & 0xF00) | ((greenBitChannel / lightVals.length) & 0xF000)
297 | | ((blueBitChannel / lightVals.length) & 0xF0000) | ((sunlightBitChannel / lightVals.length) & 0xF00000);
298 | }
299 |
300 | protected void side(Block block, SubSide side, int iconIndex) {
301 |
302 | IIcon icon;
303 | TextureSubmap map;
304 | if (iconIndex >= 16) {
305 | iconIndex -= 16;
306 | map = submapSmall;
307 | } else {
308 | map = submap;
309 | }
310 |
311 | if (map == null) {
312 | icon = getBlockIconFromSideAndMetadata(block, side.normal.ordinal(), meta);
313 | } else {
314 | int x = iconIndex % map.getWidth();
315 | int y = iconIndex / map.getHeight();
316 | icon = map.getSubIcon(x, y);
317 | }
318 |
319 | double umax = icon.getMaxU();
320 | double umin = icon.getMinU();
321 | double vmax = icon.getMaxV();
322 | double vmin = icon.getMinV();
323 |
324 | minU = umin;
325 | maxU = umax;
326 | minV = vmin;
327 | maxV = vmax;
328 |
329 | // uCache[0] = umin;
330 | // uCache[1] = umax;
331 | // uCache[2] = umax;
332 | // uCache[3] = umin;
333 | //
334 | // vCache[0] = vmax;
335 | // vCache[1] = vmax;
336 | // vCache[2] = vmin;
337 | // vCache[3] = vmin;
338 |
339 | side.render(this);
340 | }
341 |
342 | @Override
343 | public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
344 | pre(ForgeDirection.WEST);
345 | if (!inWorld || hasOverrideBlockTexture() || submap == null) {
346 | super.renderFaceXNeg(block, 0, 0, 0, icon);
347 | } else {
348 | int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 4);
349 |
350 | fillLightmap(brightnessBottomRight, brightnessTopRight, brightnessTopLeft, brightnessBottomLeft);
351 | fillColormap(colorRedBottomRight, colorRedTopRight, colorRedTopLeft, colorRedBottomLeft, redmap);
352 | fillColormap(colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, colorGreenBottomLeft, grnmap);
353 | fillColormap(colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, colorBlueBottomLeft, blumap);
354 |
355 | getLight(0, 0);
356 | side(block, XNEG_LB, tex[0]);
357 | getLight(1, 0);
358 | side(block, XNEG_RB, tex[1]);
359 | getLight(1, 1);
360 | side(block, XNEG_RT, tex[2]);
361 | getLight(0, 1);
362 | side(block, XNEG_LT, tex[3]);
363 | }
364 | post(ForgeDirection.WEST);
365 | }
366 |
367 | @Override
368 | public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
369 | pre(ForgeDirection.EAST);
370 | if (!inWorld || hasOverrideBlockTexture() || submap == null) {
371 | super.renderFaceXPos(block, 0, 0, 0, icon);
372 | } else {
373 | int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 5);
374 |
375 | fillLightmap(brightnessTopLeft, brightnessBottomLeft, brightnessBottomRight, brightnessTopRight);
376 | fillColormap(colorRedTopLeft, colorRedBottomLeft, colorRedBottomRight, colorRedTopRight, redmap);
377 | fillColormap(colorGreenTopLeft, colorGreenBottomLeft, colorGreenBottomRight, colorGreenTopRight, grnmap);
378 | fillColormap(colorBlueTopLeft, colorBlueBottomLeft, colorBlueBottomRight, colorBlueTopRight, blumap);
379 |
380 | getLight(0, 0);
381 | side(block, XPOS_LB, tex[0]);
382 | getLight(1, 0);
383 | side(block, XPOS_RB, tex[1]);
384 | getLight(1, 1);
385 | side(block, XPOS_RT, tex[2]);
386 | getLight(0, 1);
387 | side(block, XPOS_LT, tex[3]);
388 | }
389 | post(ForgeDirection.EAST);
390 | }
391 |
392 | @Override
393 | public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
394 | pre(ForgeDirection.NORTH);
395 | if (!inWorld || hasOverrideBlockTexture() || submap == null) {
396 | super.renderFaceZNeg(block, 0, 0, 0, icon);
397 | } else {
398 | int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 2);
399 |
400 | fillLightmap(brightnessBottomRight, brightnessTopRight, brightnessTopLeft, brightnessBottomLeft);
401 | fillColormap(colorRedBottomRight, colorRedTopRight, colorRedTopLeft, colorRedBottomLeft, redmap);
402 | fillColormap(colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, colorGreenBottomLeft, grnmap);
403 | fillColormap(colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, colorBlueBottomLeft, blumap);
404 |
405 | getLight(0, 0);
406 | side(block, ZNEG_LB, tex[0]);
407 | getLight(1, 0);
408 | side(block, ZNEG_RB, tex[1]);
409 | getLight(1, 1);
410 | side(block, ZNEG_RT, tex[2]);
411 | getLight(0, 1);
412 | side(block, ZNEG_LT, tex[3]);
413 | }
414 | post(ForgeDirection.NORTH);
415 | }
416 |
417 | @Override
418 | public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
419 | pre(ForgeDirection.SOUTH);
420 | if (!inWorld || hasOverrideBlockTexture() || submap == null) {
421 | super.renderFaceZPos(block, 0, 0, 0, icon);
422 | } else {
423 | int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 3);
424 |
425 |
426 | fillLightmap(brightnessBottomLeft, brightnessBottomRight, brightnessTopRight, brightnessTopLeft);
427 | fillColormap(colorRedBottomLeft, colorRedBottomRight, colorRedTopRight, colorRedTopLeft, redmap);
428 | fillColormap(colorGreenBottomLeft, colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, grnmap);
429 | fillColormap(colorBlueBottomLeft, colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, blumap);
430 |
431 | getLight(0, 0);
432 | side(block, ZPOS_LB, tex[0]);
433 | getLight(1, 0);
434 | side(block, ZPOS_RB, tex[1]);
435 | getLight(1, 1);
436 | side(block, ZPOS_RT, tex[2]);
437 | getLight(0, 1);
438 | side(block, ZPOS_LT, tex[3]);
439 | }
440 | post(ForgeDirection.SOUTH);
441 | }
442 |
443 | @Override
444 | public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
445 | pre(ForgeDirection.DOWN);
446 | if (!inWorld || hasOverrideBlockTexture() || submap == null) {
447 | super.renderFaceYNeg(block, 0, 0, 0, icon);
448 | } else {
449 | int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 0);
450 |
451 | fillLightmap(brightnessBottomLeft, brightnessBottomRight, brightnessTopRight, brightnessTopLeft);
452 | fillColormap(colorRedBottomLeft, colorRedBottomRight, colorRedTopRight, colorRedTopLeft, redmap);
453 | fillColormap(colorGreenBottomLeft, colorGreenBottomRight, colorGreenTopRight, colorGreenTopLeft, grnmap);
454 | fillColormap(colorBlueBottomLeft, colorBlueBottomRight, colorBlueTopRight, colorBlueTopLeft, blumap);
455 |
456 | getLight(0, 0);
457 | side(block, YNEG_LB, tex[0]);
458 | getLight(1, 0);
459 | side(block, YNEG_RB, tex[1]);
460 | getLight(1, 1);
461 | side(block, YNEG_RT, tex[2]);
462 | getLight(0, 1);
463 | side(block, YNEG_LT, tex[3]);
464 | }
465 | post(ForgeDirection.DOWN);
466 | }
467 |
468 | @Override
469 | public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
470 | pre(ForgeDirection.UP);
471 | if (!inWorld || hasOverrideBlockTexture() || submap == null) {
472 | super.renderFaceYPos(block, 0, 0, 0, icon);
473 | } else {
474 | int tex[] = ctm.getSubmapIndices(blockAccess, bx, by, bz, 1);
475 |
476 | fillLightmap(brightnessTopRight, brightnessTopLeft, brightnessBottomLeft, brightnessBottomRight);
477 | fillColormap(colorRedTopRight, colorRedTopLeft, colorRedBottomLeft, colorRedBottomRight, redmap);
478 | fillColormap(colorGreenTopRight, colorGreenTopLeft, colorGreenBottomLeft, colorGreenBottomRight, grnmap);
479 | fillColormap(colorBlueTopRight, colorBlueTopLeft, colorBlueBottomLeft, colorBlueBottomRight, blumap);
480 |
481 | getLight(0, 0);
482 | side(block, YPOS_LB, tex[0]);
483 | getLight(1, 0);
484 | side(block, YPOS_RB, tex[1]);
485 | getLight(1, 1);
486 | side(block, YPOS_RT, tex[2]);
487 | getLight(0, 1);
488 | side(block, YPOS_LT, tex[3]);
489 | }
490 | post(ForgeDirection.UP);
491 | }
492 |
493 | protected void pre(ForgeDirection face) {
494 | manager.preRenderSide(this, blockAccess, bx, by, bz, face);
495 | }
496 |
497 | protected void post(ForgeDirection face) {
498 | manager.postRenderSide(this, blockAccess, bx, by, bz, face);
499 | }
500 | }
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/RenderBlocksColumn.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import net.minecraft.block.Block;
4 | import net.minecraft.client.renderer.RenderBlocks;
5 | import net.minecraft.util.IIcon;
6 | import net.minecraft.world.IBlockAccess;
7 |
8 | /**
9 | * Used by {@link TextureType#CTMV}
10 | */
11 | public class RenderBlocksColumn extends RenderBlocks {
12 |
13 | public TextureSubmap submap;
14 | public IIcon iconTop;
15 | private boolean inWorld;
16 |
17 | private IIcon sides[] = new IIcon[6];
18 | private CTM ctm = CTM.getInstance();
19 |
20 | public RenderBlocksColumn() {
21 | super();
22 | }
23 |
24 | boolean connected(IBlockAccess world, int x, int y, int z, Block block, int meta) {
25 | Block inWorld = ctm.getBlockOrFacade(world, x, y, z, -1);
26 | return inWorld != null && inWorld.equals(block) && ctm.getBlockOrFacadeMetadata(world, x, y, z, -1) == meta;
27 | }
28 |
29 | @Override
30 | public boolean renderStandardBlock(Block block, int x, int y, int z) {
31 | int metadata = blockAccess.getBlockMetadata(x, y, z);
32 | inWorld = true;
33 |
34 | boolean yp = connected(blockAccess, x, y + 1, z, block, metadata);
35 | boolean yn = connected(blockAccess, x, y - 1, z, block, metadata);
36 |
37 | if (yp || yn) {
38 | sides[0] = iconTop;
39 | sides[1] = iconTop;
40 |
41 | if (yp && yn)
42 | sides[2] = submap.getSubIcon(0, 1);
43 | else if (yp)
44 | sides[2] = submap.getSubIcon(1, 1);
45 | else
46 | sides[2] = submap.getSubIcon(1, 0);
47 |
48 | sides[3] = sides[4] = sides[5] = sides[2];
49 | } else {
50 | boolean xp = connected(blockAccess, x + 1, y, z, block, metadata);
51 | boolean xn = connected(blockAccess, x - 1, y, z, block, metadata);
52 |
53 | if (xp && (connected(blockAccess, x + 1, y + 1, z, block, metadata) || connected(blockAccess, x + 1, y - 1, z, block, metadata)))
54 | xp = false;
55 | if (xn && (connected(blockAccess, x - 1, y + 1, z, block, metadata) || connected(blockAccess, x - 1, y - 1, z, block, metadata)))
56 | xn = false;
57 |
58 | if (xp || xn) {
59 | uvRotateEast = 2;
60 | uvRotateWest = 1;
61 | uvRotateTop = 1;
62 | uvRotateBottom = 1;
63 |
64 | sides[4] = iconTop;
65 | sides[5] = iconTop;
66 |
67 | // for some reason along the x axis the bottom texture on the ends is backwards. This flips it around.
68 | if (xp && xn) {
69 | sides[1] = submap.getSubIcon(0, 1);
70 | sides[0] = sides[1];
71 | } else if (xp) {
72 | sides[1] = submap.getSubIcon(1, 1);
73 | sides[0] = submap.getSubIcon(1, 0);
74 | } else {
75 | sides[1] = submap.getSubIcon(1, 0);
76 | sides[0] = submap.getSubIcon(1, 1);
77 | }
78 | sides[2] = sides[3] = sides[1];
79 | } else {
80 | boolean zp = connected(blockAccess, x, y, z + 1, block, metadata);
81 | boolean zn = connected(blockAccess, x, y, z - 1, block, metadata);
82 |
83 | if (zp && (connected(blockAccess, x, y + 1, z + 1, block, metadata) || connected(blockAccess, x, y - 1, z + 1, block, metadata)))
84 | zp = false;
85 | if (zp && (connected(blockAccess, x + 1, y, z + 1, block, metadata) || connected(blockAccess, x - 1, y, z + 1, block, metadata)))
86 | zp = false;
87 | if (zn && (connected(blockAccess, x, y + 1, z - 1, block, metadata) || connected(blockAccess, x, y - 1, z - 1, block, metadata)))
88 | zn = false;
89 | if (zn && (connected(blockAccess, x + 1, y, z - 1, block, metadata) || connected(blockAccess, x - 1, y, z - 1, block, metadata)))
90 | zn = false;
91 |
92 | if (zp || zn) {
93 | uvRotateSouth = 1;
94 | uvRotateNorth = 2;
95 |
96 | sides[2] = iconTop;
97 | sides[3] = iconTop;
98 |
99 | if (zp && zn)
100 | sides[0] = submap.getSubIcon(0, 1);
101 | else if (zp)
102 | sides[0] = submap.getSubIcon(1, 0);
103 | else
104 | sides[0] = submap.getSubIcon(1, 1);
105 |
106 | sides[1] = sides[4] = sides[5] = sides[0];
107 | } else {
108 | sides[0] = sides[1] = iconTop;
109 | sides[2] = sides[3] = sides[4] = sides[5] = submap.getSubIcon(0, 0);
110 | }
111 | }
112 | }
113 |
114 | boolean flag = super.renderStandardBlock(block, x, y, z);
115 |
116 | uvRotateSouth = 0;
117 | uvRotateEast = 0;
118 | uvRotateWest = 0;
119 | uvRotateNorth = 0;
120 | uvRotateTop = 0;
121 | uvRotateBottom = 0;
122 |
123 | inWorld = false;
124 | return flag;
125 | }
126 |
127 | @Override
128 | public void renderFaceXNeg(Block block, double x, double y, double z, IIcon icon) {
129 | super.renderFaceXNeg(block, x, y, z, getIcon(icon, 4));
130 | }
131 |
132 | @Override
133 | public void renderFaceXPos(Block block, double x, double y, double z, IIcon icon) {
134 | super.renderFaceXPos(block, x, y, z, getIcon(icon, 5));
135 | }
136 |
137 | @Override
138 | public void renderFaceZNeg(Block block, double x, double y, double z, IIcon icon) {
139 | super.renderFaceZNeg(block, x, y, z, getIcon(icon, 2));
140 | }
141 |
142 | @Override
143 | public void renderFaceZPos(Block block, double x, double y, double z, IIcon icon) {
144 | super.renderFaceZPos(block, x, y, z, getIcon(icon, 3));
145 | }
146 |
147 | @Override
148 | public void renderFaceYNeg(Block block, double x, double y, double z, IIcon icon) {
149 | super.renderFaceYNeg(block, x, y, z, getIcon(icon, 0));
150 | }
151 |
152 | @Override
153 | public void renderFaceYPos(Block block, double x, double y, double z, IIcon icon) {
154 | super.renderFaceYPos(block, x, y, z, getIcon(icon, 1));
155 | }
156 |
157 | private IIcon getIcon(IIcon defaultIcon, int side) {
158 | return sides[side] == null || !inWorld ? defaultIcon : sides[side];
159 | }
160 | }
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/SubmapManagerCTM.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import lombok.Getter;
4 | import net.minecraft.block.Block;
5 | import net.minecraft.client.renderer.RenderBlocks;
6 | import net.minecraft.client.renderer.texture.IIconRegister;
7 | import net.minecraft.util.IIcon;
8 | import net.minecraft.world.IBlockAccess;
9 | import net.minecraftforge.common.util.ForgeDirection;
10 | import cpw.mods.fml.relauncher.Side;
11 | import cpw.mods.fml.relauncher.SideOnly;
12 |
13 | /**
14 | * A convenience implementation of {@link ISubmapManager} which does the standard CTM behavior.
15 | */
16 | public class SubmapManagerCTM implements ISubmapManager {
17 |
18 | @SideOnly(Side.CLIENT)
19 | private static RenderBlocksCTM rb;
20 |
21 | @Getter
22 | private TextureSubmap submap, submapSmall;
23 | @Getter
24 | private final String textureName;
25 |
26 | /**
27 | * @param textureName
28 | * The path to the texture, excluding the mod ID (this is passed into {@link #registerIcons(String, Block, IIconRegister)}).
29 | *
30 | * The CTM texture will be this path, plus {@code "-ctm"}.
31 | */
32 | public SubmapManagerCTM(String textureName) {
33 | this.textureName = textureName;
34 | }
35 |
36 | @Override
37 | public IIcon getIcon(int side, int meta) {
38 | return submapSmall.getBaseIcon();
39 | }
40 |
41 | @Override
42 | public IIcon getIcon(IBlockAccess world, int x, int y, int z, int side) {
43 | return getIcon(side, world.getBlockMetadata(x, y, z));
44 | }
45 |
46 | @Override
47 | @SideOnly(Side.CLIENT)
48 | public void registerIcons(String modName, Block block, IIconRegister register) {
49 | submap = new TextureSubmap(register.registerIcon(modName + ":" + textureName + "-ctm"), 4, 4);
50 | submapSmall = new TextureSubmap(register.registerIcon(modName + ":" + textureName), 2, 2);
51 | }
52 |
53 | @Override
54 | @SideOnly(Side.CLIENT)
55 | public RenderBlocks createRenderContext(RenderBlocks rendererOld, Block block, IBlockAccess world) {
56 | if (rb == null) {
57 | rb = new RenderBlocksCTM();
58 | }
59 | rb.setRenderBoundsFromBlock(block);
60 | rb.submap = submap;
61 | rb.submapSmall = submapSmall;
62 | return rb;
63 | }
64 |
65 | @Override
66 | public void preRenderSide(RenderBlocks renderer, IBlockAccess world, int x, int y, int z, ForgeDirection side) {
67 | }
68 |
69 | @Override
70 | public void postRenderSide(RenderBlocks renderer, IBlockAccess world, int x, int y, int z, ForgeDirection side) {
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/main/java/team/chisel/ctmlib/TextureSubmap.java:
--------------------------------------------------------------------------------
1 | package team.chisel.ctmlib;
2 |
3 | import java.util.List;
4 |
5 | import org.apache.commons.lang3.ArrayUtils;
6 |
7 | import lombok.experimental.Delegate;
8 | import net.minecraft.util.IIcon;
9 | import net.minecraftforge.client.event.TextureStitchEvent;
10 | import net.minecraftforge.common.MinecraftForge;
11 |
12 | import com.google.common.collect.Lists;
13 |
14 | import cpw.mods.fml.common.eventhandler.SubscribeEvent;
15 | import cpw.mods.fml.relauncher.Side;
16 | import cpw.mods.fml.relauncher.SideOnly;
17 |
18 | /**
19 | * This class is used to split up a large IIcon into smaller "submapped" icons used for CTM and other texture manipulation.
20 | */
21 | public class TextureSubmap implements IIcon, ISubmap {
22 |
23 | private static List submaps = Lists.newArrayList();
24 | private static TextureSubmap dummy = new TextureSubmap(null, 0, 0);
25 | static {
26 | MinecraftForge.EVENT_BUS.register(dummy);
27 | }
28 |
29 | private int width, height;
30 | @Delegate
31 | private IIcon baseIcon;
32 |
33 | protected IIcon[][] icons;
34 |
35 | /**
36 | * Construct a new submap. A submap is not required to be square, but it is required to be rectangular.
37 | *
38 | * @param baseIcon
39 | * The IIcon to submap.
40 | * @param width
41 | * The width of the map, in icons.
42 | * @param height
43 | * The height of the map, in icons.
44 | */
45 | public TextureSubmap(IIcon baseIcon, int width, int height) {
46 | this.baseIcon = baseIcon;
47 | this.width = width;
48 | this.height = height;
49 | this.icons = new IIcon[width][height];
50 | submaps.add(this);
51 | }
52 |
53 | /**
54 | * The large "base" icon used for the submap.
55 | */
56 | public IIcon getBaseIcon() {
57 | return baseIcon;
58 | }
59 |
60 | /**
61 | * Gets all the icons in this submap. This is an expensive operation as it first clones the entire 2D array.
62 | *
63 | * @return All icons in this submap.
64 | */
65 | public IIcon[][] getAllIcons() {
66 | IIcon[][] ret = ArrayUtils.clone(icons);
67 | for (int i = 0; i < ret.length; i++) {
68 | ret[i] = ArrayUtils.clone(ret[i]);
69 | }
70 | return ret;
71 | }
72 |
73 | /* ==== ISubmap ==== */
74 |
75 | /**
76 | * 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.
77 | */
78 | @Override
79 | public IIcon getSubIcon(int x, int y) {
80 | x = x % icons.length;
81 | return icons[x][y % icons[x].length];
82 | }
83 |
84 | @Override
85 | public int getWidth() {
86 | return width;
87 | }
88 |
89 | @Override
90 | public int getHeight() {
91 | return height;
92 | }
93 |
94 | /* ==== Internal Stitching Logic ==== */
95 |
96 | /**
97 | * For internal use only, this is used to create the virtual "subicons" used in the map.
98 | */
99 | @SubscribeEvent
100 | public final void TexturesStitched(TextureStitchEvent.Post event) {
101 | for (TextureSubmap ts : submaps) {
102 | ts.texturesStitched();
103 | }
104 | }
105 |
106 | public void texturesStitched() {
107 | for (int x = 0; x < width; x++) {
108 | for (int y = 0; y < height; y++) {
109 | icons[x][y] = new TextureVirtual(getBaseIcon(), width, height, x, y);
110 | }
111 | }
112 | }
113 |
114 | private class TextureVirtual implements IIcon {
115 |
116 | private int width, height;
117 | private float umin, umax, vmin, vmax;
118 | private String name;
119 | private IIcon parentIcon;
120 |
121 | private TextureVirtual(IIcon parent, int w, int h, int x, int y) {
122 | parentIcon = parent;
123 |
124 | umin = parentIcon.getInterpolatedU(16.0 * (x) / w);
125 | umax = parentIcon.getInterpolatedU(16.0 * (x + 1) / w);
126 | vmin = parentIcon.getInterpolatedV(16.0 * (y) / h);
127 | vmax = parentIcon.getInterpolatedV(16.0 * (y + 1) / h);
128 |
129 | name = parentIcon.getIconName() + "|" + x + "." + y;
130 |
131 | width = parentIcon.getIconWidth();
132 | height = parentIcon.getIconHeight();
133 | }
134 |
135 | @Override
136 | @SideOnly(Side.CLIENT)
137 | public float getMinU() {
138 | return umin;
139 | }
140 |
141 | @Override
142 | @SideOnly(Side.CLIENT)
143 | public float getMaxU() {
144 | return umax;
145 | }
146 |
147 | @Override
148 | @SideOnly(Side.CLIENT)
149 | public float getInterpolatedU(double d0) {
150 | return (float) (umin + (umax - umin) * d0 / 16.0);
151 | }
152 |
153 | @Override
154 | @SideOnly(Side.CLIENT)
155 | public float getMinV() {
156 | return vmin;
157 | }
158 |
159 | @Override
160 | @SideOnly(Side.CLIENT)
161 | public float getMaxV() {
162 | return vmax;
163 | }
164 |
165 | @Override
166 | @SideOnly(Side.CLIENT)
167 | public float getInterpolatedV(double d0) {
168 | return (float) (vmin + (vmax - vmin) * d0 / 16.0);
169 | }
170 |
171 | @Override
172 | @SideOnly(Side.CLIENT)
173 | public String getIconName() {
174 | return name;
175 | }
176 |
177 | @Override
178 | @SideOnly(Side.CLIENT)
179 | public int getIconWidth() {
180 | return width;
181 | }
182 |
183 | @Override
184 | @SideOnly(Side.CLIENT)
185 | public int getIconHeight() {
186 | return height;
187 | }
188 | }
189 | }
190 |
--------------------------------------------------------------------------------