package mekanism.client.render; import java.util.Arrays; import java.util.List; import java.util.Map; import mekanism.client.render.MekanismRenderer.Model3D; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.VertexBuffer; import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureMap; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.client.renderer.vertex.VertexFormat; import net.minecraft.client.renderer.vertex.VertexFormatElement; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumFacing.Axis; import net.minecraft.util.EnumFacing.AxisDirection; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3i; import net.minecraft.world.IBlockAccess; import org.lwjgl.opengl.GL11; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.collect.Maps; /* * Adapted from BuildCraft */ public class RenderResizableCuboid { protected RenderManager manager = Minecraft.getMinecraft().getRenderManager(); public static final Vec3d VEC_ONE = vec3(1); public static final Vec3d VEC_ZERO = vec3(0); public static final Vec3d VEC_HALF = vec3(0.5); public interface IBlockLocation { Vec3d transformToWorld(Vec3d vec); } public interface IFacingLocation { EnumFacing transformToWorld(EnumFacing face); } public enum DefaultFacingLocation implements IFacingLocation { INSTANCE; @Override public EnumFacing transformToWorld(EnumFacing face) { return face; } } public enum EnumShadeType { FACE(DefaultVertexFormats.COLOR_4UB), LIGHT(DefaultVertexFormats.TEX_2S), AMBIENT_OCCLUSION(DefaultVertexFormats.COLOR_4UB); private final VertexFormatElement element; private EnumShadeType(VertexFormatElement element) { this.element = element; } } public enum EnumShadeArgument { NONE, FACE(EnumShadeType.FACE), FACE_LIGHT(EnumShadeType.FACE, EnumShadeType.LIGHT), FACE_OCCLUDE(EnumShadeType.FACE, EnumShadeType.AMBIENT_OCCLUSION), FACE_LIGHT_OCCLUDE(EnumShadeType.FACE, EnumShadeType.LIGHT, EnumShadeType.AMBIENT_OCCLUSION), LIGHT(EnumShadeType.LIGHT), LIGHT_OCCLUDE(EnumShadeType.LIGHT, EnumShadeType.AMBIENT_OCCLUSION), OCCLUDE(EnumShadeType.AMBIENT_OCCLUSION); public final ImmutableSet<EnumShadeType> types; final VertexFormat vertexFormat; EnumShadeArgument(EnumShadeType... types) { this.vertexFormat = new VertexFormat(); vertexFormat.addElement(DefaultVertexFormats.POSITION_3F); vertexFormat.addElement(DefaultVertexFormats.TEX_2F); for (EnumShadeType type : types) { if (!vertexFormat.getElements().contains(type.element)) vertexFormat.addElement(type.element); } this.types = ImmutableSet.copyOf(types); } public boolean isEnabled(EnumShadeType type) { return types.contains(type); } } public static final RenderResizableCuboid INSTANCE = new RenderResizableCuboid(); /** The AO map assumes that each direction in the world has a different amount of light going towards it. */ private static final Map<EnumFacing, Vec3d> aoMap = Maps.newEnumMap(EnumFacing.class); private static final int U_MIN = 0; private static final int U_MAX = 1; private static final int V_MIN = 2; private static final int V_MAX = 3; static { // Static constants taken directly from minecraft's block renderer // ( net.minecraft.client.renderer.BlockModelRenderer.EnumNeighborInfo ) aoMap.put(EnumFacing.UP, vec3(1)); aoMap.put(EnumFacing.DOWN, vec3(0.5)); aoMap.put(EnumFacing.NORTH, vec3(0.8)); aoMap.put(EnumFacing.SOUTH, vec3(0.8)); aoMap.put(EnumFacing.EAST, vec3(0.6)); aoMap.put(EnumFacing.WEST, vec3(0.6)); } /** This will render a cuboid from its middle. */ public void renderCubeFromCentre(Model3D cuboid) { GlStateManager.pushMatrix(); GL11.glTranslated(-cuboid.sizeX() / 2d, -cuboid.sizeY() / 2d, -cuboid.sizeZ() / 2d); renderCube(cuboid, EnumShadeArgument.NONE, null, null, null); GlStateManager.popMatrix(); } public void renderCube(Model3D cuboid) { renderCube(cuboid, EnumShadeArgument.NONE, null, null, null); } public void renderCube(Model3D cube, EnumShadeArgument shadeTypes, IBlockLocation formula, IFacingLocation faceFormula, IBlockAccess world) { if (faceFormula == null) { faceFormula = DefaultFacingLocation.INSTANCE; } TextureAtlasSprite[] sprites = cube.textures; int[] flips = cube.textureFlips; if (flips == null) { flips = new int[6]; } Vec3d textureStart = new Vec3d(cube.textureStartX / 16D, cube.textureStartY / 16D, cube.textureStartZ / 16D); Vec3d textureSize = new Vec3d(cube.textureSizeX / 16D, cube.textureSizeY / 16D, cube.textureSizeZ / 16D); Vec3d textureOffset = new Vec3d(cube.textureOffsetX / 16D, cube.textureOffsetY / 16D, cube.textureOffsetZ / 16D); Vec3d size = new Vec3d(cube.sizeX(), cube.sizeY(), cube.sizeZ()); manager.renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); Tessellator tess = Tessellator.getInstance(); VertexBuffer wr = tess.getBuffer(); GlStateManager.enableAlpha(); GlStateManager.alphaFunc(GL11.GL_GREATER, 0.1F); GlStateManager.disableLighting(); wr.begin(GL11.GL_QUADS, shadeTypes.vertexFormat); for (EnumFacing face : EnumFacing.values()) { renderCuboidFace(wr, face, sprites, flips, textureStart, textureSize, size, textureOffset, shadeTypes, formula, faceFormula, world); } tess.draw(); GlStateManager.disableAlpha(); GlStateManager.enableLighting(); GlStateManager.enableFog(); } private void renderCuboidFace(VertexBuffer wr, EnumFacing face, TextureAtlasSprite[] sprites, int[] flips, Vec3d textureStart, Vec3d textureSize, Vec3d size, Vec3d textureOffset, EnumShadeArgument shadeTypes, IBlockLocation locationFormula, IFacingLocation faceFormula, IBlockAccess access) { int ordinal = face.ordinal(); if (sprites[ordinal] == null) { return; } Vec3d textureEnd = textureStart.add(textureSize); float[] uv = getUVArray(sprites[ordinal], flips[ordinal], face, textureStart, textureEnd); List<RenderInfo> renderInfoList = getRenderInfos(uv, face, size, textureSize, textureOffset); Axis u = face.getAxis() == Axis.X ? Axis.Z : Axis.X; Axis v = face.getAxis() == Axis.Y ? Axis.Z : Axis.Y; double other = face.getAxisDirection() == AxisDirection.POSITIVE ? getValue(size, face.getAxis()) : 0; /* Swap the face if this is positive: the renderer returns indexes that ALWAYS are for the negative face, so * light it properly this way */ face = face.getAxisDirection() == AxisDirection.NEGATIVE ? face : face.getOpposite(); EnumFacing opposite = face.getOpposite(); for (RenderInfo ri : renderInfoList) { renderPoint(wr, face, u, v, other, ri, true, false, locationFormula, faceFormula, access, shadeTypes); renderPoint(wr, face, u, v, other, ri, true, true, locationFormula, faceFormula, access, shadeTypes); renderPoint(wr, face, u, v, other, ri, false, true, locationFormula, faceFormula, access, shadeTypes); renderPoint(wr, face, u, v, other, ri, false, false, locationFormula, faceFormula, access, shadeTypes); renderPoint(wr, opposite, u, v, other, ri, false, false, locationFormula, faceFormula, access, shadeTypes); renderPoint(wr, opposite, u, v, other, ri, false, true, locationFormula, faceFormula, access, shadeTypes); renderPoint(wr, opposite, u, v, other, ri, true, true, locationFormula, faceFormula, access, shadeTypes); renderPoint(wr, opposite, u, v, other, ri, true, false, locationFormula, faceFormula, access, shadeTypes); } } public static Vec3d withValue(Vec3d vector, Axis axis, double value) { if (axis == Axis.X) return new Vec3d(value, vector.yCoord, vector.zCoord); else if (axis == Axis.Y) return new Vec3d(vector.xCoord, value, vector.zCoord); else if (axis == Axis.Z) return new Vec3d(vector.xCoord, vector.yCoord, value); else throw new RuntimeException("Was given a null axis! That was probably not intentional, consider this a bug! (Vector = " + vector + ")"); } private void renderPoint(VertexBuffer wr, EnumFacing face, Axis u, Axis v, double other, RenderInfo ri, boolean minU, boolean minV, IBlockLocation locationFormula, IFacingLocation faceFormula, IBlockAccess access, EnumShadeArgument shadeTypes) { int U_ARRAY = minU ? U_MIN : U_MAX; int V_ARRAY = minV ? V_MIN : V_MAX; Vec3d vertex = withValue(VEC_ZERO, u, ri.xyz[U_ARRAY]); vertex = withValue(vertex, v, ri.xyz[V_ARRAY]); vertex = withValue(vertex, face.getAxis(), other); wr.pos(vertex.xCoord, vertex.yCoord, vertex.zCoord); wr.tex(ri.uv[U_ARRAY], ri.uv[V_ARRAY]); if (shadeTypes.isEnabled(EnumShadeType.FACE)) { setWorldRendererRGB(wr, aoMap.get(faceFormula.transformToWorld(face))); } if (shadeTypes.isEnabled(EnumShadeType.AMBIENT_OCCLUSION)) { applyLocalAO(wr, faceFormula.transformToWorld(face), locationFormula, access, shadeTypes, vertex); } else if (shadeTypes.isEnabled(EnumShadeType.LIGHT)) { Vec3d transVertex = locationFormula.transformToWorld(vertex); BlockPos pos = convertFloor(transVertex); IBlockState block = access.getBlockState(pos); int combindedLight = block.getPackedLightmapCoords(access, pos); wr.lightmap(combindedLight >> 16 & 65535, combindedLight & 65535); } wr.endVertex(); } public static BlockPos convertFloor(Vec3d vec) { return new BlockPos(vec.xCoord, vec.yCoord, vec.zCoord); } public static Vec3d convert(Vec3i vec3i) { return new Vec3d(vec3i.getX(), vec3i.getY(), vec3i.getZ()); } public static Vec3d convert(EnumFacing face) { if (face == null) { return VEC_ZERO; } return new Vec3d(face.getFrontOffsetX(), face.getFrontOffsetY(), face.getFrontOffsetZ()); } public static EnumFacing[] getNeighbours(EnumFacing face) { EnumFacing[] faces = new EnumFacing[4]; int ordinal = 0; for (EnumFacing next : EnumFacing.values()) { if (next.getAxis() != face.getAxis()) { faces[ordinal] = next; ordinal++; } } return faces; } private void applyLocalAO(VertexBuffer wr, EnumFacing face, IBlockLocation locationFormula, IBlockAccess access, EnumShadeArgument shadeTypes, Vec3d vertex) { // This doesn't work. At all. boolean allAround = false; int numPositions = allAround ? 7 : 5; int[] skyLight = new int[numPositions]; int[] blockLight = new int[numPositions]; float[] colorMultiplier = new float[numPositions]; double[] distances = new double[numPositions]; double totalDist = 0; Vec3d transVertex = locationFormula.transformToWorld(vertex); BlockPos pos = convertFloor(transVertex); IBlockState state = access.getBlockState(pos); Block block = state.getBlock(); int combindedLight = state.getPackedLightmapCoords(access, pos); skyLight[0] = combindedLight / 0x10000; blockLight[0] = combindedLight % 0x10000; colorMultiplier[0] = state.getAmbientOcclusionLightValue(); distances[0] = transVertex.distanceTo(convertMiddle(pos)); int index = 0; EnumFacing[] testArray = allAround ? EnumFacing.values() : getNeighbours(face); for (EnumFacing otherFace : testArray) { Vec3d nearestOther = vertex.add(convert(otherFace)); pos = convertFloor(locationFormula.transformToWorld(nearestOther)); state = access.getBlockState(pos); combindedLight = state.getPackedLightmapCoords(access, pos); index++; skyLight[index] = (int) (combindedLight / 0x10000); blockLight[index] = (int) (combindedLight % 0x10000); colorMultiplier[index] = state.getAmbientOcclusionLightValue(); // The extra 0.1 is to stop any 1 divided by 0 errors distances[index] = 1 / (transVertex.distanceTo(convertMiddle(pos)) + 0.1); totalDist += distances[index]; } double avgBlockLight = 0; double avgSkyLight = 0; float avgColorMultiplier = 0; for (int i = 0; i < numPositions; i++) { double part = distances[i] / totalDist; avgBlockLight += blockLight[i] * part; avgSkyLight += skyLight[i] * part; avgColorMultiplier += colorMultiplier[i] * part; } if (shadeTypes.isEnabled(EnumShadeType.LIGHT)) { int capBlockLight = (int) avgBlockLight; int capSkyLight = (int) avgSkyLight; wr.lightmap(capBlockLight, capSkyLight); } Vec3d color; if (shadeTypes.isEnabled(EnumShadeType.FACE)) { color = aoMap.get(face); } else { color = VEC_ONE; } color = multiply(color, avgColorMultiplier); setWorldRendererRGB(wr, color); } public static void setWorldRendererRGB(VertexBuffer wr, Vec3d color) { wr.color((float) color.xCoord, (float) color.yCoord, (float) color.zCoord, 1f); } public static Vec3d vec3(double value) { return new Vec3d(value, value, value); } public static Vec3d multiply(Vec3d vec, double multiple) { return new Vec3d(vec.xCoord * multiple, vec.yCoord * multiple, vec.zCoord * multiple); } public static Vec3d convertMiddle(Vec3i vec3i) { return convert(vec3i).add(VEC_HALF); } /** Note that this method DOES take into account its position. But not its rotation. (Open an issue on github if you * need rotation, and a second method will be made that does all the trig required) */ public void renderCubeStatic(List<BakedQuad> quads, Model3D cuboid) { TextureAtlasSprite[] sprites = cuboid.textures; int[] flips = cuboid.textureFlips; if (flips == null) { flips = new int[6]; } double textureStartX = cuboid.textureStartX / 16D; double textureStartY = cuboid.textureStartY / 16D; double textureStartZ = cuboid.textureStartZ / 16D; double textureSizeX = cuboid.textureSizeX / 16D; double textureSizeY = cuboid.textureSizeY / 16D; double textureSizeZ = cuboid.textureSizeZ / 16D; double textureEndX = textureSizeX + textureStartX; double textureEndY = textureSizeY + textureStartY; double textureEndZ = textureSizeZ + textureStartZ; double textureOffsetX = cuboid.textureOffsetX / 16D; double textureOffsetY = cuboid.textureOffsetY / 16D; double textureOffsetZ = cuboid.textureOffsetZ / 16D; double sizeX = cuboid.sizeX(); double sizeY = cuboid.sizeY(); double sizeZ = cuboid.sizeZ(); if (sprites[0] != null) { // Down float[] uv = getUVArray(sprites[0], flips[0], textureStartX, textureEndX, textureStartZ, textureEndZ); for (RenderInfo ri : getRenderInfos(uv, sizeX, sizeZ, textureSizeX, textureSizeZ, textureOffsetX, textureOffsetZ)) { ri = ri.offset(cuboid, Axis.Y); double[][] arr = new double[4][]; arr[0] = new double[] { ri.xyz[U_MAX], cuboid.posY, ri.xyz[V_MIN], -1, ri.uv[U_MAX], ri.uv[V_MIN], 0 }; arr[1] = new double[] { ri.xyz[U_MAX], cuboid.posY, ri.xyz[V_MAX], -1, ri.uv[U_MAX], ri.uv[V_MAX], 0 }; arr[2] = new double[] { ri.xyz[U_MIN], cuboid.posY, ri.xyz[V_MAX], -1, ri.uv[U_MIN], ri.uv[V_MAX], 0 }; arr[3] = new double[] { ri.xyz[U_MIN], cuboid.posY, ri.xyz[V_MIN], -1, ri.uv[U_MIN], ri.uv[V_MIN], 0 }; convertToDoubleQuads(quads, arr, EnumFacing.DOWN, sprites[0]); } } if (sprites[1] != null) { // Up float[] uv = getUVArray(sprites[1], flips[1], textureStartX, textureEndX, textureStartZ, textureEndZ); for (RenderInfo ri : getRenderInfos(uv, sizeX, sizeZ, textureSizeX, textureSizeZ, textureOffsetX, textureOffsetZ)) { ri = ri.offset(cuboid, Axis.Y); double[][] arr = new double[4][]; arr[0] = new double[] { ri.xyz[U_MAX], sizeY + cuboid.posY, ri.xyz[V_MIN], -1, ri.uv[U_MAX], ri.uv[V_MIN], 0 }; arr[1] = new double[] { ri.xyz[U_MAX], sizeY + cuboid.posY, ri.xyz[V_MAX], -1, ri.uv[U_MAX], ri.uv[V_MAX], 0 }; arr[2] = new double[] { ri.xyz[U_MIN], sizeY + cuboid.posY, ri.xyz[V_MAX], -1, ri.uv[U_MIN], ri.uv[V_MAX], 0 }; arr[3] = new double[] { ri.xyz[U_MIN], sizeY + cuboid.posY, ri.xyz[V_MIN], -1, ri.uv[U_MIN], ri.uv[V_MIN], 0 }; convertToDoubleQuads(quads, arr, EnumFacing.UP, sprites[1]); } } if (sprites[2] != null) { // North (-Z) float[] uv = getUVArray(sprites[2], flips[2], textureStartX, textureEndX, textureStartY, textureEndY); for (RenderInfo ri : getRenderInfos(uv, sizeX, sizeY, textureSizeX, textureSizeY, textureOffsetX, textureOffsetY)) { ri = ri.offset(cuboid, Axis.Z); double[][] arr = new double[4][]; arr[0] = new double[] { ri.xyz[U_MAX], ri.xyz[V_MIN], cuboid.posZ, -1, ri.uv[U_MAX], ri.uv[V_MIN], 0 }; arr[1] = new double[] { ri.xyz[U_MAX], ri.xyz[V_MAX], cuboid.posZ, -1, ri.uv[U_MAX], ri.uv[V_MAX], 0 }; arr[2] = new double[] { ri.xyz[U_MIN], ri.xyz[V_MAX], cuboid.posZ, -1, ri.uv[U_MIN], ri.uv[V_MAX], 0 }; arr[3] = new double[] { ri.xyz[U_MIN], ri.xyz[V_MIN], cuboid.posZ, -1, ri.uv[U_MIN], ri.uv[V_MIN], 0 }; convertToDoubleQuads(quads, arr, EnumFacing.NORTH, sprites[2]); } } if (sprites[3] != null) { // South (+Z) float[] uv = getUVArray(sprites[3], flips[3], textureStartX, textureEndX, textureStartY, textureEndY); for (RenderInfo ri : getRenderInfos(uv, sizeX, sizeY, textureSizeX, textureSizeY, textureOffsetX, textureOffsetY)) { ri = ri.offset(cuboid, Axis.Z); double[][] arr = new double[4][]; arr[0] = new double[] { ri.xyz[U_MAX], ri.xyz[V_MIN], cuboid.posZ + sizeZ, -1, ri.uv[U_MAX], ri.uv[V_MIN], 0 }; arr[1] = new double[] { ri.xyz[U_MAX], ri.xyz[V_MAX], cuboid.posZ + sizeZ, -1, ri.uv[U_MAX], ri.uv[V_MAX], 0 }; arr[2] = new double[] { ri.xyz[U_MIN], ri.xyz[V_MAX], cuboid.posZ + sizeZ, -1, ri.uv[U_MIN], ri.uv[V_MAX], 0 }; arr[3] = new double[] { ri.xyz[U_MIN], ri.xyz[V_MIN], cuboid.posZ + sizeZ, -1, ri.uv[U_MIN], ri.uv[V_MIN], 0 }; convertToDoubleQuads(quads, arr, EnumFacing.SOUTH, sprites[3]); } } if (sprites[4] != null) { // West (-X) float[] uv = getUVArray(sprites[4], flips[4], textureStartZ, textureEndZ, textureStartY, textureEndY); for (RenderInfo ri : getRenderInfos(uv, sizeZ, sizeY, textureSizeZ, textureSizeY, textureOffsetZ, textureOffsetY)) { ri = ri.offset(cuboid, Axis.X); double[][] arr = new double[4][]; arr[0] = new double[] { cuboid.posX, ri.xyz[V_MIN], ri.xyz[U_MAX], -1, ri.uv[U_MAX], ri.uv[V_MIN], 0 }; arr[1] = new double[] { cuboid.posX, ri.xyz[V_MAX], ri.xyz[U_MAX], -1, ri.uv[U_MAX], ri.uv[V_MAX], 0 }; arr[2] = new double[] { cuboid.posX, ri.xyz[V_MAX], ri.xyz[U_MIN], -1, ri.uv[U_MIN], ri.uv[V_MAX], 0 }; arr[3] = new double[] { cuboid.posX, ri.xyz[V_MIN], ri.xyz[U_MIN], -1, ri.uv[U_MIN], ri.uv[V_MIN], 0 }; convertToDoubleQuads(quads, arr, EnumFacing.WEST, sprites[4]); } } if (sprites[5] != null) { // East (+X) float[] uv = getUVArray(sprites[5], flips[5], textureStartZ, textureEndZ, textureStartY, textureEndY); for (RenderInfo ri : getRenderInfos(uv, sizeZ, sizeY, textureSizeZ, textureSizeY, textureOffsetZ, textureOffsetY)) { ri = ri.offset(cuboid, Axis.X); double[][] arr = new double[4][]; arr[0] = new double[] { cuboid.posX + sizeX, ri.xyz[V_MIN], ri.xyz[U_MAX], -1, ri.uv[U_MAX], ri.uv[V_MIN], 0 }; arr[1] = new double[] { cuboid.posX + sizeX, ri.xyz[V_MAX], ri.xyz[U_MAX], -1, ri.uv[U_MAX], ri.uv[V_MAX], 0 }; arr[2] = new double[] { cuboid.posX + sizeX, ri.xyz[V_MAX], ri.xyz[U_MIN], -1, ri.uv[U_MIN], ri.uv[V_MAX], 0 }; arr[3] = new double[] { cuboid.posX + sizeX, ri.xyz[V_MIN], ri.xyz[U_MIN], -1, ri.uv[U_MIN], ri.uv[V_MIN], 0 }; convertToDoubleQuads(quads, arr, EnumFacing.EAST, sprites[5]); } } } private void convertToDoubleQuads(List<BakedQuad> quads, double[][] points, EnumFacing face, TextureAtlasSprite sprite) { BakedQuad quad = convertToQuad(points, face, sprite); quads.add(quad); double[][] otherPoints = new double[][] { points[3], points[2], points[1], points[0] }; quad = convertToQuad(otherPoints, face, sprite); quads.add(quad); } private BakedQuad convertToQuad(double[][] points, EnumFacing face, TextureAtlasSprite sprite) { int[] list = new int[points.length * points[0].length]; for (int i = 0; i < points.length; i++) { double[] arr = points[i]; for (int j = 0; j < arr.length; j++) { double d = arr[j]; int used = 0; if (j == 3 || j == 6) {// Shade or unused used = (int) d; } else { used = Float.floatToRawIntBits((float) d); } list[i * arr.length + j] = used; } } return new BakedQuad(list, -1, face, sprite); } /** Returns an array containing [uMin, uMax, vMin, vMax]. start* and end* must be doubles between 0 and 1 */ private float[] getUVArray(TextureAtlasSprite sprite, int flips, double startU, double endU, double startV, double endV) { float minU = sprite.getInterpolatedU(startU * 16); float maxU = sprite.getInterpolatedU(endU * 16); float minV = sprite.getInterpolatedV(startV * 16); float maxV = sprite.getInterpolatedV(endV * 16); float[] uvarray = new float[] { minU, maxU, minV, maxV }; if (flips % 2 == 1) { float holder = uvarray[0]; uvarray[0] = uvarray[1]; uvarray[1] = holder; } if (flips >> 1 % 2 == 1) { float holder = uvarray[2]; uvarray[2] = uvarray[3]; uvarray[3] = holder; } return uvarray; } private float[] getUVArray(TextureAtlasSprite sprite, int flips, EnumFacing face, Vec3d start, Vec3d end) { Axis u = face.getAxis() == Axis.X ? Axis.Z : Axis.X; Axis v = face.getAxis() == Axis.Y ? Axis.Z : Axis.Y; float minU = sprite.getInterpolatedU(getValue(start, u) * 16); float maxU = sprite.getInterpolatedU(getValue(end, u) * 16); float minV = sprite.getInterpolatedV(getValue(start, v) * 16); float maxV = sprite.getInterpolatedV(getValue(end, v) * 16); float[] uvarray = new float[] { minU, maxU, minV, maxV }; if (flips % 2 == 1) { float holder = uvarray[0]; uvarray[0] = uvarray[1]; uvarray[1] = holder; } if (flips >> 1 % 2 == 1) { float holder = uvarray[2]; uvarray[2] = uvarray[3]; uvarray[3] = holder; } return uvarray; } private List<RenderInfo> getRenderInfos(float[] uv, EnumFacing face, Vec3d size, Vec3d texSize, Vec3d texOffset) { Axis u = face.getAxis() == Axis.X ? Axis.Z : Axis.X; Axis v = face.getAxis() == Axis.Y ? Axis.Z : Axis.Y; double sizeU = getValue(size, u); double sizeV = getValue(size, v); double textureSizeU = getValue(texSize, u); double textureSizeV = getValue(texSize, v); double textureOffsetU = getValue(texOffset, u); double textureOffsetV = getValue(texOffset, v); return getRenderInfos(uv, sizeU, sizeV, textureSizeU, textureSizeV, textureOffsetU, textureOffsetV); } public static double getValue(Vec3d vector, Axis axis) { if (axis == Axis.X) return vector.xCoord; else if (axis == Axis.Y) return vector.yCoord; else if (axis == Axis.Z) return vector.zCoord; else throw new RuntimeException("Was given a null axis! That was probably not intentional, consider this a bug! (Vector = " + vector + ")"); } /** A way to automatically generate the different positions given the same arguments. * * @param rotation TODO */ private List<RenderInfo> getRenderInfos(float[] uv, double sizeU, double sizeV, double textureSizeU, double textureSizeV, double textureOffsetU, double textureOffsetV) { List<RenderInfo> infos = Lists.newArrayList(); boolean firstU = true; for (double u = 0; u < sizeU; u += textureSizeU) { float[] uvCu = Arrays.copyOf(uv, 4); double addU = textureSizeU; boolean lowerU = false; // If there is an offset then make sure the texture positions are changed properly if (firstU && textureOffsetU != 0) { uvCu[U_MIN] = uvCu[U_MIN] + (uvCu[U_MAX] - uvCu[U_MIN]) * (float) textureOffsetU; addU -= textureOffsetU; // addU = 1 - textureOffsetU; lowerU = true; } // If the size of the texture is greater than the cuboid goes on for then make sure the texture // positions are lowered if (u + addU > sizeU) { addU = sizeU - u; if (firstU && textureOffsetU != 0) { uvCu[U_MAX] = uvCu[U_MIN] + (uvCu[U_MAX] - uvCu[U_MIN]) * (float) (addU / (textureSizeU - textureOffsetU)); } else { uvCu[U_MAX] = uvCu[U_MIN] + (uvCu[U_MAX] - uvCu[U_MIN]) * (float) (addU / textureSizeU); } } firstU = false; boolean firstV = true; for (double v = 0; v < sizeV; v += textureSizeV) { float[] uvCv = Arrays.copyOf(uvCu, 4); double addV = textureSizeV; boolean lowerV = false; if (firstV && textureOffsetV != 0) { uvCv[V_MIN] = uvCv[V_MIN] + (uvCv[V_MAX] - uvCv[V_MIN]) * (float) textureOffsetV; addV -= textureOffsetV; lowerV = true; } if (v + addV > sizeV) { addV = sizeV - v; if (firstV && textureOffsetV != 0) { uvCv[V_MAX] = uvCv[V_MIN] + (uvCv[V_MAX] - uvCv[V_MIN]) * (float) (addV / (textureSizeV - textureOffsetV)); } else { uvCv[V_MAX] = uvCv[V_MIN] + (uvCv[V_MAX] - uvCv[V_MIN]) * (float) (addV / textureSizeV); } } double[] xyz = new double[4]; xyz[U_MIN] = u; xyz[U_MAX] = u + addU; xyz[V_MIN] = v; xyz[V_MAX] = v + addV; infos.add(new RenderInfo(uvCv, xyz)); if (lowerV) { v -= textureOffsetV; } firstV = false; } // If we lowered the U because the cuboid started on an offset, reset it back to what was actually // rendered, not what the for loop assumes if (lowerU) { u -= textureOffsetU; } } return infos; } private static final class RenderInfo { private final float[] uv; private final double[] xyz; public RenderInfo(float[] uv, double[] xyz) { this.uv = uv; this.xyz = xyz; } public RenderInfo offset(Model3D ent, Axis axis) { switch (axis) { case X: { return new RenderInfo(uv, new double[] { xyz[0] + ent.posZ, xyz[1] + ent.posZ, xyz[2] + ent.posY, xyz[3] + ent.posY }); } case Y: { return new RenderInfo(uv, new double[] { xyz[0] + ent.posX, xyz[1] + ent.posX, xyz[2] + ent.posZ, xyz[3] + ent.posZ }); } case Z: { return new RenderInfo(uv, new double[] { xyz[0] + ent.posX, xyz[1] + ent.posX, xyz[2] + ent.posY, xyz[3] + ent.posY }); } } return new RenderInfo(uv, xyz); } } }