diff options
| author | makamys <makamys@outlook.com> | 2021-05-07 08:42:32 +0200 |
|---|---|---|
| committer | makamys <makamys@outlook.com> | 2021-05-07 08:42:32 +0200 |
| commit | c233ea31da07957d8872d3859d6e75b99937becd (patch) | |
| tree | b9d4301b7b503aa466d4af2db8498918f3e6dfee /src/main/java/makamys/lodmod/renderer | |
| parent | bc6c84d2d2342073b6f4d1b8c1213b7102bb7ebf (diff) | |
| download | Neodymium-c233ea31da07957d8872d3859d6e75b99937becd.tar.gz Neodymium-c233ea31da07957d8872d3859d6e75b99937becd.tar.bz2 Neodymium-c233ea31da07957d8872d3859d6e75b99937becd.zip | |
Port mod from MCP to Forge!
The only known regression is the sides of LOD=1 chunks look darker for some
reason
Diffstat (limited to 'src/main/java/makamys/lodmod/renderer')
| -rw-r--r-- | src/main/java/makamys/lodmod/renderer/ChunkMesh.java | 352 | ||||
| -rw-r--r-- | src/main/java/makamys/lodmod/renderer/FarChunkCache.java | 12 | ||||
| -rw-r--r-- | src/main/java/makamys/lodmod/renderer/FarWorldRenderer.java | 14 | ||||
| -rw-r--r-- | src/main/java/makamys/lodmod/renderer/LODChunk.java | 67 | ||||
| -rw-r--r-- | src/main/java/makamys/lodmod/renderer/LODRegion.java | 62 | ||||
| -rw-r--r-- | src/main/java/makamys/lodmod/renderer/Mesh.java | 15 | ||||
| -rw-r--r-- | src/main/java/makamys/lodmod/renderer/MeshQuad.java | 424 | ||||
| -rw-r--r-- | src/main/java/makamys/lodmod/renderer/MyRenderer.java | 748 | ||||
| -rw-r--r-- | src/main/java/makamys/lodmod/renderer/SimpleChunkMesh.java | 150 |
9 files changed, 1844 insertions, 0 deletions
diff --git a/src/main/java/makamys/lodmod/renderer/ChunkMesh.java b/src/main/java/makamys/lodmod/renderer/ChunkMesh.java new file mode 100644 index 0000000..4175b9f --- /dev/null +++ b/src/main/java/makamys/lodmod/renderer/ChunkMesh.java @@ -0,0 +1,352 @@ +package makamys.lodmod.renderer; + +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import java.nio.IntBuffer; +import java.nio.ShortBuffer; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.lwjgl.BufferUtils; + +import makamys.lodmod.ducks.IWorldRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.tileentity.TileEntity; + +public class ChunkMesh extends Mesh { + + int x; + int y; + int z; + Flags flags; + + private ChunkMesh(int x, int y, int z, Flags flags, int quadCount, byte[] data, List<String> stringTable) { + this.x = x; + this.y = y; + this.z = z; + this.flags = flags; + this.quadCount = quadCount; + + this.buffer = createBuffer(data, stringTable); + } + + public ChunkMesh(int x, int y, int z, Flags flags, int quadCount, List<MeshQuad> quads) { + this.x = x; + this.y = y; + this.z = z; + this.flags = flags; + this.quadCount = quadCount; + + this.buffer = createBuffer(quads); + } + + private ByteBuffer createBuffer(List<MeshQuad> quads) { + if(!(flags.hasTexture && flags.hasColor && flags.hasBrightness && !flags.hasNormals)) { + // for simplicity's sake we just assume this setup + System.out.println("invalid mesh properties, expected a chunk"); + return null; + } + + ByteBuffer buffer = BufferUtils.createByteBuffer(quadCount * 6 * getStride()); + FloatBuffer floatBuffer = buffer.asFloatBuffer(); + ShortBuffer shortBuffer = buffer.asShortBuffer(); + IntBuffer intBuffer = buffer.asIntBuffer(); + + try { + for(MeshQuad quad : quads) { + if(quad.deleted) { + continue; + } + String spriteName = quad.spriteName; + + TextureAtlasSprite tas = ((TextureMap)Minecraft.getMinecraft().getTextureManager().getTexture(TextureMap.locationBlocksTexture)).getAtlasSprite(spriteName); + + for (int vertexNum = 0; vertexNum < 6; vertexNum++) { + int vi = new int[]{0, 1, 3, 1, 2, 3}[vertexNum]; + int simpleX = quad.xs[vi]; + if(simpleX == 255) simpleX = 256; + int simpleY = quad.ys[vi]; + if(simpleY == 255) simpleY = 256; + int simpleZ = quad.zs[vi]; + if(simpleZ == 255) simpleZ = 256; + floatBuffer.put(x * 16 + simpleX / 16f); // x + floatBuffer.put(y * 16 + simpleY / 16f); // y + floatBuffer.put(z * 16 + simpleZ / 16f); // z + + int relU = quad.relUs[vi]; + int relV = quad.relVs[vi]; + + floatBuffer.put(tas.getMinU() + (tas.getMaxU() - tas.getMinU()) * (relU / 16f)); // u + floatBuffer.put(tas.getMinV() + (tas.getMaxV() - tas.getMinV()) * (relV / 16f)); // v + + shortBuffer.position(floatBuffer.position() * 2); + + shortBuffer.put((short)quad.bUs[vi]); // bU + shortBuffer.put((short)quad.bVs[vi]); // bV + + intBuffer.position(shortBuffer.position() / 2); + + intBuffer.put(quad.cs[vi]); // c + + floatBuffer.position(intBuffer.position()); + } + } + } catch(Exception e) { + e.printStackTrace(); + } + + buffer.position(floatBuffer.position() * 4); + buffer.flip(); + + return buffer; + } + + private ByteBuffer createBuffer(byte[] data, List<String> stringTable) { + if(!(flags.hasTexture && flags.hasColor && flags.hasBrightness && !flags.hasNormals)) { + // for simplicity's sake we just assume this setup + System.out.println("invalid mesh properties, expected a chunk"); + return null; + } + int coordsOffset = quadCount * 2; + int textureOffset = quadCount * (2 + 4 + 4 + 4); + int brightnessOffset = quadCount * (2 + 4 + 4 + 4 + 4 + 4); + int colorOffset = quadCount * (2 + 4 + 4 + 4 + 4 + 4 + 4 + 4); + + DataInputStream in = new DataInputStream(new ByteArrayInputStream(data)); + + ByteBuffer buffer = BufferUtils.createByteBuffer(quadCount * 6 * getStride()); + FloatBuffer floatBuffer = buffer.asFloatBuffer(); + ShortBuffer shortBuffer = buffer.asShortBuffer(); + IntBuffer intBuffer = buffer.asIntBuffer(); + + try { + for(int quadI = 0; quadI < quadCount; quadI++) { + short spriteIndex = readShortAt(in, quadI * 2); + String spriteName = stringTable.get(spriteIndex); + + TextureAtlasSprite tas = ((TextureMap)Minecraft.getMinecraft().getTextureManager().getTexture(TextureMap.locationBlocksTexture)).getAtlasSprite(spriteName); + + for (int vertexNum = 0; vertexNum < 6; vertexNum++) { + int vi = new int[]{0, 1, 3, 1, 2, 3}[vertexNum]; + int vertexI = 4 * quadI + vi; + int offset = vertexI * getStride(); + int simpleX = Byte.toUnsignedInt(data[coordsOffset + 0 * 4 * quadCount + 4 * quadI + vi]); + if(simpleX == 255) simpleX = 256; + int simpleY = Byte.toUnsignedInt(data[coordsOffset + 1 * 4 * quadCount + 4 * quadI + vi]); + if(simpleY == 255) simpleY = 256; + int simpleZ = Byte.toUnsignedInt(data[coordsOffset + 2 * 4 * quadCount + 4 * quadI + vi]); + if(simpleZ == 255) simpleZ = 256; + floatBuffer.put(x * 16 + simpleX / 16f); // x + floatBuffer.put(y * 16 + simpleY / 16f); // y + floatBuffer.put(z * 16 + simpleZ / 16f); // z + + byte relU = data[textureOffset + 0 * 4 * quadCount + 4 * quadI + vi]; + byte relV = data[textureOffset + 1 * 4 * quadCount + 4 * quadI + vi]; + + floatBuffer.put(tas.getMinU() + (tas.getMaxU() - tas.getMinU()) * (relU / 16f)); // u + floatBuffer.put(tas.getMinV() + (tas.getMaxV() - tas.getMinV()) * (relV / 16f)); // v + + shortBuffer.position(floatBuffer.position() * 2); + + shortBuffer.put((short)Byte.toUnsignedInt(data[brightnessOffset + 0 * 4 * quadCount + 4 * quadI + vi])); // bU + shortBuffer.put((short)Byte.toUnsignedInt(data[brightnessOffset + 1 * 4 * quadCount + 4 * quadI + vi])); // bV + + intBuffer.position(shortBuffer.position() / 2); + + intBuffer.put(readIntAt(in, colorOffset + 4 * 4 * quadI + 4 * vi)); // c + + floatBuffer.position(intBuffer.position()); + } + } + } catch(Exception e) { + e.printStackTrace(); + } + + buffer.position(floatBuffer.position() * 4); + buffer.flip(); + + return buffer; + } + + // Java is weird. + public static short readShortAt(DataInputStream in, int offset) { + try { + in.reset(); + in.skip(offset); + return in.readShort(); + } catch(IOException e) { + return -1; + } + } + + public static int readIntAt(DataInputStream in, int offset) { + try { + in.reset(); + in.skip(offset); + return in.readInt(); + } catch(IOException e) { + return -1; + } + } + + public int getStride() { + return (3 * 4 + (flags.hasTexture ? 8 : 0) + (flags.hasBrightness ? 4 : 0) + (flags.hasColor ? 4 : 0) + (flags.hasNormals ? 4 : 0)); + } + + static ChunkMesh loadChunkMesh(int x, int y, int z) { + try(DataInputStream in = new DataInputStream(new FileInputStream("tessellator_dump.dat"))){ + List<String> stringTable = Files.readAllLines(Paths.get("tessellator_strings.txt")); + + while(true) { + int xOffset = in.readInt(); + int yOffset = in.readInt(); + int zOffset = in.readInt(); + Flags flags = new Flags(in.readByte()); + + int quadSize = 2 + 4 * (3 + (flags.hasTexture ? 2 : 0) + (flags.hasBrightness ? 2 : 0) + + (flags.hasColor ? 4 : 0) + (flags.hasNormals ? 4 : 0)); + + + int quadCount = in.readInt(); + + if(xOffset == x && yOffset == y && zOffset == z) { + byte[] data = new byte[quadSize * quadCount]; + in.read(data, 0, data.length); + + return new ChunkMesh(x, y, z, flags, quadCount, data, stringTable); + } else { + in.skip(quadSize * quadCount); + } + } + } catch(EOFException eof) { + System.out.println("end??"); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + // TODO don't repeat yourself + static List<ChunkMesh> loadAll(){ + List<ChunkMesh> list = new ArrayList<ChunkMesh>(); + try(DataInputStream in = new DataInputStream(new FileInputStream("tessellator_dump.dat"))){ + List<String> stringTable = Files.readAllLines(Paths.get("tessellator_strings.txt")); + + while(true) { + int xOffset = in.readInt(); + int yOffset = in.readInt(); + int zOffset = in.readInt(); + Flags flags = new Flags(in.readByte()); + + int quadSize = 2 + 4 * (3 + (flags.hasTexture ? 2 : 0) + (flags.hasBrightness ? 2 : 0) + + (flags.hasColor ? 4 : 0) + (flags.hasNormals ? 4 : 0)); + + + int quadCount = in.readInt(); + + if(quadCount > 0) { + byte[] data = new byte[quadSize * quadCount]; + in.read(data, 0, data.length); + + list.add(new ChunkMesh(xOffset, yOffset, zOffset, flags, quadCount, data, stringTable)); + } + } + } catch(EOFException eof) { + System.out.println("end??"); + } catch (IOException e) { + e.printStackTrace(); + } + return list; + } + + static void saveChunks(List<Integer> coords) { + System.out.println("saving " + (coords.size() / 3) + " cchunks"); + for(int i = 0; i < coords.size(); i += 3) { + if(i % 300 == 0) { + System.out.println((i / 3) + " / " + (coords.size() / 3)); + } + int theX = coords.get(i); + int theY = coords.get(i + 1); + int theZ = coords.get(i + 2); + + WorldRenderer wr = new WorldRenderer(Minecraft.getMinecraft().theWorld, new ArrayList<TileEntity>(), theX * 16, theY * 16, theZ * 16, 100000); + /* + if (this.occlusionEnabled) + { + this.worldRenderers[(var6 * this.renderChunksTall + var5) * this.renderChunksWide + var4].glOcclusionQuery = this.glOcclusionQueryBase.get(var3); + }*/ + + wr.isWaitingOnOcclusionQuery = false; + wr.isVisible = true; + wr.isInFrustum = true; + wr.chunkIndex = 0; + wr.markDirty(); + wr.updateRenderer(Minecraft.getMinecraft().thePlayer); + } + //Tessellator.endSave(); + } + + static List<ChunkMesh> getChunkMesh(int theX, int theY, int theZ) { + WorldRenderer wr = new WorldRenderer(Minecraft.getMinecraft().theWorld, new ArrayList<TileEntity>(), theX * 16, theY * 16, theZ * 16, 100000); + + wr.isWaitingOnOcclusionQuery = false; + wr.isVisible = true; + wr.isInFrustum = true; + wr.chunkIndex = 0; + wr.markDirty(); + wr.updateRenderer(Minecraft.getMinecraft().thePlayer); + return ((IWorldRenderer)wr).getChunkMeshes(); + } + + public static class Flags { + boolean hasTexture; + boolean hasBrightness; + boolean hasColor; + boolean hasNormals; + + public Flags(byte flags) { + hasTexture = (flags & 1) != 0; + hasBrightness = (flags & 2) != 0; + hasColor = (flags & 4) != 0; + hasNormals = (flags & 8) != 0; + } + + public Flags(boolean hasTexture, boolean hasBrightness, boolean hasColor, boolean hasNormals) { + this.hasTexture = hasTexture; + this.hasBrightness = hasBrightness; + this.hasColor = hasColor; + this.hasNormals = hasNormals; + } + + public byte toByte() { + byte flags = 0; + if(hasTexture) { + flags |= 1; + } + if(hasBrightness) { + flags |= 2; + } + if(hasColor) { + flags |= 4; + } + if(hasNormals) { + flags |= 8; + } + return flags; + } + } + +} + diff --git a/src/main/java/makamys/lodmod/renderer/FarChunkCache.java b/src/main/java/makamys/lodmod/renderer/FarChunkCache.java new file mode 100644 index 0000000..f4e3cca --- /dev/null +++ b/src/main/java/makamys/lodmod/renderer/FarChunkCache.java @@ -0,0 +1,12 @@ +package makamys.lodmod.renderer; + +import net.minecraft.world.ChunkCache; +import net.minecraft.world.World; + +public class FarChunkCache extends ChunkCache { + + public FarChunkCache(World p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8) { + super(p1, p2, p3, p4, p5, p6, p7, p8); + } + +} diff --git a/src/main/java/makamys/lodmod/renderer/FarWorldRenderer.java b/src/main/java/makamys/lodmod/renderer/FarWorldRenderer.java new file mode 100644 index 0000000..72eb4fd --- /dev/null +++ b/src/main/java/makamys/lodmod/renderer/FarWorldRenderer.java @@ -0,0 +1,14 @@ +package makamys.lodmod.renderer; + +import java.util.List; + +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.world.World; + +public class FarWorldRenderer extends WorldRenderer { + + public FarWorldRenderer(World p1, List p2, int p3, int p4, int p5, int p6) { + super(p1, p2, p3, p4, p5, p6); + } + +} diff --git a/src/main/java/makamys/lodmod/renderer/LODChunk.java b/src/main/java/makamys/lodmod/renderer/LODChunk.java new file mode 100644 index 0000000..bae235c --- /dev/null +++ b/src/main/java/makamys/lodmod/renderer/LODChunk.java @@ -0,0 +1,67 @@ +package makamys.lodmod.renderer; + +import java.util.List; + +import net.minecraft.entity.Entity; +import net.minecraft.world.chunk.Chunk; + +public class LODChunk { + + int x, z; + Chunk chunk; + public boolean waitingForData = false; + int lod = 0; + boolean visible; + + SimpleChunkMesh simpleMesh; + ChunkMesh[] chunkMeshes = new ChunkMesh[32]; + + public LODChunk(int x, int z) { + this.x = x; + this.z = z; + } + + @Override + public String toString() { + return "LODChunk(" + x + ", " + z + ")"; + } + + public double distSq(Entity entity) { + return Math.pow(entity.posX - x * 16, 2) + Math.pow(entity.posZ - z * 16, 2); + } + + public void putChunkMeshes(int cy, List<ChunkMesh> newChunkMeshes) { + for(int i = 0; i < 2; i++) { + if(chunkMeshes[cy * 2 + i] != null) { + MyRenderer.setMeshVisible(chunkMeshes[cy * 2 + i], false); + chunkMeshes[cy * 2 + i] = null; + } + } + + for(int i = 0; i < newChunkMeshes.size(); i++) { + chunkMeshes[cy * 2 + i] = newChunkMeshes.get(i); + MyRenderer.sendMeshToGPU(newChunkMeshes.get(i)); + } + } + + public boolean hasChunkMeshes() { + for(ChunkMesh cm : chunkMeshes) { + if(cm != null) { + return true; + } + } + return false; + } + + public void tick(Entity player) { + double distSq = distSq(player); + if(distSq < Math.pow(32 * 16, 2)) { + MyRenderer.setLOD(this, 2); + } else if(distSq < Math.pow(64 * 16, 2)) { + MyRenderer.setLOD(this, 1); + } else { + MyRenderer.setVisible(this, false); + } + } + +} diff --git a/src/main/java/makamys/lodmod/renderer/LODRegion.java b/src/main/java/makamys/lodmod/renderer/LODRegion.java new file mode 100644 index 0000000..695202d --- /dev/null +++ b/src/main/java/makamys/lodmod/renderer/LODRegion.java @@ -0,0 +1,62 @@ +package makamys.lodmod.renderer; + +import net.minecraft.entity.Entity; +import net.minecraft.world.chunk.Chunk; + +public class LODRegion { + + private LODChunk[][] data = new LODChunk[32][32]; + + int regionX, regionZ; + + public LODRegion(int regionX, int regionZ) { + this.regionX = regionX; + this.regionZ = regionZ; + + for(int i = 0; i < 32; i++) { + for(int j = 0; j < 32; j++) { + data[i][j] = new LODChunk(regionX * 32 + i, regionZ * 32 + j); + } + } + } + + public static LODRegion load(int regionX, int regionZ) { + return new LODRegion(regionX, regionZ); // TODO + } + + public LODChunk getChunkAbsolute(int chunkXAbs, int chunkZAbs) { + return getChunk(chunkXAbs - regionX * 32, chunkZAbs - regionZ * 32); + } + + public LODChunk getChunk(int x, int z) { + if(x >= 0 && x < 32 && z >= 0 && z < 32) { + return data[x][z]; + } else { + return null; + } + } + + public LODChunk putChunk(Chunk chunk) { + int relX = chunk.xPosition - regionX * 32; + int relZ = chunk.zPosition - regionZ * 32; + + if(relX >= 0 && relX < 32 && relZ >= 0 && relZ < 32) { + data[relX][relZ].chunk = chunk; + data[relX][relZ].waitingForData = false; + return data[relX][relZ]; + } + return null; + } + + public void tick(Entity player) { + for(int i = 0; i < 32; i++) { + for(int j = 0; j < 32; j++) { + LODChunk chunk = data[i][j]; + if(chunk != null) { + chunk.tick(player); + } + } + } + } + +} diff --git a/src/main/java/makamys/lodmod/renderer/Mesh.java b/src/main/java/makamys/lodmod/renderer/Mesh.java new file mode 100644 index 0000000..be379f6 --- /dev/null +++ b/src/main/java/makamys/lodmod/renderer/Mesh.java @@ -0,0 +1,15 @@ +package makamys.lodmod.renderer; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +public abstract class Mesh { + + public ByteBuffer buffer; + public int quadCount; + public boolean visible; + public int iFirst, iCount; + + public abstract int getStride(); + +} diff --git a/src/main/java/makamys/lodmod/renderer/MeshQuad.java b/src/main/java/makamys/lodmod/renderer/MeshQuad.java new file mode 100644 index 0000000..a48815d --- /dev/null +++ b/src/main/java/makamys/lodmod/renderer/MeshQuad.java @@ -0,0 +1,424 @@ +package makamys.lodmod.renderer; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Comparator; +import java.util.Locale; +import java.util.Map; + +import makamys.lodmod.renderer.MeshQuad.QuadPlaneComparator; +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 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) { + 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 = MyRenderer.getSpriteIndexForUV(avgU, avgV); + sprite = MyRenderer.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]); + float y = Float.intBitsToFloat(rawBuffer[i + 1]); + float z = Float.intBitsToFloat(rawBuffer[i + 2]); + + 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]); + } + } + } + + // ma |
