Began rewriting of the render build subsystem
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.translucentGeometryBuffer != null) {
|
||||
this.translucentGeometryBuffer.free();
|
||||
if (this.buffer != null) {
|
||||
this.buffer.free();
|
||||
}
|
||||
}
|
||||
|
||||
public BuiltSectionGeometry clone() {
|
||||
return new BuiltSectionGeometry(this.position, this.geometryBuffer!=null?this.geometryBuffer.copy():null, this.translucentGeometryBuffer!=null?this.translucentGeometryBuffer.copy():null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user