Alot of work
This commit is contained in:
@@ -8,6 +8,6 @@ import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
|||||||
public class Voxelmon implements ClientModInitializer {
|
public class Voxelmon implements ClientModInitializer {
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeClient() {
|
public void onInitializeClient() {
|
||||||
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> TestSparseGenCommand.register(dispatcher));
|
//CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> TestSparseGenCommand.register(dispatcher));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.DebugUtil;
|
||||||
import me.cortex.voxelmon.core.util.RingUtil;
|
import me.cortex.voxelmon.core.util.RingUtil;
|
||||||
import me.cortex.voxelmon.core.world.WorldEngine;
|
import me.cortex.voxelmon.core.world.WorldEngine;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
|
||||||
//Can use ring logic
|
//Can use ring logic
|
||||||
// i.e. when a player moves the rings of each lod change (how it was doing in the original attempt)
|
// 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
|
//NOTE: This is in our render distance units, to convert to chunks at lvl 0 multiply by 2
|
||||||
int DIST = 16;//24;
|
int DIST = 16;//24;
|
||||||
|
|
||||||
this.rings[0] = new TransitionRing2D(5, DIST, (x,z)->{
|
this.rings[0] = new TransitionRing2D(5, (int) Math.ceil(MinecraftClient.getInstance().gameRenderer.getViewDistance()/16)/2, (x, z)->{
|
||||||
if (true) {
|
if (false) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (int y = -2; y < 10; y++) {
|
for (int y = -2; y < 10; y++) {
|
||||||
@@ -97,6 +98,9 @@ public class DistanceTracker {
|
|||||||
|
|
||||||
//Note radius is in shiftScale
|
//Note radius is in shiftScale
|
||||||
private TransitionRing2D(int shiftSize, int radius, Transition2DCallback onEntry, Transition2DCallback onExit) {
|
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
|
//trigger just less than every shiftSize scale
|
||||||
this.triggerRangeSquared = 1<<((shiftSize<<1) - 1);
|
this.triggerRangeSquared = 1<<((shiftSize<<1) - 1);
|
||||||
this.shiftSize = shiftSize;
|
this.shiftSize = shiftSize;
|
||||||
@@ -111,15 +115,22 @@ public class DistanceTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void update(int x, int z) {
|
public void update(int x, int z) {
|
||||||
int dx = this.lastUpdateX - x;
|
int MAX_STEPS_PER_UPDATE = 1;
|
||||||
int dz = this.lastUpdateZ - z;
|
|
||||||
int distSquared = dx*dx + dz*dz;
|
|
||||||
|
long dx = this.lastUpdateX - x;
|
||||||
|
long dz = this.lastUpdateZ - z;
|
||||||
|
long distSquared = dx*dx + dz*dz;
|
||||||
if (distSquared < this.triggerRangeSquared) {
|
if (distSquared < this.triggerRangeSquared) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO: fixme: this last update needs to be incremented by a delta since
|
||||||
|
|
||||||
//Update the last update position
|
//Update the last update position
|
||||||
this.lastUpdateX = x;
|
int maxStep = this.triggerRangeSquared/2;
|
||||||
this.lastUpdateZ = z;
|
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();
|
Long2IntOpenHashMap ops = new Long2IntOpenHashMap();
|
||||||
|
|
||||||
|
int zcount = MAX_STEPS_PER_UPDATE;
|
||||||
int dir = nz<this.currentZ?-1:1;
|
int dir = nz<this.currentZ?-1:1;
|
||||||
while (nz != this.currentZ) {
|
while (nz != this.currentZ) {
|
||||||
for (int corner : this.cornerPoints) {
|
for (int corner : this.cornerPoints) {
|
||||||
@@ -156,8 +167,11 @@ public class DistanceTracker {
|
|||||||
//ops.addTo(Prel(0, -this.radius+Math.min(0, dir)), -dir);
|
//ops.addTo(Prel(0, -this.radius+Math.min(0, dir)), -dir);
|
||||||
|
|
||||||
this.currentZ += dir;
|
this.currentZ += dir;
|
||||||
|
|
||||||
|
if (--zcount == 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int xcount = MAX_STEPS_PER_UPDATE;
|
||||||
dir = nx<this.currentX?-1:1;
|
dir = nx<this.currentX?-1:1;
|
||||||
while (nx != this.currentX) {
|
while (nx != this.currentX) {
|
||||||
|
|
||||||
@@ -174,6 +188,8 @@ public class DistanceTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.currentX += dir;
|
this.currentX += dir;
|
||||||
|
|
||||||
|
if (--xcount == 0) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import me.cortex.voxelmon.core.world.WorldSection;
|
|||||||
import me.cortex.voxelmon.core.world.other.BiomeColour;
|
import me.cortex.voxelmon.core.world.other.BiomeColour;
|
||||||
import me.cortex.voxelmon.core.world.other.BlockStateColour;
|
import me.cortex.voxelmon.core.world.other.BlockStateColour;
|
||||||
import me.cortex.voxelmon.core.world.other.ColourResolver;
|
import me.cortex.voxelmon.core.world.other.ColourResolver;
|
||||||
|
import me.cortex.voxelmon.core.world.other.Mapper;
|
||||||
import me.cortex.voxelmon.importers.WorldImporter;
|
import me.cortex.voxelmon.importers.WorldImporter;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
@@ -45,6 +46,18 @@ import static org.lwjgl.opengl.ARBFramebufferObject.glBindFramebuffer;
|
|||||||
//There is strict forward only dataflow
|
//There is strict forward only dataflow
|
||||||
//Ingest -> world engine -> raw render data -> render data
|
//Ingest -> world engine -> raw render data -> render data
|
||||||
public class VoxelCore {
|
public class VoxelCore {
|
||||||
|
private static final Set<Block> 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<Block> biomeTintableUpFace = new HashSet<>(List.of(Blocks.GRASS_BLOCK));
|
||||||
|
private static final Set<Block> waterTint = new HashSet<>(List.of(Blocks.WATER));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static VoxelCore INSTANCE = new VoxelCore();
|
public static VoxelCore INSTANCE = new VoxelCore();
|
||||||
|
|
||||||
private final WorldEngine world;
|
private final WorldEngine world;
|
||||||
@@ -55,12 +68,11 @@ public class VoxelCore {
|
|||||||
private final AbstractFarWorldRenderer renderer;
|
private final AbstractFarWorldRenderer renderer;
|
||||||
private final PostProcessing postProcessing;
|
private final PostProcessing postProcessing;
|
||||||
|
|
||||||
|
|
||||||
public VoxelCore() {
|
public VoxelCore() {
|
||||||
//Trigger the shared index buffer loading
|
//Trigger the shared index buffer loading
|
||||||
SharedIndexBuffer.INSTANCE.id();
|
SharedIndexBuffer.INSTANCE.id();
|
||||||
this.renderer = new Gl46FarWorldRenderer();
|
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.renderTracker = new RenderTracker(this.world, this.renderer);
|
||||||
this.renderGen = new RenderGenerationService(this.world,4, this.renderTracker::processBuildResult);
|
this.renderGen = new RenderGenerationService(this.world,4, this.renderTracker::processBuildResult);
|
||||||
@@ -71,6 +83,8 @@ public class VoxelCore {
|
|||||||
|
|
||||||
this.postProcessing = new PostProcessing();
|
this.postProcessing = new PostProcessing();
|
||||||
|
|
||||||
|
this.world.getMapper().setCallbacks(this::stateUpdate, this::biomeUpdate);
|
||||||
|
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
|
Runtime.getRuntime().addShutdownHook(new Thread(this::shutdown));
|
||||||
|
|
||||||
|
|
||||||
@@ -97,39 +111,38 @@ public class VoxelCore {
|
|||||||
//WorldImporter importer = new WorldImporter(this.world, MinecraftClient.getInstance().world);
|
//WorldImporter importer = new WorldImporter(this.world, MinecraftClient.getInstance().world);
|
||||||
//importer.importWorldAsyncStart(new File("saves/New World/region"));
|
//importer.importWorldAsyncStart(new File("saves/New World/region"));
|
||||||
|
|
||||||
Set<Block> 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<Block> biomeTintableUpFace = new HashSet<>(List.of(Blocks.GRASS_BLOCK));
|
|
||||||
|
|
||||||
Set<Block> waterTint = new HashSet<>(List.of(Blocks.WATER));
|
for (var state : this.world.getMapper().getStateEntries()) {
|
||||||
|
this.stateUpdate(state);
|
||||||
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<<Direction.UP.getId();
|
|
||||||
}
|
|
||||||
if (waterTint.contains(state.getBlock())) {
|
|
||||||
tintMsk |= 1<<6;
|
|
||||||
}
|
|
||||||
this.renderer.enqueueUpdate(new BlockStateColour(i++, tintMsk, ColourResolver.resolveColour(state)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 0;
|
for (var biome : this.world.getMapper().getBiomeEntries()) {
|
||||||
for (var biome : this.world.getMapper().getBiomes()) {
|
this.biomeUpdate(biome);
|
||||||
long dualColour = ColourResolver.resolveBiomeColour(biome);
|
|
||||||
this.renderer.enqueueUpdate(new BiomeColour(i++, (int) dualColour, (int) (dualColour>>32)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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<<Direction.UP.getId();
|
||||||
|
}
|
||||||
|
if (waterTint.contains(state.getBlock())) {
|
||||||
|
tintMsk |= 1<<6;
|
||||||
|
}
|
||||||
|
this.renderer.enqueueUpdate(new BlockStateColour(entry.id, tintMsk, ColourResolver.resolveColour(state)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void biomeUpdate(Mapper.BiomeEntry entry) {
|
||||||
|
long dualColour = ColourResolver.resolveBiomeColour(entry.biome);
|
||||||
|
this.renderer.enqueueUpdate(new BiomeColour(entry.id, (int) dualColour, (int) (dualColour>>32)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void enqueueIngest(WorldChunk worldChunk) {
|
public void enqueueIngest(WorldChunk worldChunk) {
|
||||||
this.world.ingestService.enqueueIngest(worldChunk);
|
this.world.ingestService.enqueueIngest(worldChunk);
|
||||||
}
|
}
|
||||||
@@ -145,9 +158,9 @@ public class VoxelCore {
|
|||||||
DebugUtil.setPositionMatrix(matrices);
|
DebugUtil.setPositionMatrix(matrices);
|
||||||
matrices.pop();
|
matrices.pop();
|
||||||
|
|
||||||
int boundFB = GlStateManager.getBoundFramebuffer();
|
//int boundFB = GlStateManager.getBoundFramebuffer();
|
||||||
this.postProcessing.setSize(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight);
|
//this.postProcessing.setSize(MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight);
|
||||||
this.postProcessing.bindClearFramebuffer();
|
//this.postProcessing.bindClearFramebuffer();
|
||||||
|
|
||||||
//TODO: FIXME: since we just bound the post processing FB the depth information isnt
|
//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
|
// 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
|
// this is cause the terrain might not exist and so all the caves are visible causing hell for the
|
||||||
// occlusion culler
|
// occlusion culler
|
||||||
this.renderer.renderFarAwayOpaque(matrices, cameraX, cameraY, cameraZ);
|
this.renderer.renderFarAwayOpaque(matrices, cameraX, cameraY, cameraZ);
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, boundFB);
|
//glBindFramebuffer(GL_FRAMEBUFFER, boundFB);
|
||||||
this.postProcessing.renderPost(boundFB);
|
// this.postProcessing.renderPost(boundFB);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDebugInfo(List<String> debug) {
|
public void addDebugInfo(List<String> debug) {
|
||||||
|
|||||||
@@ -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)
|
//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
|
// could maybe tosomething else
|
||||||
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
import me.cortex.voxelmon.core.gl.GlBuffer;
|
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.building.BuiltSectionGeometry;
|
||||||
import me.cortex.voxelmon.core.rendering.util.UploadStream;
|
import me.cortex.voxelmon.core.rendering.util.UploadStream;
|
||||||
import me.cortex.voxelmon.core.world.other.BiomeColour;
|
import me.cortex.voxelmon.core.world.other.BiomeColour;
|
||||||
import me.cortex.voxelmon.core.world.other.BlockStateColour;
|
import me.cortex.voxelmon.core.world.other.BlockStateColour;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.render.Camera;
|
import net.minecraft.client.render.Camera;
|
||||||
import net.minecraft.client.render.Frustum;
|
import net.minecraft.client.render.Frustum;
|
||||||
import net.minecraft.client.render.RenderLayer;
|
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
import org.joml.FrustumIntersection;
|
import org.joml.FrustumIntersection;
|
||||||
import org.joml.Matrix4f;
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.ARBMultiDrawIndirect.glMultiDrawElementsIndirect;
|
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.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,
|
//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
|
// 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<BiomeColour> biomeUpdateQueue = new ConcurrentLinkedDeque<>();
|
private final ConcurrentLinkedDeque<BiomeColour> biomeUpdateQueue = new ConcurrentLinkedDeque<>();
|
||||||
protected final GlBuffer stateDataBuffer;
|
protected final GlBuffer stateDataBuffer;
|
||||||
protected final GlBuffer biomeDataBuffer;
|
protected final GlBuffer biomeDataBuffer;
|
||||||
protected final GlBuffer light = null;
|
protected final GlBuffer lightDataBuffer;
|
||||||
|
|
||||||
|
|
||||||
//Current camera base level section position
|
//Current camera base level section position
|
||||||
@@ -68,6 +55,7 @@ public abstract class AbstractFarWorldRenderer {
|
|||||||
//TODO: make these both dynamically sized
|
//TODO: make these both dynamically sized
|
||||||
this.stateDataBuffer = new GlBuffer((1<<16)*28, 0);//Capacity for 1<<16 entries
|
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.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();
|
this.geometry = new GeometryManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,12 +68,26 @@ public abstract class AbstractFarWorldRenderer {
|
|||||||
this.sy = camera.getBlockPos().getY() >> 5;
|
this.sy = camera.getBlockPos().getY() >> 5;
|
||||||
this.sz = camera.getBlockPos().getZ() >> 5;
|
this.sz = camera.getBlockPos().getZ() >> 5;
|
||||||
|
|
||||||
|
|
||||||
//TODO: move this to a render function that is only called
|
//TODO: move this to a render function that is only called
|
||||||
// once per frame when using multi viewport mods
|
// once per frame when using multi viewport mods
|
||||||
//it shouldent matter if its called multiple times a frame however, as its synced with fences
|
//it shouldent matter if its called multiple times a frame however, as its synced with fences
|
||||||
UploadStream.INSTANCE.tick();
|
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();
|
this.geometry.uploadResults();
|
||||||
//Upload any block state changes
|
//Upload any block state changes
|
||||||
while (!this.stateUpdateQueue.isEmpty()) {
|
while (!this.stateUpdateQueue.isEmpty()) {
|
||||||
@@ -129,5 +131,6 @@ public abstract class AbstractFarWorldRenderer {
|
|||||||
this.uniformBuffer.free();
|
this.uniformBuffer.free();
|
||||||
this.stateDataBuffer.free();
|
this.stateDataBuffer.free();
|
||||||
this.biomeDataBuffer.free();
|
this.biomeDataBuffer.free();
|
||||||
|
this.lightDataBuffer.free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,16 @@
|
|||||||
package me.cortex.voxelmon.core.rendering;
|
package me.cortex.voxelmon.core.rendering;
|
||||||
|
|
||||||
import com.mojang.blaze3d.platform.GlStateManager;
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
import com.mojang.blaze3d.systems.RenderSystem;
|
||||||
import me.cortex.voxelmon.core.gl.GlBuffer;
|
import me.cortex.voxelmon.core.gl.GlBuffer;
|
||||||
import me.cortex.voxelmon.core.gl.shader.Shader;
|
import me.cortex.voxelmon.core.gl.shader.Shader;
|
||||||
import me.cortex.voxelmon.core.gl.shader.ShaderType;
|
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.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 me.cortex.voxelmon.mixin.joml.AccessFrustumIntersection;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.render.RenderLayer;
|
import net.minecraft.client.render.RenderLayer;
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
import net.minecraft.client.util.math.MatrixStack;
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.joml.Vector3f;
|
import org.joml.Vector3f;
|
||||||
import org.joml.Vector4f;
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import java.util.List;
|
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, 4, this.glVisibilityBuffer.id);
|
||||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, this.stateDataBuffer.id);//State LUT
|
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, 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);
|
glBindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +75,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer {
|
|||||||
//RenderSystem.defaultBlendFunc();
|
//RenderSystem.defaultBlendFunc();
|
||||||
|
|
||||||
this.updateUniformBuffer(stack, cx, cy, cz);
|
this.updateUniformBuffer(stack, cx, cy, cz);
|
||||||
|
|
||||||
UploadStream.INSTANCE.commit();
|
UploadStream.INSTANCE.commit();
|
||||||
|
|
||||||
glBindVertexArray(this.vao);
|
glBindVertexArray(this.vao);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import me.cortex.voxelmon.core.gl.GlFramebuffer;
|
|||||||
import me.cortex.voxelmon.core.gl.GlTexture;
|
import me.cortex.voxelmon.core.gl.GlTexture;
|
||||||
import me.cortex.voxelmon.core.gl.shader.Shader;
|
import me.cortex.voxelmon.core.gl.shader.Shader;
|
||||||
import me.cortex.voxelmon.core.gl.shader.ShaderType;
|
import me.cortex.voxelmon.core.gl.shader.ShaderType;
|
||||||
|
import org.lwjgl.opengl.GL11C;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.ARBFramebufferObject.*;
|
import static org.lwjgl.opengl.ARBFramebufferObject.*;
|
||||||
import static org.lwjgl.opengl.ARBShaderImageLoadStore.glBindImageTexture;
|
import static org.lwjgl.opengl.ARBShaderImageLoadStore.glBindImageTexture;
|
||||||
@@ -30,6 +31,11 @@ public class PostProcessing {
|
|||||||
.add(ShaderType.COMPUTE, "voxelmon:lod/ssao/ssao.comp")
|
.add(ShaderType.COMPUTE, "voxelmon:lod/ssao/ssao.comp")
|
||||||
.compile();
|
.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() {
|
public PostProcessing() {
|
||||||
this.framebuffer = new GlFramebuffer();
|
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
|
//Executes the post processing and emits to whatever framebuffer is currently bound via a blit
|
||||||
public void renderPost(int outputFb) {
|
public void renderPost(int outputFb) {
|
||||||
this.ssao.bind();
|
this.ssao.bind();
|
||||||
glBindImageTexture(0, this.colour.id, 0, false,0, GL_READ_WRITE, GL_RGBA8);
|
|
||||||
glActiveTexture(GL_TEXTURE1);
|
glActiveTexture(GL_TEXTURE1);
|
||||||
glBindTexture(GL_TEXTURE_2D, this.depthStencil.id);
|
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);
|
//glDispatchCompute(this.width/32, this.height/32, 1);
|
||||||
glTextureBarrier();
|
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() {
|
public void shutdown() {
|
||||||
@@ -74,5 +82,6 @@ public class PostProcessing {
|
|||||||
if (this.colour != null) this.colour.free();
|
if (this.colour != null) this.colour.free();
|
||||||
if (this.depthStencil != null) this.depthStencil.free();
|
if (this.depthStencil != null) this.depthStencil.free();
|
||||||
this.ssao.free();
|
this.ssao.free();
|
||||||
|
//this.blit.free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,12 +50,13 @@ public class RenderDataFactory {
|
|||||||
for (int z = 0; z < 32; z++) {
|
for (int z = 0; z < 32; z++) {
|
||||||
for (int x = 0; x < 32; x++) {
|
for (int x = 0; x < 32; x++) {
|
||||||
var self = data[WorldSection.getIndex(x, y, z)];
|
var self = data[WorldSection.getIndex(x, y, z)];
|
||||||
if (self == Mapper.AIR) {
|
if (Mapper.isAir(self)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
long up = -1;
|
||||||
if (y < 31) {
|
if (y < 31) {
|
||||||
var up = data[WorldSection.getIndex(x, y + 1, z)];
|
up = data[WorldSection.getIndex(x, y + 1, z)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,19 +67,19 @@ public class RenderDataFactory {
|
|||||||
connectedData = connectedSection.copyData();
|
connectedData = connectedSection.copyData();
|
||||||
connectedSection.release();
|
connectedSection.release();
|
||||||
}
|
}
|
||||||
var up = connectedData[WorldSection.getIndex(x, 0, z)];
|
up = connectedData[WorldSection.getIndex(x, 0, z)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.mesher.put(x, z, self);
|
this.mesher.put(x, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var quads = this.mesher.process();
|
var quads = this.mesher.process();
|
||||||
for (int i = 0; i < quads.length; i++) {
|
for (int i = 0; i < quads.length; i++) {
|
||||||
var quad = quads[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;
|
connectedData = null;
|
||||||
@@ -92,12 +93,13 @@ public class RenderDataFactory {
|
|||||||
for (int y = 0; y < 32; y++) {
|
for (int y = 0; y < 32; y++) {
|
||||||
for (int z = 0; z < 32; z++) {
|
for (int z = 0; z < 32; z++) {
|
||||||
var self = data[WorldSection.getIndex(x, y, z)];
|
var self = data[WorldSection.getIndex(x, y, z)];
|
||||||
if (self == Mapper.AIR) {
|
if (Mapper.isAir(self)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
long up = -1;
|
||||||
if (x < 31) {
|
if (x < 31) {
|
||||||
var up = data[WorldSection.getIndex(x + 1, y, z)];
|
up = data[WorldSection.getIndex(x + 1, y, z)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,19 +110,19 @@ public class RenderDataFactory {
|
|||||||
connectedData = connectedSection.copyData();
|
connectedData = connectedSection.copyData();
|
||||||
connectedSection.release();
|
connectedSection.release();
|
||||||
}
|
}
|
||||||
var up = connectedData[WorldSection.getIndex(0, y, z)];
|
up = connectedData[WorldSection.getIndex(0, y, z)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.mesher.put(y, z, self);
|
this.mesher.put(y, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var quads = this.mesher.process();
|
var quads = this.mesher.process();
|
||||||
for (int i = 0; i < quads.length; i++) {
|
for (int i = 0; i < quads.length; i++) {
|
||||||
var quad = quads[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;
|
connectedData = null;
|
||||||
@@ -134,12 +136,13 @@ public class RenderDataFactory {
|
|||||||
for (int x = 0; x < 32; x++) {
|
for (int x = 0; x < 32; x++) {
|
||||||
for (int y = 0; y < 32; y++) {
|
for (int y = 0; y < 32; y++) {
|
||||||
var self = data[WorldSection.getIndex(x, y, z)];
|
var self = data[WorldSection.getIndex(x, y, z)];
|
||||||
if (self == Mapper.AIR) {
|
if (Mapper.isAir(self)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
long up = -1;
|
||||||
if (z < 31) {
|
if (z < 31) {
|
||||||
var up = data[WorldSection.getIndex(x, y, z + 1)];
|
up = data[WorldSection.getIndex(x, y, z + 1)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -150,19 +153,19 @@ public class RenderDataFactory {
|
|||||||
connectedData = connectedSection.copyData();
|
connectedData = connectedSection.copyData();
|
||||||
connectedSection.release();
|
connectedSection.release();
|
||||||
}
|
}
|
||||||
var up = connectedData[WorldSection.getIndex(x, y, 0)];
|
up = connectedData[WorldSection.getIndex(x, y, 0)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.mesher.put(x, y, self);
|
this.mesher.put(x, y, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var quads = this.mesher.process();
|
var quads = this.mesher.process();
|
||||||
for (int i = 0; i < quads.length; i++) {
|
for (int i = 0; i < quads.length; i++) {
|
||||||
var quad = quads[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;
|
connectedData = null;
|
||||||
@@ -176,12 +179,13 @@ public class RenderDataFactory {
|
|||||||
for (int y = 0; y < 32; y++) {
|
for (int y = 0; y < 32; y++) {
|
||||||
for (int z = 0; z < 32; z++) {
|
for (int z = 0; z < 32; z++) {
|
||||||
var self = data[WorldSection.getIndex(x, y, z)];
|
var self = data[WorldSection.getIndex(x, y, z)];
|
||||||
if (self == Mapper.AIR) {
|
if (Mapper.isAir(self)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
long up = -1;
|
||||||
if (x != 0) {
|
if (x != 0) {
|
||||||
var up = data[WorldSection.getIndex(x - 1, y, z)];
|
up = data[WorldSection.getIndex(x - 1, y, z)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,19 +196,19 @@ public class RenderDataFactory {
|
|||||||
connectedData = connectedSection.copyData();
|
connectedData = connectedSection.copyData();
|
||||||
connectedSection.release();
|
connectedSection.release();
|
||||||
}
|
}
|
||||||
var up = connectedData[WorldSection.getIndex(31, y, z)];
|
up = connectedData[WorldSection.getIndex(31, y, z)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.mesher.put(y, z, self);
|
this.mesher.put(y, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var quads = this.mesher.process();
|
var quads = this.mesher.process();
|
||||||
for (int i = 0; i < quads.length; i++) {
|
for (int i = 0; i < quads.length; i++) {
|
||||||
var quad = quads[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;
|
connectedData = null;
|
||||||
@@ -218,12 +222,13 @@ public class RenderDataFactory {
|
|||||||
for (int x = 0; x < 32; x++) {
|
for (int x = 0; x < 32; x++) {
|
||||||
for (int y = 0; y < 32; y++) {
|
for (int y = 0; y < 32; y++) {
|
||||||
var self = data[WorldSection.getIndex(x, y, z)];
|
var self = data[WorldSection.getIndex(x, y, z)];
|
||||||
if (self == Mapper.AIR) {
|
if (Mapper.isAir(self)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
long up = -1;
|
||||||
if (z != 0) {
|
if (z != 0) {
|
||||||
var up = data[WorldSection.getIndex(x, y, z - 1)];
|
up = data[WorldSection.getIndex(x, y, z - 1)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -234,19 +239,19 @@ public class RenderDataFactory {
|
|||||||
connectedData = connectedSection.copyData();
|
connectedData = connectedSection.copyData();
|
||||||
connectedSection.release();
|
connectedSection.release();
|
||||||
}
|
}
|
||||||
var up = connectedData[WorldSection.getIndex(x, y, 31)];
|
up = connectedData[WorldSection.getIndex(x, y, 31)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.mesher.put(x, y, self);
|
this.mesher.put(x, y, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var quads = this.mesher.process();
|
var quads = this.mesher.process();
|
||||||
for (int i = 0; i < quads.length; i++) {
|
for (int i = 0; i < quads.length; i++) {
|
||||||
var quad = quads[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;
|
connectedData = null;
|
||||||
@@ -260,12 +265,13 @@ public class RenderDataFactory {
|
|||||||
for (int x = 0; x < 32; x++) {
|
for (int x = 0; x < 32; x++) {
|
||||||
for (int z = 0; z < 32; z++) {
|
for (int z = 0; z < 32; z++) {
|
||||||
var self = data[WorldSection.getIndex(x, y, z)];
|
var self = data[WorldSection.getIndex(x, y, z)];
|
||||||
if (self == Mapper.AIR) {
|
if (Mapper.isAir(self)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
long up = -1;
|
||||||
if (y != 0) {
|
if (y != 0) {
|
||||||
var up = data[WorldSection.getIndex(x, y - 1, z)];
|
up = data[WorldSection.getIndex(x, y - 1, z)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,19 +282,19 @@ public class RenderDataFactory {
|
|||||||
connectedData = connectedSection.copyData();
|
connectedData = connectedSection.copyData();
|
||||||
connectedSection.release();
|
connectedSection.release();
|
||||||
}
|
}
|
||||||
var up = connectedData[WorldSection.getIndex(x, 31, z)];
|
up = connectedData[WorldSection.getIndex(x, 31, z)];
|
||||||
if (up != Mapper.AIR) {
|
if (!Mapper.isTranslucent(up)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.mesher.put(x, z, self);
|
this.mesher.put(x, z, (self&~(0xFFL<<56))|(up&(0xFFL<<56)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var quads = this.mesher.process();
|
var quads = this.mesher.process();
|
||||||
for (int i = 0; i < quads.length; i++) {
|
for (int i = 0; i < quads.length; i++) {
|
||||||
var quad = quads[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;
|
connectedData = null;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import me.cortex.voxelmon.core.rendering.RenderTracker;
|
|||||||
import me.cortex.voxelmon.core.world.WorldEngine;
|
import me.cortex.voxelmon.core.world.WorldEngine;
|
||||||
import me.cortex.voxelmon.core.world.WorldSection;
|
import me.cortex.voxelmon.core.world.WorldSection;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.world.chunk.ChunkNibbleArray;
|
||||||
import net.minecraft.world.chunk.WorldChunk;
|
import net.minecraft.world.chunk.WorldChunk;
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
@@ -25,6 +26,7 @@ public class RenderGenerationService {
|
|||||||
private final Thread[] workers;
|
private final Thread[] workers;
|
||||||
|
|
||||||
private final Long2ObjectLinkedOpenHashMap<BuildTask> taskQueue = new Long2ObjectLinkedOpenHashMap<>();
|
private final Long2ObjectLinkedOpenHashMap<BuildTask> taskQueue = new Long2ObjectLinkedOpenHashMap<>();
|
||||||
|
|
||||||
private final Semaphore taskCounter = new Semaphore(0);
|
private final Semaphore taskCounter = new Semaphore(0);
|
||||||
private final WorldEngine world;
|
private final WorldEngine world;
|
||||||
private final Consumer<BuiltSectionGeometry> resultConsumer;
|
private final Consumer<BuiltSectionGeometry> resultConsumer;
|
||||||
@@ -63,9 +65,14 @@ public class RenderGenerationService {
|
|||||||
if (buildFlags != 0) {
|
if (buildFlags != 0) {
|
||||||
var mesh = factory.generateMesh(section, buildFlags);
|
var mesh = factory.generateMesh(section, buildFlags);
|
||||||
this.resultConsumer.accept(mesh.clone());
|
this.resultConsumer.accept(mesh.clone());
|
||||||
var prevCache = this.renderCache.put(mesh.position, mesh);
|
|
||||||
if (prevCache != null) {
|
if (false) {
|
||||||
prevCache.free();
|
var prevCache = this.renderCache.put(mesh.position, mesh);
|
||||||
|
if (prevCache != null) {
|
||||||
|
prevCache.free();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
mesh.free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
section.release();
|
section.release();
|
||||||
@@ -106,7 +113,7 @@ public class RenderGenerationService {
|
|||||||
this.taskCounter.release();
|
this.taskCounter.release();
|
||||||
return new BuildTask(()->{
|
return new BuildTask(()->{
|
||||||
if (checker.check(lvl, x, y, z)) {
|
if (checker.check(lvl, x, y, z)) {
|
||||||
return this.world.acquire(lvl, x, y, z);
|
return this.world.acquireIfExists(lvl, x, y, z);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,5 +163,13 @@ public class Mesher2D {
|
|||||||
this.meshed.clear();
|
this.meshed.clear();
|
||||||
Arrays.fill(this.data, 0);
|
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)];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
package me.cortex.voxelmon.core.voxelization;
|
|
||||||
|
|
||||||
public interface I3dByteSupplier {
|
|
||||||
byte supply(int x, int y, int z);
|
|
||||||
}
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -18,10 +18,11 @@ public class WorldConversionFactory {
|
|||||||
return ((y<<2)|(z<<1)|x) + 4*4*4;
|
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,
|
public static VoxelizedSection convert(Mapper stateMapper,
|
||||||
PalettedContainer<BlockState> blockContainer,
|
PalettedContainer<BlockState> blockContainer,
|
||||||
ReadableContainer<RegistryEntry<Biome>> biomeContainer,
|
ReadableContainer<RegistryEntry<Biome>> biomeContainer,
|
||||||
I3dByteSupplier lightSupplier,
|
ILightingSupplier lightSupplier,
|
||||||
int sx,
|
int sx,
|
||||||
int sy,
|
int sy,
|
||||||
int sz) {
|
int sz) {
|
||||||
@@ -41,9 +42,10 @@ public class WorldConversionFactory {
|
|||||||
int y = (oy<<2)|iy;
|
int y = (oy<<2)|iy;
|
||||||
int z = (oz<<2)|iz;
|
int z = (oz<<2)|iz;
|
||||||
var state = blockContainer.get(x, y, z);
|
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++;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import java.lang.ref.Reference;
|
|||||||
|
|
||||||
public class ActiveSectionTracker {
|
public class ActiveSectionTracker {
|
||||||
//Deserialize into the supplied section, returns true on success, false on failure
|
//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
|
//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);
|
long key = WorldEngine.getWorldSectionId(lvl, x, y, z);
|
||||||
var cache = this.loadedSectionCache[lvl];
|
var cache = this.loadedSectionCache[lvl];
|
||||||
VolatileHolder<WorldSection> holder = null;
|
VolatileHolder<WorldSection> 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 this thread was the one to create the reference then its the thread to load the section
|
||||||
if (isLoader) {
|
if (isLoader) {
|
||||||
var section = new WorldSection(lvl, x, y, z, this);
|
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
|
//TODO: Instead if throwing an exception do something better
|
||||||
throw new IllegalStateException("Unable to load section");
|
throw new IllegalStateException("Unable to load section");
|
||||||
}
|
}
|
||||||
section.acquire();
|
section.acquire();
|
||||||
holder.obj = section;
|
holder.obj = section;
|
||||||
|
if (nullOnEmpty && status == 1) {//If its air return null as stated, release the section aswell
|
||||||
|
section.release();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
return section;
|
return section;
|
||||||
} else {
|
} else {
|
||||||
WorldSection section = null;
|
WorldSection section = null;
|
||||||
@@ -59,7 +64,7 @@ public class ActiveSectionTracker {
|
|||||||
return section;
|
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) {
|
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();
|
section.acquire();
|
||||||
var section2 = tracker.acquire(0,0,0,0);
|
var section2 = tracker.acquire(0,0,0,0, false);
|
||||||
section.release();
|
section.release();
|
||||||
section.release();
|
section.release();
|
||||||
section = tracker.acquire(0,0,0,0);
|
section = tracker.acquire(0,0,0,0, false);
|
||||||
section.release();
|
section.release();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,15 @@
|
|||||||
package me.cortex.voxelmon.core.world;
|
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.Long2ShortOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||||
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
import org.lwjgl.util.zstd.Zstd;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
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.*;
|
import static org.lwjgl.util.zstd.Zstd.*;
|
||||||
|
|
||||||
public class SaveLoadSystem {
|
public class SaveLoadSystem {
|
||||||
public static byte[] serialize(WorldSection section) {
|
public static ByteBuffer serialize(WorldSection section) {
|
||||||
var data = section.copyData();
|
var data = section.copyData();
|
||||||
var compressed = new Short[data.length];
|
var compressed = new Short[data.length];
|
||||||
Long2ShortOpenHashMap LUT = new Long2ShortOpenHashMap();
|
Long2ShortOpenHashMap LUT = new Long2ShortOpenHashMap();
|
||||||
@@ -57,24 +49,17 @@ public class SaveLoadSystem {
|
|||||||
raw.rewind();
|
raw.rewind();
|
||||||
ByteBuffer compressedData = MemoryUtil.memAlloc((int)ZSTD_COMPRESSBOUND(raw.remaining()));
|
ByteBuffer compressedData = MemoryUtil.memAlloc((int)ZSTD_COMPRESSBOUND(raw.remaining()));
|
||||||
long compressedSize = ZSTD_compress(compressedData, raw, 15);
|
long compressedSize = ZSTD_compress(compressedData, raw, 15);
|
||||||
byte[] out = new byte[(int) compressedSize];
|
|
||||||
compressedData.limit((int) compressedSize);
|
compressedData.limit((int) compressedSize);
|
||||||
compressedData.get(out);
|
compressedData.rewind();
|
||||||
|
|
||||||
MemoryUtil.memFree(raw);
|
MemoryUtil.memFree(raw);
|
||||||
MemoryUtil.memFree(compressedData);
|
|
||||||
|
|
||||||
//Compress into a key + data pallet format
|
//Compress into a key + data pallet format
|
||||||
return out;
|
return compressedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean deserialize(WorldSection section, byte[] data) {
|
public static boolean deserialize(WorldSection section, ByteBuffer data) {
|
||||||
var buff = MemoryUtil.memAlloc(data.length);
|
|
||||||
buff.put(data);
|
|
||||||
buff.rewind();
|
|
||||||
var decompressed = MemoryUtil.memAlloc(32*32*32*4*2);
|
var decompressed = MemoryUtil.memAlloc(32*32*32*4*2);
|
||||||
long size = ZSTD_decompress(decompressed, buff);
|
long size = ZSTD_decompress(decompressed, data);
|
||||||
MemoryUtil.memFree(buff);
|
|
||||||
decompressed.limit((int) size);
|
decompressed.limit((int) size);
|
||||||
|
|
||||||
long hash = 0;
|
long hash = 0;
|
||||||
|
|||||||
@@ -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.SectionSavingService;
|
||||||
import me.cortex.voxelmon.core.world.service.VoxelIngestService;
|
import me.cortex.voxelmon.core.world.service.VoxelIngestService;
|
||||||
import me.cortex.voxelmon.core.world.storage.StorageBackend;
|
import me.cortex.voxelmon.core.world.storage.StorageBackend;
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -41,25 +42,36 @@ public class WorldEngine {
|
|||||||
this.sectionTracker = new ActiveSectionTracker(maxMipLayers, this::unsafeLoadSection);
|
this.sectionTracker = new ActiveSectionTracker(maxMipLayers, this::unsafeLoadSection);
|
||||||
|
|
||||||
this.savingService = new SectionSavingService(this, savingServiceWorkers);
|
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());
|
var data = this.storage.getSectionData(into.getKey());
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
if (!SaveLoadSystem.deserialize(into, data)) {
|
try {
|
||||||
this.storage.deleteSectionData(into.getKey());
|
if (!SaveLoadSystem.deserialize(into, data)) {
|
||||||
//TODO: regenerate the section from children
|
this.storage.deleteSectionData(into.getKey());
|
||||||
Arrays.fill(into.data, Mapper.AIR);
|
//TODO: regenerate the section from children
|
||||||
System.err.println("Section " + into.lvl + ", " + into.x + ", " + into.y + ", " + into.z + " was unable to load, setting to air");
|
Arrays.fill(into.data, Mapper.AIR);
|
||||||
return true;
|
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) {
|
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
|
//TODO: Fixme/optimize, cause as the lvl gets higher, the size of x,y,z gets smaller so i can dynamically compact the format
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import net.minecraft.nbt.NbtCompound;
|
|||||||
import net.minecraft.nbt.NbtIo;
|
import net.minecraft.nbt.NbtIo;
|
||||||
import net.minecraft.nbt.NbtOps;
|
import net.minecraft.nbt.NbtOps;
|
||||||
import net.minecraft.registry.entry.RegistryEntry;
|
import net.minecraft.registry.entry.RegistryEntry;
|
||||||
|
import net.minecraft.stat.Stat;
|
||||||
import net.minecraft.world.biome.Biome;
|
import net.minecraft.world.biome.Biome;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
@@ -17,9 +18,9 @@ import java.io.ByteArrayInputStream;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Comparator;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.List;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
|
||||||
//There are independent mappings for biome and block states, these get combined in the shader and allow for more
|
//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 UNKNOWN_MAPPING = -1;
|
||||||
public static final int AIR = 0;
|
public static final int AIR = 0;
|
||||||
|
|
||||||
private final Object2ObjectOpenHashMap<BlockState, StateEntry> block2stateEntry = new Object2ObjectOpenHashMap<>();
|
private final Map<BlockState, StateEntry> block2stateEntry = new ConcurrentHashMap<>(2000,0.75f, 10);
|
||||||
private final ObjectArrayList<StateEntry> blockId2stateEntry = new ObjectArrayList<>();
|
private final ObjectArrayList<StateEntry> blockId2stateEntry = new ObjectArrayList<>();
|
||||||
private final Object2ObjectOpenHashMap<String, BiomeEntry> biome2biomeEntry = new Object2ObjectOpenHashMap<>();
|
private final Map<String, BiomeEntry> biome2biomeEntry = new ConcurrentHashMap<>(2000,0.75f, 10);
|
||||||
private final ObjectArrayList<BiomeEntry> biomeId2biomeEntry = new ObjectArrayList<>();
|
private final ObjectArrayList<BiomeEntry> biomeId2biomeEntry = new ObjectArrayList<>();
|
||||||
|
|
||||||
|
private Consumer<StateEntry> newStateCallback;
|
||||||
|
private Consumer<BiomeEntry> newBiomeCallback;
|
||||||
public Mapper(StorageBackend storage) {
|
public Mapper(StorageBackend storage) {
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
//Insert air since its a special entry (index 0)
|
//Insert air since its a special entry (index 0)
|
||||||
@@ -46,6 +50,20 @@ public class Mapper {
|
|||||||
this.loadFromStorage();
|
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<StateEntry> stateCallback, Consumer<BiomeEntry> biomeCallback) {
|
||||||
|
this.newStateCallback = stateCallback;
|
||||||
|
this.newBiomeCallback = biomeCallback;
|
||||||
|
}
|
||||||
|
|
||||||
private void loadFromStorage() {
|
private void loadFromStorage() {
|
||||||
var mappings = this.storage.getIdMappings();
|
var mappings = this.storage.getIdMappings();
|
||||||
List<StateEntry> sentries = new ArrayList<>();
|
List<StateEntry> sentries = new ArrayList<>();
|
||||||
@@ -88,7 +106,7 @@ public class Mapper {
|
|||||||
|
|
||||||
private StateEntry registerNewBlockState(BlockState state) {
|
private StateEntry registerNewBlockState(BlockState state) {
|
||||||
StateEntry entry = new StateEntry(this.blockId2stateEntry.size(), state);
|
StateEntry entry = new StateEntry(this.blockId2stateEntry.size(), state);
|
||||||
this.block2stateEntry.put(state, entry);
|
//this.block2stateEntry.put(state, entry);
|
||||||
this.blockId2stateEntry.add(entry);
|
this.blockId2stateEntry.add(entry);
|
||||||
|
|
||||||
byte[] serialized = entry.serialize();
|
byte[] serialized = entry.serialize();
|
||||||
@@ -97,12 +115,14 @@ public class Mapper {
|
|||||||
buffer.rewind();
|
buffer.rewind();
|
||||||
this.storage.putIdMapping(entry.id | (BLOCK_STATE_TYPE<<30), buffer);
|
this.storage.putIdMapping(entry.id | (BLOCK_STATE_TYPE<<30), buffer);
|
||||||
MemoryUtil.memFree(buffer);
|
MemoryUtil.memFree(buffer);
|
||||||
|
|
||||||
|
if (this.newStateCallback!=null)this.newStateCallback.accept(entry);
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BiomeEntry registerNewBiome(String biome) {
|
private BiomeEntry registerNewBiome(String biome) {
|
||||||
BiomeEntry entry = new BiomeEntry(this.biome2biomeEntry.size(), biome);
|
BiomeEntry entry = new BiomeEntry(this.biome2biomeEntry.size(), biome);
|
||||||
this.biome2biomeEntry.put(biome, entry);
|
//this.biome2biomeEntry.put(biome, entry);
|
||||||
this.biomeId2biomeEntry.add(entry);
|
this.biomeId2biomeEntry.add(entry);
|
||||||
|
|
||||||
byte[] serialized = entry.serialize();
|
byte[] serialized = entry.serialize();
|
||||||
@@ -111,62 +131,54 @@ public class Mapper {
|
|||||||
buffer.rewind();
|
buffer.rewind();
|
||||||
this.storage.putIdMapping(entry.id | (BIOME_TYPE<<30), buffer);
|
this.storage.putIdMapping(entry.id | (BIOME_TYPE<<30), buffer);
|
||||||
MemoryUtil.memFree(buffer);
|
MemoryUtil.memFree(buffer);
|
||||||
|
|
||||||
|
if (this.newBiomeCallback!=null)this.newBiomeCallback.accept(entry);
|
||||||
return 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> biome) {
|
public long getBaseId(byte light, BlockState state, RegistryEntry<Biome> biome) {
|
||||||
StateEntry sentry = null;
|
if (state.isAir()) return ((long)light)<<56;//Special case and fast return for air, dont care about the biome
|
||||||
BiomeEntry bentry = null;
|
StateEntry sentry = this.block2stateEntry.computeIfAbsent(state, this::registerNewBlockState);
|
||||||
synchronized (this.block2stateEntry) {
|
|
||||||
sentry = this.block2stateEntry.get(state);
|
String biomeId = biome.getKey().get().getValue().toString();
|
||||||
if (sentry == null) {
|
BiomeEntry bentry = this.biome2biomeEntry.computeIfAbsent(biomeId, this::registerNewBiome);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Byte.toUnsignedLong(light)<<56)|(Integer.toUnsignedLong(bentry.id) << 47)|(Integer.toUnsignedLong(sentry.id)<<27);
|
return (Byte.toUnsignedLong(light)<<56)|(Integer.toUnsignedLong(bentry.id) << 47)|(Integer.toUnsignedLong(sentry.id)<<27);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockState[] getBlockStates() {
|
//TODO: fixme: synchronize access to this.blockId2stateEntry
|
||||||
synchronized (this.block2stateEntry) {
|
public StateEntry[] getStateEntries() {
|
||||||
BlockState[] out = new BlockState[this.blockId2stateEntry.size()];
|
var set = new ArrayList<>(this.blockId2stateEntry);
|
||||||
int i = 0;
|
StateEntry[] out = new StateEntry[set.size()];
|
||||||
for (var entry : this.blockId2stateEntry) {
|
int i = 0;
|
||||||
if (entry.id != i++) {
|
for (var entry : set) {
|
||||||
throw new IllegalStateException();
|
if (entry.id != i++) {
|
||||||
}
|
throw new IllegalStateException();
|
||||||
out[i-1] = entry.state;
|
|
||||||
}
|
}
|
||||||
return out;
|
out[i-1] = entry;
|
||||||
}
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getBiomes() {
|
//TODO: fixme: synchronize access to this.biomeId2biomeEntry
|
||||||
synchronized (this.biome2biomeEntry) {
|
public BiomeEntry[] getBiomeEntries() {
|
||||||
String[] out = new String[this.biome2biomeEntry.size()];
|
var set = new ArrayList<>(this.biomeId2biomeEntry);
|
||||||
int i = 0;
|
BiomeEntry[] out = new BiomeEntry[set.size()];
|
||||||
for (var entry : this.biomeId2biomeEntry) {
|
int i = 0;
|
||||||
if (entry.id != i++) {
|
for (var entry : set) {
|
||||||
throw new IllegalStateException();
|
if (entry.id != i++) {
|
||||||
}
|
throw new IllegalStateException();
|
||||||
out[i-1] = entry.biome;
|
|
||||||
}
|
}
|
||||||
return out;
|
out[i-1] = entry;
|
||||||
}
|
}
|
||||||
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class StateEntry {
|
public static final class StateEntry {
|
||||||
private final int id;
|
public final int id;
|
||||||
private final BlockState state;
|
public final BlockState state;
|
||||||
public StateEntry(int id, BlockState state) {
|
public StateEntry(int id, BlockState state) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.state = state;
|
this.state = state;
|
||||||
@@ -200,8 +212,8 @@ public class Mapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final class BiomeEntry {
|
public static final class BiomeEntry {
|
||||||
private final int id;
|
public final int id;
|
||||||
private final String biome;
|
public final String biome;
|
||||||
|
|
||||||
public BiomeEntry(int id, String biome) {
|
public BiomeEntry(int id, String biome) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|||||||
@@ -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: 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
|
//TODO: also average out the light level and set that as the new light level
|
||||||
//For now just take the most top corner
|
//For now just take the most top corner
|
||||||
if (I111 != 0) {
|
if (!Mapper.isAir(I111)) {
|
||||||
return I111;
|
return I111;
|
||||||
}
|
}
|
||||||
if (I110 != 0) {
|
if (!Mapper.isAir(I110)) {
|
||||||
return I110;
|
return I110;
|
||||||
}
|
}
|
||||||
if (I011 != 0) {
|
if (!Mapper.isAir(I011)) {
|
||||||
return I011;
|
return I011;
|
||||||
}
|
}
|
||||||
if (I010 != 0) {
|
if (!Mapper.isAir(I010)) {
|
||||||
return I010;
|
return I010;
|
||||||
}
|
}
|
||||||
if (I101 != 0) {
|
if (!Mapper.isAir(I101)) {
|
||||||
return I101;
|
return I101;
|
||||||
}
|
}
|
||||||
if (I100 != 0) {
|
if (!Mapper.isAir(I100)) {
|
||||||
return I100;
|
return I100;
|
||||||
}
|
}
|
||||||
if (I001 != 0) {
|
if (!Mapper.isAir(I001)) {
|
||||||
return I001;
|
return I001;
|
||||||
}
|
}
|
||||||
if (I000 != 0) {
|
if (!Mapper.isAir(I000)) {
|
||||||
return I000;
|
return I000;
|
||||||
}
|
}
|
||||||
|
//TODO: need to account for different light levels of "air"
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,14 +44,9 @@ public class SectionSavingService {
|
|||||||
section.assertNotFree();
|
section.assertNotFree();
|
||||||
section.inSaveQueue.set(false);
|
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);
|
var saveData = SaveLoadSystem.serialize(section);
|
||||||
//Note: this is done like this because else the gc can collect the buffer before the transaction is completed
|
this.world.storage.setSectionData(section.getKey(), saveData);
|
||||||
// thus the transaction reads from undefined memory
|
MemoryUtil.memFree(saveData);
|
||||||
ByteBuffer buffer = MemoryUtil.memAlloc(saveData.length);
|
|
||||||
buffer.put(saveData).rewind();
|
|
||||||
this.world.storage.setSectionData(section.getKey(), buffer);
|
|
||||||
MemoryUtil.memFree(buffer);
|
|
||||||
|
|
||||||
section.release();
|
section.release();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,28 +1,43 @@
|
|||||||
package me.cortex.voxelmon.core.world.service;
|
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.VoxelizedSection;
|
||||||
import me.cortex.voxelmon.core.voxelization.WorldConversionFactory;
|
import me.cortex.voxelmon.core.voxelization.WorldConversionFactory;
|
||||||
import me.cortex.voxelmon.core.world.WorldEngine;
|
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.WorldChunk;
|
||||||
|
import net.minecraft.world.chunk.light.LightStorage;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
import java.util.concurrent.Semaphore;
|
import java.util.concurrent.Semaphore;
|
||||||
|
|
||||||
public class VoxelIngestService {
|
public class VoxelIngestService {
|
||||||
private volatile boolean running = true;
|
private volatile boolean running = true;
|
||||||
private final Thread worker;
|
private final Thread[] workers;
|
||||||
|
|
||||||
private final ConcurrentLinkedDeque<WorldChunk> ingestQueue = new ConcurrentLinkedDeque<>();
|
private final ConcurrentLinkedDeque<WorldChunk> ingestQueue = new ConcurrentLinkedDeque<>();
|
||||||
private final Semaphore ingestCounter = new Semaphore(0);
|
private final Semaphore ingestCounter = new Semaphore(0);
|
||||||
|
|
||||||
private final WorldEngine world;
|
private final ConcurrentHashMap<Long, Pair<ChunkNibbleArray, ChunkNibbleArray>> captureLightMap = new ConcurrentHashMap<>(1000,0.75f, 7);
|
||||||
public VoxelIngestService(WorldEngine world) {
|
|
||||||
this.worker = new Thread(this::ingestWorker);
|
|
||||||
this.worker.setDaemon(false);
|
|
||||||
this.worker.setName("Ingest service");
|
|
||||||
this.worker.start();
|
|
||||||
|
|
||||||
|
private final WorldEngine world;
|
||||||
|
public VoxelIngestService(WorldEngine world, int workers) {
|
||||||
this.world = world;
|
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() {
|
private void ingestWorker() {
|
||||||
@@ -33,6 +48,7 @@ public class VoxelIngestService {
|
|||||||
int i = chunk.getBottomSectionCoord() - 1;
|
int i = chunk.getBottomSectionCoord() - 1;
|
||||||
for (var section : chunk.getSectionArray()) {
|
for (var section : chunk.getSectionArray()) {
|
||||||
i++;
|
i++;
|
||||||
|
var lighting = this.captureLightMap.remove(ChunkSectionPos.from(chunk.getPos(), i).asLong());
|
||||||
if (section.isEmpty()) {
|
if (section.isEmpty()) {
|
||||||
this.world.insertUpdate(VoxelizedSection.createEmpty(chunk.getPos().x, i, chunk.getPos().z));
|
this.world.insertUpdate(VoxelizedSection.createEmpty(chunk.getPos().x, i, chunk.getPos().z));
|
||||||
} else {
|
} else {
|
||||||
@@ -40,7 +56,20 @@ public class VoxelIngestService {
|
|||||||
this.world.getMapper(),
|
this.world.getMapper(),
|
||||||
section.getBlockStateContainer(),
|
section.getBlockStateContainer(),
|
||||||
section.getBiomeContainer(),
|
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<state.getLuminance()) {
|
||||||
|
block = state.getLuminance();
|
||||||
|
}
|
||||||
|
sky = 15-sky;//This is cause sky light is inverted which saves memory when saving empty sections
|
||||||
|
return (byte) (sky|(block<<4));
|
||||||
|
}
|
||||||
|
},
|
||||||
chunk.getPos().x,
|
chunk.getPos().x,
|
||||||
i,
|
i,
|
||||||
chunk.getPos().z
|
chunk.getPos().z
|
||||||
@@ -53,6 +82,24 @@ public class VoxelIngestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void enqueueIngest(WorldChunk chunk) {
|
public void enqueueIngest(WorldChunk chunk) {
|
||||||
|
var lp = chunk.getWorld().getLightingProvider();
|
||||||
|
var blockLight = lp.get(LightType.BLOCK);
|
||||||
|
var skyLight = lp.get(LightType.SKY);
|
||||||
|
int i = chunk.getBottomSectionCoord() - 1;
|
||||||
|
for (var section : chunk.getSectionArray()) {
|
||||||
|
i++;
|
||||||
|
if (section == null) continue;
|
||||||
|
if (section.isEmpty()) continue;
|
||||||
|
var pos = ChunkSectionPos.from(chunk.getPos(), i);
|
||||||
|
if (lp.getStatus(LightType.BLOCK, pos) == LightStorage.Status.EMPTY)
|
||||||
|
continue;
|
||||||
|
var bl = blockLight.getLightSection(pos);
|
||||||
|
var sl = skyLight.getLightSection(pos);
|
||||||
|
if (bl == null && sl == null) continue;
|
||||||
|
bl = bl==null?null:bl.copy();
|
||||||
|
sl = sl==null?null:sl.copy();
|
||||||
|
this.captureLightMap.put(pos.asLong(), Pair.of(bl, sl));
|
||||||
|
}
|
||||||
this.ingestQueue.add(chunk);
|
this.ingestQueue.add(chunk);
|
||||||
this.ingestCounter.release();
|
this.ingestCounter.release();
|
||||||
}
|
}
|
||||||
@@ -62,10 +109,20 @@ public class VoxelIngestService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
if (!this.worker.isAlive()) {
|
boolean anyAlive = false;
|
||||||
System.err.println("Ingest worker already dead on shutdown! this is very very bad, check log for errors from this thread");
|
boolean allAlive = true;
|
||||||
|
for (var worker : this.workers) {
|
||||||
|
anyAlive |= worker.isAlive();
|
||||||
|
allAlive &= worker.isAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anyAlive) {
|
||||||
|
System.err.println("Ingest workers already dead on shutdown! this is very very bad, check log for errors from this thread");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (!allAlive) {
|
||||||
|
System.err.println("Some ingest workers already dead on shutdown! this is very very bad, check log for errors from this thread");
|
||||||
|
}
|
||||||
|
|
||||||
//Wait for the ingest to finish
|
//Wait for the ingest to finish
|
||||||
while (this.ingestCounter.availablePermits() != 0) {
|
while (this.ingestCounter.availablePermits() != 0) {
|
||||||
@@ -75,6 +132,10 @@ public class VoxelIngestService {
|
|||||||
this.running = false;
|
this.running = false;
|
||||||
this.ingestCounter.release(1000);
|
this.ingestCounter.release(1000);
|
||||||
//Wait for thread to join
|
//Wait for thread to join
|
||||||
try {this.worker.join();} catch (InterruptedException e) {throw new RuntimeException(e);}
|
try {
|
||||||
|
for (var worker : this.workers) {
|
||||||
|
worker.join();
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {throw new RuntimeException(e);}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package me.cortex.voxelmon.core.world.storage;
|
package me.cortex.voxelmon.core.world.storage;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
import org.lwjgl.util.lmdb.MDBVal;
|
import org.lwjgl.util.lmdb.MDBVal;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -80,7 +81,7 @@ public class StorageBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//TODO: make batch get and updates
|
//TODO: make batch get and updates
|
||||||
public byte[] getSectionData(long key) {
|
public ByteBuffer getSectionData(long key) {
|
||||||
return this.synchronizedTransaction(() -> this.sectionDatabase.transaction(MDB_RDONLY, transaction->{
|
return this.synchronizedTransaction(() -> this.sectionDatabase.transaction(MDB_RDONLY, transaction->{
|
||||||
var buff = transaction.stack.malloc(8);
|
var buff = transaction.stack.malloc(8);
|
||||||
buff.putLong(0, key);
|
buff.putLong(0, key);
|
||||||
@@ -88,9 +89,9 @@ public class StorageBackend {
|
|||||||
if (bb == null) {
|
if (bb == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var res = new byte[bb.remaining()];
|
var copy = MemoryUtil.memAlloc(bb.remaining());
|
||||||
bb.get(res);
|
MemoryUtil.memCopy(bb, copy);
|
||||||
return res;
|
return copy;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ public class WorldImporter {
|
|||||||
this.world.getMapper(),
|
this.world.getMapper(),
|
||||||
blockStates,
|
blockStates,
|
||||||
biomes,
|
biomes,
|
||||||
(bx, by, bz) -> (byte) 0,
|
(bx, by, bz, state) -> (byte) 0,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
z
|
z
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ import org.spongepowered.asm.mixin.injection.Constant;
|
|||||||
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
import org.spongepowered.asm.mixin.injection.ModifyConstant;
|
||||||
|
|
||||||
@Mixin(BackgroundRenderer.class)
|
@Mixin(BackgroundRenderer.class)
|
||||||
|
|
||||||
public class MixinBackgroundRenderer {
|
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) {
|
private static float changeFog(float fog) {
|
||||||
return 9999999f;
|
return 9999999f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
|||||||
|
|
||||||
@Mixin(GameRenderer.class)
|
@Mixin(GameRenderer.class)
|
||||||
public class MixinGameRenderer {
|
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<Float> cir) {
|
public void method_32796(CallbackInfoReturnable<Float> cir) {
|
||||||
cir.setReturnValue(16 * 3000f);
|
cir.setReturnValue(16 * 3000f);
|
||||||
cir.cancel();
|
cir.cancel();
|
||||||
|
|||||||
@@ -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) {
|
private float redirectMax(float a, float b) {
|
||||||
return a;
|
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) {
|
private float changeRD(GameRenderer instance) {
|
||||||
float viewDistance = instance.getViewDistance();
|
float viewDistance = instance.getViewDistance();
|
||||||
return 16*1512;
|
return 16*1512;
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
layout(binding=0) uniform sampler2D colourTexture;
|
||||||
|
in vec2 uv;
|
||||||
|
void main() {
|
||||||
|
gl_Colour = texture(colourTexture, uv);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position =
|
||||||
|
}
|
||||||
@@ -66,5 +66,9 @@ layout(binding = 6, std430) readonly restrict buffer BiomeBuffer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
layout(binding = 7, std430) readonly restrict buffer LightingBuffer {
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -65,6 +65,9 @@ void main() {
|
|||||||
uint biomeId = extractBiomeId(quad);
|
uint biomeId = extractBiomeId(quad);
|
||||||
State stateInfo = stateData[stateId];
|
State stateInfo = stateData[stateId];
|
||||||
colour = uint2vec4RGBA(stateInfo.faceColours[face]);
|
colour = uint2vec4RGBA(stateInfo.faceColours[face]);
|
||||||
|
|
||||||
|
colour *= getLighting(extractLightId(quad));
|
||||||
|
|
||||||
if (((stateInfo.biomeTintMsk>>face)&1) == 1) {
|
if (((stateInfo.biomeTintMsk>>face)&1) == 1) {
|
||||||
vec4 biomeColour = uint2vec4RGBA(biomeData[biomeId].foliage);
|
vec4 biomeColour = uint2vec4RGBA(biomeData[biomeId].foliage);
|
||||||
colour *= biomeColour;
|
colour *= biomeColour;
|
||||||
|
|||||||
@@ -18,5 +18,6 @@
|
|||||||
],
|
],
|
||||||
"depends": {
|
"depends": {
|
||||||
"fabricloader": ">=0.14.22"
|
"fabricloader": ">=0.14.22"
|
||||||
}
|
},
|
||||||
|
"accessWidener": "voxelmon.accesswidener"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
accessWidener v1 named
|
accessWidener v1 named
|
||||||
|
|
||||||
accessible field net/minecraft/client/texture/SpriteContents image Lnet/minecraft/client/texture/NativeImage;
|
accessible field net/minecraft/client/texture/SpriteContents image Lnet/minecraft/client/texture/NativeImage;
|
||||||
accessible field net/minecraft/client/render/Frustum frustumIntersection Lorg/joml/FrustumIntersection;
|
accessible field net/minecraft/client/render/Frustum frustumIntersection Lorg/joml/FrustumIntersection;
|
||||||
|
accessible field net/minecraft/client/render/LightmapTextureManager texture Lnet/minecraft/client/texture/NativeImageBackedTexture;
|
||||||
@@ -8,12 +8,10 @@
|
|||||||
"minecraft.MixinDebugHud",
|
"minecraft.MixinDebugHud",
|
||||||
"minecraft.MixinGameRenderer",
|
"minecraft.MixinGameRenderer",
|
||||||
"minecraft.MixinMinecraftClient",
|
"minecraft.MixinMinecraftClient",
|
||||||
"minecraft.MixinWorldRenderer"
|
"minecraft.MixinWorldRenderer",
|
||||||
|
"joml.AccessFrustumIntersection"
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
},
|
}
|
||||||
"mixins": [
|
|
||||||
"joml.AccessFrustumIntersection"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user