Began rewriting of the render build subsystem

This commit is contained in:
mcrcortex
2024-01-25 20:01:25 +10:00
parent 77b5c91737
commit dd401ed15b
9 changed files with 405 additions and 348 deletions

View File

@@ -37,6 +37,9 @@ public class ModelManager {
//Model data might also contain a constant colour if the colour resolver produces a constant colour, this saves space in the
// section buffer reverse indexing
//model data also contains if a face should be randomly rotated,flipped etc to get rid of moire effect
// this would be done in the fragment shader
//The Meta-cache contains critical information needed for meshing, colour provider bit, per-face = is empty, has alpha, is solid, full width, full height
// alpha means that some pixels have alpha values and belong in the translucent rendering layer,
// is empty means that the face is air/shouldent be rendered as there is nothing there

View File

@@ -5,6 +5,7 @@ package me.cortex.zenith.client.core.rendering;
import me.cortex.zenith.client.core.gl.GlBuffer;
import me.cortex.zenith.client.core.model.ModelManager;
import me.cortex.zenith.client.core.rendering.building.BuiltSection;
import me.cortex.zenith.client.core.rendering.building.BuiltSectionGeometry;
import me.cortex.zenith.client.core.rendering.util.UploadStream;
import net.minecraft.client.MinecraftClient;
@@ -84,7 +85,7 @@ public abstract class AbstractFarWorldRenderer {
public abstract void renderFarAwayOpaque(MatrixStack stack, double cx, double cy, double cz);
public void enqueueResult(BuiltSectionGeometry result) {
public void enqueueResult(BuiltSection result) {
this.geometry.enqueueResult(result);
}

View File

@@ -4,6 +4,7 @@ import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.cortex.zenith.client.core.gl.GlBuffer;
import me.cortex.zenith.client.core.rendering.building.BuiltSection;
import me.cortex.zenith.client.core.rendering.building.BuiltSectionGeometry;
import me.cortex.zenith.client.core.rendering.util.BufferArena;
import me.cortex.zenith.client.core.rendering.util.UploadStream;
@@ -31,7 +32,7 @@ public class GeometryManager {
}
}
private final ConcurrentLinkedDeque<BuiltSectionGeometry> buildResults = new ConcurrentLinkedDeque<>();
private final ConcurrentLinkedDeque<BuiltSection> buildResults = new ConcurrentLinkedDeque<>();
private int sectionCount = 0;
private final Long2IntOpenHashMap pos2id = new Long2IntOpenHashMap();
@@ -48,15 +49,15 @@ public class GeometryManager {
this.pos2id.defaultReturnValue(-1);
}
public void enqueueResult(BuiltSectionGeometry sectionGeometry) {
public void enqueueResult(BuiltSection sectionGeometry) {
this.buildResults.add(sectionGeometry);
}
private SectionMeta createMeta(BuiltSectionGeometry geometry) {
long geometryPtr = this.geometryBuffer.upload(geometry.geometryBuffer);
private SectionMeta createMeta(BuiltSection geometry) {
long geometryPtr = this.geometryBuffer.upload(geometry.buffer);
//TODO: support translucent geometry
return new SectionMeta(geometry.position, geometryPtr, (int) (geometry.geometryBuffer.size/8), 0, -1,0, 0);
return new SectionMeta(geometry.position, geometryPtr, (int) (geometry.buffer.size/8), 0, -1,0, 0);
}
private void freeMeta(SectionMeta meta) {
@@ -71,7 +72,7 @@ public class GeometryManager {
void uploadResults() {
while (!this.buildResults.isEmpty()) {
var result = this.buildResults.pop();
boolean isDelete = result.geometryBuffer == null && result.translucentGeometryBuffer == null;
boolean isDelete = result.buffer == null && result.translucentGeometryBuffer == null;
if (isDelete) {
int id = -1;
if ((id = this.pos2id.remove(result.position)) != -1) {

View File

@@ -1,5 +1,6 @@
package me.cortex.zenith.client.core.rendering;
import me.cortex.zenith.client.core.rendering.building.BuiltSection;
import me.cortex.zenith.client.core.rendering.building.BuiltSectionGeometry;
import me.cortex.zenith.client.core.rendering.building.RenderGenerationService;
import me.cortex.zenith.common.world.WorldEngine;
@@ -41,7 +42,7 @@ public class RenderTracker {
//Removes a lvl 0 section from the world renderer
public void remLvl0(int x, int y, int z) {
this.activeSections.remove(WorldEngine.getWorldSectionId(0, x, y, z));
this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(0, x, y, z), null, null));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(0, x, y, z), null, null));
this.renderGen.removeTask(0, x, y, z);
}
@@ -63,14 +64,14 @@ public class RenderTracker {
this.renderGen.enqueueTask(lvl, x, y, z, this::shouldStillBuild, this::getBuildFlagsOrAbort);
this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)), null, null));
this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1), null, null));
this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)), null, null));
this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1), null, null));
this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)), null, null));
this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1), null, null));
this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)), null, null));
this.renderer.enqueueResult(new BuiltSectionGeometry(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1), null, null));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)), null, null));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1), null, null));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)), null, null));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1), null, null));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)), null, null));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1), null, null));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)), null, null));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1), null, null));
this.renderGen.removeTask(lvl-1, (x<<1), (y<<1), (z<<1));
@@ -95,7 +96,7 @@ public class RenderTracker {
this.activeSections.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1), O);
this.activeSections.remove(WorldEngine.getWorldSectionId(lvl, x, y, z));
this.renderer.enqueueResult(new BuiltSectionGeometry(lvl, x, y, z, null, null));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl, x, y, z), null, null));
this.renderGen.removeTask(lvl, x, y, z);
this.renderGen.enqueueTask(lvl - 1, (x<<1), (y<<1), (z<<1), this::shouldStillBuild, this::getBuildFlagsOrAbort);
@@ -134,7 +135,7 @@ public class RenderTracker {
//called by the RenderGenerationService about built geometry, the RenderTracker checks if it can use the result (e.g. the LoD hasnt changed/still correct etc)
// and dispatches it to the renderer
// it also batch collects the geometry sections until all the geometry for an operation is collected, then it executes the operation, its removes flickering
public void processBuildResult(BuiltSectionGeometry section) {
public void processBuildResult(BuiltSection section) {
//Check that we still want the section
if (this.activeSections.containsKey(section.position)) {
this.renderer.enqueueResult(section);

View File

@@ -0,0 +1,29 @@
package me.cortex.zenith.client.core.rendering.building;
import java.util.Objects;
//TODO: also have an AABB size stored
public final class BuiltSection {
public final long position;
public final BuiltSectionGeometry opaque;
public final BuiltSectionGeometry translucent;
public BuiltSection(long position, BuiltSectionGeometry opaque, BuiltSectionGeometry translucent) {
this.position = position;
this.opaque = opaque;
this.translucent = translucent;
}
public BuiltSection clone() {
return new BuiltSection(this.position, this.opaque != null ? this.opaque.clone() : null, this.translucent != null ? this.translucent.clone() : null);
}
public void free() {
if (this.opaque != null) {
this.opaque.free();
}
if (this.translucent != null) {
this.translucent.free();
}
}
}

View File

@@ -1,32 +1,21 @@
package me.cortex.zenith.client.core.rendering.building;
import me.cortex.zenith.common.util.MemoryBuffer;
import me.cortex.zenith.common.world.WorldEngine;
public class BuiltSectionGeometry {
public final long position;
public final MemoryBuffer geometryBuffer;
public final MemoryBuffer translucentGeometryBuffer;
import java.util.Arrays;
public BuiltSectionGeometry(int lvl, int x, int y, int z, MemoryBuffer geometryBuffer, MemoryBuffer translucentGeometryBuffer) {
this(WorldEngine.getWorldSectionId(lvl, x, y, z), geometryBuffer, translucentGeometryBuffer);
}
public BuiltSectionGeometry(long position, MemoryBuffer geometryBuffer, MemoryBuffer translucentGeometryBuffer) {
this.position = position;
this.geometryBuffer = geometryBuffer;
this.translucentGeometryBuffer = translucentGeometryBuffer;
/**
* @param startOffsets Will be converted to ending offsets when doing data computation
*/
public record BuiltSectionGeometry(MemoryBuffer buffer, short[] startOffsets) {
public BuiltSectionGeometry clone() {
return new BuiltSectionGeometry(this.buffer != null ? this.buffer.copy() : null, Arrays.copyOf(this.startOffsets, this.startOffsets.length));
}
public void free() {
if (this.geometryBuffer != null) {
this.geometryBuffer.free();
if (this.buffer != null) {
this.buffer.free();
}
if (this.translucentGeometryBuffer != null) {
this.translucentGeometryBuffer.free();
}
}
public BuiltSectionGeometry clone() {
return new BuiltSectionGeometry(this.position, this.geometryBuffer!=null?this.geometryBuffer.copy():null, this.translucentGeometryBuffer!=null?this.translucentGeometryBuffer.copy():null);
}
}

View File

@@ -1,23 +1,15 @@
package me.cortex.zenith.client.core.rendering.building;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import me.cortex.zenith.common.util.MemoryBuffer;
import me.cortex.zenith.client.core.util.Mesher2D;
import me.cortex.zenith.common.world.WorldEngine;
import me.cortex.zenith.common.world.WorldSection;
import me.cortex.zenith.common.world.other.Mapper;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.math.Direction;
import org.lwjgl.system.MemoryUtil;
public class RenderDataFactory {
private final Mesher2D mesher = new Mesher2D(5,15);//15
private final LongArrayList outData = new LongArrayList(1000);
private final WorldEngine world;
private final QuadEncoder encoder;
private final long[] sectionCache = new long[32*32*32];
private final long[] connectedSectionCache = new long[32*32*32];
private final QuadEncoder encoder;
public RenderDataFactory(WorldEngine world) {
this.world = world;
this.encoder = new QuadEncoder(world.getMapper(), MinecraftClient.getInstance().getBlockColors(), MinecraftClient.getInstance().world);
@@ -32,304 +24,10 @@ public class RenderDataFactory {
//buildMask in the lower 6 bits contains the faces to build, the next 6 bits are whether the edge face builds against
// its neigbor or not (0 if it does 1 if it doesnt (0 is default behavior))
public BuiltSectionGeometry generateMesh(WorldSection section, int buildMask) {
//TODO: to speed it up more, check like section.isEmpty() and stuff like that, have masks for if a slice/layer is entirly air etc
//TODO: instead of having it check its neighbors with the same lod level, compare against 1 level lower, this will prevent cracks and seams from
// appearing between lods
//if (section.definitelyEmpty()) {//Fast path if its known the entire chunk is empty
// return new BuiltSectionGeometry(section.getKey(), null, null);
//}
public BuiltSection generateMesh(WorldSection section, int buildMask) {
section.copyDataTo(this.sectionCache);
var data = this.sectionCache;
long[] connectedData = null;
int dirId = Direction.UP.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int y = 0; y < 32; y++) {
this.mesher.reset();
for (int z = 0; z < 32; z++) {
for (int x = 0; x < 32; x++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (y < 31) {
up = data[WorldSection.getIndex(x, y + 1, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (y == 31 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x, section.y + 1, section.z);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(x, 0, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
//Recodes the id to include the correct lighting
this.mesher.put(x, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 1, y, quad));
}
}
connectedData = null;
}
dirId = Direction.EAST.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int x = 0; x < 32; x++) {
this.mesher.reset();
for (int y = 0; y < 32; y++) {
for (int z = 0; z < 32; z++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (x < 31) {
up = data[WorldSection.getIndex(x + 1, y, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (x == 31 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x + 1, section.y, section.z);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(0, y, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
this.mesher.put(y, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 5, x, quad));
}
}
connectedData = null;
}
dirId = Direction.SOUTH.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int z = 0; z < 32; z++) {
this.mesher.reset();
for (int x = 0; x < 32; x++) {
for (int y = 0; y < 32; y++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (z < 31) {
up = data[WorldSection.getIndex(x, y, z + 1)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (z == 31 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x, section.y, section.z + 1);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(x, y, 0)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
this.mesher.put(x, y, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 3, z, quad));
}
}
connectedData = null;
}
dirId = Direction.WEST.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int x = 31; x != -1; x--) {
this.mesher.reset();
for (int y = 0; y < 32; y++) {
for (int z = 0; z < 32; z++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (x != 0) {
up = data[WorldSection.getIndex(x - 1, y, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (x == 0 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x - 1, section.y, section.z);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(31, y, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
this.mesher.put(y, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 4, x, quad));
}
}
connectedData = null;
}
dirId = Direction.NORTH.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int z = 31; z != -1; z--) {
this.mesher.reset();
for (int x = 0; x < 32; x++) {
for (int y = 0; y < 32; y++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (z != 0) {
up = data[WorldSection.getIndex(x, y, z - 1)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (z == 0 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x, section.y, section.z - 1);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(x, y, 31)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
this.mesher.put(x, y, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 2, z, quad));
}
}
connectedData = null;
}
dirId = Direction.DOWN.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int y = 31; y != -1; y--) {
this.mesher.reset();
for (int x = 0; x < 32; x++) {
for (int z = 0; z < 32; z++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (y != 0) {
up = data[WorldSection.getIndex(x, y - 1, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (y == 0 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x, section.y - 1, section.z);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(x, 31, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
this.mesher.put(x, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 0, y, quad));
}
}
connectedData = null;
}
if (this.outData.isEmpty()) {
return new BuiltSectionGeometry(section.getKey(), null, null);
}
var output = new MemoryBuffer(this.outData.size()*8L);
for (int i = 0; i < this.outData.size(); i++) {
MemoryUtil.memPutLong(output.address + i * 8L, this.outData.getLong(i));
}
this.outData.clear();
return new BuiltSectionGeometry(section.getKey(), output, null);
return new BuiltSection(section.getKey(), null, null);
}
}

View File

@@ -0,0 +1,335 @@
package me.cortex.zenith.client.core.rendering.building;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import me.cortex.zenith.common.util.MemoryBuffer;
import me.cortex.zenith.client.core.util.Mesher2D;
import me.cortex.zenith.common.world.WorldEngine;
import me.cortex.zenith.common.world.WorldSection;
import me.cortex.zenith.common.world.other.Mapper;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.math.Direction;
import org.lwjgl.system.MemoryUtil;
public class RenderDataFactoryOld {
private final Mesher2D mesher = new Mesher2D(5,15);//15
private final LongArrayList outData = new LongArrayList(1000);
private final WorldEngine world;
private final long[] sectionCache = new long[32*32*32];
private final long[] connectedSectionCache = new long[32*32*32];
private final QuadEncoder encoder;
public RenderDataFactoryOld(WorldEngine world) {
this.world = world;
this.encoder = new QuadEncoder(world.getMapper(), MinecraftClient.getInstance().getBlockColors(), MinecraftClient.getInstance().world);
}
//TODO: MAKE a render cache that caches each WorldSection directional face generation, cause then can just pull that directly
// instead of needing to regen the entire thing
//section is already acquired and gets released by the parent
//buildMask in the lower 6 bits contains the faces to build, the next 6 bits are whether the edge face builds against
// its neigbor or not (0 if it does 1 if it doesnt (0 is default behavior))
public void generateMesh(WorldSection section, int buildMask) {
//TODO: to speed it up more, check like section.isEmpty() and stuff like that, have masks for if a slice/layer is entirly air etc
//TODO: instead of having it check its neighbors with the same lod level, compare against 1 level lower, this will prevent cracks and seams from
// appearing between lods
//if (section.definitelyEmpty()) {//Fast path if its known the entire chunk is empty
// return new BuiltSectionGeometry(section.getKey(), null, null);
//}
section.copyDataTo(this.sectionCache);
var data = this.sectionCache;
long[] connectedData = null;
int dirId = Direction.UP.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int y = 0; y < 32; y++) {
this.mesher.reset();
for (int z = 0; z < 32; z++) {
for (int x = 0; x < 32; x++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (y < 31) {
up = data[WorldSection.getIndex(x, y + 1, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (y == 31 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x, section.y + 1, section.z);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(x, 0, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
//Recodes the id to include the correct lighting
this.mesher.put(x, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 1, y, quad));
}
}
connectedData = null;
}
dirId = Direction.EAST.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int x = 0; x < 32; x++) {
this.mesher.reset();
for (int y = 0; y < 32; y++) {
for (int z = 0; z < 32; z++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (x < 31) {
up = data[WorldSection.getIndex(x + 1, y, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (x == 31 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x + 1, section.y, section.z);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(0, y, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
this.mesher.put(y, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 5, x, quad));
}
}
connectedData = null;
}
dirId = Direction.SOUTH.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int z = 0; z < 32; z++) {
this.mesher.reset();
for (int x = 0; x < 32; x++) {
for (int y = 0; y < 32; y++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (z < 31) {
up = data[WorldSection.getIndex(x, y, z + 1)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (z == 31 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x, section.y, section.z + 1);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(x, y, 0)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
this.mesher.put(x, y, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 3, z, quad));
}
}
connectedData = null;
}
dirId = Direction.WEST.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int x = 31; x != -1; x--) {
this.mesher.reset();
for (int y = 0; y < 32; y++) {
for (int z = 0; z < 32; z++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (x != 0) {
up = data[WorldSection.getIndex(x - 1, y, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (x == 0 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x - 1, section.y, section.z);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(31, y, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
this.mesher.put(y, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 4, x, quad));
}
}
connectedData = null;
}
dirId = Direction.NORTH.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int z = 31; z != -1; z--) {
this.mesher.reset();
for (int x = 0; x < 32; x++) {
for (int y = 0; y < 32; y++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (z != 0) {
up = data[WorldSection.getIndex(x, y, z - 1)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (z == 0 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x, section.y, section.z - 1);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(x, y, 31)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
this.mesher.put(x, y, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 2, z, quad));
}
}
connectedData = null;
}
dirId = Direction.DOWN.getId();
if ((buildMask&(1<<dirId))!=0) {
for (int y = 31; y != -1; y--) {
this.mesher.reset();
for (int x = 0; x < 32; x++) {
for (int z = 0; z < 32; z++) {
var self = data[WorldSection.getIndex(x, y, z)];
if (Mapper.isAir(self)) {
continue;
}
long up = -1;
if (y != 0) {
up = data[WorldSection.getIndex(x, y - 1, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
if (y == 0 && ((buildMask>>(6+dirId))&1) == 0) {
//Load and copy the data into a local cache, TODO: optimize so its not doing billion of copies
if (connectedData == null) {
var connectedSection = this.world.acquire(section.lvl, section.x, section.y - 1, section.z);
connectedSection.copyDataTo(this.connectedSectionCache);
connectedData = this.connectedSectionCache;
connectedSection.release();
}
up = connectedData[WorldSection.getIndex(x, 31, z)];
if (!Mapper.isTranslucent(up)) {
continue;
}
}
this.mesher.put(x, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
}
}
var count = this.mesher.process();
var array = this.mesher.getArray();
for (int i = 0; i < count; i++) {
var quad = array[i];
this.outData.add(this.encoder.encode(this.mesher.getDataFromQuad(quad), 0, y, quad));
}
}
connectedData = null;
}
if (this.outData.isEmpty()) {
//return new BuiltSectionGeometry(section.getKey(), null, null);
}
var output = new MemoryBuffer(this.outData.size()*8L);
for (int i = 0; i < this.outData.size(); i++) {
MemoryUtil.memPutLong(output.address + i * 8L, this.outData.getLong(i));
}
this.outData.clear();
//return new BuiltSectionGeometry(section.getKey(), output, null);
}
}

View File

@@ -22,9 +22,9 @@ public class RenderGenerationService {
private final Semaphore taskCounter = new Semaphore(0);
private final WorldEngine world;
private final Consumer<BuiltSectionGeometry> resultConsumer;
private final Consumer<BuiltSection> resultConsumer;
public RenderGenerationService(WorldEngine world, int workers, Consumer<BuiltSectionGeometry> consumer) {
public RenderGenerationService(WorldEngine world, int workers, Consumer<BuiltSection> consumer) {
this.world = world;
this.resultConsumer = consumer;
this.workers = new Thread[workers];
@@ -36,7 +36,7 @@ public class RenderGenerationService {
}
}
private final ConcurrentHashMap<Long, BuiltSectionGeometry> renderCache = new ConcurrentHashMap<>(1000,0.75f,10);
private final ConcurrentHashMap<Long, BuiltSection> renderCache = new ConcurrentHashMap<>(1000,0.75f,10);
//TODO: add a generated render data cache
private void renderWorker() {
@@ -156,6 +156,6 @@ public class RenderGenerationService {
while (!this.taskQueue.isEmpty()) {
this.taskQueue.removeFirst();
}
this.renderCache.values().forEach(BuiltSectionGeometry::free);
this.renderCache.values().forEach(BuiltSection::free);
}
}