pipes: improve render performance

This commit is contained in:
Player 2013-10-27 12:14:14 +01:00
parent bdf93e12ba
commit 36da365ccb
3 changed files with 85 additions and 145 deletions

View file

@ -81,7 +81,7 @@ public class BlockGenericPipe extends BlockContainer {
private static final ForgeDirection[] DIR_VALUES = ForgeDirection.values(); private static final ForgeDirection[] DIR_VALUES = ForgeDirection.values();
private static Random rand = new Random(); private static Random rand = new Random();
private boolean skippedFirstIconRegister; private boolean skippedFirstIconRegister;
private boolean[] renderSide = new boolean[6]; private int renderMask = 0;
/* Defined subprograms ************************************************* */ /* Defined subprograms ************************************************* */
public BlockGenericPipe(int i) { public BlockGenericPipe(int i) {
@ -114,34 +114,26 @@ public class BlockGenericPipe extends BlockContainer {
return false; return false;
} }
public void setRenderAxis(char axis) { public void setRenderMask(int mask) {
Arrays.fill(renderSide, false); renderMask = mask;
if (axis == 'x') {
renderSide[4] = true;
renderSide[5] = true;
}
if (axis == 'y') {
renderSide[0] = true;
renderSide[1] = true;
}
if (axis == 'z') {
renderSide[2] = true;
renderSide[3] = true;
}
} }
public final void setRenderAllSides() { public final void setRenderAllSides() {
Arrays.fill(renderSide, true); renderMask = 0x3f;
} }
public void setRenderSide(ForgeDirection side, boolean render) { public void setRenderSide(ForgeDirection side, boolean render) {
renderSide[side.ordinal()] = render; if (render) {
renderMask |= 1 << side.ordinal();
} else {
renderMask &= ~(1 << side.ordinal());
}
} }
@Override @Override
@SideOnly(Side.CLIENT) @SideOnly(Side.CLIENT)
public boolean shouldSideBeRendered(IBlockAccess blockAccess, int x, int y, int z, int side) { public boolean shouldSideBeRendered(IBlockAccess blockAccess, int x, int y, int z, int side) {
return renderSide[side]; return (renderMask & (1 << side)) != 0;
} }
@Override @Override
@ -1100,27 +1092,27 @@ public class BlockGenericPipe extends BlockContainer {
double pz = z + rand.nextDouble() * (block.getBlockBoundsMaxZ() - block.getBlockBoundsMinZ() - (b * 2.0F)) + b + block.getBlockBoundsMinZ(); double pz = z + rand.nextDouble() * (block.getBlockBoundsMaxZ() - block.getBlockBoundsMinZ() - (b * 2.0F)) + b + block.getBlockBoundsMinZ();
if (sideHit == 0) { if (sideHit == 0) {
py = (double) y + block.getBlockBoundsMinY() - (double) b; py = y + block.getBlockBoundsMinY() - b;
} }
if (sideHit == 1) { if (sideHit == 1) {
py = (double) y + block.getBlockBoundsMaxY() + (double) b; py = y + block.getBlockBoundsMaxY() + b;
} }
if (sideHit == 2) { if (sideHit == 2) {
pz = (double) z + block.getBlockBoundsMinZ() - (double) b; pz = z + block.getBlockBoundsMinZ() - b;
} }
if (sideHit == 3) { if (sideHit == 3) {
pz = (double) z + block.getBlockBoundsMaxZ() + (double) b; pz = z + block.getBlockBoundsMaxZ() + b;
} }
if (sideHit == 4) { if (sideHit == 4) {
px = (double) x + block.getBlockBoundsMinX() - (double) b; px = x + block.getBlockBoundsMinX() - b;
} }
if (sideHit == 5) { if (sideHit == 5) {
px = (double) x + block.getBlockBoundsMaxX() + (double) b; px = x + block.getBlockBoundsMaxX() + b;
} }
EntityDiggingFX fx = new EntityDiggingFX(worldObj, px, py, pz, 0.0D, 0.0D, 0.0D, block, sideHit, worldObj.getBlockMetadata(x, y, z)); EntityDiggingFX fx = new EntityDiggingFX(worldObj, px, py, pz, 0.0D, 0.0D, 0.0D, block, sideHit, worldObj.getBlockMetadata(x, y, z));
@ -1156,9 +1148,9 @@ public class BlockGenericPipe extends BlockContainer {
for (int i = 0; i < its; ++i) { for (int i = 0; i < its; ++i) {
for (int j = 0; j < its; ++j) { for (int j = 0; j < its; ++j) {
for (int k = 0; k < its; ++k) { for (int k = 0; k < its; ++k) {
double px = x + (i + 0.5D) / (double) its; double px = x + (i + 0.5D) / its;
double py = y + (j + 0.5D) / (double) its; double py = y + (j + 0.5D) / its;
double pz = z + (k + 0.5D) / (double) its; double pz = z + (k + 0.5D) / its;
int random = rand.nextInt(6); int random = rand.nextInt(6);
EntityDiggingFX fx = new EntityDiggingFX(worldObj, px, py, pz, px - x - 0.5D, py - y - 0.5D, pz - z - 0.5D, BuildCraftTransport.genericPipeBlock, random, meta); EntityDiggingFX fx = new EntityDiggingFX(worldObj, px, py, pz, px - x - 0.5D, py - y - 0.5D, pz - z - 0.5D, BuildCraftTransport.genericPipeBlock, random, meta);
fx.setParticleIcon(icon); fx.setParticleIcon(icon);

View file

@ -7,142 +7,82 @@ import buildcraft.transport.BlockGenericPipe;
import buildcraft.transport.IPipeRenderState; import buildcraft.transport.IPipeRenderState;
import buildcraft.transport.PipeIconProvider; import buildcraft.transport.PipeIconProvider;
import buildcraft.transport.PipeRenderState; import buildcraft.transport.PipeRenderState;
import buildcraft.transport.TransportConstants;
import buildcraft.transport.TransportProxy; import buildcraft.transport.TransportProxy;
import buildcraft.core.utils.MatrixTranformations; import buildcraft.core.utils.MatrixTranformations;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler; import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.client.renderer.RenderBlocks; import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Icon;
import net.minecraft.world.IBlockAccess; import net.minecraft.world.IBlockAccess;
import net.minecraftforge.common.ForgeDirection; import net.minecraftforge.common.ForgeDirection;
public class PipeRendererWorld implements ISimpleBlockRenderingHandler { public class PipeRendererWorld implements ISimpleBlockRenderingHandler {
private void renderAllFaceExeptAxe(RenderBlocks renderblocks, BlockGenericPipe block, Icon icon, int x, int y, int z, char axe) {
float minX = (float) renderblocks.renderMinX;
float minY = (float) renderblocks.renderMinY;
float minZ = (float) renderblocks.renderMinZ;
float maxX = (float) renderblocks.renderMaxX;
float maxY = (float) renderblocks.renderMaxY;
float maxZ = (float) renderblocks.renderMaxZ;
if (axe != 'x') {
renderTwoWayXFace(renderblocks, block, icon, x, y, z, minY, minZ, maxY, maxZ, minX);
renderTwoWayXFace(renderblocks, block, icon, x, y, z, minY, minZ, maxY, maxZ, maxX);
}
if (axe != 'y') {
renderTwoWayYFace(renderblocks, block, icon, x, y, z, minX, minZ, maxX, maxZ, minY);
renderTwoWayYFace(renderblocks, block, icon, x, y, z, minX, minZ, maxX, maxZ, maxY);
}
if (axe != 'z') {
renderTwoWayZFace(renderblocks, block, icon, x, y, z, minX, minY, maxX, maxY, minZ);
renderTwoWayZFace(renderblocks, block, icon, x, y, z, minX, minY, maxX, maxY, maxZ);
}
}
private void renderTwoWayXFace(RenderBlocks renderblocks, BlockGenericPipe block, Icon icon, int xCoord, int yCoord, int zCoord, float minY, float minZ, float maxY, float maxZ, float x) {
renderblocks.setRenderBounds(x, minY, minZ, x, maxY, maxZ);
block.setRenderAxis('x');
renderblocks.renderStandardBlock(block, xCoord, yCoord, zCoord);
block.setRenderAxis('a');
}
private void renderTwoWayYFace(RenderBlocks renderblocks, BlockGenericPipe block, Icon icon, int xCoord, int yCoord, int zCoord, float minX, float minZ, float maxX, float maxZ, float y) {
renderblocks.setRenderBounds(minX, y, minZ, maxX, y, maxZ);
block.setRenderAxis('y');
renderblocks.renderStandardBlock(block, xCoord, yCoord, zCoord);
block.setRenderAxis('a');
}
private void renderTwoWayZFace(RenderBlocks renderblocks, BlockGenericPipe block, Icon icon, int xCoord, int yCoord, int zCoord, float minX, float minY, float maxX, float maxY, float z) {
renderblocks.setRenderBounds(minX, minY, z, maxX, maxY, z);
block.setRenderAxis('z');
renderblocks.renderStandardBlock(block, xCoord, yCoord, zCoord);
block.setRenderAxis('a');
}
public void renderPipe(RenderBlocks renderblocks, IBlockAccess iblockaccess, BlockGenericPipe block, IPipeRenderState renderState, int x, int y, int z) { public void renderPipe(RenderBlocks renderblocks, IBlockAccess iblockaccess, BlockGenericPipe block, IPipeRenderState renderState, int x, int y, int z) {
float minSize = CoreConstants.PIPE_MIN_POS;
float maxSize = CoreConstants.PIPE_MAX_POS;
PipeRenderState state = renderState.getRenderState(); PipeRenderState state = renderState.getRenderState();
IIconProvider icons = renderState.getPipeIcons(); IIconProvider icons = renderState.getPipeIcons();
if (icons == null) if (icons == null)
return; return;
boolean west = false; int connectivity = state.pipeConnectionMatrix.getMask();
boolean east = false; float[] dim = new float[6];
boolean down = false;
boolean up = false;
boolean north = false;
boolean south = false;
if (state.pipeConnectionMatrix.isConnected(ForgeDirection.WEST)) { // render the unconnected pipe faces of the center block (if any)
state.currentTexture = icons.getIcon(state.textureMatrix.getTextureIndex(ForgeDirection.WEST));
renderblocks.setRenderBounds(0.0F, minSize, minSize, minSize, maxSize, maxSize); if (connectivity != 0x3f) { // note: 0x3f = 0x111111 = all sides
renderAllFaceExeptAxe(renderblocks, block, state.currentTexture, x, y, z, 'x'); resetToCenterDimensions(dim);
west = true;
state.currentTexture = icons.getIcon(state.textureMatrix.getTextureIndex(ForgeDirection.UNKNOWN));
renderTwoWayBlock(renderblocks, block, x, y, z, dim, connectivity ^ 0x3f);
} }
if (state.pipeConnectionMatrix.isConnected(ForgeDirection.EAST)) { // render the connecting pipe faces
state.currentTexture = icons.getIcon(state.textureMatrix.getTextureIndex(ForgeDirection.EAST));
renderblocks.setRenderBounds(maxSize, minSize, minSize, 1.0F, maxSize, maxSize);
renderAllFaceExeptAxe(renderblocks, block, state.currentTexture, x, y, z, 'x');
east = true;
}
if (state.pipeConnectionMatrix.isConnected(ForgeDirection.DOWN)) { for (int dir = 0; dir < 6; dir++) {
state.currentTexture = icons.getIcon(state.textureMatrix.getTextureIndex(ForgeDirection.DOWN)); int mask = 1 << dir;
renderblocks.setRenderBounds(minSize, 0.0F, minSize, maxSize, minSize, maxSize); if ((connectivity & mask) == 0) continue; // no connection towards dir
renderAllFaceExeptAxe(renderblocks, block, state.currentTexture, x, y, z, 'y');
down = true;
}
if (state.pipeConnectionMatrix.isConnected(ForgeDirection.UP)) { // center piece offsets
state.currentTexture = icons.getIcon(state.textureMatrix.getTextureIndex(ForgeDirection.UP)); resetToCenterDimensions(dim);
renderblocks.setRenderBounds(minSize, maxSize, minSize, maxSize, 1.0F, maxSize);
renderAllFaceExeptAxe(renderblocks, block, state.currentTexture, x, y, z, 'y');
up = true;
}
if (state.pipeConnectionMatrix.isConnected(ForgeDirection.NORTH)) { // extend block towards dir as it's connected to there
state.currentTexture = icons.getIcon(state.textureMatrix.getTextureIndex(ForgeDirection.NORTH)); dim[dir / 2] = dir % 2 == 0 ? 0 : CoreConstants.PIPE_MAX_POS;
renderblocks.setRenderBounds(minSize, minSize, 0.0F, maxSize, maxSize, minSize); dim[dir / 2 + 3] = dir % 2 == 0 ? CoreConstants.PIPE_MIN_POS : 1;
renderAllFaceExeptAxe(renderblocks, block, state.currentTexture, x, y, z, 'z');
north = true;
}
if (state.pipeConnectionMatrix.isConnected(ForgeDirection.SOUTH)) { // the mask points to all faces perpendicular to dir, i.e. dirs 0+1 -> mask 111100, 1+2 -> 110011, 3+5 -> 001111
state.currentTexture = icons.getIcon(state.textureMatrix.getTextureIndex(ForgeDirection.SOUTH)); int renderMask = (3 << (dir / 2 * 2)) ^ 0x3f;
renderblocks.setRenderBounds(minSize, minSize, maxSize, maxSize, maxSize, 1.0F);
renderAllFaceExeptAxe(renderblocks, block, state.currentTexture, x, y, z, 'z');
south = true;
}
state.currentTexture = icons.getIcon(state.textureMatrix.getTextureIndex(ForgeDirection.UNKNOWN)); // render sub block
renderblocks.setRenderBounds(minSize, minSize, minSize, maxSize, maxSize, maxSize); state.currentTexture = icons.getIcon(state.textureMatrix.getTextureIndex(ForgeDirection.VALID_DIRECTIONS[dir]));
if (!west)
renderTwoWayXFace(renderblocks, block, state.currentTexture, x, y, z, minSize, minSize, maxSize, maxSize, minSize); renderTwoWayBlock(renderblocks, block, x, y, z, dim, renderMask);
if (!east) }
renderTwoWayXFace(renderblocks, block, state.currentTexture, x, y, z, minSize, minSize, maxSize, maxSize, maxSize);
if (!down)
renderTwoWayYFace(renderblocks, block, state.currentTexture, x, y, z, minSize, minSize, maxSize, maxSize, minSize);
if (!up)
renderTwoWayYFace(renderblocks, block, state.currentTexture, x, y, z, minSize, minSize, maxSize, maxSize, maxSize);
if (!north)
renderTwoWayZFace(renderblocks, block, state.currentTexture, x, y, z, minSize, minSize, maxSize, maxSize, minSize);
if (!south)
renderTwoWayZFace(renderblocks, block, state.currentTexture, x, y, z, minSize, minSize, maxSize, maxSize, maxSize);
renderblocks.setRenderBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); renderblocks.setRenderBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
pipeFacadeRenderer(renderblocks, block, state, x, y, z); pipeFacadeRenderer(renderblocks, block, state, x, y, z);
pipePlugRenderer(renderblocks, block, state, x, y, z); pipePlugRenderer(renderblocks, block, state, x, y, z);
}
private void resetToCenterDimensions(float[] dim) {
for (int i = 0; i < 3; i++) dim[i] = CoreConstants.PIPE_MIN_POS;
for (int i = 3; i < 6; i++) dim[i] = CoreConstants.PIPE_MAX_POS;
}
/**
* Render a block with normal and inverted vertex order so back face culling doesn't have any effect.
*/
private void renderTwoWayBlock(RenderBlocks renderblocks, BlockGenericPipe block, int x, int y, int z, float[] dim, int mask) {
assert mask != 0;
block.setRenderMask(mask);
renderblocks.setRenderBounds(dim[2], dim[0], dim[1], dim[5], dim[3], dim[4]);
renderblocks.renderStandardBlock(block, x, y, z);
block.setRenderMask((mask & 0x15) << 1 | (mask & 0x2a) >> 1); // pairwise swapped mask
renderblocks.setRenderBounds(dim[5], dim[3], dim[4], dim[2], dim[0], dim[1]);
renderblocks.renderStandardBlock(block, x, y, z);
} }
private void pipeFacadeRenderer(RenderBlocks renderblocks, BlockGenericPipe block, PipeRenderState state, int x, int y, int z) { private void pipeFacadeRenderer(RenderBlocks renderblocks, BlockGenericPipe block, PipeRenderState state, int x, int y, int z) {

View file

@ -3,26 +3,34 @@ package buildcraft.transport.utils;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.BitSet;
import net.minecraftforge.common.ForgeDirection; import net.minecraftforge.common.ForgeDirection;
public class ConnectionMatrix { public class ConnectionMatrix {
private int mask = 0;
private BitSet _connected = new BitSet(ForgeDirection.VALID_DIRECTIONS.length);
private final BitSetCodec _bitSetCodec = new BitSetCodec();
private boolean dirty = false; private boolean dirty = false;
public boolean isConnected(ForgeDirection direction) { public boolean isConnected(ForgeDirection direction) {
return _connected.get(direction.ordinal()); // test if the direction.ordinal()'th bit of mask is set
return (mask & (1 << direction.ordinal())) != 0;
} }
public void setConnected(ForgeDirection direction, boolean value) { public void setConnected(ForgeDirection direction, boolean value) {
if (_connected.get(direction.ordinal()) != value) { if (isConnected(direction) != value) {
_connected.set(direction.ordinal(), value); // invert the direction.ordinal()'th bit of mask
mask ^= 1 << direction.ordinal();
dirty = true; dirty = true;
} }
} }
/**
* Return a mask representing the connectivity for all sides.
*
* @return mask in ForgeDirection order, least significant bit = first entry
*/
public int getMask() {
return mask;
}
public boolean isDirty() { public boolean isDirty() {
return dirty; return dirty;
} }
@ -32,14 +40,14 @@ public class ConnectionMatrix {
} }
public void writeData(DataOutputStream data) throws IOException { public void writeData(DataOutputStream data) throws IOException {
data.writeByte(_bitSetCodec.encode(_connected)); data.writeByte(mask);
} }
public void readData(DataInputStream data) throws IOException { public void readData(DataInputStream data) throws IOException {
BitSet connection = new BitSet(ForgeDirection.VALID_DIRECTIONS.length); byte newMask = data.readByte();
_bitSetCodec.decode(data.readByte(), connection);
if (!_connected.equals(connection)) { if (newMask != mask) {
_connected = connection; mask = newMask;
dirty = true; dirty = true;
} }
} }