aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/makamys/neodymium/renderer/MeshQuad.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/makamys/neodymium/renderer/MeshQuad.java')
-rw-r--r--src/main/java/makamys/neodymium/renderer/MeshQuad.java426
1 files changed, 426 insertions, 0 deletions
diff --git a/src/main/java/makamys/neodymium/renderer/MeshQuad.java b/src/main/java/makamys/neodymium/renderer/MeshQuad.java
new file mode 100644
index 0000000..427355f
--- /dev/null
+++ b/src/main/java/makamys/neodymium/renderer/MeshQuad.java
@@ -0,0 +1,426 @@
+package makamys.neodymium.renderer;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.Locale;
+import java.util.Map;
+
+import makamys.neodymium.renderer.MeshQuad.QuadPlaneComparator;
+import makamys.neodymium.util.SpriteUtil;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+import net.minecraft.client.renderer.texture.TextureMap;
+import net.minecraft.util.EnumFacing;
+
+public class MeshQuad {
+ public int spriteIndex;
+ public String spriteName;
+ public int[] xs = new int[4];
+ public int[] ys = new int[4];
+ public int[] zs = new int[4];
+ public int baseX = -1, baseY, baseZ;
+ public int minX = Integer.MAX_VALUE;
+ public int minY = Integer.MAX_VALUE;
+ public int minZ = Integer.MAX_VALUE;
+ public int maxX = Integer.MIN_VALUE;
+ public int maxY = Integer.MIN_VALUE;
+ public int maxZ = Integer.MIN_VALUE;
+ public int[] relUs = new int[4];
+ public int[] relVs = new int[4];
+ public int[] bUs = new int[4];
+ public int[] bVs = new int[4];
+ public int[] cs = new int[4];
+ public int[] normals = new int[4];
+ public boolean deleted;
+ public boolean isFullQuad;
+
+ public static final int PLANE_NONE = -1, PLANE_XY = 0, PLANE_XZ = 1, PLANE_YZ = 2;
+ public int plane = PLANE_NONE;
+ public int offset;
+ public ChunkMesh.Flags flags;
+
+ public static int[] totalMergeCountByPlane = new int[3];
+
+ private int minPositive(int a, int b) {
+ if(a == -1) {
+ return b;
+ } else {
+ return a < b ? a : b;
+ }
+ }
+ private int maxPositive(int a, int b) {
+ if(a == -1) {
+ return b;
+ } else {
+ return a > b ? a : b;
+ }
+ }
+
+ public MeshQuad(int[] rawBuffer, int offset, ChunkMesh.Flags flags, int offsetX, int offsetY, int offsetZ) {
+ this.offset = offset;
+ this.flags = flags;
+ int i = offset;
+ float[] us = new float[4];
+ float uSum = 0;
+ float[] vs = new float[4];
+ float vSum = 0;
+ for(int vertexI = 0; vertexI < 4; vertexI++) {
+ float u = Float.intBitsToFloat(rawBuffer[vertexI * 8 + i + 3]);
+ float v = Float.intBitsToFloat(rawBuffer[vertexI * 8 + i + 4]);
+
+ us[vertexI] = u;
+ vs[vertexI] = v;
+
+ uSum += u;
+ vSum += v;
+ }
+
+ float avgU = uSum / 4f;
+ float avgV = vSum / 4f;
+
+ TextureAtlasSprite sprite = null;
+ Map<String, TextureAtlasSprite> uploadedSprites = ((TextureMap)Minecraft.getMinecraft().getTextureManager().getTexture(TextureMap.locationBlocksTexture)).mapUploadedSprites;
+
+ spriteIndex = SpriteUtil.getSpriteIndexForUV(avgU, avgV);
+ sprite = SpriteUtil.getSprite(spriteIndex);
+
+ if(sprite == null) {
+ System.out.println("Error: couldn't find sprite");
+ } else {
+ spriteName = sprite.getIconName();
+ for(int vertexI = 0; vertexI < 4; vertexI++) {
+ float x = Float.intBitsToFloat(rawBuffer[i + 0]) - offsetX;
+ float y = Float.intBitsToFloat(rawBuffer[i + 1]) - offsetY;
+ float z = Float.intBitsToFloat(rawBuffer[i + 2]) - offsetZ;
+
+ int simpleX = (int)(x * 16);
+ //if(simpleX == 256) simpleX = 255;
+ int simpleY = (int)(y * 16);
+ //if(simpleY == 256) simpleY = 255;
+ int simpleZ = (int)(z * 16);
+ //if(simpleZ == 256) simpleZ = 255;
+
+ xs[vertexI] = simpleX;
+ ys[vertexI] = simpleY;
+ zs[vertexI] = simpleZ;
+
+ // hasTexture
+ float u = us[vertexI];
+ float v = vs[vertexI];
+
+ int simpleRelU = (int)((u - sprite.getMinU()) / (sprite.getMaxU() - sprite.getMinU()) * 16);
+ int simpleRelV = (int)((v - sprite.getMinV()) / (sprite.getMaxV() - sprite.getMinV()) * 16);
+ if(flags.hasTexture) {
+ relUs[vertexI] = simpleRelU;
+ relVs[vertexI] = simpleRelV;
+ }
+
+ // hasBrightness
+ int brightness = rawBuffer[i + 7];
+ int brightnessU = brightness & 0xFFFF;
+ int brightnessV = (brightness >> 16) & 0xFFFF;
+ if(flags.hasBrightness) {
+ bUs[vertexI] = (int)brightnessU;
+ bVs[vertexI] = (int)brightnessV;
+ }
+
+ // hasColor
+ int color = rawBuffer[i + 5];
+ if(flags.hasColor) {
+ cs[vertexI] = color;
+ }
+
+ // hasNormals
+ int normal = rawBuffer[i + 6];
+ if(flags.hasNormals) {
+ normals[vertexI] = normal;
+ }
+
+ i += 8;
+ }
+ }
+
+ updateMinMaxXYZ();
+
+ if(ys[0] == ys[1] && ys[1] == ys[2] && ys[2] == ys[3]) {
+ plane = PLANE_XZ;
+ } else if(xs[0] == xs[1] && xs[1] == xs[2] && xs[2] == xs[3]) {
+ plane = PLANE_YZ;
+ } else if(zs[0] == zs[1] && zs[1] == zs[2] && zs[2] == zs[3]) {
+ plane = PLANE_XY;
+ } else {
+ plane = PLANE_NONE;
+ }
+
+ boolean equalToAABB = true;
+ for(int minOrMaxX = 0; minOrMaxX < 2; minOrMaxX++) {
+ for(int minOrMaxY = 0; minOrMaxY < 2; minOrMaxY++) {
+ for(int minOrMaxZ = 0; minOrMaxZ < 2; minOrMaxZ++) {
+ if(getCornerVertex(minOrMaxX == 1, minOrMaxY == 1, minOrMaxZ == 1) == -1) {
+ equalToAABB = false;
+ break;
+ }
+ }
+ }
+ }
+
+ switch(plane) {
+ case PLANE_XY:
+ isFullQuad = equalToAABB && (maxX - minX) == 16 && (maxY - minY) == 16;
+ break;
+ case PLANE_XZ:
+ isFullQuad = equalToAABB && (maxX - minX) == 16 && (maxZ - minZ) == 16;
+ break;
+ case PLANE_YZ:
+ isFullQuad = equalToAABB && (maxY - minY) == 16 && (maxZ - minZ) == 16;
+ break;
+ default:
+ isFullQuad = false;
+ }
+
+ for(int c = 0; c < 3; c++) {
+ if(getMin(c) < 0 || getMax(c) < 0 || getMax(c) - getMin(c) > 16 || getMin(c) > 256 || getMax(c) > 256) {
+ this.deleted = true;
+ // TODO handle weirdness more gracefully
+ }
+ }
+ }
+
+ // yeah this is kinda unoptimal
+ private int getCornerVertex(boolean minOrMaxX, boolean minOrMaxY, boolean minOrMaxZ) {
+ int aabbCornerX = !minOrMaxX ? minX : maxX;
+ int aabbCornerY = !minOrMaxY ? minY : maxY;
+ int aabbCornerZ = !minOrMaxZ ? minZ : maxZ;
+
+ for(int vi = 0; vi < 4; vi++) {
+ if(xs[vi] == aabbCornerX && ys[vi] == aabbCornerY && zs[vi] == aabbCornerZ) {
+ return vi;
+ }
+ }
+ return -1;
+ }
+
+ public void tryToMerge(MeshQuad o) {
+ if(isValid(this) && isValid(o) && plane == o.plane
+ && spriteIndex == o.spriteIndex && isFullQuad && o.isFullQuad) {
+ int numVerticesTouching = 0;
+ for(int i = 0; i < 4; i++) {
+ for(int j = 0; j < 4; j++) {
+ if(xs[i] == o.xs[j] && ys[i] == o.ys[j] && zs[i] == o.zs[j]) {
+ numVerticesTouching++;
+ }
+ }
+ }
+ if(numVerticesTouching == 2) {
+ mergeWithQuad(o);
+
+ totalMergeCountByPlane[plane]++;
+
+ o.deleted = true;
+ }
+ }
+ }
+
+ private void mergeWithQuad(MeshQuad o) {
+ if(minX < o.minX) {
+ copyEdgeFrom(o, EnumFacing.EAST);
+ } else if(minX > o.minX) {
+ copyEdgeFrom(o, EnumFacing.WEST);
+ } else if(minY < o.minY) {
+ copyEdgeFrom(o, EnumFacing.UP);
+ } else if(minY > o.minY) {
+ copyEdgeFrom(o, EnumFacing.DOWN);
+ } else if(minZ < o.minZ) {
+ copyEdgeFrom(o, EnumFacing.NORTH);
+ } else if(minX > o.minX) {
+ copyEdgeFrom(o, EnumFacing.SOUTH);
+ }
+ }
+
+ private void copyEdgeFrom(MeshQuad o, EnumFacing side) {
+ int whichX, whichY, whichZ;
+ whichX = whichY = whichZ = -1;
+
+ switch(plane) {
+ case PLANE_XY:
+ whichZ = 0;
+ break;
+ case PLANE_XZ:
+ whichY = 0;
+ break;
+ case PLANE_YZ:
+ whichX = 0;
+ break;
+ }
+
+ switch(side) {
+ case EAST:
+ copyCornerVertexFrom(o, 1, whichY, whichZ);
+ break;
+ case WEST:
+ copyCornerVertexFrom(o, 0, whichY, whichZ);
+ break;
+ case UP:
+ copyCornerVertexFrom(o, whichX, 1, whichZ);
+ break;
+ case DOWN:
+ copyCornerVertexFrom(o, whichX, 0, whichZ);
+ break;
+ case NORTH:
+ copyCornerVertexFrom(o, whichX, whichY, 1);
+ break;
+ case SOUTH:
+ copyCornerVertexFrom(o, whichX, whichY, 0);
+ break;
+ }
+
+ updateMinMaxXYZ();
+ }
+
+ private void updateMinMaxXYZ() {
+ for(int i = 0; i < 4; i++) {
+ minX = Math.min(minX, xs[i]);
+ minY = Math.min(minY, ys[i]);
+ minZ = Math.min(minZ, zs[i]);
+ maxX = Math.max(maxX, xs[i]);
+ maxY = Math.max(maxY, ys[i]);
+ maxZ = Math.max(maxZ, zs[i]);
+ }
+ }
+
+ private void copyCornerVertexFrom(MeshQuad o, int whichX, int whichY, int whichZ) {
+ int whichXMin, whichXMax, whichYMin, whichYMax, whichZMin, whichZMax;
+ whichXMin = whichYMin = whichZMin = 0;
+ whichXMax = whichYMax = whichZMax = 1;
+
+ if(whichX != -1) whichXMin = whichXMax = whichX;
+ if(whichY != -1) whichYMin = whichYMax = whichY;
+ if(whichZ != -1) whichZMin = whichZMax = whichZ;
+
+ for(int minOrMaxX = whichXMin; minOrMaxX <= whichXMax; minOrMaxX++) {
+ for(int minOrMaxY = whichYMin; minOrMaxY <= whichYMax; minOrMaxY++) {
+ for(int minOrMaxZ = whichZMin; minOrMaxZ <= whichZMax; minOrMaxZ++) {
+ copyVertexFrom(o,
+ o.getCornerVertex(minOrMaxX == 1, minOrMaxY == 1, minOrMaxZ == 1),
+ getCornerVertex(minOrMaxX == 1, minOrMaxY == 1, minOrMaxZ == 1));
+ }
+ }
+ }
+ }
+
+ private void copyVertexFrom(MeshQuad o, int src, int dest) {
+ xs[dest] = o.xs[src];
+ ys[dest] = o.ys[src];
+ zs[dest] = o.zs[src];
+ relUs[dest] = o.relUs[src];
+ relVs[dest] = o.relVs[src];
+ bUs[dest] = o.bUs[src];
+ bVs[dest] = o.bVs[src];
+ cs[dest] = o.cs[src];
+ normals[dest] = o.normals[src];
+ }
+
+ public void writeToDisk(DataOutputStream out, int pass) throws IOException {
+ if(deleted) {
+ return;
+ }
+
+ if(flags.hasTexture) {
+ if(pass == 0) out.writeShort(spriteIndex);
+ }
+ for (int vertexI = 0; vertexI < 4; vertexI++) {
+ if(pass == 1) out.writeByte(xs[vertexI] == 256 ? 255 : xs[vertexI]);
+ if(pass == 2) out.writeByte(ys[vertexI] == 256 ? 255 : ys[vertexI]);
+ if(pass == 3) out.writeByte(zs[vertexI] == 256 ? 255 : zs[vertexI]);
+
+ if (flags.hasTexture) {
+ if(pass == 4) out.writeByte(relUs[vertexI]);
+ if(pass == 5) out.writeByte(relVs[vertexI]);
+ }
+
+ if (flags.hasBrightness) {
+ if(pass == 6) out.writeByte(bUs[vertexI]);
+ if(pass == 7) out.writeByte(bVs[vertexI]);
+ }
+
+ if (flags.hasColor) {
+ if(pass == 8) out.writeInt(cs[vertexI]);
+ }
+
+ if (flags.hasNormals) {
+ if(pass == 9) out.writeInt(normals[vertexI]);
+ }
+ }
+ }
+
+ // maybe minXYZ and maxXYZ should be arrays instead
+ public int getMin(int coord) {
+ return coord == 0 ? minX : coord == 1 ? minY : coord == 2 ? minZ : -1;
+ }
+
+ public int getMax(int coord) {
+ return coord == 0 ? maxX : coord == 1 ? maxY : coord == 2 ? maxZ : -1;
+ }
+
+ public boolean onSamePlaneAs(MeshQuad o) {
+ return isValid(this) && isValid(o) && plane == o.plane &&
+ ((plane == PLANE_XY && minZ == o.minZ) ||
+ (plane == PLANE_XZ && minY == o.minY) ||
+ (plane == PLANE_YZ && minX == o.minX));
+ }
+
+ // this should be static..
+ public boolean isValid(MeshQuad q) {
+ return q != null && !q.deleted;
+ }
+
+ public boolean isClockwiseXZ() {
+ return (xs[1] - xs[0]) * (zs[2] - zs[0]) - (xs[2] - xs[0]) * (zs[1] - zs[0]) < 0;
+ }
+
+ @Override
+ public String toString() {
+ return String.format(Locale.ENGLISH, "%s(%.1f, %.1f, %.1f -- %.1f, %.1f, %.1f) %s", deleted ? "XXX " : "", minX/16f, minY/16f, minZ/16f, maxX/16f, maxY/16f, maxZ/16f, spriteName);
+ }
+
+ public static class QuadPlaneComparator implements Comparator<MeshQuad> {
+
+ public static final QuadPlaneComparator[] quadPlaneComparators = new QuadPlaneComparator[]{
+ new QuadPlaneComparator(2, 1, 0), // PLANE_XY -> ZYX
+ new QuadPlaneComparator(1, 2, 0), // PLANE_XZ -> YZX
+ new QuadPlaneComparator(0, 2, 1) // PLANE_YZ -> XZY
+ };
+
+ private int c0, c1, c2;
+
+ public QuadPlaneComparator(int firstCoordToCompare, int secondCoordToCompare, int thirdCoordToCompare) {
+ this.c0 = firstCoordToCompare;
+ this.c1 = secondCoordToCompare;
+ this.c2 = thirdCoordToCompare;
+ }
+
+ @Override
+ public int compare(MeshQuad a, MeshQuad b) {
+ if(a.getMin(c0) < b.getMin(c0)) {
+ return -1;
+ } else if(a.getMin(c0) > b.getMin(c0)) {
+ return 1;
+ } else {
+ if(a.getMin(c1) < b.getMin(c1)) {
+ return -1;
+ } else if(a.getMin(c1) > b.getMin(c1)) {
+ return 1;
+ } else {
+ if(a.getMin(c2) < b.getMin(c2)) {
+ return -1;
+ } else if(a.getMin(c2) > b.getMin(c2)) {
+ return 1;
+ } else {
+ return (int)Math.signum(a.offset - b.offset);
+ }
+ }
+ }
+ }
+ }
+}