Remove old render stuff

This commit is contained in:
mcrcortex
2024-08-04 22:10:14 +10:00
parent e4fe056586
commit 0fdba1bf64
12 changed files with 11 additions and 1509 deletions

View File

@@ -1,460 +0,0 @@
package me.cortex.voxy.client.core;
//Contains the logic to determine what is loaded and at what LoD level, dispatches render changes
// also determines what faces are built etc
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import me.cortex.voxy.client.core.rendering.RenderTracker;
import me.cortex.voxy.client.core.util.RingUtil;
import net.minecraft.client.MinecraftClient;
//Can use ring logic
// i.e. when a player moves the rings of each lod change (how it was doing in the original attempt)
// also have it do directional quad culling and rebuild the chunk if needed (this shouldent happen very often) (the reason is to significantly reduce draw calls)
// make the rebuild range like +-5 chunks along each axis (that means at higher levels, should only need to rebuild like)
// 4 sections or something
public class DistanceTracker {
private final TransitionRing2D[] loDRings;
private final TransitionRing2D[] cacheLoadRings;
private final TransitionRing2D[] cacheUnloadRings;
private final TransitionRing2D mostOuterNonClampedRing;
private final RenderTracker tracker;
private final int minYSection;
private final int maxYSection;
private final int renderDistance;
public DistanceTracker(RenderTracker tracker, int[] lodRingScales, int renderDistance, int minY, int maxY) {
this.loDRings = new TransitionRing2D[lodRingScales.length];
this.cacheLoadRings = new TransitionRing2D[lodRingScales.length];
this.cacheUnloadRings = new TransitionRing2D[lodRingScales.length];
this.tracker = tracker;
this.minYSection = minY;
this.maxYSection = maxY;
this.renderDistance = renderDistance;
boolean wasRdClamped = false;
//The rings 0+ start at 64 vanilla rd, no matter what the game is set at, that is if the game is set to 32 rd
// there will still be 32 chunks untill the first lod drop
// if the game is set to 16, then there will be 48 chunks until the drop
for (int i = 0; i < this.loDRings.length; i++) {
int scaleP = lodRingScales[i];
boolean isTerminatingRing = ((lodRingScales[i]+2)<<(1+i) >= renderDistance)&&renderDistance>0;
if (isTerminatingRing) {
scaleP = Math.max(renderDistance >> (1+i), 1);
wasRdClamped = true;
}
int scale = scaleP;
//TODO: FIXME: check that the level shift is right when inc/dec
int capRing = i;
this.loDRings[i] = new TransitionRing2D((isTerminatingRing?5:6)+i, isTerminatingRing?scale<<1:scale, (x, z) -> {
if (isTerminatingRing) {
add(capRing, x, z);
} else
this.dec(capRing+1, x, z);
}, (x, z) -> {
if (isTerminatingRing) {
remove(capRing, x, z);
//remove(capRing, (x<<1), (z<<1));
} else
this.inc(capRing+1, x, z);
});
//TODO:FIXME i think the radius is wrong and (lodRingScales[i]) needs to be (lodRingScales[i]<<1) since the transition ring (the thing above)
// acts on LoD level + 1
//TODO: check this is actually working lmao and make it generate parent level lods on the exit instead of entry so it looks correct when flying backwards
if (!isTerminatingRing) {
//TODO: COMPLETLY REDO THE CACHING SYSTEM CAUSE THE LOGIC IS COMPLETLY INCORRECT
// we want basicly 2 rings offset by an amount such that when a position is near an lod transition point
// it will be meshed (both the higher and lower quality lods), enabling semless loading
// the issue is when to uncache these methods
/*
//TODO: FIX AND FINISH!!!
this.cacheLoadRings[i] = new TransitionRing2D(5 + i, (scale << 1) + 2, (x, z) -> {
//When entering a cache ring, trigger a mesh op and inject into cache
for (int y = this.minYSection >> capRing; y <= this.maxYSection >> capRing; y++) {
this.tracker.addCache(capRing, x, y, z);
}
}, (x, z) -> {
for (int y = this.minYSection >> capRing; y <= this.maxYSection >> capRing; y++) {
this.tracker.removeCache(capRing, x, y, z);
}
});
this.cacheUnloadRings[i] = new TransitionRing2D(5 + i, Math.max(1, (scale << 1) - 2), (x, z) -> {
for (int y = this.minYSection >> capRing; y <= this.maxYSection >> capRing; y++) {
this.tracker.removeCache(capRing, x, y, z);
}
}, (x, z) -> {
});
*/
}
if (isTerminatingRing) {
break;
}
}
if (!wasRdClamped) {
this.mostOuterNonClampedRing = new TransitionRing2D(5+this.loDRings.length, Math.max(renderDistance, 2048)>>this.loDRings.length, (x,z)->
add(this.loDRings.length, x, z), (x,z)->{
if (renderDistance > 0) {
remove(this.loDRings.length,x,z);
}
});
} else {
this.mostOuterNonClampedRing = null;
}
}
private void inc(int lvl, int x, int z) {
for (int y = this.minYSection>>lvl; y <= this.maxYSection>>lvl; y++) {
this.tracker.inc(lvl, x, y, z);
}
}
private void dec(int lvl, int x, int z) {
for (int y = this.minYSection>>lvl; y <= this.maxYSection>>lvl; y++) {
this.tracker.dec(lvl, x, y, z);
}
}
private void add(int lvl, int x, int z) {
for (int y = this.minYSection>>lvl; y <= this.maxYSection>>lvl; y++) {
this.tracker.add(lvl, x, y, z);
}
}
private void remove(int lvl, int x, int z) {
for (int y = this.minYSection>>lvl; y <= this.maxYSection>>lvl; y++) {
this.tracker.remove(lvl, x, y, z);
this.tracker.removeCache(lvl, x, y, z);
}
}
//How it works is there are N ring zones (one zone for each lod boundary)
// the transition zone is what determines what lods are rendered etc (and it biases higher lod levels cause its easier)
// the transition zone is only ever checked when the player moves 1<<(4+lodlvl) blocks, its position is set
//if the center suddenly changes (say more than 1<<(7+lodlvl) block) then invalidate the entire ring and recompute
// the lod sections
public void setCenter(int x, int y, int z) {
for (var ring : this.cacheLoadRings) {
if (ring!=null)
ring.update(x, z);
}
if (this.mostOuterNonClampedRing!=null)
this.mostOuterNonClampedRing.update(x, z);
//Update in reverse order (biggest lod to smallest lod)
for (int i = this.loDRings.length-1; -1<i; i-- ) {
var ring = this.loDRings[i];
if (ring != null)
ring.update(x, z);
}
for (var ring : this.cacheUnloadRings) {
if (ring!=null)
ring.update(x, z);
}
}
public void init(int x, int z) {
for (var ring : this.cacheLoadRings) {
if (ring != null)
ring.setCenter(x, z);
}
for (var ring : this.cacheUnloadRings) {
if (ring != null)
ring.setCenter(x, z);
}
for (var ring : this.loDRings) {
if (ring != null)
ring.setCenter(x, z);
}
if (this.mostOuterNonClampedRing!=null)
this.mostOuterNonClampedRing.setCenter(x, z);
var thread = new Thread(()-> {
for (var ring : this.cacheLoadRings) {
if (ring != null)
ring.fill(x, z);
}
for (var ring : this.cacheUnloadRings) {
if (ring != null)
ring.fill(x, z);
}
//This is an ungodly terrible hack to make the lods load in a semi ok order
for (var ring : this.loDRings)
if (ring != null)
ring.fill(x, z);
if (this.mostOuterNonClampedRing!=null)
this.mostOuterNonClampedRing.fill(x, z);
for (int i = this.loDRings.length - 1; 0 <= i; i--) {
if (this.loDRings[i] != null) {
this.loDRings[i].fill(x, z);
}
}
});
thread.setName("LoD Ring Initializer");
thread.start();
//TODO: FIXME: need to destory on shutdown
}
//TODO: add a new class thing that can track the central axis point so that
// geometry can be rebuilt with new flags with correct facing geometry built
// (could also make it so that it emits 3x the amount of draw calls, but that seems very bad idea)
private interface Transition2DCallback {
void callback(int x, int z);
}
private static final class TransitionRing2D {
private final int triggerRangeSquared;
private final int shiftSize;
private final Transition2DCallback enter;
private final Transition2DCallback exit;
private final int[] cornerPoints;
private final int radius;
private int lastUpdateX;
private int lastUpdateZ;
private int currentX;
private int currentZ;
//Note radius is in shiftScale
private TransitionRing2D(int shiftSize, int radius, Transition2DCallback onEntry, Transition2DCallback onExit) {
this(shiftSize, radius, onEntry, onExit, 0, 0, 0);
}
private TransitionRing2D(int shiftSize, int radius, Transition2DCallback onEntry, Transition2DCallback onExit, int ix, int iy, int iz) {
//trigger just less than every shiftSize scale
this.triggerRangeSquared = 1<<((shiftSize<<1) - 1);
this.shiftSize = shiftSize;
this.enter = onEntry;
this.exit = onExit;
this.cornerPoints = RingUtil.generatingBoundingCorner2D(radius);
this.radius = radius;
}
private long Prel(int x, int z) {
return (Integer.toUnsignedLong(this.currentZ + z)<<32)|Integer.toUnsignedLong(this.currentX + x);
}
public void update(int x, int z) {
long dx = this.lastUpdateX - x;
long dz = this.lastUpdateZ - z;
long distSquared = dx*dx + dz*dz;
if (distSquared < this.triggerRangeSquared) {
return;
}
//Update the last update position
int maxStep = this.triggerRangeSquared/2;
this.lastUpdateX += Math.min(maxStep,Math.max(-maxStep, x-this.lastUpdateX));
this.lastUpdateZ += Math.min(maxStep,Math.max(-maxStep, z-this.lastUpdateZ));
//Compute movement if it happened
int nx = x>>this.shiftSize;
int nz = z>>this.shiftSize;
if (nx == this.currentX && nz == this.currentZ) {
//No movement
return;
}
//FIXME: not right, needs to only call load/unload on entry and exit, cause atm its acting like a loaded circle
Long2IntOpenHashMap ops = new Long2IntOpenHashMap();
while (true) {
int dir = nz < this.currentZ ? -1 : 1;
if (nz != this.currentZ) {
for (int corner : this.cornerPoints) {
int cx = corner >>> 16;
int cz = corner & 0xFFFF;
ops.addTo(Prel(cx, cz + Math.max(0, dir)), dir);
ops.addTo(Prel(cx, -cz + Math.min(0, dir)), -dir);
if (cx != 0) {
ops.addTo(Prel(-cx, cz + Math.max(0, dir)), dir);
ops.addTo(Prel(-cx, -cz + Math.min(0, dir)), -dir);
}
}
this.currentZ += dir;
}
dir = nx < this.currentX ? -1 : 1;
if (nx != this.currentX) {
for (int corner : this.cornerPoints) {
int cx = corner & 0xFFFF;
int cz = corner >>> 16;
ops.addTo(Prel(cx + Math.max(0, dir), cz), dir);
ops.addTo(Prel(-cx + Math.min(0, dir), cz), -dir);
if (cz != 0) {
ops.addTo(Prel(cx + Math.max(0, dir), -cz), dir);
ops.addTo(Prel(-cx + Math.min(0, dir), -cz), -dir);
}
}
this.currentX += dir;
}
//Only break once the coords match
if (nx == this.currentX && nz == this.currentZ) {
break;
}
}
ops.forEach((pos,val)->{
if (val > 0) {
this.enter.callback((int) (long)pos, (int) (pos>>32));
}
if (val < 0) {
this.exit.callback((int) (long)pos, (int) (pos>>32));
}
});
ops.clear();
}
public void fill(int x, int z) {
this.fill(x, z, null);
}
public void fill(int x, int z, Transition2DCallback outsideCallback) {
int cx = x>>this.shiftSize;
int cz = z>>this.shiftSize;
int r2 = this.radius*this.radius;
for (int a = -this.radius; a <= this.radius; a++) {
//IntStream.range(-this.radius, this.radius+1).parallel().forEach(a->{
int b = (int) Math.floor(Math.sqrt(r2-(a*a)));
for (int c = -b; c <= b; c++) {
this.enter.callback(a + cx, c + cz);
}
if (outsideCallback != null) {
for (int c = -this.radius; c < -b; c++) {
outsideCallback.callback(a + cx, c + cz);
}
for (int c = b+1; c <= this.radius; c++) {
outsideCallback.callback(a + cx, c + cz);
}
}
}//);
}
public void setCenter(int x, int z) {
int cx = x>>this.shiftSize;
int cz = z>>this.shiftSize;
this.currentX = cx;
this.currentZ = cz;
this.lastUpdateX = x + (((int)(Math.random()*4))<<(this.shiftSize-4));
this.lastUpdateZ = z + (((int)(Math.random()*4))<<(this.shiftSize-4));
}
}
}
/*
public void update(int x, int z) {
int MAX_STEPS_PER_UPDATE = 1;
long dx = this.lastUpdateX - x;
long dz = this.lastUpdateZ - z;
long distSquared = dx*dx + dz*dz;
if (distSquared < this.triggerRangeSquared) {
return;
}
//TODO: fixme: this last update needs to be incremented by a delta since
//Update the last update position
int maxStep = this.triggerRangeSquared/2;
this.lastUpdateX += Math.min(maxStep,Math.max(-maxStep, x-this.lastUpdateX));
this.lastUpdateZ += Math.min(maxStep,Math.max(-maxStep, z-this.lastUpdateZ));
//Compute movement if it happened
int nx = x>>this.shiftSize;
int nz = z>>this.shiftSize;
if (nx == this.currentX && nz == this.currentZ) {
//No movement
return;
}
//FIXME: not right, needs to only call load/unload on entry and exit, cause atm its acting like a loaded circle
Long2IntOpenHashMap ops = new Long2IntOpenHashMap();
int zcount = MAX_STEPS_PER_UPDATE;
int dir = nz<this.currentZ?-1:1;
while (nz != this.currentZ) {
for (int corner : this.cornerPoints) {
int cx = corner>>>16;
int cz = corner&0xFFFF;
ops.addTo(Prel( cx, cz+Math.max(0, dir)), dir);
ops.addTo(Prel( cx,-cz+Math.min(0, dir)),-dir);
if (cx != 0) {
ops.addTo(Prel(-cx, cz+Math.max(0, dir)), dir);
ops.addTo(Prel(-cx,-cz+Math.min(0, dir)),-dir);
}
}
//ops.addTo(Prel(0, this.radius+Math.max(0, dir)), dir);
//ops.addTo(Prel(0, -this.radius+Math.min(0, dir)), -dir);
this.currentZ += dir;
if (--zcount == 0) break;
}
int xcount = MAX_STEPS_PER_UPDATE;
dir = nx<this.currentX?-1:1;
while (nx != this.currentX) {
for (int corner : this.cornerPoints) {
int cx = corner&0xFFFF;
int cz = corner>>>16;
ops.addTo(Prel( cx+Math.max(0, dir), cz), dir);
ops.addTo(Prel(-cx+Math.min(0, dir), cz),-dir);
if (cz != 0) {
ops.addTo(Prel(cx + Math.max(0, dir), -cz), dir);
ops.addTo(Prel(-cx + Math.min(0, dir), -cz), -dir);
}
}
this.currentX += dir;
if (--xcount == 0) break;
}
ops.forEach((pos,val)->{
if (val > 0) {
this.enter.callback((int) (long)pos, (int) (pos>>32));
}
if (val < 0) {
this.exit.callback((int) (long)pos, (int) (pos>>32));
}
});
ops.clear();
}*/

View File

@@ -1,203 +0,0 @@
package me.cortex.voxy.client.core.rendering;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractFarWorldRenderer;
import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.common.world.WorldSection;
//Tracks active sections, dispatches updates to the build system, everything related to rendering flows through here
public class RenderTracker {
private final WorldEngine world;
private RenderGenerationService renderGen;
private final AbstractFarWorldRenderer renderer;
private final LongSet[] sets;
public RenderTracker(WorldEngine world, AbstractFarWorldRenderer renderer) {
this.world = world;
this.renderer = renderer;
this.sets = new LongSet[1<<4];
for (int i = 0; i < this.sets.length; i++) {
this.sets[i] = new LongOpenHashSet();
}
}
public void setRenderGen(RenderGenerationService renderGen) {
this.renderGen = renderGen;
}
public static long mixStafford13(long seed) {
seed = (seed ^ seed >>> 30) * -4658895280553007687L;
seed = (seed ^ seed >>> 27) * -7723592293110705685L;
return seed ^ seed >>> 31;
}
private LongSet getSet(long key) {
return this.sets[(int) (mixStafford13(key) & (this.sets.length-1))];
}
private void put(long key) {
var set = this.getSet(key);
synchronized (set) {
set.add(key);
}
}
private void remove(long key) {
var set = this.getSet(key);
synchronized (set) {
set.remove(key);
}
}
private boolean contains(long key) {
var set = this.getSet(key);
synchronized (set) {
return set.contains(key);
}
}
//TODO: replace this:: with a class cached lambda ref (cause doing this:: still does a lambda allocation)
//Adds a lvl 0 section into the world renderer
public void addLvl0(int x, int y, int z) {
this.put(WorldEngine.getWorldSectionId(0, x, y, z));
this.renderGen.enqueueTask(0, x, y, z, this::shouldStillBuild);
}
//Removes a lvl 0 section from the world renderer
public void remLvl0(int x, int y, int z) {
this.remove(WorldEngine.getWorldSectionId(0, x, y, z));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(0, x, y, z)));
this.renderGen.removeTask(0, x, y, z);
}
//Increases from lvl-1 to lvl at the coordinates (which are in lvl space)
public void inc(int lvl, int x, int y, int z) {
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)));
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1));
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)));
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1));
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)));
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1));
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)));
this.remove(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1));
this.put(WorldEngine.getWorldSectionId(lvl, x, y, z));
//TODO: make a seperate object to hold the build data and link it with the location in a
// concurrent hashmap or something, this is so that e.g. the build data position
// can be updated
//TODO: replace this:: with a class cached lambda ref (cause doing this:: still does a lambda allocation)
this.renderGen.enqueueTask(lvl, x, y, z, this::shouldStillBuild);
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1))));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1)));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1))));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1)));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1))));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1)));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1))));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1)));
this.renderGen.removeTask(lvl-1, (x<<1), (y<<1), (z<<1));
this.renderGen.removeTask(lvl-1, (x<<1), (y<<1), (z<<1)+1);
this.renderGen.removeTask(lvl-1, (x<<1), (y<<1)+1, (z<<1));
this.renderGen.removeTask(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1);
this.renderGen.removeTask(lvl-1, (x<<1)+1, (y<<1), (z<<1));
this.renderGen.removeTask(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1);
this.renderGen.removeTask(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1));
this.renderGen.removeTask(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1);
}
//Decreases from lvl to lvl-1 at the coordinates (which are in lvl space)
public void dec(int lvl, int x, int y, int z) {
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)));
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1), (z<<1)+1));
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)));
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1), (y<<1)+1, (z<<1)+1));
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)));
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1), (z<<1)+1));
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)));
this.put(WorldEngine.getWorldSectionId(lvl-1, (x<<1)+1, (y<<1)+1, (z<<1)+1));
this.remove(WorldEngine.getWorldSectionId(lvl, x, y, z));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl, x, y, z)));
this.renderGen.removeTask(lvl, x, y, z);
//TODO: replace this:: with a class cached lambda ref (cause doing this:: still does a lambda allocation)
this.renderGen.enqueueTask(lvl - 1, (x<<1), (y<<1), (z<<1), this::shouldStillBuild);
this.renderGen.enqueueTask(lvl - 1, (x<<1), (y<<1), (z<<1)+1, this::shouldStillBuild);
this.renderGen.enqueueTask(lvl - 1, (x<<1), (y<<1)+1, (z<<1), this::shouldStillBuild);
this.renderGen.enqueueTask(lvl - 1, (x<<1), (y<<1)+1, (z<<1)+1, this::shouldStillBuild);
this.renderGen.enqueueTask(lvl - 1, (x<<1)+1, (y<<1), (z<<1), this::shouldStillBuild);
this.renderGen.enqueueTask(lvl - 1, (x<<1)+1, (y<<1), (z<<1)+1, this::shouldStillBuild);
this.renderGen.enqueueTask(lvl - 1, (x<<1)+1, (y<<1)+1, (z<<1), this::shouldStillBuild);
this.renderGen.enqueueTask(lvl - 1, (x<<1)+1, (y<<1)+1, (z<<1)+1, this::shouldStillBuild);
}
//Enqueues a renderTask for a section to cache the result
public void addCache(int lvl, int x, int y, int z) {
this.renderGen.markCache(lvl, x, y, z);
this.renderGen.enqueueTask(lvl, x, y, z, ((lvl1, x1, y1, z1) -> true));//TODO: replace the true identity lambda with a callback check to the render cache
}
//Removes the position from the cache
public void removeCache(int lvl, int x, int y, int z) {
this.renderGen.unmarkCache(lvl, x, y, z);
}
public void remove(int lvl, int x, int y, int z) {
this.remove(WorldEngine.getWorldSectionId(lvl, x, y, z));
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl, x, y, z)));
}
public void add(int lvl, int x, int y, int z) {
this.put(WorldEngine.getWorldSectionId(lvl, x, y, z));
//TODO: replace this:: with a class cached lambda ref (cause doing this:: still does a lambda allocation)
this.renderGen.enqueueTask(lvl, x, y, z, this::shouldStillBuild);
}
//Called by the world engine when a section gets dirtied
public void sectionUpdated(WorldSection section) {
if (this.contains(section.key)) {
//TODO:FIXME: if the section gets updated, that means that its neighbors might need to be updated aswell
// (due to block occlusion)
//TODO: FIXME: REBUILDING THE ENTIRE NEIGHBORS when probably only the internal layout changed is NOT SMART
this.renderGen.clearCache(section.lvl, section.x, section.y, section.z);
this.renderGen.clearCache(section.lvl, section.x-1, section.y, section.z);
this.renderGen.clearCache(section.lvl, section.x+1, section.y, section.z);
this.renderGen.clearCache(section.lvl, section.x, section.y, section.z-1);
this.renderGen.clearCache(section.lvl, section.x, section.y, section.z+1);
//TODO: replace this:: with a class cached lambda ref (cause doing this:: still does a lambda allocation)
this.renderGen.enqueueTask(section.lvl, section.x, section.y, section.z, this::shouldStillBuild);
this.renderGen.enqueueTask(section.lvl, section.x-1, section.y, section.z, this::shouldStillBuild);
this.renderGen.enqueueTask(section.lvl, section.x+1, section.y, section.z, this::shouldStillBuild);
this.renderGen.enqueueTask(section.lvl, section.x, section.y, section.z-1, this::shouldStillBuild);
this.renderGen.enqueueTask(section.lvl, section.x, section.y, section.z+1, this::shouldStillBuild);
}
//this.renderGen.enqueueTask(section);
}
//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(BuiltSection section) {
//Check that we still want the section
if (this.contains(section.position)) {
this.renderer.enqueueResult(section);
} else {
section.free();
}
}
public boolean shouldStillBuild(int lvl, int x, int y, int z) {
return this.contains(WorldEngine.getWorldSectionId(lvl, x, y, z));
}
}

View File

@@ -1,167 +0,0 @@
package me.cortex.voxy.client.core.rendering.geometry.OLD;
//NOTE: an idea on how to do it is so that any render section, we _keep_ aquired (yes this will be very memory intensive)
// could maybe tosomething else
import com.mojang.blaze3d.systems.RenderSystem;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.model.ModelFactory;
import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.world.other.Mapper;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.Frustum;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
import org.joml.FrustumIntersection;
import org.lwjgl.system.MemoryUtil;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import static org.lwjgl.opengl.ARBMultiDrawIndirect.glMultiDrawElementsIndirect;
import static org.lwjgl.opengl.GL30.*;
//can make it so that register the key of the sections we have rendered, then when a section changes and is registered,
// dispatch an update to the render section data builder which then gets consumed by the render system and updates
// the rendered data
//Contains all the logic to render the world and manage gpu memory
// processes section load,unload,update render data and renders the world each frame
//Todo: tinker with having the compute shader where each thread is a position to render? maybe idk
public abstract class AbstractFarWorldRenderer <T extends Viewport, J extends AbstractGeometryManager> {
public static final int STATIC_VAO = glGenVertexArrays();
protected final GlBuffer uniformBuffer;
protected final J geometry;
protected final ModelFactory models;
protected final GlBuffer lightDataBuffer;
protected final int maxSections;
//Current camera base level section position
protected int sx;
protected int sy;
protected int sz;
protected FrustumIntersection frustum;
//private final List<T> viewports = new ArrayList<>();
protected IntArrayList updatedSectionIds;
private final ConcurrentLinkedDeque<Mapper.StateEntry> blockStateUpdates = new ConcurrentLinkedDeque<>();
private final ConcurrentLinkedDeque<Mapper.BiomeEntry> biomeUpdates = new ConcurrentLinkedDeque<>();
public AbstractFarWorldRenderer(ModelFactory models, J geometry) {
this.maxSections = geometry.getMaxSections();
this.uniformBuffer = new GlBuffer(1024);
this.lightDataBuffer = new GlBuffer(256*4);//256 of uint
this.geometry = geometry;
this.models = models;
}
public void setupRender(Frustum frustum, Camera camera) {
this.frustum = frustum.frustumIntersection;
this.sx = camera.getBlockPos().getX() >> 5;
this.sy = camera.getBlockPos().getY() >> 5;
this.sz = camera.getBlockPos().getZ() >> 5;
//TODO: move this to a render function that is only called
// once per frame when using multi viewport mods
//it shouldent matter if its called multiple times a frame however, as its synced with fences
UploadStream.INSTANCE.tick();
DownloadStream.INSTANCE.tick();
//Update the lightmap
{
long upload = UploadStream.INSTANCE.upload(this.lightDataBuffer, 0, 256*4);
var lmt = MinecraftClient.getInstance().gameRenderer.getLightmapTextureManager().texture.getImage();
for (int light = 0; light < 256; light++) {
int x = light&0xF;
int y = ((light>>4)&0xF);
int sample = lmt.getColor(x,y);
sample = ((sample&0xFF0000)>>16)|(sample&0xFF00)|((sample&0xFF)<<16);
MemoryUtil.memPutInt(upload + (((x<<4)|(15-y))*4), sample|(0xFF<<28));//Skylight is inverted
}
}
//Upload any new geometry
this.updatedSectionIds = this.geometry.uploadResults();
{
boolean didHaveBiomeChange = false;
//Do any BiomeChanges
while (!this.biomeUpdates.isEmpty()) {
var update = this.biomeUpdates.pop();
var biomeReg = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME);
this.models.addBiome(update.id, biomeReg.get(Identifier.of(update.biome)));
didHaveBiomeChange = true;
}
if (didHaveBiomeChange) {
UploadStream.INSTANCE.commit();
}
int maxUpdatesPerFrame = 40;
//Do any BlockChanges
while ((!this.blockStateUpdates.isEmpty()) && (maxUpdatesPerFrame-- > 0)) {
var update = this.blockStateUpdates.pop();
//this.models.addEntry(update.id, update.state);
System.err.println("DEFUNKED: " + update);
}
//this.models.bakery.renderFaces(Blocks.ROSE_BUSH.getDefaultState(), 1234, false);
}
//TODO: fix this in a better way than this ungodly hacky stuff, causes clouds to dissapear
//RenderSystem.setShaderFogColor(1f, 1f, 1f, 0f);
RenderSystem.setShaderFogEnd(99999999);
RenderSystem.setShaderFogStart(9999999);
}
public abstract void renderFarAwayOpaque(T viewport);
public abstract void renderFarAwayTranslucent(T viewport);
public void enqueueResult(BuiltSection result) {
this.geometry.enqueueResult(result);
}
public void addBlockState(Mapper.StateEntry entry) {
this.blockStateUpdates.add(entry);
}
public void addBiome(Mapper.BiomeEntry entry) {
this.biomeUpdates.add(entry);
}
public void addDebugData(List<String> debug) {
debug.add("Geometry buffer usage: " + ((float)Math.round((this.geometry.getGeometryBufferUsage()*100000))/1000) + "%");
debug.add("Render Sections: " + this.geometry.getSectionCount());
}
public void shutdown() {
this.geometry.free();
this.uniformBuffer.free();
this.lightDataBuffer.free();
}
public final T createViewport() {
var viewport = createViewport0();
//this.viewports.add(viewport);
return viewport;
}
protected abstract T createViewport0();
public boolean generateMeshlets() {
return false;
}
}

View File

@@ -1,35 +0,0 @@
package me.cortex.voxy.client.core.rendering.geometry.OLD;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
import java.util.concurrent.ConcurrentLinkedDeque;
public abstract class AbstractGeometryManager {
protected int sectionCount = 0;
protected final int maxSections;
protected final ConcurrentLinkedDeque<BuiltSection> buildResults = new ConcurrentLinkedDeque<>();
protected AbstractGeometryManager(int maxSections) {
this.maxSections = maxSections;
}
abstract IntArrayList uploadResults();
int getMaxSections() {
return this.maxSections;
}
public void enqueueResult(BuiltSection sectionGeometry) {
this.buildResults.add(sectionGeometry);
}
public abstract float getGeometryBufferUsage();
public int getSectionCount() {
return this.sectionCount;
}
public abstract void free();
}

View File

@@ -1,185 +0,0 @@
package me.cortex.voxy.client.core.rendering.geometry.OLD;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
import me.cortex.voxy.client.core.rendering.util.BufferArena;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import net.minecraft.client.MinecraftClient;
import net.minecraft.text.Text;
import org.lwjgl.system.MemoryUtil;
public class DefaultGeometryManager extends AbstractGeometryManager {
private static final int SECTION_METADATA_SIZE = 32;
private final Long2IntOpenHashMap pos2id = new Long2IntOpenHashMap();
private final LongArrayList id2pos = new LongArrayList();
private final ObjectArrayList<SectionMeta> sectionMetadata = new ObjectArrayList<>();
private final IntArrayList markSectionIds = new IntArrayList();//Section ids to mark as visible (either due to being new, or swapping)
private final GlBuffer sectionMetaBuffer;
private final BufferArena geometryBuffer;
private final int geometryElementSize;
public DefaultGeometryManager(long geometryBufferSize, int maxSections) {
this(geometryBufferSize, maxSections, 8);//8 is default quad size
}
public DefaultGeometryManager(long geometryBufferSize, int maxSections, int elementSize) {
super(maxSections);
this.sectionMetaBuffer = new GlBuffer(((long) maxSections) * SECTION_METADATA_SIZE);
this.geometryBuffer = new BufferArena(geometryBufferSize, elementSize);
this.pos2id.defaultReturnValue(-1);
this.geometryElementSize = elementSize;
}
IntArrayList uploadResults() {
this.markSectionIds.clear();
while (!this.buildResults.isEmpty()) {
var result = this.buildResults.pop();
boolean isDelete = result.isEmpty();
if (isDelete) {
int id = -1;
if ((id = this.pos2id.remove(result.position)) != -1) {
if (this.id2pos.getLong(id) != result.position) {
throw new IllegalStateException("Removed position id not the same requested");
}
var meta = this.sectionMetadata.get(id);
this.freeMeta(meta);
this.sectionCount--;
if (id == this.sectionCount) {
//if we are at the end of the array dont have to do anything (maybe just upload a blank data, just to be sure)
//Remove the last element
this.sectionMetadata.remove(id);
this.id2pos.removeLong(id);
} else {
long swapLodPos = this.id2pos.getLong(this.sectionCount);
this.pos2id.put(swapLodPos, id);
this.id2pos.set(id, swapLodPos);
//Remove from the lists
this.id2pos.removeLong(this.sectionCount);
var swapMeta = this.sectionMetadata.remove(this.sectionCount);
this.sectionMetadata.set(id, swapMeta);
if (swapMeta.position != swapLodPos) {
throw new IllegalStateException();
}
long ptr = UploadStream.INSTANCE.upload(this.sectionMetaBuffer, (long) SECTION_METADATA_SIZE * id, SECTION_METADATA_SIZE);
swapMeta.writeMetadata(ptr);
this.markSectionIds.add(id);
}
}
} else {
int id = -1;
if ((id = this.pos2id.get(result.position)) != -1) {
//Update the existing data
var meta = this.sectionMetadata.get(id);
if (meta.position != result.position) {
throw new IllegalStateException("Meta position != result position");
}
//Delete the old data
this.freeMeta(meta);
//Create the new meta
meta = this.createMeta(result);
if (meta == null) {
continue;
}
this.sectionMetadata.set(id, meta);
long ptr = UploadStream.INSTANCE.upload(this.sectionMetaBuffer, (long)SECTION_METADATA_SIZE * id, SECTION_METADATA_SIZE);
meta.writeMetadata(ptr);
} else {
//Create the new meta
var meta = this.createMeta(result);
if (meta == null) {
continue;
}
//Add to the end of the array
id = this.sectionCount++;
this.pos2id.put(result.position, id);
this.id2pos.add(result.position);
this.sectionMetadata.add(meta);
long ptr = UploadStream.INSTANCE.upload(this.sectionMetaBuffer, (long)SECTION_METADATA_SIZE * id, SECTION_METADATA_SIZE);
meta.writeMetadata(ptr);
this.markSectionIds.add(id);
}
}
//Assert some invarients
if (this.id2pos.size() != this.sectionCount || this.sectionCount != this.pos2id.size()) {
throw new IllegalStateException("Invariants broken");
}
result.free();
}
return this.markSectionIds;
}
public void free() {
while (!this.buildResults.isEmpty()) {
this.buildResults.pop().free();
}
this.sectionMetaBuffer.free();
this.geometryBuffer.free();
}
public int geometryId() {
return this.geometryBuffer.id();
}
public int metaId() {
return this.sectionMetaBuffer.id;
}
public float getGeometryBufferUsage() {
return this.geometryBuffer.usage();
}
//=========================================================================================================================================================================================
//=========================================================================================================================================================================================
//=========================================================================================================================================================================================
//TODO: pack the offsets of each axis so that implicit face culling can work
//Note! the opaquePreDataCount and translucentPreDataCount are never writen to the meta buffer, as they are indexed in reverse relative to the base opaque and translucent geometry
protected record SectionMeta(long position, int aabb, int geometryPtr, int size, int[] offsets) {
public void writeMetadata(long ptr) {
//THIS IS DUE TO ENDIANNESS and that we are splitting a long into 2 ints
MemoryUtil.memPutInt(ptr, (int) (this.position>>32)); ptr += 4;
MemoryUtil.memPutInt(ptr, (int) this.position); ptr += 4;
MemoryUtil.memPutInt(ptr, (int) this.aabb); ptr += 4;
MemoryUtil.memPutInt(ptr, this.geometryPtr + this.offsets[0]); ptr += 4;
MemoryUtil.memPutInt(ptr, (this.offsets[1]-this.offsets[0])|((this.offsets[2]-this.offsets[1])<<16)); ptr += 4;
MemoryUtil.memPutInt(ptr, (this.offsets[3]-this.offsets[2])|((this.offsets[4]-this.offsets[3])<<16)); ptr += 4;
MemoryUtil.memPutInt(ptr, (this.offsets[5]-this.offsets[4])|((this.offsets[6]-this.offsets[5])<<16)); ptr += 4;
MemoryUtil.memPutInt(ptr, (this.offsets[7]-this.offsets[6])|((this.size -this.offsets[7])<<16)); ptr += 4;
}
}
protected SectionMeta createMeta(BuiltSection geometry) {
int geometryPtr = (int) this.geometryBuffer.upload(geometry.geometryBuffer);
if (geometryPtr == -1) {
String msg = "Buffer arena out of memory, please increase it in settings or decrease LoD quality";
MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(Text.literal(msg));
System.err.println(msg);
return null;
}
return new SectionMeta(geometry.position, geometry.aabb, geometryPtr, (int) (geometry.geometryBuffer.size/this.geometryElementSize), geometry.offsets);
}
protected void freeMeta(SectionMeta meta) {
if (meta.geometryPtr != -1) {
this.geometryBuffer.free(meta.geometryPtr);
}
}
}

View File

@@ -1,226 +0,0 @@
package me.cortex.voxy.client.core.rendering.geometry.OLD;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.model.ModelFactory;
import me.cortex.voxy.client.core.rendering.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection;
import net.minecraft.client.render.RenderLayer;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import org.lwjgl.system.MemoryUtil;
import static org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB;
import static org.lwjgl.opengl.ARBIndirectParameters.glMultiDrawElementsIndirectCountARB;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_SHORT;
import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate;
import static org.lwjgl.opengl.GL30.glBindVertexArray;
import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER;
import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER;
import static org.lwjgl.opengl.GL42.*;
import static org.lwjgl.opengl.GL42.GL_FRAMEBUFFER_BARRIER_BIT;
import static org.lwjgl.opengl.GL43.*;
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
import static org.lwjgl.opengl.GL45.glBindTextureUnit;
import static org.lwjgl.opengl.GL45.glClearNamedBufferData;
import static org.lwjgl.opengl.GL45C.nglClearNamedBufferData;
public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer<Gl46Viewport, DefaultGeometryManager> {
private final Shader commandGen = Shader.make()
.add(ShaderType.COMPUTE, "voxy:lod/gl46/cmdgen.comp")
.compile();
private final Shader lodShader = Shader.make()
.add(ShaderType.VERTEX, "voxy:lod/gl46/quads2.vert")
.add(ShaderType.FRAGMENT, "voxy:lod/gl46/quads.frag")
.compile();
//TODO: Note the cull shader needs a different element array since its rastering cubes not quads
private final Shader cullShader = Shader.make()
.add(ShaderType.VERTEX, "voxy:lod/gl46/cull/raster.vert")
.add(ShaderType.FRAGMENT, "voxy:lod/gl46/cull/raster.frag")
.compile();
private final GlBuffer glCommandBuffer;
private final GlBuffer glCommandCountBuffer;
public Gl46FarWorldRenderer(ModelFactory modelManager, int geometryBuffer, int maxSections) {
super(modelManager, new DefaultGeometryManager(geometryBuffer*8L, maxSections));
this.glCommandBuffer = new GlBuffer(maxSections*5L*4 * 6);
this.glCommandCountBuffer = new GlBuffer(4*2);
nglClearNamedBufferData(this.glCommandBuffer.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
}
protected void bindResources(Gl46Viewport viewport) {
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometry.geometryId());
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.glCommandBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, this.glCommandCountBuffer.id);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.geometry.metaId());
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.visibilityBuffer.id);
//glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.models.getBufferId());
//glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, this.models.getColourBufferId());
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, this.lightDataBuffer.id);//Lighting LUT
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.glCommandBuffer.id);
glBindBuffer(GL_PARAMETER_BUFFER_ARB, this.glCommandCountBuffer.id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id());
//Bind the texture atlas
//glBindSampler(0, this.models.getSamplerId());
//glBindTextureUnit(0, this.models.getTextureId());
}
//FIXME: dont do something like this as it breaks multiviewport mods
// the issue is that voxy expects the counter to be incremented by one each frame (the compute shader generating the commands)
// checks `frameId - 1`, this means in a multiviewport, effectivly only the stuff that was marked visible by the last viewport
// would be visible in the current viewport (if there are 2 viewports)
//To fix the issue, need to make a viewport api, that independently tracks the frameId and has its own glVisibilityBuffer buffer
private void updateUniformBuffer(Gl46Viewport viewport) {
long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, this.uniformBuffer.size());
var mat = new Matrix4f(viewport.projection).mul(viewport.modelView);
var innerTranslation = new Vector3f((float) (viewport.cameraX-(this.sx<<5)), (float) (viewport.cameraY-(this.sy<<5)), (float) (viewport.cameraZ-(this.sz<<5)));
mat.translate(-innerTranslation.x, -innerTranslation.y, -innerTranslation.z);
mat.getToAddress(ptr); ptr += 4*4*4;
MemoryUtil.memPutInt(ptr, this.sx); ptr += 4;
MemoryUtil.memPutInt(ptr, this.sy); ptr += 4;
MemoryUtil.memPutInt(ptr, this.sz); ptr += 4;
MemoryUtil.memPutInt(ptr, this.geometry.getSectionCount()); ptr += 4;
var planes = ((AccessFrustumIntersection)this.frustum).getPlanes();
for (var plane : planes) {
plane.getToAddress(ptr); ptr += 4*4;
}
innerTranslation.getToAddress(ptr); ptr += 4*3;
MemoryUtil.memPutInt(ptr, viewport.frameId++); ptr += 4;
}
public void renderFarAwayOpaque(Gl46Viewport viewport) {
if (this.geometry.getSectionCount() == 0) {
return;
}
{//Mark all of the updated sections as being visible from last frame
for (int id : this.updatedSectionIds) {
long ptr = UploadStream.INSTANCE.upload(viewport.visibilityBuffer, id * 4L, 4);
MemoryUtil.memPutInt(ptr, viewport.frameId - 1);//(visible from last frame)
}
}
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
//this.models.bakery.renderFaces(Blocks.WATER.getDefaultState().with(FluidBlock.LEVEL, 1), 1234, true);
//this.models.bakery.renderFaces(Blocks.CHEST.getDefaultState(), 1234, false);
RenderLayer.getCutoutMipped().startDrawing();
//RenderSystem.enableBlend();
//RenderSystem.defaultBlendFunc();
this.updateUniformBuffer(viewport);
UploadStream.INSTANCE.commit();
glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO);
nglClearNamedBufferData(this.glCommandCountBuffer.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
this.commandGen.bind();
this.bindResources(viewport);
glDispatchCompute((this.geometry.getSectionCount()+127)/128, 1, 1);
glMemoryBarrier(GL_COMMAND_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT | GL_UNIFORM_BARRIER_BIT);
this.lodShader.bind();
this.bindResources(viewport);
glDisable(GL_CULL_FACE);
//glPointSize(10);
//TODO: replace glMultiDrawElementsIndirectCountARB with glMultiDrawElementsIndirect on intel gpus, since it performs so much better
//glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, 0, drawCnt, 0);
//glLineWidth(2);
glMultiDrawElementsIndirectCountARB(GL_TRIANGLES, GL_UNSIGNED_SHORT, 0, 0, (int) (this.geometry.getSectionCount()*4.4), 0);
glEnable(GL_CULL_FACE);
/*
DownloadStream.INSTANCE.download(this.glCommandCountBuffer, 0, 4, (ptr, siz) -> {
int cnt = MemoryUtil.memGetInt(ptr);
drawCnt = cnt;
});
DownloadStream.INSTANCE.commit();
*/
glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT);
this.cullShader.bind();
this.bindResources(viewport);
glColorMask(false, false, false, false);
glDepthMask(false);
glDrawElementsInstanced(GL_TRIANGLES, 6 * 2 * 3, GL_UNSIGNED_BYTE, (1 << 16) * 6 * 2, this.geometry.getSectionCount());
glDepthMask(true);
glColorMask(true, true, true, true);
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
//TODO: need to do temporal rasterization here
glBindVertexArray(0);
glBindSampler(0, 0);
glBindTextureUnit(0, 0);
RenderLayer.getCutoutMipped().endDrawing();
}
@Override
public void renderFarAwayTranslucent(Gl46Viewport viewport) {
RenderLayer.getTranslucent().startDrawing();
glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
//TODO: maybe change this so the alpha isnt applied in the same way or something?? since atm the texture bakery uses a very hacky
// blend equation to make it avoid double applying translucency
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
//glBindSampler(0, this.models.getSamplerId());
//glBindTextureUnit(0, this.models.getTextureId());
//RenderSystem.blendFunc(GlStateManager.SrcFactor.ONE, GlStateManager.DstFactor.ONE);
this.lodShader.bind();
this.bindResources(viewport);
glMultiDrawElementsIndirectCountARB(GL_TRIANGLES, GL_UNSIGNED_SHORT, 400_000 * 4 * 5, 4, this.geometry.getSectionCount(), 0);
glEnable(GL_CULL_FACE);
glBindVertexArray(0);
glBindSampler(0, 0);
glBindTextureUnit(0, 0);
glDisable(GL_BLEND);
RenderLayer.getTranslucent().endDrawing();
}
protected Gl46Viewport createViewport0() {
return new Gl46Viewport(this);
}
@Override
public void shutdown() {
super.shutdown();
this.commandGen.free();
this.lodShader.free();
this.cullShader.free();
this.glCommandBuffer.free();
this.glCommandCountBuffer.free();
}
}

View File

@@ -1,193 +0,0 @@
package me.cortex.voxy.client.core.rendering.geometry.OLD;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.shader.PrintfInjector;
import me.cortex.voxy.client.core.model.ModelFactory;
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
import me.cortex.voxy.client.core.rendering.hierarchical.DebugRenderer;
import me.cortex.voxy.client.core.rendering.hierarchical.HierarchicalOcclusionRenderer;
import me.cortex.voxy.client.core.rendering.hierarchical.INodeInteractor;
import me.cortex.voxy.client.core.rendering.hierarchical.MeshManager;
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
import me.cortex.voxy.client.core.rendering.util.UploadStream;
import me.cortex.voxy.common.world.WorldEngine;
import me.cortex.voxy.common.world.other.Mapper;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.Camera;
import net.minecraft.client.render.Frustum;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.util.Identifier;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.Consumer;
import static org.lwjgl.opengl.GL31.glDrawElementsInstanced;
import static org.lwjgl.opengl.GL40.glDrawElementsIndirect;
import static org.lwjgl.opengl.GL43.*;
public class Gl46HierarchicalRenderer {
private final HierarchicalOcclusionRenderer sectionSelector;
private final MeshManager meshManager = new MeshManager();
private final List<String> printfQueue = new ArrayList<>();
private final PrintfInjector printf = new PrintfInjector(100000, 10, line->{
if (line.startsWith("LOG")) {
System.err.println(line);
}
this.printfQueue.add(line);
}, this.printfQueue::clear);
private final GlBuffer renderSections = new GlBuffer(100_000 * 4 + 4).zero();
private final GlBuffer debugNodeQueue = new GlBuffer(1000000*4+4).zero();
private final DebugRenderer debugRenderer = new DebugRenderer();
private final ConcurrentLinkedDeque<Mapper.StateEntry> blockStateUpdates = new ConcurrentLinkedDeque<>();
private final ConcurrentLinkedDeque<Mapper.BiomeEntry> biomeUpdates = new ConcurrentLinkedDeque<>();
protected final ConcurrentLinkedDeque<BuiltSection> buildResults = new ConcurrentLinkedDeque<>();
private final ModelFactory modelManager;
private RenderGenerationService sectionGenerationService;
private Consumer<BuiltSection> resultConsumer;
public Gl46HierarchicalRenderer(ModelFactory model) {
this.modelManager = model;
this.sectionSelector = new HierarchicalOcclusionRenderer(new INodeInteractor() {
public void watchUpdates(long pos) {
//System.err.println("Watch: " + pos);
}
public void unwatchUpdates(long pos) {
//System.err.println("Unwatch: " + pos);
}
public void requestMesh(long pos) {
Gl46HierarchicalRenderer.this.sectionGenerationService.enqueueTask(
WorldEngine.getLevel(pos),
WorldEngine.getX(pos),
WorldEngine.getY(pos),
WorldEngine.getZ(pos)
);
}
public void setMeshUpdateCallback(Consumer<BuiltSection> mesh) {
Gl46HierarchicalRenderer.this.resultConsumer = mesh;
}
}, this.meshManager, this.printf);
}
public void setupRender(Frustum frustum, Camera camera) {
{//Tick upload and download queues
UploadStream.INSTANCE.tick();
DownloadStream.INSTANCE.tick();
}
}
public void renderFarAwayOpaque(Gl46HierarchicalViewport viewport) {
//Process all the build results
while (!this.buildResults.isEmpty()) {
this.resultConsumer.accept(this.buildResults.pop());
}
//Render terrain from previous frame (renderSections)
if (true) {//Run the hierarchical selector over the buffer to generate the set of render sections
var i = new int[1];
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, i);
this.sectionSelector.doHierarchicalTraversalSelection(viewport, i[0], this.renderSections, this.debugNodeQueue);
this.debugRenderer.render(viewport, this.sectionSelector.getNodeDataBuffer(), this.debugNodeQueue);
}
this.printf.download();
}
public void renderFarAwayTranslucent(Gl46HierarchicalViewport viewport) {
}
public void addDebugData(List<String> debug) {
debug.add("Printf Queue: ");
debug.addAll(this.printfQueue);
this.printfQueue.clear();
}
public void addBlockState(Mapper.StateEntry stateEntry) {
this.blockStateUpdates.add(stateEntry);
}
public void addBiome(Mapper.BiomeEntry biomeEntry) {
this.biomeUpdates.add(biomeEntry);
}
public void processBuildResult(BuiltSection section) {
this.buildResults.add(section);
}
public void initPosition(int X, int Z) {
for (int x = -10; x <= 10; x++) {
for (int z = -10; z <= 10; z++) {
for (int y = -1; y <= 0; y++) {
long pos = WorldEngine.getWorldSectionId(4, x,y,z);
this.sectionSelector.nodeManager.insertTopLevelNode(pos);
}
}
}
}
public boolean generateMeshlets() {
return false;
}
public void setRenderGen(RenderGenerationService renderService) {
this.sectionGenerationService = renderService;
}
public Gl46HierarchicalViewport createViewport() {
return new Gl46HierarchicalViewport(this);
}
public void shutdown() {
this.meshManager.free();
this.sectionSelector.free();
this.printf.free();
this.debugRenderer.free();
}
}

View File

@@ -1,12 +0,0 @@
package me.cortex.voxy.client.core.rendering.geometry.OLD;
import me.cortex.voxy.client.core.rendering.Viewport;
public class Gl46HierarchicalViewport extends Viewport<Gl46HierarchicalViewport> {
public Gl46HierarchicalViewport(Gl46HierarchicalRenderer renderer) {
}
protected void delete0() {
}
}

View File

@@ -1,18 +0,0 @@
package me.cortex.voxy.client.core.rendering.geometry.OLD;
import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.rendering.Viewport;
import static org.lwjgl.opengl.ARBIndirectParameters.glMultiDrawElementsIndirectCountARB;
import static org.lwjgl.opengl.GL45C.glClearNamedBufferData;
public class Gl46Viewport extends Viewport<Gl46Viewport> {
GlBuffer visibilityBuffer;
public Gl46Viewport(Gl46FarWorldRenderer renderer) {
this.visibilityBuffer = new GlBuffer(renderer.maxSections*4L).zero();
}
protected void delete0() {
this.visibilityBuffer.free();
}
}

View File

@@ -3,9 +3,9 @@ package me.cortex.voxy.client.core.rendering.hierarchical;
import me.cortex.voxy.client.core.gl.GlBuffer; import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractFarWorldRenderer; import me.cortex.voxy.client.core.rendering.RenderService;
import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport;
import me.cortex.voxy.client.core.rendering.SharedIndexBuffer; import me.cortex.voxy.client.core.rendering.SharedIndexBuffer;
import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import org.joml.Matrix4f; import org.joml.Matrix4f;
@@ -36,7 +36,7 @@ public class DebugRenderer {
private final GlBuffer uniformBuffer = new GlBuffer(1024).zero(); private final GlBuffer uniformBuffer = new GlBuffer(1024).zero();
private final GlBuffer drawBuffer = new GlBuffer(1024).zero(); private final GlBuffer drawBuffer = new GlBuffer(1024).zero();
private void uploadUniform(Gl46HierarchicalViewport viewport) { private void uploadUniform(Viewport<?> viewport) {
long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 1024); long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 1024);
int sx = MathHelper.floor(viewport.cameraX)>>5; int sx = MathHelper.floor(viewport.cameraX)>>5;
int sy = MathHelper.floor(viewport.cameraY)>>5; int sy = MathHelper.floor(viewport.cameraY)>>5;
@@ -55,7 +55,7 @@ public class DebugRenderer {
MemoryUtil.memPutInt(ptr, viewport.height); ptr += 4; MemoryUtil.memPutInt(ptr, viewport.height); ptr += 4;
} }
public void render(Gl46HierarchicalViewport viewport, GlBuffer nodeData, GlBuffer nodeList) { public void render(Viewport<?> viewport, GlBuffer nodeData, GlBuffer nodeList) {
this.uploadUniform(viewport); this.uploadUniform(viewport);
UploadStream.INSTANCE.commit(); UploadStream.INSTANCE.commit();
@@ -67,7 +67,7 @@ public class DebugRenderer {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
this.debugShader.bind(); this.debugShader.bind();
glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO); glBindVertexArray(RenderService.STATIC_VAO);
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.drawBuffer.id); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.drawBuffer.id);
GL15.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BYTE.id()); GL15.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BYTE.id());
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id); glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id);

View File

@@ -4,7 +4,7 @@ import me.cortex.voxy.client.core.gl.GlBuffer;
import me.cortex.voxy.client.core.gl.shader.PrintfInjector; import me.cortex.voxy.client.core.gl.shader.PrintfInjector;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport; import me.cortex.voxy.client.core.rendering.Viewport;
import me.cortex.voxy.client.core.rendering.util.HiZBuffer; import me.cortex.voxy.client.core.rendering.util.HiZBuffer;
import me.cortex.voxy.client.core.rendering.util.UploadStream; import me.cortex.voxy.client.core.rendering.util.UploadStream;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@@ -46,7 +46,8 @@ public class HierarchicalOcclusionRenderer {
.compile(); .compile();
} }
private void uploadUniform(Gl46HierarchicalViewport viewport) {
private void uploadUniform(Viewport<?> viewport) {
long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 1024); long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 1024);
int sx = MathHelper.floor(viewport.cameraX)>>5; int sx = MathHelper.floor(viewport.cameraX)>>5;
int sy = MathHelper.floor(viewport.cameraY)>>5; int sy = MathHelper.floor(viewport.cameraY)>>5;
@@ -71,7 +72,7 @@ public class HierarchicalOcclusionRenderer {
MemoryUtil.memPutFloat(ptr, 64*64); ptr += 4; MemoryUtil.memPutFloat(ptr, 64*64); ptr += 4;
} }
public void doHierarchicalTraversalSelection(Gl46HierarchicalViewport viewport, int depthBuffer, GlBuffer renderSelectionResult, GlBuffer debugNodeOutput) { public void doHierarchicalTraversalSelection(Viewport<?> viewport, int depthBuffer, GlBuffer renderSelectionResult, GlBuffer debugNodeOutput) {
this.uploadUniform(viewport); this.uploadUniform(viewport);
this.nodeManager.upload(); this.nodeManager.upload();

View File

@@ -4,7 +4,7 @@ import me.cortex.voxy.client.core.gl.GlFramebuffer;
import me.cortex.voxy.client.core.gl.GlTexture; import me.cortex.voxy.client.core.gl.GlTexture;
import me.cortex.voxy.client.core.gl.shader.Shader; import me.cortex.voxy.client.core.gl.shader.Shader;
import me.cortex.voxy.client.core.gl.shader.ShaderType; import me.cortex.voxy.client.core.gl.shader.ShaderType;
import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractFarWorldRenderer; import me.cortex.voxy.client.core.rendering.RenderService;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import static org.lwjgl.opengl.ARBDirectStateAccess.*; import static org.lwjgl.opengl.ARBDirectStateAccess.*;
@@ -67,7 +67,7 @@ public class HiZBuffer {
} }
this.alloc(width, height); this.alloc(width, height);
} }
glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO); glBindVertexArray(RenderService.STATIC_VAO);
int boundFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING); int boundFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING);
this.hiz.bind(); this.hiz.bind();
this.fb.bind(GL_DEPTH_ATTACHMENT, this.texture, 0).verify(); this.fb.bind(GL_DEPTH_ATTACHMENT, this.texture, 0).verify();