diff --git a/src/main/java/me/cortex/voxy/client/core/DistanceTracker.java b/src/main/java/me/cortex/voxy/client/core/DistanceTracker.java deleted file mode 100644 index 1cc1767e..00000000 --- a/src/main/java/me/cortex/voxy/client/core/DistanceTracker.java +++ /dev/null @@ -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 { - 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>>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>>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(); - }*/ diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/RenderTracker.java b/src/main/java/me/cortex/voxy/client/core/rendering/RenderTracker.java deleted file mode 100644 index 43042936..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/RenderTracker.java +++ /dev/null @@ -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)); - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractFarWorldRenderer.java deleted file mode 100644 index 7ab2c94c..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractFarWorldRenderer.java +++ /dev/null @@ -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 { - 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 viewports = new ArrayList<>(); - - protected IntArrayList updatedSectionIds; - - private final ConcurrentLinkedDeque blockStateUpdates = new ConcurrentLinkedDeque<>(); - private final ConcurrentLinkedDeque 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 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; - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractGeometryManager.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractGeometryManager.java deleted file mode 100644 index 29d5d485..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/AbstractGeometryManager.java +++ /dev/null @@ -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 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(); - -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/DefaultGeometryManager.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/DefaultGeometryManager.java deleted file mode 100644 index daffa753..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/DefaultGeometryManager.java +++ /dev/null @@ -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 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); - } - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46FarWorldRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46FarWorldRenderer.java deleted file mode 100644 index 17ec91bf..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46FarWorldRenderer.java +++ /dev/null @@ -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 { - 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(); - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46HierarchicalRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46HierarchicalRenderer.java deleted file mode 100644 index 36ab164c..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46HierarchicalRenderer.java +++ /dev/null @@ -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 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 blockStateUpdates = new ConcurrentLinkedDeque<>(); - private final ConcurrentLinkedDeque biomeUpdates = new ConcurrentLinkedDeque<>(); - - protected final ConcurrentLinkedDeque buildResults = new ConcurrentLinkedDeque<>(); - - private final ModelFactory modelManager; - private RenderGenerationService sectionGenerationService; - private Consumer 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 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 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(); - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46HierarchicalViewport.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46HierarchicalViewport.java deleted file mode 100644 index d31ad1a1..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46HierarchicalViewport.java +++ /dev/null @@ -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 { - public Gl46HierarchicalViewport(Gl46HierarchicalRenderer renderer) { - } - - protected void delete0() { - - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46Viewport.java b/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46Viewport.java deleted file mode 100644 index d6d599ad..00000000 --- a/src/main/java/me/cortex/voxy/client/core/rendering/geometry/OLD/Gl46Viewport.java +++ /dev/null @@ -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 { - GlBuffer visibilityBuffer; - public Gl46Viewport(Gl46FarWorldRenderer renderer) { - this.visibilityBuffer = new GlBuffer(renderer.maxSections*4L).zero(); - } - - protected void delete0() { - this.visibilityBuffer.free(); - } -} diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/DebugRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/DebugRenderer.java index 2a616c49..9e19b9ff 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/DebugRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/DebugRenderer.java @@ -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.shader.Shader; 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.geometry.OLD.Gl46HierarchicalViewport; +import me.cortex.voxy.client.core.rendering.RenderService; 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 net.minecraft.util.math.MathHelper; import org.joml.Matrix4f; @@ -36,7 +36,7 @@ public class DebugRenderer { private final GlBuffer uniformBuffer = 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); int sx = MathHelper.floor(viewport.cameraX)>>5; int sy = MathHelper.floor(viewport.cameraY)>>5; @@ -55,7 +55,7 @@ public class DebugRenderer { 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); UploadStream.INSTANCE.commit(); @@ -67,7 +67,7 @@ public class DebugRenderer { glEnable(GL_DEPTH_TEST); this.debugShader.bind(); - glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO); + glBindVertexArray(RenderService.STATIC_VAO); glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.drawBuffer.id); GL15.glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BYTE.id()); glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/HierarchicalOcclusionRenderer.java b/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/HierarchicalOcclusionRenderer.java index 5dcbc9ea..4b759303 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/HierarchicalOcclusionRenderer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/hierarchical/HierarchicalOcclusionRenderer.java @@ -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.Shader; 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.UploadStream; import net.minecraft.util.math.MathHelper; @@ -46,7 +46,8 @@ public class HierarchicalOcclusionRenderer { .compile(); } - private void uploadUniform(Gl46HierarchicalViewport viewport) { + + private void uploadUniform(Viewport viewport) { long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, 1024); int sx = MathHelper.floor(viewport.cameraX)>>5; int sy = MathHelper.floor(viewport.cameraY)>>5; @@ -71,7 +72,7 @@ public class HierarchicalOcclusionRenderer { 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.nodeManager.upload(); diff --git a/src/main/java/me/cortex/voxy/client/core/rendering/util/HiZBuffer.java b/src/main/java/me/cortex/voxy/client/core/rendering/util/HiZBuffer.java index 2bb0cb58..451777c0 100644 --- a/src/main/java/me/cortex/voxy/client/core/rendering/util/HiZBuffer.java +++ b/src/main/java/me/cortex/voxy/client/core/rendering/util/HiZBuffer.java @@ -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.shader.Shader; 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 static org.lwjgl.opengl.ARBDirectStateAccess.*; @@ -67,7 +67,7 @@ public class HiZBuffer { } this.alloc(width, height); } - glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO); + glBindVertexArray(RenderService.STATIC_VAO); int boundFB = GL11.glGetInteger(GL_DRAW_FRAMEBUFFER_BINDING); this.hiz.bind(); this.fb.bind(GL_DEPTH_ATTACHMENT, this.texture, 0).verify();