diff --git a/src/main/java/me/cortex/voxelmon/Voxelmon.java b/src/main/java/me/cortex/voxelmon/Voxelmon.java index 0550f0c9..846a0ad2 100644 --- a/src/main/java/me/cortex/voxelmon/Voxelmon.java +++ b/src/main/java/me/cortex/voxelmon/Voxelmon.java @@ -8,6 +8,6 @@ import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; public class Voxelmon implements ClientModInitializer { @Override public void onInitializeClient() { - CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> TestSparseGenCommand.register(dispatcher)); + //CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> TestSparseGenCommand.register(dispatcher)); } } diff --git a/src/main/java/me/cortex/voxelmon/core/DistanceTracker.java b/src/main/java/me/cortex/voxelmon/core/DistanceTracker.java index bb0fb0c6..b4bb6382 100644 --- a/src/main/java/me/cortex/voxelmon/core/DistanceTracker.java +++ b/src/main/java/me/cortex/voxelmon/core/DistanceTracker.java @@ -11,6 +11,7 @@ import me.cortex.voxelmon.core.rendering.building.RenderGenerationService; import me.cortex.voxelmon.core.util.DebugUtil; import me.cortex.voxelmon.core.util.RingUtil; import me.cortex.voxelmon.core.world.WorldEngine; +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) @@ -27,8 +28,8 @@ public class DistanceTracker { //NOTE: This is in our render distance units, to convert to chunks at lvl 0 multiply by 2 int DIST = 16;//24; - this.rings[0] = new TransitionRing2D(5, DIST, (x,z)->{ - if (true) { + this.rings[0] = new TransitionRing2D(5, (int) Math.ceil(MinecraftClient.getInstance().gameRenderer.getViewDistance()/16)/2, (x, z)->{ + if (false) { return; } for (int y = -2; y < 10; y++) { @@ -97,6 +98,9 @@ public class DistanceTracker { //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; @@ -111,15 +115,22 @@ public class DistanceTracker { } public void update(int x, int z) { - int dx = this.lastUpdateX - x; - int dz = this.lastUpdateZ - z; - int distSquared = dx*dx + dz*dz; + 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 - this.lastUpdateX = x; - this.lastUpdateZ = z; + 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)); @@ -137,7 +148,7 @@ public class DistanceTracker { Long2IntOpenHashMap ops = new Long2IntOpenHashMap(); - + int zcount = MAX_STEPS_PER_UPDATE; int dir = nz world engine -> raw render data -> render data public class VoxelCore { + private static final Set biomeTintableAllFaces = new HashSet<>(List.of(Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.ACACIA_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.VINE, Blocks.MANGROVE_LEAVES, + Blocks.TALL_GRASS, Blocks.LARGE_FERN, + + Blocks.SPRUCE_LEAVES, + Blocks.BIRCH_LEAVES, + Blocks.PINK_PETALS, + Blocks.FERN, Blocks.GRASS, Blocks.POTTED_FERN)); + private static final Set biomeTintableUpFace = new HashSet<>(List.of(Blocks.GRASS_BLOCK)); + private static final Set waterTint = new HashSet<>(List.of(Blocks.WATER)); + + + public static VoxelCore INSTANCE = new VoxelCore(); private final WorldEngine world; @@ -55,12 +68,11 @@ public class VoxelCore { private final AbstractFarWorldRenderer renderer; private final PostProcessing postProcessing; - public VoxelCore() { //Trigger the shared index buffer loading SharedIndexBuffer.INSTANCE.id(); this.renderer = new Gl46FarWorldRenderer(); - this.world = new WorldEngine(new File("storagefile.db"), 20, 5);//"storagefile.db"//"ethoslab.db" + this.world = new WorldEngine(new File("storagefile2.db"), 20, 5);//"storagefile.db"//"ethoslab.db" this.renderTracker = new RenderTracker(this.world, this.renderer); this.renderGen = new RenderGenerationService(this.world,4, this.renderTracker::processBuildResult); @@ -71,6 +83,8 @@ public class VoxelCore { this.postProcessing = new PostProcessing(); + this.world.getMapper().setCallbacks(this::stateUpdate, this::biomeUpdate); + Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown)); @@ -97,39 +111,38 @@ public class VoxelCore { //WorldImporter importer = new WorldImporter(this.world, MinecraftClient.getInstance().world); //importer.importWorldAsyncStart(new File("saves/New World/region")); - Set biomeTintableAllFaces = new HashSet<>(List.of(Blocks.OAK_LEAVES, Blocks.JUNGLE_LEAVES, Blocks.ACACIA_LEAVES, Blocks.DARK_OAK_LEAVES, Blocks.VINE, Blocks.MANGROVE_LEAVES, - Blocks.TALL_GRASS, Blocks.LARGE_FERN)); - biomeTintableAllFaces.add(Blocks.SPRUCE_LEAVES); - biomeTintableAllFaces.add(Blocks.BIRCH_LEAVES); - biomeTintableAllFaces.add(Blocks.PINK_PETALS); - biomeTintableAllFaces.addAll(List.of(Blocks.FERN, Blocks.GRASS, Blocks.POTTED_FERN)); - Set biomeTintableUpFace = new HashSet<>(List.of(Blocks.GRASS_BLOCK)); - Set waterTint = new HashSet<>(List.of(Blocks.WATER)); - - int i = 0; - for (var state : this.world.getMapper().getBlockStates()) { - int tintMsk = 0; - if (biomeTintableAllFaces.contains(state.getBlock())) { - tintMsk |= (1<<6)-1; - } - if (biomeTintableUpFace.contains(state.getBlock())) { - tintMsk |= 1<>32))); + for (var biome : this.world.getMapper().getBiomeEntries()) { + this.biomeUpdate(biome); } } + private void stateUpdate(Mapper.StateEntry entry) { + var state = entry.state; + int tintMsk = 0; + if (biomeTintableAllFaces.contains(state.getBlock())) { + tintMsk |= (1<<6)-1; + } + if (biomeTintableUpFace.contains(state.getBlock())) { + tintMsk |= 1<>32))); + } + + public void enqueueIngest(WorldChunk worldChunk) { this.world.ingestService.enqueueIngest(worldChunk); } @@ -145,9 +158,9 @@ public class VoxelCore { DebugUtil.setPositionMatrix(matrices); matrices.pop(); - int boundFB = GlStateManager.getBoundFramebuffer(); - this.postProcessing.setSize(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight); - this.postProcessing.bindClearFramebuffer(); + //int boundFB = GlStateManager.getBoundFramebuffer(); + //this.postProcessing.setSize(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight); + //this.postProcessing.bindClearFramebuffer(); //TODO: FIXME: since we just bound the post processing FB the depth information isnt // copied over, we must do this manually and also copy it with respect to the @@ -158,8 +171,8 @@ public class VoxelCore { // this is cause the terrain might not exist and so all the caves are visible causing hell for the // occlusion culler this.renderer.renderFarAwayOpaque(matrices, cameraX, cameraY, cameraZ); - glBindFramebuffer(GL_FRAMEBUFFER, boundFB); - this.postProcessing.renderPost(boundFB); + //glBindFramebuffer(GL_FRAMEBUFFER, boundFB); + // this.postProcessing.renderPost(boundFB); } public void addDebugInfo(List debug) { diff --git a/src/main/java/me/cortex/voxelmon/core/rendering/AbstractFarWorldRenderer.java b/src/main/java/me/cortex/voxelmon/core/rendering/AbstractFarWorldRenderer.java index af85d2bb..e3b9b0bd 100644 --- a/src/main/java/me/cortex/voxelmon/core/rendering/AbstractFarWorldRenderer.java +++ b/src/main/java/me/cortex/voxelmon/core/rendering/AbstractFarWorldRenderer.java @@ -3,36 +3,23 @@ package me.cortex.voxelmon.core.rendering; //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 me.cortex.voxelmon.core.gl.GlBuffer; -import me.cortex.voxelmon.core.gl.shader.Shader; -import me.cortex.voxelmon.core.gl.shader.ShaderType; import me.cortex.voxelmon.core.rendering.building.BuiltSectionGeometry; import me.cortex.voxelmon.core.rendering.util.UploadStream; import me.cortex.voxelmon.core.world.other.BiomeColour; import me.cortex.voxelmon.core.world.other.BlockStateColour; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.Camera; import net.minecraft.client.render.Frustum; -import net.minecraft.client.render.RenderLayer; import net.minecraft.client.util.math.MatrixStack; import org.joml.FrustumIntersection; -import org.joml.Matrix4f; 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.GL11.GL_TRIANGLES; -import static org.lwjgl.opengl.GL11.GL_UNSIGNED_SHORT; -import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER; -import static org.lwjgl.opengl.GL15.glBindBuffer; import static org.lwjgl.opengl.GL30.*; -import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER; -import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER; -import static org.lwjgl.opengl.GL42.GL_COMMAND_BARRIER_BIT; -import static org.lwjgl.opengl.GL42.glMemoryBarrier; -import static org.lwjgl.opengl.GL43.*; //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 @@ -53,7 +40,7 @@ public abstract class AbstractFarWorldRenderer { private final ConcurrentLinkedDeque biomeUpdateQueue = new ConcurrentLinkedDeque<>(); protected final GlBuffer stateDataBuffer; protected final GlBuffer biomeDataBuffer; - protected final GlBuffer light = null; + protected final GlBuffer lightDataBuffer; //Current camera base level section position @@ -68,6 +55,7 @@ public abstract class AbstractFarWorldRenderer { //TODO: make these both dynamically sized this.stateDataBuffer = new GlBuffer((1<<16)*28, 0);//Capacity for 1<<16 entries this.biomeDataBuffer = new GlBuffer(512*4*2, 0);//capacity for 1<<9 entries + this.lightDataBuffer = new GlBuffer(256*4, 0);//256 of uint this.geometry = new GeometryManager(); } @@ -80,12 +68,26 @@ public abstract class AbstractFarWorldRenderer { 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(); + //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 + } + } + this.geometry.uploadResults(); //Upload any block state changes while (!this.stateUpdateQueue.isEmpty()) { @@ -129,5 +131,6 @@ public abstract class AbstractFarWorldRenderer { this.uniformBuffer.free(); this.stateDataBuffer.free(); this.biomeDataBuffer.free(); + this.lightDataBuffer.free(); } } diff --git a/src/main/java/me/cortex/voxelmon/core/rendering/Gl46FarWorldRenderer.java b/src/main/java/me/cortex/voxelmon/core/rendering/Gl46FarWorldRenderer.java index 695613bb..2610a22e 100644 --- a/src/main/java/me/cortex/voxelmon/core/rendering/Gl46FarWorldRenderer.java +++ b/src/main/java/me/cortex/voxelmon/core/rendering/Gl46FarWorldRenderer.java @@ -1,21 +1,16 @@ package me.cortex.voxelmon.core.rendering; -import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; import me.cortex.voxelmon.core.gl.GlBuffer; import me.cortex.voxelmon.core.gl.shader.Shader; import me.cortex.voxelmon.core.gl.shader.ShaderType; -import me.cortex.voxelmon.core.rendering.building.BuiltSectionGeometry; import me.cortex.voxelmon.core.rendering.util.UploadStream; -import me.cortex.voxelmon.core.util.MemoryBuffer; -import me.cortex.voxelmon.core.world.WorldEngine; -import me.cortex.voxelmon.core.world.WorldSection; import me.cortex.voxelmon.mixin.joml.AccessFrustumIntersection; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.RenderLayer; import net.minecraft.client.util.math.MatrixStack; import org.joml.Matrix4f; import org.joml.Vector3f; -import org.joml.Vector4f; import org.lwjgl.system.MemoryUtil; import java.util.List; @@ -67,7 +62,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.glVisibilityBuffer.id); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, this.stateDataBuffer.id);//State LUT glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.biomeDataBuffer.id);//Biome LUT - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, 0);//Lighting LUT + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, this.lightDataBuffer.id);//Lighting LUT glBindVertexArray(0); } @@ -80,6 +75,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer { //RenderSystem.defaultBlendFunc(); this.updateUniformBuffer(stack, cx, cy, cz); + UploadStream.INSTANCE.commit(); glBindVertexArray(this.vao); diff --git a/src/main/java/me/cortex/voxelmon/core/rendering/PostProcessing.java b/src/main/java/me/cortex/voxelmon/core/rendering/PostProcessing.java index 7ac53723..e76d901d 100644 --- a/src/main/java/me/cortex/voxelmon/core/rendering/PostProcessing.java +++ b/src/main/java/me/cortex/voxelmon/core/rendering/PostProcessing.java @@ -4,6 +4,7 @@ import me.cortex.voxelmon.core.gl.GlFramebuffer; import me.cortex.voxelmon.core.gl.GlTexture; import me.cortex.voxelmon.core.gl.shader.Shader; import me.cortex.voxelmon.core.gl.shader.ShaderType; +import org.lwjgl.opengl.GL11C; import static org.lwjgl.opengl.ARBFramebufferObject.*; import static org.lwjgl.opengl.ARBShaderImageLoadStore.glBindImageTexture; @@ -30,6 +31,11 @@ public class PostProcessing { .add(ShaderType.COMPUTE, "voxelmon:lod/ssao/ssao.comp") .compile(); + //private final Shader blit = Shader.make() + // .add(ShaderType.VERTEX, "voxelmon:lod/blit_nodepth/quad.vert") + // .add(ShaderType.FRAGMENT, "voxelmon:lod/blit_nodepth/quad.frag") + // .compile(); + public PostProcessing() { this.framebuffer = new GlFramebuffer(); } @@ -61,12 +67,14 @@ public class PostProcessing { //Executes the post processing and emits to whatever framebuffer is currently bound via a blit public void renderPost(int outputFb) { this.ssao.bind(); - glBindImageTexture(0, this.colour.id, 0, false,0, GL_READ_WRITE, GL_RGBA8); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, this.depthStencil.id); + glBindImageTexture(0, this.colour.id, 0, false,0, GL_READ_WRITE, GL_RGBA8); //glDispatchCompute(this.width/32, this.height/32, 1); glTextureBarrier(); - glBlitNamedFramebuffer(this.framebuffer.id, outputFb, 0,0, this.width, this.height, 0, 0, this.width, this.height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, this.colour.id); + glDrawArrays(GL11C.GL_TRIANGLES, 0, 3); } public void shutdown() { @@ -74,5 +82,6 @@ public class PostProcessing { if (this.colour != null) this.colour.free(); if (this.depthStencil != null) this.depthStencil.free(); this.ssao.free(); + //this.blit.free(); } } diff --git a/src/main/java/me/cortex/voxelmon/core/rendering/building/RenderDataFactory.java b/src/main/java/me/cortex/voxelmon/core/rendering/building/RenderDataFactory.java index e1aaa5b0..b31f412c 100644 --- a/src/main/java/me/cortex/voxelmon/core/rendering/building/RenderDataFactory.java +++ b/src/main/java/me/cortex/voxelmon/core/rendering/building/RenderDataFactory.java @@ -50,12 +50,13 @@ public class RenderDataFactory { for (int z = 0; z < 32; z++) { for (int x = 0; x < 32; x++) { var self = data[WorldSection.getIndex(x, y, z)]; - if (self == Mapper.AIR) { + if (Mapper.isAir(self)) { continue; } + long up = -1; if (y < 31) { - var up = data[WorldSection.getIndex(x, y + 1, z)]; - if (up != Mapper.AIR) { + up = data[WorldSection.getIndex(x, y + 1, z)]; + if (!Mapper.isTranslucent(up)) { continue; } } @@ -66,19 +67,19 @@ public class RenderDataFactory { connectedData = connectedSection.copyData(); connectedSection.release(); } - var up = connectedData[WorldSection.getIndex(x, 0, z)]; - if (up != Mapper.AIR) { + up = connectedData[WorldSection.getIndex(x, 0, z)]; + if (!Mapper.isTranslucent(up)) { continue; } } - this.mesher.put(x, z, self); + this.mesher.put(x, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); } } var quads = this.mesher.process(); for (int i = 0; i < quads.length; i++) { var quad = quads[i]; - this.outData.add(QuadFormat.encode(null, data[WorldSection.getIndex(Mesher2D.getX(quad), y, Mesher2D.getZ(quad))], 1, y, quad)); + this.outData.add(QuadFormat.encode(null, this.mesher.getDataFromQuad(quad), 1, y, quad)); } } connectedData = null; @@ -92,12 +93,13 @@ public class RenderDataFactory { for (int y = 0; y < 32; y++) { for (int z = 0; z < 32; z++) { var self = data[WorldSection.getIndex(x, y, z)]; - if (self == Mapper.AIR) { + if (Mapper.isAir(self)) { continue; } + long up = -1; if (x < 31) { - var up = data[WorldSection.getIndex(x + 1, y, z)]; - if (up != Mapper.AIR) { + up = data[WorldSection.getIndex(x + 1, y, z)]; + if (!Mapper.isTranslucent(up)) { continue; } } @@ -108,19 +110,19 @@ public class RenderDataFactory { connectedData = connectedSection.copyData(); connectedSection.release(); } - var up = connectedData[WorldSection.getIndex(0, y, z)]; - if (up != Mapper.AIR) { + up = connectedData[WorldSection.getIndex(0, y, z)]; + if (!Mapper.isTranslucent(up)) { continue; } } - this.mesher.put(y, z, self); + this.mesher.put(y, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); } } var quads = this.mesher.process(); for (int i = 0; i < quads.length; i++) { var quad = quads[i]; - this.outData.add(QuadFormat.encode(null, data[WorldSection.getIndex(x, Mesher2D.getX(quad), Mesher2D.getZ(quad))], 5, x, quad)); + this.outData.add(QuadFormat.encode(null, this.mesher.getDataFromQuad(quad), 5, x, quad)); } } connectedData = null; @@ -134,12 +136,13 @@ public class RenderDataFactory { for (int x = 0; x < 32; x++) { for (int y = 0; y < 32; y++) { var self = data[WorldSection.getIndex(x, y, z)]; - if (self == Mapper.AIR) { + if (Mapper.isAir(self)) { continue; } + long up = -1; if (z < 31) { - var up = data[WorldSection.getIndex(x, y, z + 1)]; - if (up != Mapper.AIR) { + up = data[WorldSection.getIndex(x, y, z + 1)]; + if (!Mapper.isTranslucent(up)) { continue; } } @@ -150,19 +153,19 @@ public class RenderDataFactory { connectedData = connectedSection.copyData(); connectedSection.release(); } - var up = connectedData[WorldSection.getIndex(x, y, 0)]; - if (up != Mapper.AIR) { + up = connectedData[WorldSection.getIndex(x, y, 0)]; + if (!Mapper.isTranslucent(up)) { continue; } } - this.mesher.put(x, y, self); + this.mesher.put(x, y, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); } } var quads = this.mesher.process(); for (int i = 0; i < quads.length; i++) { var quad = quads[i]; - this.outData.add(QuadFormat.encode(null, data[WorldSection.getIndex(Mesher2D.getX(quad), Mesher2D.getZ(quad), z)], 3, z, quad)); + this.outData.add(QuadFormat.encode(null, this.mesher.getDataFromQuad(quad), 3, z, quad)); } } connectedData = null; @@ -176,12 +179,13 @@ public class RenderDataFactory { for (int y = 0; y < 32; y++) { for (int z = 0; z < 32; z++) { var self = data[WorldSection.getIndex(x, y, z)]; - if (self == Mapper.AIR) { + if (Mapper.isAir(self)) { continue; } + long up = -1; if (x != 0) { - var up = data[WorldSection.getIndex(x - 1, y, z)]; - if (up != Mapper.AIR) { + up = data[WorldSection.getIndex(x - 1, y, z)]; + if (!Mapper.isTranslucent(up)) { continue; } } @@ -192,19 +196,19 @@ public class RenderDataFactory { connectedData = connectedSection.copyData(); connectedSection.release(); } - var up = connectedData[WorldSection.getIndex(31, y, z)]; - if (up != Mapper.AIR) { + up = connectedData[WorldSection.getIndex(31, y, z)]; + if (!Mapper.isTranslucent(up)) { continue; } } - this.mesher.put(y, z, self); + this.mesher.put(y, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); } } var quads = this.mesher.process(); for (int i = 0; i < quads.length; i++) { var quad = quads[i]; - this.outData.add(QuadFormat.encode(null, data[WorldSection.getIndex(x, Mesher2D.getX(quad), Mesher2D.getZ(quad))], 4, x, quad)); + this.outData.add(QuadFormat.encode(null, this.mesher.getDataFromQuad(quad), 4, x, quad)); } } connectedData = null; @@ -218,12 +222,13 @@ public class RenderDataFactory { for (int x = 0; x < 32; x++) { for (int y = 0; y < 32; y++) { var self = data[WorldSection.getIndex(x, y, z)]; - if (self == Mapper.AIR) { + if (Mapper.isAir(self)) { continue; } + long up = -1; if (z != 0) { - var up = data[WorldSection.getIndex(x, y, z - 1)]; - if (up != Mapper.AIR) { + up = data[WorldSection.getIndex(x, y, z - 1)]; + if (!Mapper.isTranslucent(up)) { continue; } } @@ -234,19 +239,19 @@ public class RenderDataFactory { connectedData = connectedSection.copyData(); connectedSection.release(); } - var up = connectedData[WorldSection.getIndex(x, y, 31)]; - if (up != Mapper.AIR) { + up = connectedData[WorldSection.getIndex(x, y, 31)]; + if (!Mapper.isTranslucent(up)) { continue; } } - this.mesher.put(x, y, self); + this.mesher.put(x, y, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); } } var quads = this.mesher.process(); for (int i = 0; i < quads.length; i++) { var quad = quads[i]; - this.outData.add(QuadFormat.encode(null, data[WorldSection.getIndex(Mesher2D.getX(quad), Mesher2D.getZ(quad), z)], 2, z, quad)); + this.outData.add(QuadFormat.encode(null, this.mesher.getDataFromQuad(quad), 2, z, quad)); } } connectedData = null; @@ -260,12 +265,13 @@ public class RenderDataFactory { for (int x = 0; x < 32; x++) { for (int z = 0; z < 32; z++) { var self = data[WorldSection.getIndex(x, y, z)]; - if (self == Mapper.AIR) { + if (Mapper.isAir(self)) { continue; } + long up = -1; if (y != 0) { - var up = data[WorldSection.getIndex(x, y - 1, z)]; - if (up != Mapper.AIR) { + up = data[WorldSection.getIndex(x, y - 1, z)]; + if (!Mapper.isTranslucent(up)) { continue; } } @@ -276,19 +282,19 @@ public class RenderDataFactory { connectedData = connectedSection.copyData(); connectedSection.release(); } - var up = connectedData[WorldSection.getIndex(x, 31, z)]; - if (up != Mapper.AIR) { + up = connectedData[WorldSection.getIndex(x, 31, z)]; + if (!Mapper.isTranslucent(up)) { continue; } } - this.mesher.put(x, z, self); + this.mesher.put(x, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56))); } } var quads = this.mesher.process(); for (int i = 0; i < quads.length; i++) { var quad = quads[i]; - this.outData.add(QuadFormat.encode(null, data[WorldSection.getIndex(Mesher2D.getX(quad), y, Mesher2D.getZ(quad))], 0, y, quad)); + this.outData.add(QuadFormat.encode(null, this.mesher.getDataFromQuad(quad), 0, y, quad)); } } connectedData = null; diff --git a/src/main/java/me/cortex/voxelmon/core/rendering/building/RenderGenerationService.java b/src/main/java/me/cortex/voxelmon/core/rendering/building/RenderGenerationService.java index 106cefc2..b24c51d7 100644 --- a/src/main/java/me/cortex/voxelmon/core/rendering/building/RenderGenerationService.java +++ b/src/main/java/me/cortex/voxelmon/core/rendering/building/RenderGenerationService.java @@ -6,6 +6,7 @@ import me.cortex.voxelmon.core.rendering.RenderTracker; import me.cortex.voxelmon.core.world.WorldEngine; import me.cortex.voxelmon.core.world.WorldSection; import net.minecraft.util.math.Direction; +import net.minecraft.world.chunk.ChunkNibbleArray; import net.minecraft.world.chunk.WorldChunk; import java.util.concurrent.ConcurrentHashMap; @@ -25,6 +26,7 @@ public class RenderGenerationService { private final Thread[] workers; private final Long2ObjectLinkedOpenHashMap taskQueue = new Long2ObjectLinkedOpenHashMap<>(); + private final Semaphore taskCounter = new Semaphore(0); private final WorldEngine world; private final Consumer resultConsumer; @@ -63,9 +65,14 @@ public class RenderGenerationService { if (buildFlags != 0) { var mesh = factory.generateMesh(section, buildFlags); this.resultConsumer.accept(mesh.clone()); - var prevCache = this.renderCache.put(mesh.position, mesh); - if (prevCache != null) { - prevCache.free(); + + if (false) { + var prevCache = this.renderCache.put(mesh.position, mesh); + if (prevCache != null) { + prevCache.free(); + } + } else { + mesh.free(); } } section.release(); @@ -106,7 +113,7 @@ public class RenderGenerationService { this.taskCounter.release(); return new BuildTask(()->{ if (checker.check(lvl, x, y, z)) { - return this.world.acquire(lvl, x, y, z); + return this.world.acquireIfExists(lvl, x, y, z); } else { return null; } diff --git a/src/main/java/me/cortex/voxelmon/core/util/Mesher2D.java b/src/main/java/me/cortex/voxelmon/core/util/Mesher2D.java index 187e9549..84947f12 100644 --- a/src/main/java/me/cortex/voxelmon/core/util/Mesher2D.java +++ b/src/main/java/me/cortex/voxelmon/core/util/Mesher2D.java @@ -163,5 +163,13 @@ public class Mesher2D { this.meshed.clear(); Arrays.fill(this.data, 0); } + + public long getDataFromQuad(int quad) { + return this.getData(getX(quad), getZ(quad)); + } + + public long getData(int x, int z) { + return this.data[this.getIdx(x, z)]; + } } diff --git a/src/main/java/me/cortex/voxelmon/core/voxelization/I3dByteSupplier.java b/src/main/java/me/cortex/voxelmon/core/voxelization/I3dByteSupplier.java deleted file mode 100644 index 6e697fcf..00000000 --- a/src/main/java/me/cortex/voxelmon/core/voxelization/I3dByteSupplier.java +++ /dev/null @@ -1,5 +0,0 @@ -package me.cortex.voxelmon.core.voxelization; - -public interface I3dByteSupplier { - byte supply(int x, int y, int z); -} diff --git a/src/main/java/me/cortex/voxelmon/core/voxelization/ILightingSupplier.java b/src/main/java/me/cortex/voxelmon/core/voxelization/ILightingSupplier.java new file mode 100644 index 00000000..05fb1fcc --- /dev/null +++ b/src/main/java/me/cortex/voxelmon/core/voxelization/ILightingSupplier.java @@ -0,0 +1,7 @@ +package me.cortex.voxelmon.core.voxelization; + +import net.minecraft.block.BlockState; + +public interface ILightingSupplier { + byte supply(int x, int y, int z, BlockState state); +} diff --git a/src/main/java/me/cortex/voxelmon/core/voxelization/WorldConversionFactory.java b/src/main/java/me/cortex/voxelmon/core/voxelization/WorldConversionFactory.java index 175da487..c5002149 100644 --- a/src/main/java/me/cortex/voxelmon/core/voxelization/WorldConversionFactory.java +++ b/src/main/java/me/cortex/voxelmon/core/voxelization/WorldConversionFactory.java @@ -18,10 +18,11 @@ public class WorldConversionFactory { return ((y<<2)|(z<<1)|x) + 4*4*4; } + //TODO: add a local mapper cache since it should be smaller and faster public static VoxelizedSection convert(Mapper stateMapper, PalettedContainer blockContainer, ReadableContainer> biomeContainer, - I3dByteSupplier lightSupplier, + ILightingSupplier lightSupplier, int sx, int sy, int sz) { @@ -41,9 +42,10 @@ public class WorldConversionFactory { int y = (oy<<2)|iy; int z = (oz<<2)|iz; var state = blockContainer.get(x, y, z); - if (!state.isAir()) { + byte light = lightSupplier.supply(x,y,z,state); + if (!(state.isAir() && (light==0))) {//TODO:FIXME:optimize this in such a way that having skylight access/no skylight means that an entire section is created, WHICH IS VERY BAD FOR PERFORMANCE!!!! nonAir++; - current[I(ix, iy, iz)] = stateMapper.getBaseId(lightSupplier.supply(x,y,z), state, biome); + current[I(ix, iy, iz)] = stateMapper.getBaseId(light, state, biome); } } } diff --git a/src/main/java/me/cortex/voxelmon/core/world/ActiveSectionTracker.java b/src/main/java/me/cortex/voxelmon/core/world/ActiveSectionTracker.java index 26d1c68e..7a4f7621 100644 --- a/src/main/java/me/cortex/voxelmon/core/world/ActiveSectionTracker.java +++ b/src/main/java/me/cortex/voxelmon/core/world/ActiveSectionTracker.java @@ -7,7 +7,7 @@ import java.lang.ref.Reference; public class ActiveSectionTracker { //Deserialize into the supplied section, returns true on success, false on failure - public interface SectionLoader {boolean load(WorldSection section);} + public interface SectionLoader {int load(WorldSection section);} //Loaded section world cache, TODO: get rid of VolatileHolder and use something more sane @@ -21,7 +21,7 @@ public class ActiveSectionTracker { } } - public WorldSection acquire(int lvl, int x, int y, int z) { + public WorldSection acquire(int lvl, int x, int y, int z, boolean nullOnEmpty) { long key = WorldEngine.getWorldSectionId(lvl, x, y, z); var cache = this.loadedSectionCache[lvl]; VolatileHolder holder = null; @@ -42,12 +42,17 @@ public class ActiveSectionTracker { //If this thread was the one to create the reference then its the thread to load the section if (isLoader) { var section = new WorldSection(lvl, x, y, z, this); - if (!this.loader.load(section)) { + int status = this.loader.load(section); + if (status < 0) { //TODO: Instead if throwing an exception do something better throw new IllegalStateException("Unable to load section"); } section.acquire(); holder.obj = section; + if (nullOnEmpty && status == 1) {//If its air return null as stated, release the section aswell + section.release(); + return null; + } return section; } else { WorldSection section = null; @@ -59,7 +64,7 @@ public class ActiveSectionTracker { return section; } } - return this.acquire(lvl, x, y, z); + return this.acquire(lvl, x, y, z, nullOnEmpty); } } @@ -85,14 +90,14 @@ public class ActiveSectionTracker { public static void main(String[] args) { - var tracker = new ActiveSectionTracker(1, a->true); + var tracker = new ActiveSectionTracker(1, a->0); - var section = tracker.acquire(0,0,0,0); + var section = tracker.acquire(0,0,0,0, false); section.acquire(); - var section2 = tracker.acquire(0,0,0,0); + var section2 = tracker.acquire(0,0,0,0, false); section.release(); section.release(); - section = tracker.acquire(0,0,0,0); + section = tracker.acquire(0,0,0,0, false); section.release(); } diff --git a/src/main/java/me/cortex/voxelmon/core/world/SaveLoadSystem.java b/src/main/java/me/cortex/voxelmon/core/world/SaveLoadSystem.java index f81b14c2..eaf239c5 100644 --- a/src/main/java/me/cortex/voxelmon/core/world/SaveLoadSystem.java +++ b/src/main/java/me/cortex/voxelmon/core/world/SaveLoadSystem.java @@ -1,23 +1,15 @@ package me.cortex.voxelmon.core.world; -import it.unimi.dsi.fastutil.ints.Int2ShortOpenHashMap; -import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.longs.Long2ShortOpenHashMap; import it.unimi.dsi.fastutil.longs.LongArrayList; -import it.unimi.dsi.fastutil.shorts.ShortArrayList; import org.lwjgl.system.MemoryUtil; -import org.lwjgl.util.zstd.Zstd; import java.nio.ByteBuffer; -import java.util.Arrays; -import java.util.Comparator; -import java.util.Map; -import java.util.Random; import static org.lwjgl.util.zstd.Zstd.*; public class SaveLoadSystem { - public static byte[] serialize(WorldSection section) { + public static ByteBuffer serialize(WorldSection section) { var data = section.copyData(); var compressed = new Short[data.length]; Long2ShortOpenHashMap LUT = new Long2ShortOpenHashMap(); @@ -57,24 +49,17 @@ public class SaveLoadSystem { raw.rewind(); ByteBuffer compressedData = MemoryUtil.memAlloc((int)ZSTD_COMPRESSBOUND(raw.remaining())); long compressedSize = ZSTD_compress(compressedData, raw, 15); - byte[] out = new byte[(int) compressedSize]; compressedData.limit((int) compressedSize); - compressedData.get(out); - + compressedData.rewind(); MemoryUtil.memFree(raw); - MemoryUtil.memFree(compressedData); //Compress into a key + data pallet format - return out; + return compressedData; } - public static boolean deserialize(WorldSection section, byte[] data) { - var buff = MemoryUtil.memAlloc(data.length); - buff.put(data); - buff.rewind(); + public static boolean deserialize(WorldSection section, ByteBuffer data) { var decompressed = MemoryUtil.memAlloc(32*32*32*4*2); - long size = ZSTD_decompress(decompressed, buff); - MemoryUtil.memFree(buff); + long size = ZSTD_decompress(decompressed, data); decompressed.limit((int) size); long hash = 0; diff --git a/src/main/java/me/cortex/voxelmon/core/world/WorldEngine.java b/src/main/java/me/cortex/voxelmon/core/world/WorldEngine.java index 1fb7ae51..66685415 100644 --- a/src/main/java/me/cortex/voxelmon/core/world/WorldEngine.java +++ b/src/main/java/me/cortex/voxelmon/core/world/WorldEngine.java @@ -7,6 +7,7 @@ import me.cortex.voxelmon.core.world.other.Mapper; import me.cortex.voxelmon.core.world.service.SectionSavingService; import me.cortex.voxelmon.core.world.service.VoxelIngestService; import me.cortex.voxelmon.core.world.storage.StorageBackend; +import org.lwjgl.system.MemoryUtil; import java.io.File; import java.util.Arrays; @@ -41,25 +42,36 @@ public class WorldEngine { this.sectionTracker = new ActiveSectionTracker(maxMipLayers, this::unsafeLoadSection); this.savingService = new SectionSavingService(this, savingServiceWorkers); - this.ingestService = new VoxelIngestService(this); + this.ingestService = new VoxelIngestService(this, 2); } - private boolean unsafeLoadSection(WorldSection into) { + private int unsafeLoadSection(WorldSection into) { var data = this.storage.getSectionData(into.getKey()); if (data != null) { - if (!SaveLoadSystem.deserialize(into, data)) { - this.storage.deleteSectionData(into.getKey()); - //TODO: regenerate the section from children - Arrays.fill(into.data, Mapper.AIR); - System.err.println("Section " + into.lvl + ", " + into.x + ", " + into.y + ", " + into.z + " was unable to load, setting to air"); - return true; + try { + if (!SaveLoadSystem.deserialize(into, data)) { + this.storage.deleteSectionData(into.getKey()); + //TODO: regenerate the section from children + Arrays.fill(into.data, Mapper.AIR); + System.err.println("Section " + into.lvl + ", " + into.x + ", " + into.y + ", " + into.z + " was unable to load, setting to air"); + return -1; + } else { + return 0; + } + } finally { + MemoryUtil.memFree(data); } + } else { + return 1; } - return true; + } + + public WorldSection acquireIfExists(int lvl, int x, int y, int z) { + return this.sectionTracker.acquire(lvl, x, y, z, true); } public WorldSection acquire(int lvl, int x, int y, int z) { - return this.sectionTracker.acquire(lvl, x, y, z); + return this.sectionTracker.acquire(lvl, x, y, z, false); } //TODO: Fixme/optimize, cause as the lvl gets higher, the size of x,y,z gets smaller so i can dynamically compact the format diff --git a/src/main/java/me/cortex/voxelmon/core/world/other/Mapper.java b/src/main/java/me/cortex/voxelmon/core/world/other/Mapper.java index 406e21bc..3f310bea 100644 --- a/src/main/java/me/cortex/voxelmon/core/world/other/Mapper.java +++ b/src/main/java/me/cortex/voxelmon/core/world/other/Mapper.java @@ -10,6 +10,7 @@ import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtIo; import net.minecraft.nbt.NbtOps; import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.stat.Stat; import net.minecraft.world.biome.Biome; import org.lwjgl.system.MemoryUtil; @@ -17,9 +18,9 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Consumer; //There are independent mappings for biome and block states, these get combined in the shader and allow for more @@ -32,10 +33,13 @@ public class Mapper { public static final int UNKNOWN_MAPPING = -1; public static final int AIR = 0; - private final Object2ObjectOpenHashMap block2stateEntry = new Object2ObjectOpenHashMap<>(); + private final Map block2stateEntry = new ConcurrentHashMap<>(2000,0.75f, 10); private final ObjectArrayList blockId2stateEntry = new ObjectArrayList<>(); - private final Object2ObjectOpenHashMap biome2biomeEntry = new Object2ObjectOpenHashMap<>(); + private final Map biome2biomeEntry = new ConcurrentHashMap<>(2000,0.75f, 10); private final ObjectArrayList biomeId2biomeEntry = new ObjectArrayList<>(); + + private Consumer newStateCallback; + private Consumer newBiomeCallback; public Mapper(StorageBackend storage) { this.storage = storage; //Insert air since its a special entry (index 0) @@ -46,6 +50,20 @@ public class Mapper { this.loadFromStorage(); } + public static boolean isTranslucent(long id) { + //Atm hardcode to air + return ((id>>27)&((1<<20)-1)) == 0; + } + + public static boolean isAir(long id) { + return ((id>>27)&((1<<20)-1)) == 0; + } + + public void setCallbacks(Consumer stateCallback, Consumer biomeCallback) { + this.newStateCallback = stateCallback; + this.newBiomeCallback = biomeCallback; + } + private void loadFromStorage() { var mappings = this.storage.getIdMappings(); List sentries = new ArrayList<>(); @@ -88,7 +106,7 @@ public class Mapper { private StateEntry registerNewBlockState(BlockState state) { StateEntry entry = new StateEntry(this.blockId2stateEntry.size(), state); - this.block2stateEntry.put(state, entry); + //this.block2stateEntry.put(state, entry); this.blockId2stateEntry.add(entry); byte[] serialized = entry.serialize(); @@ -97,12 +115,14 @@ public class Mapper { buffer.rewind(); this.storage.putIdMapping(entry.id | (BLOCK_STATE_TYPE<<30), buffer); MemoryUtil.memFree(buffer); + + if (this.newStateCallback!=null)this.newStateCallback.accept(entry); return entry; } private BiomeEntry registerNewBiome(String biome) { BiomeEntry entry = new BiomeEntry(this.biome2biomeEntry.size(), biome); - this.biome2biomeEntry.put(biome, entry); + //this.biome2biomeEntry.put(biome, entry); this.biomeId2biomeEntry.add(entry); byte[] serialized = entry.serialize(); @@ -111,62 +131,54 @@ public class Mapper { buffer.rewind(); this.storage.putIdMapping(entry.id | (BIOME_TYPE<<30), buffer); MemoryUtil.memFree(buffer); + + if (this.newBiomeCallback!=null)this.newBiomeCallback.accept(entry); return entry; } - //TODO:FIXME: IS VERY SLOW NEED TO MAKE IT LOCK FREE + //TODO:FIXME: IS VERY SLOW NEED TO MAKE IT LOCK FREE, or at minimum use a concurrent map public long getBaseId(byte light, BlockState state, RegistryEntry biome) { - StateEntry sentry = null; - BiomeEntry bentry = null; - synchronized (this.block2stateEntry) { - sentry = this.block2stateEntry.get(state); - if (sentry == null) { - sentry = this.registerNewBlockState(state); - } - } - synchronized (this.biome2biomeEntry) { - String biomeId = biome.getKey().get().getValue().toString(); - bentry = this.biome2biomeEntry.get(biomeId); - if (bentry == null) { - bentry = this.registerNewBiome(biomeId); - } - } + if (state.isAir()) return ((long)light)<<56;//Special case and fast return for air, dont care about the biome + StateEntry sentry = this.block2stateEntry.computeIfAbsent(state, this::registerNewBlockState); + + String biomeId = biome.getKey().get().getValue().toString(); + BiomeEntry bentry = this.biome2biomeEntry.computeIfAbsent(biomeId, this::registerNewBiome); return (Byte.toUnsignedLong(light)<<56)|(Integer.toUnsignedLong(bentry.id) << 47)|(Integer.toUnsignedLong(sentry.id)<<27); } - public BlockState[] getBlockStates() { - synchronized (this.block2stateEntry) { - BlockState[] out = new BlockState[this.blockId2stateEntry.size()]; - int i = 0; - for (var entry : this.blockId2stateEntry) { - if (entry.id != i++) { - throw new IllegalStateException(); - } - out[i-1] = entry.state; + //TODO: fixme: synchronize access to this.blockId2stateEntry + public StateEntry[] getStateEntries() { + var set = new ArrayList<>(this.blockId2stateEntry); + StateEntry[] out = new StateEntry[set.size()]; + int i = 0; + for (var entry : set) { + if (entry.id != i++) { + throw new IllegalStateException(); } - return out; + out[i-1] = entry; } + return out; } - public String[] getBiomes() { - synchronized (this.biome2biomeEntry) { - String[] out = new String[this.biome2biomeEntry.size()]; - int i = 0; - for (var entry : this.biomeId2biomeEntry) { - if (entry.id != i++) { - throw new IllegalStateException(); - } - out[i-1] = entry.biome; + //TODO: fixme: synchronize access to this.biomeId2biomeEntry + public BiomeEntry[] getBiomeEntries() { + var set = new ArrayList<>(this.biomeId2biomeEntry); + BiomeEntry[] out = new BiomeEntry[set.size()]; + int i = 0; + for (var entry : set) { + if (entry.id != i++) { + throw new IllegalStateException(); } - return out; + out[i-1] = entry; } + return out; } public static final class StateEntry { - private final int id; - private final BlockState state; + public final int id; + public final BlockState state; public StateEntry(int id, BlockState state) { this.id = id; this.state = state; @@ -200,8 +212,8 @@ public class Mapper { } public static final class BiomeEntry { - private final int id; - private final String biome; + public final int id; + public final String biome; public BiomeEntry(int id, String biome) { this.id = id; diff --git a/src/main/java/me/cortex/voxelmon/core/world/other/Mipper.java b/src/main/java/me/cortex/voxelmon/core/world/other/Mipper.java index 6326b259..64a23cfd 100644 --- a/src/main/java/me/cortex/voxelmon/core/world/other/Mipper.java +++ b/src/main/java/me/cortex/voxelmon/core/world/other/Mipper.java @@ -10,30 +10,31 @@ public class Mipper { //TODO: mip with respect to all the variables, what that means is take whatever has the highest count and return that //TODO: also average out the light level and set that as the new light level //For now just take the most top corner - if (I111 != 0) { + if (!Mapper.isAir(I111)) { return I111; } - if (I110 != 0) { + if (!Mapper.isAir(I110)) { return I110; } - if (I011 != 0) { + if (!Mapper.isAir(I011)) { return I011; } - if (I010 != 0) { + if (!Mapper.isAir(I010)) { return I010; } - if (I101 != 0) { + if (!Mapper.isAir(I101)) { return I101; } - if (I100 != 0) { + if (!Mapper.isAir(I100)) { return I100; } - if (I001 != 0) { + if (!Mapper.isAir(I001)) { return I001; } - if (I000 != 0) { + if (!Mapper.isAir(I000)) { return I000; } + //TODO: need to account for different light levels of "air" return 0; } } diff --git a/src/main/java/me/cortex/voxelmon/core/world/service/SectionSavingService.java b/src/main/java/me/cortex/voxelmon/core/world/service/SectionSavingService.java index 634467a4..42255ee3 100644 --- a/src/main/java/me/cortex/voxelmon/core/world/service/SectionSavingService.java +++ b/src/main/java/me/cortex/voxelmon/core/world/service/SectionSavingService.java @@ -44,14 +44,9 @@ public class SectionSavingService { section.assertNotFree(); section.inSaveQueue.set(false); - //TODO: stop converting between all these types and just use a native buffer all the time var saveData = SaveLoadSystem.serialize(section); - //Note: this is done like this because else the gc can collect the buffer before the transaction is completed - // thus the transaction reads from undefined memory - ByteBuffer buffer = MemoryUtil.memAlloc(saveData.length); - buffer.put(saveData).rewind(); - this.world.storage.setSectionData(section.getKey(), buffer); - MemoryUtil.memFree(buffer); + this.world.storage.setSectionData(section.getKey(), saveData); + MemoryUtil.memFree(saveData); section.release(); } diff --git a/src/main/java/me/cortex/voxelmon/core/world/service/VoxelIngestService.java b/src/main/java/me/cortex/voxelmon/core/world/service/VoxelIngestService.java index d4467b4a..23978091 100644 --- a/src/main/java/me/cortex/voxelmon/core/world/service/VoxelIngestService.java +++ b/src/main/java/me/cortex/voxelmon/core/world/service/VoxelIngestService.java @@ -1,28 +1,43 @@ package me.cortex.voxelmon.core.world.service; +import it.unimi.dsi.fastutil.Pair; import me.cortex.voxelmon.core.voxelization.VoxelizedSection; import me.cortex.voxelmon.core.voxelization.WorldConversionFactory; import me.cortex.voxelmon.core.world.WorldEngine; +import net.minecraft.block.Blocks; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.ChunkSectionPos; +import net.minecraft.world.LightType; +import net.minecraft.world.chunk.ChunkNibbleArray; +import net.minecraft.world.chunk.ChunkSection; import net.minecraft.world.chunk.WorldChunk; +import net.minecraft.world.chunk.light.LightStorage; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.Semaphore; public class VoxelIngestService { private volatile boolean running = true; - private final Thread worker; + private final Thread[] workers; private final ConcurrentLinkedDeque ingestQueue = new ConcurrentLinkedDeque<>(); private final Semaphore ingestCounter = new Semaphore(0); - private final WorldEngine world; - public VoxelIngestService(WorldEngine world) { - this.worker = new Thread(this::ingestWorker); - this.worker.setDaemon(false); - this.worker.setName("Ingest service"); - this.worker.start(); + private final ConcurrentHashMap> captureLightMap = new ConcurrentHashMap<>(1000,0.75f, 7); + private final WorldEngine world; + public VoxelIngestService(WorldEngine world, int workers) { this.world = world; + + this.workers = new Thread[workers]; + for (int i = 0; i < workers; i++) { + var worker = new Thread(this::ingestWorker); + worker.setDaemon(false); + worker.setName("Ingest service #" + i); + worker.start(); + this.workers[i] = worker; + } } private void ingestWorker() { @@ -33,6 +48,7 @@ public class VoxelIngestService { int i = chunk.getBottomSectionCoord() - 1; for (var section : chunk.getSectionArray()) { i++; + var lighting = this.captureLightMap.remove(ChunkSectionPos.from(chunk.getPos(), i).asLong()); if (section.isEmpty()) { this.world.insertUpdate(VoxelizedSection.createEmpty(chunk.getPos().x, i, chunk.getPos().z)); } else { @@ -40,7 +56,20 @@ public class VoxelIngestService { this.world.getMapper(), section.getBlockStateContainer(), section.getBiomeContainer(), - (x, y, z) -> (byte) 0, + (x, y, z, state) -> { + if (lighting == null || ((lighting.first() != null && lighting.first().isUninitialized())&&(lighting.second()!=null&&lighting.second().isUninitialized()))) { + return (byte) 0xFF; + } else { + //Lighting is a piece of shit cause its done per face + int block = lighting.first()!=null?Math.min(15,lighting.first().get(x, y, z)):0xF; + int sky = lighting.second()!=null?Math.min(15,lighting.second().get(x, y, z)):0xF; + if (block this.sectionDatabase.transaction(MDB_RDONLY, transaction->{ var buff = transaction.stack.malloc(8); buff.putLong(0, key); @@ -88,9 +89,9 @@ public class StorageBackend { if (bb == null) { return null; } - var res = new byte[bb.remaining()]; - bb.get(res); - return res; + var copy = MemoryUtil.memAlloc(bb.remaining()); + MemoryUtil.memCopy(bb, copy); + return copy; })); } diff --git a/src/main/java/me/cortex/voxelmon/importers/WorldImporter.java b/src/main/java/me/cortex/voxelmon/importers/WorldImporter.java index 5c172071..9fdd1137 100644 --- a/src/main/java/me/cortex/voxelmon/importers/WorldImporter.java +++ b/src/main/java/me/cortex/voxelmon/importers/WorldImporter.java @@ -169,7 +169,7 @@ public class WorldImporter { this.world.getMapper(), blockStates, biomes, - (bx, by, bz) -> (byte) 0, + (bx, by, bz, state) -> (byte) 0, x, y, z diff --git a/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinBackgroundRenderer.java b/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinBackgroundRenderer.java index f3c144d4..9aec41d9 100644 --- a/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinBackgroundRenderer.java +++ b/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinBackgroundRenderer.java @@ -6,9 +6,8 @@ import org.spongepowered.asm.mixin.injection.Constant; import org.spongepowered.asm.mixin.injection.ModifyConstant; @Mixin(BackgroundRenderer.class) - public class MixinBackgroundRenderer { - @ModifyConstant(method = "applyFog", constant = @Constant(floatValue = 192.0F)) + @ModifyConstant(method = "applyFog", constant = @Constant(floatValue = 192.0F), require = 0) private static float changeFog(float fog) { return 9999999f; } diff --git a/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinGameRenderer.java b/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinGameRenderer.java index 1662325a..80d87313 100644 --- a/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinGameRenderer.java +++ b/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinGameRenderer.java @@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(GameRenderer.class) public class MixinGameRenderer { - @Inject(method = "getFarPlaneDistance", at = @At("HEAD"), cancellable = true) + @Inject(method = "getFarPlaneDistance", at = @At("HEAD"), cancellable = true, require = 0) public void method_32796(CallbackInfoReturnable cir) { cir.setReturnValue(16 * 3000f); cir.cancel(); diff --git a/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinWorldRenderer.java b/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinWorldRenderer.java index ce7f9b4c..91db6c22 100644 --- a/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinWorldRenderer.java +++ b/src/main/java/me/cortex/voxelmon/mixin/minecraft/MixinWorldRenderer.java @@ -36,12 +36,12 @@ public abstract class MixinWorldRenderer { - @Redirect(method = "render", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F")) + @Redirect(method = "render", at = @At(value = "INVOKE", target = "Ljava/lang/Math;max(FF)F"), require = 0) private float redirectMax(float a, float b) { return a; } - @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;getViewDistance()F")) + @Redirect(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/GameRenderer;getViewDistance()F"), require = 0) private float changeRD(GameRenderer instance) { float viewDistance = instance.getViewDistance(); return 16*1512; diff --git a/src/main/resources/assets/voxelmon/shaders/lod/blit_nodepth/quad.frag b/src/main/resources/assets/voxelmon/shaders/lod/blit_nodepth/quad.frag new file mode 100644 index 00000000..d08ad6b9 --- /dev/null +++ b/src/main/resources/assets/voxelmon/shaders/lod/blit_nodepth/quad.frag @@ -0,0 +1,5 @@ +layout(binding=0) uniform sampler2D colourTexture; +in vec2 uv; +void main() { + gl_Colour = texture(colourTexture, uv); +} \ No newline at end of file diff --git a/src/main/resources/assets/voxelmon/shaders/lod/blit_nodepth/quad.vert b/src/main/resources/assets/voxelmon/shaders/lod/blit_nodepth/quad.vert new file mode 100644 index 00000000..2e5fda98 --- /dev/null +++ b/src/main/resources/assets/voxelmon/shaders/lod/blit_nodepth/quad.vert @@ -0,0 +1,4 @@ + +void main() { + gl_Position = +} \ No newline at end of file diff --git a/src/main/resources/assets/voxelmon/shaders/lod/gl46/bindings.glsl b/src/main/resources/assets/voxelmon/shaders/lod/gl46/bindings.glsl index cea12256..c3d5cf3a 100644 --- a/src/main/resources/assets/voxelmon/shaders/lod/gl46/bindings.glsl +++ b/src/main/resources/assets/voxelmon/shaders/lod/gl46/bindings.glsl @@ -66,5 +66,9 @@ layout(binding = 6, std430) readonly restrict buffer BiomeBuffer { }; layout(binding = 7, std430) readonly restrict buffer LightingBuffer { - vec4 lightData[]; + uint lightData[]; }; + +vec4 getLighting(uint index) { + return vec4((uvec4(lightData[index])>>uvec4(16,8,0,24))&uvec4(0xFF))*vec4(1f/255.0f); +} diff --git a/src/main/resources/assets/voxelmon/shaders/lod/gl46/quads.vert b/src/main/resources/assets/voxelmon/shaders/lod/gl46/quads.vert index 99415bb6..679f32a4 100644 --- a/src/main/resources/assets/voxelmon/shaders/lod/gl46/quads.vert +++ b/src/main/resources/assets/voxelmon/shaders/lod/gl46/quads.vert @@ -65,6 +65,9 @@ void main() { uint biomeId = extractBiomeId(quad); State stateInfo = stateData[stateId]; colour = uint2vec4RGBA(stateInfo.faceColours[face]); + + colour *= getLighting(extractLightId(quad)); + if (((stateInfo.biomeTintMsk>>face)&1) == 1) { vec4 biomeColour = uint2vec4RGBA(biomeData[biomeId].foliage); colour *= biomeColour; diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 681f3375..ca31c2f2 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -18,5 +18,6 @@ ], "depends": { "fabricloader": ">=0.14.22" - } + }, + "accessWidener": "voxelmon.accesswidener" } diff --git a/src/main/resources/voxelmon.accesswidener b/src/main/resources/voxelmon.accesswidener index 00fa2e80..02186f2a 100644 --- a/src/main/resources/voxelmon.accesswidener +++ b/src/main/resources/voxelmon.accesswidener @@ -1,4 +1,5 @@ accessWidener v1 named accessible field net/minecraft/client/texture/SpriteContents image Lnet/minecraft/client/texture/NativeImage; -accessible field net/minecraft/client/render/Frustum frustumIntersection Lorg/joml/FrustumIntersection; \ No newline at end of file +accessible field net/minecraft/client/render/Frustum frustumIntersection Lorg/joml/FrustumIntersection; +accessible field net/minecraft/client/render/LightmapTextureManager texture Lnet/minecraft/client/texture/NativeImageBackedTexture; \ No newline at end of file diff --git a/src/main/resources/voxelmon.mixins.json b/src/main/resources/voxelmon.mixins.json index bc8bc418..7d0627e1 100644 --- a/src/main/resources/voxelmon.mixins.json +++ b/src/main/resources/voxelmon.mixins.json @@ -8,12 +8,10 @@ "minecraft.MixinDebugHud", "minecraft.MixinGameRenderer", "minecraft.MixinMinecraftClient", - "minecraft.MixinWorldRenderer" + "minecraft.MixinWorldRenderer", + "joml.AccessFrustumIntersection" ], "injectors": { "defaultRequire": 1 - }, - "mixins": [ - "joml.AccessFrustumIntersection" - ] + } }