Uhhhhh refactor??? ig???
This commit is contained in:
@@ -50,7 +50,9 @@ public class VoxyConfig {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return new VoxyConfig();
|
||||
var config = new VoxyConfig();
|
||||
config.defaultSaveConfig = ContextSelectionSystem.DEFAULT_STORAGE_CONFIG;
|
||||
return config;
|
||||
}
|
||||
public void save() {
|
||||
//Unsafe, todo: fixme! needs to be atomic!
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
package me.cortex.voxy.client.core;
|
||||
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||
import me.cortex.voxy.common.world.WorldSection;
|
||||
|
||||
public interface AbstractRenderWorldInteractor {
|
||||
void processBuildResult(BuiltSection section);
|
||||
|
||||
void sectionUpdated(WorldSection worldSection);
|
||||
|
||||
void setRenderGen(RenderGenerationService renderService);
|
||||
|
||||
void initPosition(int x, int z);
|
||||
|
||||
void setCenter(int x, int y, int z);
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package me.cortex.voxy.client.core;
|
||||
|
||||
import me.cortex.voxy.client.config.VoxyConfig;
|
||||
import me.cortex.voxy.client.core.rendering.AbstractFarWorldRenderer;
|
||||
import me.cortex.voxy.client.core.rendering.IRenderInterface;
|
||||
import me.cortex.voxy.client.core.rendering.RenderTracker;
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||
import me.cortex.voxy.client.saver.ContextSelectionSystem;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
import me.cortex.voxy.common.world.WorldSection;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
|
||||
public class DefaultRenderWorldInteractor implements AbstractRenderWorldInteractor {
|
||||
private final DistanceTracker distanceTracker;
|
||||
private final RenderTracker renderTracker;
|
||||
|
||||
|
||||
public DefaultRenderWorldInteractor(ContextSelectionSystem.WorldConfig cfg, WorldEngine world, IRenderInterface renderer) {
|
||||
this.renderTracker = new RenderTracker(world, (AbstractFarWorldRenderer) renderer);
|
||||
|
||||
//To get to chunk scale multiply the scale by 2, the scale is after how many chunks does the lods halve
|
||||
int q = VoxyConfig.CONFIG.qualityScale;
|
||||
int minY = MinecraftClient.getInstance().world.getBottomSectionCoord()/2;
|
||||
int maxY = MinecraftClient.getInstance().world.getTopSectionCoord()/2;
|
||||
|
||||
if (cfg.minYOverride != Integer.MAX_VALUE) {
|
||||
minY = cfg.minYOverride;
|
||||
}
|
||||
|
||||
if (cfg.maxYOverride != Integer.MIN_VALUE) {
|
||||
maxY = cfg.maxYOverride;
|
||||
}
|
||||
|
||||
this.distanceTracker = new DistanceTracker(this.renderTracker, new int[]{q,q,q,q},
|
||||
(VoxyConfig.CONFIG.renderDistance<0?VoxyConfig.CONFIG.renderDistance:((VoxyConfig.CONFIG.renderDistance+1)/2)),
|
||||
minY, maxY);
|
||||
|
||||
System.out.println("Distance tracker initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processBuildResult(BuiltSection section) {
|
||||
this.renderTracker.processBuildResult(section);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sectionUpdated(WorldSection worldSection) {
|
||||
this.renderTracker.sectionUpdated(worldSection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRenderGen(RenderGenerationService renderService) {
|
||||
this.renderTracker.setRenderGen(renderService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initPosition(int x, int z) {
|
||||
this.distanceTracker.init(x,z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCenter(int x, int y, int z) {
|
||||
this.distanceTracker.setCenter(x,y,z);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package me.cortex.voxy.client.core;
|
||||
|
||||
import me.cortex.voxy.client.core.rendering.AbstractFarWorldRenderer;
|
||||
import me.cortex.voxy.client.core.rendering.Viewport;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import org.vivecraft.client_vr.ClientDataHolderVR;
|
||||
@@ -9,7 +8,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ViewportSelector <T extends Viewport> {
|
||||
public class ViewportSelector <T extends Viewport<?>> {
|
||||
public static final boolean VIVECRAFT_INSTALLED = FabricLoader.getInstance().isModLoaded("vivecraft");
|
||||
|
||||
private final Supplier<T> creator;
|
||||
|
||||
@@ -3,7 +3,7 @@ package me.cortex.voxy.client.core;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import me.cortex.voxy.client.Voxy;
|
||||
import me.cortex.voxy.client.config.VoxyConfig;
|
||||
import me.cortex.voxy.client.core.model.ModelManager;
|
||||
import me.cortex.voxy.client.core.model.OffThreadModelBakerySystem;
|
||||
import me.cortex.voxy.client.core.rendering.*;
|
||||
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||
import me.cortex.voxy.client.core.rendering.post.PostProcessing;
|
||||
@@ -54,11 +54,8 @@ import static org.lwjgl.opengl.GL30C.GL_DRAW_FRAMEBUFFER_BINDING;
|
||||
//REMOVE setRenderGen like holy hell
|
||||
public class VoxelCore {
|
||||
private final WorldEngine world;
|
||||
private final RenderGenerationService renderGen;
|
||||
private final ModelManager modelManager;
|
||||
private final AbstractRenderWorldInteractor interactor;
|
||||
|
||||
private final IRenderInterface renderer;
|
||||
private final RenderService renderer;
|
||||
private final ViewportSelector viewportSelector;
|
||||
private final PostProcessing postProcessing;
|
||||
|
||||
@@ -73,46 +70,12 @@ public class VoxelCore {
|
||||
//Trigger the shared index buffer loading
|
||||
SharedIndexBuffer.INSTANCE.id();
|
||||
Capabilities.init();//Ensure clinit is called
|
||||
this.modelManager = new ModelManager(16);
|
||||
this.renderer = this.createRenderBackend();
|
||||
|
||||
this.renderer = new RenderService(this.world);
|
||||
System.out.println("Using " + this.renderer.getClass().getSimpleName());
|
||||
this.viewportSelector = new ViewportSelector<>(this.renderer::createViewport);
|
||||
|
||||
//Ungodly hacky code
|
||||
if (this.renderer instanceof AbstractRenderWorldInteractor) {
|
||||
this.interactor = (AbstractRenderWorldInteractor) this.renderer;
|
||||
} else {
|
||||
this.interactor = new DefaultRenderWorldInteractor(cfg, this.world, this.renderer);
|
||||
}
|
||||
System.out.println("Renderer initialized");
|
||||
|
||||
this.renderGen = new RenderGenerationService(this.world, this.modelManager, VoxyConfig.CONFIG.renderThreads, this.interactor::processBuildResult, this.renderer.generateMeshlets());
|
||||
this.world.setDirtyCallback(this.interactor::sectionUpdated);
|
||||
this.interactor.setRenderGen(this.renderGen);
|
||||
System.out.println("Render tracker and generator initialized");
|
||||
|
||||
|
||||
|
||||
this.postProcessing = new PostProcessing();
|
||||
|
||||
this.world.getMapper().setCallbacks(this.renderer::addBlockState, this.renderer::addBiome);
|
||||
|
||||
|
||||
////Resave the db incase it failed a recovery
|
||||
//this.world.getMapper().forceResaveStates();
|
||||
|
||||
var biomeRegistry = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME);
|
||||
for (var biome : this.world.getMapper().getBiomeEntries()) {
|
||||
//this.renderer.getModelManager().addBiome(biome.id, biomeRegistry.get(new Identifier(biome.biome)));
|
||||
this.renderer.addBiome(biome);
|
||||
}
|
||||
|
||||
for (var state : this.world.getMapper().getStateEntries()) {
|
||||
//this.renderer.getModelManager().addEntry(state.id, state.state);
|
||||
this.renderer.addBlockState(state);
|
||||
}
|
||||
//this.renderer.getModelManager().updateEntry(0, Blocks.GRASS_BLOCK.getDefaultState());
|
||||
|
||||
System.out.println("Voxy core initialized");
|
||||
}
|
||||
|
||||
@@ -120,16 +83,8 @@ public class VoxelCore {
|
||||
this.world.ingestService.enqueueIngest(worldChunk);
|
||||
}
|
||||
|
||||
boolean firstTime = true;
|
||||
public void renderSetup(Frustum frustum, Camera camera) {
|
||||
if (this.firstTime) {
|
||||
//TODO: remove initPosition
|
||||
this.interactor.initPosition(camera.getBlockPos().getX(), camera.getBlockPos().getZ());
|
||||
this.firstTime = false;
|
||||
//this.renderTracker.addLvl0(0,6,0);
|
||||
}
|
||||
this.interactor.setCenter(camera.getBlockPos().getX(), camera.getBlockPos().getY(), camera.getBlockPos().getZ());
|
||||
this.renderer.setupRender(frustum, camera);
|
||||
this.renderer.setup(camera);
|
||||
}
|
||||
|
||||
private static Matrix4f makeProjectionMatrix(float near, float far) {
|
||||
@@ -147,6 +102,7 @@ public class VoxelCore {
|
||||
return projection;
|
||||
}
|
||||
|
||||
//TODO: Make a reverse z buffer
|
||||
private static Matrix4f computeProjectionMat() {
|
||||
return new Matrix4f(RenderSystem.getProjectionMatrix()).mulLocal(
|
||||
makeProjectionMatrix(0.05f, MinecraftClient.getInstance().gameRenderer.getFarPlaneDistance()).invert()
|
||||
@@ -160,16 +116,9 @@ public class VoxelCore {
|
||||
matrices.push();
|
||||
matrices.translate(-cameraX, -cameraY, -cameraZ);
|
||||
matrices.pop();
|
||||
//this.renderer.getModelManager().updateEntry(0, Blocks.DIRT_PATH.getDefaultState());
|
||||
|
||||
//this.renderer.getModelManager().updateEntry(0, Blocks.COMPARATOR.getDefaultState());
|
||||
//this.renderer.getModelManager().updateEntry(0, Blocks.OAK_LEAVES.getDefaultState());
|
||||
|
||||
//var fb = Iris.getPipelineManager().getPipelineNullable().getSodiumTerrainPipeline().getTerrainSolidFramebuffer();
|
||||
//fb.bind();
|
||||
|
||||
var projection = computeProjectionMat();
|
||||
//var projection = RenderSystem.getProjectionMatrix();//computeProjectionMat();
|
||||
|
||||
var viewport = this.viewportSelector.getViewport();
|
||||
viewport
|
||||
.setProjection(projection)
|
||||
@@ -183,7 +132,7 @@ public class VoxelCore {
|
||||
this.renderer.renderFarAwayOpaque(viewport);
|
||||
|
||||
//Compute the SSAO of the rendered terrain
|
||||
//this.postProcessing.computeSSAO(projection, matrices);
|
||||
this.postProcessing.computeSSAO(projection, matrices);
|
||||
|
||||
//We can render the translucent directly after as it is the furthest translucent objects
|
||||
this.renderer.renderFarAwayTranslucent(viewport);
|
||||
@@ -202,9 +151,8 @@ public class VoxelCore {
|
||||
debug.add("Saving service tasks: " + this.world.savingService.getTaskCount());
|
||||
debug.add("Render service tasks: " + this.renderGen.getTaskCount());
|
||||
*/
|
||||
debug.add("I/S/R tasks: " + this.world.ingestService.getTaskCount() + "/"+this.world.savingService.getTaskCount()+"/"+this.renderGen.getTaskCount());
|
||||
debug.add("Loaded cache sizes: " + Arrays.toString(this.world.getLoadedSectionCacheSizes()));
|
||||
debug.add("Mesh cache count: " + this.renderGen.getMeshCacheCount());
|
||||
debug.add("I/S tasks: " + this.world.ingestService.getTaskCount() + "/"+this.world.savingService.getTaskCount());
|
||||
debug.add("SCS: " + Arrays.toString(this.world.getLoadedSectionCacheSizes()));
|
||||
this.renderer.addDebugData(debug);
|
||||
}
|
||||
|
||||
@@ -226,11 +174,9 @@ public class VoxelCore {
|
||||
try {this.importer.shutdown();this.importer = null;} catch (Exception e) {e.printStackTrace();}
|
||||
}
|
||||
System.out.println("Shutting down voxel core");
|
||||
try {this.renderGen.shutdown();} catch (Exception e) {e.printStackTrace();}
|
||||
System.out.println("Render gen shut down");
|
||||
try {this.world.shutdown();} catch (Exception e) {e.printStackTrace();}
|
||||
System.out.println("World engine shut down");
|
||||
try {this.renderer.shutdown(); this.viewportSelector.free(); this.modelManager.free();} catch (Exception e) {e.printStackTrace();}
|
||||
try {this.renderer.shutdown(); this.viewportSelector.free();} catch (Exception e) {e.printStackTrace();}
|
||||
System.out.println("Renderer shut down");
|
||||
if (this.postProcessing!=null){try {this.postProcessing.shutdown();} catch (Exception e) {e.printStackTrace();}}
|
||||
System.out.println("Voxel core shut down");
|
||||
@@ -264,20 +210,4 @@ public class VoxelCore {
|
||||
public WorldEngine getWorldEngine() {
|
||||
return this.world;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private IRenderInterface<?> createRenderBackend() {
|
||||
if (true) {
|
||||
return new Gl46HierarchicalRenderer(this.modelManager);
|
||||
} else if (false) {
|
||||
return new Gl46MeshletsFarWorldRenderer(this.modelManager, VoxyConfig.CONFIG.geometryBufferSize, VoxyConfig.CONFIG.maxSections);
|
||||
} else {
|
||||
if (VoxyConfig.CONFIG.useMeshShaders()) {
|
||||
return new NvMeshFarWorldRenderer(this.modelManager, VoxyConfig.CONFIG.geometryBufferSize, VoxyConfig.CONFIG.maxSections);
|
||||
} else {
|
||||
return new Gl46FarWorldRenderer(this.modelManager, VoxyConfig.CONFIG.geometryBufferSize, VoxyConfig.CONFIG.maxSections);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,10 @@ public class BakedBlockEntityModel {
|
||||
.texture(Float.intBitsToFloat(vert[7]), Float.intBitsToFloat(vert[8]));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.vertices.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
private final List<BakedVertices> layers;
|
||||
@@ -99,6 +103,7 @@ public class BakedBlockEntityModel {
|
||||
public void renderOut() {
|
||||
var vc = Tessellator.getInstance();
|
||||
for (var layer : this.layers) {
|
||||
if (layer.isEmpty()) continue;
|
||||
var bb = vc.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_TEXTURE_COLOR);
|
||||
if (layer.layer instanceof RenderLayer.MultiPhase mp) {
|
||||
Identifier textureId = mp.phases.texture.getId().orElse(null);
|
||||
|
||||
@@ -3,7 +3,6 @@ package me.cortex.voxy.client.core.model;
|
||||
import com.mojang.blaze3d.platform.GlConst;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import me.cortex.voxy.client.core.IGetVoxelCore;
|
||||
@@ -11,9 +10,7 @@ import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.gl.GlTexture;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import me.cortex.voxy.common.world.other.Mapper;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.FluidBlock;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
@@ -38,6 +35,7 @@ import org.lwjgl.system.MemoryUtil;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static me.cortex.voxy.client.core.model.ModelStore.MODEL_SIZE;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL11C.GL_NEAREST;
|
||||
import static org.lwjgl.opengl.GL11C.GL_NEAREST_MIPMAP_LINEAR;
|
||||
@@ -55,7 +53,13 @@ import static org.lwjgl.opengl.GL45C.glTextureSubImage2D;
|
||||
//TODO: support more than 65535 states, what should actually happen is a blockstate is registered, the model data is generated, then compared
|
||||
// to all other models already loaded, if it is a duplicate, create a mapping from the id to the already loaded id, this will help with meshing aswell
|
||||
// as leaves and such will be able to be merged
|
||||
public class ModelManager {
|
||||
|
||||
|
||||
|
||||
//TODO: NOTE!!! is it worth even uploading as a 16x16 texture, since automatic lod selection... doing 8x8 textures might be perfectly ok!!!
|
||||
// this _quarters_ the memory requirements for the texture atlas!!! WHICH IS HUGE saving
|
||||
public class ModelFactory {
|
||||
public static final int MODEL_TEXTURE_SIZE = 16;
|
||||
|
||||
//TODO: replace the fluid BlockState with a client model id integer of the fluidState, requires looking up
|
||||
// the fluid state in the mipper
|
||||
@@ -65,14 +69,11 @@ public class ModelManager {
|
||||
}
|
||||
}
|
||||
|
||||
public static final int MODEL_SIZE = 64;
|
||||
private final Biome DEFAULT_BIOME = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME).get(BiomeKeys.PLAINS);
|
||||
|
||||
public final ModelTextureBakery bakery;
|
||||
private final GlBuffer modelBuffer;
|
||||
private final GlBuffer modelColourBuffer;
|
||||
private final GlTexture textures;
|
||||
private final int blockSampler = glGenSamplers();
|
||||
|
||||
private final int modelTextureSize;
|
||||
|
||||
//Model data might also contain a constant colour if the colour resolver produces a constant colour, this saves space in the
|
||||
// section buffer reverse indexing
|
||||
@@ -108,21 +109,20 @@ public class ModelManager {
|
||||
private final int[] idMappings;
|
||||
private final Object2IntOpenHashMap<ModelEntry> modelTexture2id = new Object2IntOpenHashMap<>();
|
||||
|
||||
private final Mapper mapper;
|
||||
|
||||
private final List<Biome> biomes = new ArrayList<>();
|
||||
private final List<Pair<Integer, BlockState>> modelsRequiringBiomeColours = new ArrayList<>();
|
||||
|
||||
private static final ObjectSet<BlockState> LOGGED_SELF_CULLING_WARNING = new ObjectOpenHashSet<>();
|
||||
|
||||
public ModelManager(int modelTextureSize) {
|
||||
this.modelTextureSize = modelTextureSize;
|
||||
this.bakery = new ModelTextureBakery(modelTextureSize, modelTextureSize);
|
||||
this.modelBuffer = new GlBuffer(MODEL_SIZE * (1<<16));
|
||||
|
||||
this.modelColourBuffer = new GlBuffer(4 * (1<<16));
|
||||
//TODO: NOTE!!! is it worth even uploading as a 16x16 texture, since automatic lod selection... doing 8x8 textures might be perfectly ok!!!
|
||||
// this _quarters_ the memory requirements for the texture atlas!!! WHICH IS HUGE saving
|
||||
public ModelFactory(Mapper mapper) {
|
||||
this.mapper = mapper;
|
||||
this.bakery = new ModelTextureBakery(MODEL_TEXTURE_SIZE, MODEL_TEXTURE_SIZE);
|
||||
|
||||
//TODO: figure out how to do mipping :blobfox_pineapple:
|
||||
this.textures = new GlTexture().store(GL_RGBA8, 4, modelTextureSize*3*256,modelTextureSize*2*256);
|
||||
this.metadataCache = new long[1<<16];
|
||||
this.fluidStateLUT = new int[1<<16];
|
||||
this.idMappings = new int[1<<20];//Max of 1 million blockstates mapping to 65k model states
|
||||
@@ -141,15 +141,23 @@ public class ModelManager {
|
||||
|
||||
|
||||
|
||||
public void addEntry(int blockId) {
|
||||
if (this.idMappings[blockId] != -1) {
|
||||
System.err.println("Block id already added: " + blockId);
|
||||
return;
|
||||
}
|
||||
this.addEntry(blockId, this.mapper.getBlockStateFromBlockId(blockId));
|
||||
}
|
||||
|
||||
//TODO: what i need to do is seperate out fluid states from blockStates
|
||||
|
||||
|
||||
//TODO: so need a few things, per face sizes and offsets, the sizes should be computed from the pixels and find the minimum bounding pixel
|
||||
// while the depth is computed from the depth buffer data
|
||||
public int addEntry(int blockId, BlockState blockState) {
|
||||
public void addEntry(int blockId, BlockState blockState) {
|
||||
if (this.idMappings[blockId] != -1) {
|
||||
System.err.println("Block id already added: " + blockId + " for state: " + blockState);
|
||||
return this.idMappings[blockId];
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isFluid = blockState.getBlock() instanceof FluidBlock;
|
||||
@@ -162,13 +170,13 @@ public class ModelManager {
|
||||
//Insert into the fluid LUT
|
||||
var fluidState = blockState.getFluidState().getBlockState();
|
||||
|
||||
//TODO:FIXME: PASS IN THE Mapper instead of grabbing it!!! THIS IS CRTICIAL TO FIX
|
||||
int fluidStateId = ((IGetVoxelCore)MinecraftClient.getInstance().worldRenderer).getVoxelCore().getWorldEngine().getMapper().getIdForBlockState(fluidState);
|
||||
int fluidStateId = this.mapper.getIdForBlockState(fluidState);
|
||||
|
||||
|
||||
clientFluidStateId = this.idMappings[fluidStateId];
|
||||
if (clientFluidStateId == -1) {
|
||||
clientFluidStateId = this.addEntry(fluidStateId, fluidState);
|
||||
this.addEntry(fluidStateId, fluidState);
|
||||
clientFluidStateId = this.idMappings[fluidStateId];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,10 +186,11 @@ public class ModelManager {
|
||||
if (possibleDuplicate != -1) {//Duplicate found
|
||||
this.idMappings[blockId] = possibleDuplicate;
|
||||
modelId = possibleDuplicate;
|
||||
return possibleDuplicate;
|
||||
return;
|
||||
} else {//Not a duplicate so create a new entry
|
||||
modelId = this.modelTexture2id.size();
|
||||
this.idMappings[blockId] = modelId;
|
||||
//NOTE: we set the mapping at the very end so that race conditions with this and getMetadata dont occur
|
||||
//this.idMappings[blockId] = modelId;
|
||||
this.modelTexture2id.put(entry, modelId);
|
||||
}
|
||||
}
|
||||
@@ -208,7 +217,8 @@ public class ModelManager {
|
||||
|
||||
|
||||
|
||||
long uploadPtr = UploadStream.INSTANCE.upload(this.modelBuffer, (long) modelId * MODEL_SIZE, MODEL_SIZE);
|
||||
final long uploadPtrConst = MemoryUtil.nmemAlloc(MODEL_SIZE);
|
||||
long uploadPtr = uploadPtrConst;
|
||||
|
||||
|
||||
//TODO: implement;
|
||||
@@ -282,7 +292,7 @@ public class ModelManager {
|
||||
int writeCount = TextureUtils.getWrittenPixelCount(textureData[face], checkMode);
|
||||
|
||||
boolean faceCoversFullBlock = faceSize[0] == 0 && faceSize[2] == 0 &&
|
||||
faceSize[1] == (this.modelTextureSize-1) && faceSize[3] == (this.modelTextureSize-1);
|
||||
faceSize[1] == (MODEL_TEXTURE_SIZE-1) && faceSize[3] == (MODEL_TEXTURE_SIZE-1);
|
||||
|
||||
metadata |= faceCoversFullBlock?2:0;
|
||||
|
||||
@@ -294,7 +304,7 @@ public class ModelManager {
|
||||
occludesFace &= offset < 0.1;//If the face is rendered far away from the other face, then it doesnt occlude
|
||||
|
||||
if (occludesFace) {
|
||||
occludesFace &= ((float)writeCount)/(this.modelTextureSize * this.modelTextureSize) > 0.9;// only occlude if the face covers more than 90% of the face
|
||||
occludesFace &= ((float)writeCount)/(MODEL_TEXTURE_SIZE * MODEL_TEXTURE_SIZE) > 0.9;// only occlude if the face covers more than 90% of the face
|
||||
}
|
||||
metadata |= occludesFace?1:0;
|
||||
|
||||
@@ -313,7 +323,7 @@ public class ModelManager {
|
||||
|
||||
//Scale face size from 0->this.modelTextureSize-1 to 0->15
|
||||
for (int i = 0; i < 4; i++) {
|
||||
faceSize[i] = Math.round((((float)faceSize[i])/(this.modelTextureSize-1))*15);
|
||||
faceSize[i] = Math.round((((float)faceSize[i])/(MODEL_TEXTURE_SIZE-1))*15);
|
||||
}
|
||||
|
||||
int faceModelData = 0;
|
||||
@@ -348,20 +358,23 @@ public class ModelManager {
|
||||
|
||||
//modelFlags |= blockRenderLayer == RenderLayer.getSolid()?0:1;// should discard alpha
|
||||
MemoryUtil.memPutInt(uploadPtr, modelFlags);
|
||||
|
||||
int[] biomeData = null;
|
||||
int biomeIndex = -1;
|
||||
//Temporary override to always be non biome specific
|
||||
if (colourProvider == null) {
|
||||
MemoryUtil.memPutInt(uploadPtr + 4, -1);//Set the default to nothing so that its faster on the gpu
|
||||
} else if (!hasBiomeColourResolver) {
|
||||
Biome defaultBiome = MinecraftClient.getInstance().world.getRegistryManager().get(RegistryKeys.BIOME).get(BiomeKeys.PLAINS);
|
||||
MemoryUtil.memPutInt(uploadPtr + 4, captureColourConstant(colourProvider, blockState, defaultBiome)|0xFF000000);
|
||||
MemoryUtil.memPutInt(uploadPtr + 4, captureColourConstant(colourProvider, blockState, DEFAULT_BIOME)|0xFF000000);
|
||||
} else if (!this.biomes.isEmpty()) {
|
||||
//Populate the list of biomes for the model state
|
||||
int biomeIndex = this.modelsRequiringBiomeColours.size() * this.biomes.size();
|
||||
biomeIndex = this.modelsRequiringBiomeColours.size() * this.biomes.size();
|
||||
MemoryUtil.memPutInt(uploadPtr + 4, biomeIndex);
|
||||
this.modelsRequiringBiomeColours.add(new Pair<>(modelId, blockState));
|
||||
long clrUploadPtr = UploadStream.INSTANCE.upload(this.modelColourBuffer, biomeIndex * 4L, 4L * this.biomes.size());
|
||||
for (var biome : this.biomes) {
|
||||
MemoryUtil.memPutInt(clrUploadPtr, captureColourConstant(colourProvider, blockState, biome)|0xFF000000); clrUploadPtr += 4;
|
||||
//long clrUploadPtr = UploadStream.INSTANCE.upload(this.modelColourBuffer, biomeIndex * 4L, 4L * this.biomes.size());
|
||||
biomeData = new int[this.biomes.size()];
|
||||
for (int biomeId = 0; biomeId < this.biomes.size(); biomeId++) {
|
||||
biomeData[biomeId] = captureColourConstant(colourProvider, blockState, this.biomes.get(biomeId))|0xFF000000;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,13 +384,21 @@ public class ModelManager {
|
||||
//TODO
|
||||
|
||||
|
||||
this.putTextures(modelId, textureData);
|
||||
var textureUpload = this.putTextures(modelId, textureData);
|
||||
|
||||
//glGenerateTextureMipmap(this.textures.id);
|
||||
return modelId;
|
||||
|
||||
//Set the mapping at the very end
|
||||
this.idMappings[blockId] = modelId;
|
||||
|
||||
|
||||
new NewModelBufferDelta(modelId, uploadPtrConst, biomeIndex, biomeData, textureUpload);
|
||||
}
|
||||
|
||||
public void addBiome(int id, Biome biome) {
|
||||
throw new IllegalStateException("IMPLEMENT");
|
||||
|
||||
/*
|
||||
this.biomes.add(biome);
|
||||
if (this.biomes.size()-1 != id) {
|
||||
throw new IllegalStateException("Biome ordering not consistent with biome id for biome " + biome + " expected id: " + (this.biomes.size()-1) + " got id: " + id);
|
||||
@@ -391,12 +412,13 @@ public class ModelManager {
|
||||
}
|
||||
//Populate the list of biomes for the model state
|
||||
int biomeIndex = (i++) * this.biomes.size();
|
||||
MemoryUtil.memPutInt( UploadStream.INSTANCE.upload(this.modelBuffer, (entry.getLeft()*MODEL_SIZE)+ 4*6 + 4, 4), biomeIndex);
|
||||
MemoryUtil.memPutInt(UploadStream.INSTANCE.upload(this.modelBuffer, (entry.getLeft()* MODEL_SIZE)+ 4*6 + 4, 4), biomeIndex);
|
||||
long clrUploadPtr = UploadStream.INSTANCE.upload(this.modelColourBuffer, biomeIndex * 4L, 4L * this.biomes.size());
|
||||
for (var biomeE : this.biomes) {
|
||||
MemoryUtil.memPutInt(clrUploadPtr, captureColourConstant(colourProvider, entry.getRight(), biomeE)|0xFF000000); clrUploadPtr += 4;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@@ -506,95 +528,22 @@ public class ModelManager {
|
||||
return biomeDependent[0];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static boolean faceExists(long metadata, int face) {
|
||||
return ((metadata>>(8*face))&0xFF)!=0xFF;
|
||||
}
|
||||
|
||||
public static boolean faceCanBeOccluded(long metadata, int face) {
|
||||
return ((metadata>>(8*face))&0b100)==0b100;
|
||||
}
|
||||
|
||||
public static boolean faceOccludes(long metadata, int face) {
|
||||
return faceExists(metadata, face) && ((metadata>>(8*face))&0b1)==0b1;
|
||||
}
|
||||
|
||||
public static boolean faceUsesSelfLighting(long metadata, int face) {
|
||||
return ((metadata>>(8*face))&0b1000) != 0;
|
||||
}
|
||||
|
||||
public static boolean isDoubleSided(long metadata) {
|
||||
return ((metadata>>(8*6))&4) != 0;
|
||||
}
|
||||
|
||||
public static boolean isTranslucent(long metadata) {
|
||||
return ((metadata>>(8*6))&2) != 0;
|
||||
}
|
||||
|
||||
public static boolean containsFluid(long metadata) {
|
||||
return ((metadata>>(8*6))&8) != 0;
|
||||
}
|
||||
|
||||
public static boolean isFluid(long metadata) {
|
||||
return ((metadata>>(8*6))&16) != 0;
|
||||
}
|
||||
|
||||
public static boolean isBiomeColoured(long metadata) {
|
||||
return ((metadata>>(8*6))&1) != 0;
|
||||
}
|
||||
|
||||
//NOTE: this might need to be moved to per face
|
||||
public static boolean cullsSame(long metadata) {
|
||||
return ((metadata>>(8*6))&32) != 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private float[] computeModelDepth(ColourDepthTextureData[] textures, int checkMode) {
|
||||
float[] res = new float[6];
|
||||
for (var dir : Direction.values()) {
|
||||
var data = textures[dir.getId()];
|
||||
float fd = TextureUtils.computeDepth(data, TextureUtils.DEPTH_MODE_AVG, checkMode);//Compute the min float depth, smaller means closer to the camera, range 0-1
|
||||
int depth = Math.round(fd * this.modelTextureSize);
|
||||
int depth = Math.round(fd * MODEL_TEXTURE_SIZE);
|
||||
//If fd is -1, it means that there was nothing rendered on that face and it should be discarded
|
||||
if (fd < -0.1) {
|
||||
res[dir.ordinal()] = -1;
|
||||
} else {
|
||||
res[dir.ordinal()] = ((float) depth)/this.modelTextureSize;
|
||||
res[dir.ordinal()] = ((float) depth)/MODEL_TEXTURE_SIZE;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public long getModelMetadata(int blockId) {
|
||||
int map = this.idMappings[blockId];
|
||||
if (map == -1) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
map = this.idMappings[blockId];
|
||||
}
|
||||
if (map == -1) {
|
||||
throw new IdNotYetComputedException(blockId);
|
||||
}
|
||||
return this.metadataCache[map];
|
||||
}
|
||||
|
||||
public long getModelMetadataFromClientId(int clientId) {
|
||||
return this.metadataCache[clientId];
|
||||
}
|
||||
|
||||
public int getModelId(int blockId) {
|
||||
int map = this.idMappings[blockId];
|
||||
if (map == -1) {
|
||||
@@ -603,6 +552,10 @@ public class ModelManager {
|
||||
return map;
|
||||
}
|
||||
|
||||
public boolean hasModelForBlockId(int blockId) {
|
||||
return this.idMappings[blockId] != -1;
|
||||
}
|
||||
|
||||
public int getFluidClientStateId(int clientBlockStateId) {
|
||||
int map = this.fluidStateLUT[clientBlockStateId];
|
||||
if (map == -1) {
|
||||
@@ -611,24 +564,24 @@ public class ModelManager {
|
||||
return map;
|
||||
}
|
||||
|
||||
private void putTextures(int id, ColourDepthTextureData[] textures) {
|
||||
int X = (id&0xFF) * this.modelTextureSize*3;
|
||||
int Y = ((id>>8)&0xFF) * this.modelTextureSize*2;
|
||||
public long getModelMetadataFromClientId(int clientId) {
|
||||
return this.metadataCache[clientId];
|
||||
}
|
||||
|
||||
private ModelTextureUpload putTextures(int id, ColourDepthTextureData[] textures) {
|
||||
int X = (id&0xFF) * MODEL_TEXTURE_SIZE*3;
|
||||
int Y = ((id>>8)&0xFF) * MODEL_TEXTURE_SIZE*2;
|
||||
|
||||
int texIndex = 0;
|
||||
int[][] texData = new int[6*4][];
|
||||
for (int subTex = 0; subTex < 6; subTex++) {
|
||||
int x = X + (subTex>>1)*this.modelTextureSize;
|
||||
int y = Y + (subTex&1)*this.modelTextureSize;
|
||||
|
||||
GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0);
|
||||
GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0);
|
||||
GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0);
|
||||
GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4);
|
||||
var current = textures[subTex].colour();
|
||||
var next = new int[current.length>>1];
|
||||
for (int i = 0; i < 4; i++) {
|
||||
glTextureSubImage2D(this.textures.id, i, x>>i, y>>i, this.modelTextureSize>>i, this.modelTextureSize>>i, GL_RGBA, GL_UNSIGNED_BYTE, current);
|
||||
texData[texIndex++] = Arrays.copyOf(current, current.length);
|
||||
|
||||
int size = this.modelTextureSize>>(i+1);
|
||||
int size = MODEL_TEXTURE_SIZE>>(i+1);
|
||||
for (int pX = 0; pX < size; pX++) {
|
||||
for (int pY = 0; pY < size; pY++) {
|
||||
int C00 = current[(pY*2)*size+pX*2];
|
||||
@@ -643,29 +596,15 @@ public class ModelManager {
|
||||
next = new int[current.length>>1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getBufferId() {
|
||||
return this.modelBuffer.id;
|
||||
}
|
||||
|
||||
public int getTextureId() {
|
||||
return this.textures.id;
|
||||
return new ModelTextureUpload(id, texData);
|
||||
}
|
||||
|
||||
public int getSamplerId() {
|
||||
return this.blockSampler;
|
||||
}
|
||||
|
||||
public int getColourBufferId() {
|
||||
return this.modelColourBuffer.id;
|
||||
}
|
||||
|
||||
public void free() {
|
||||
this.bakery.free();
|
||||
this.modelBuffer.free();
|
||||
this.modelColourBuffer.free();
|
||||
this.textures.free();
|
||||
glDeleteSamplers(this.blockSampler);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package me.cortex.voxy.client.core.model;
|
||||
|
||||
public abstract class ModelQueries {
|
||||
public static boolean faceExists(long metadata, int face) {
|
||||
return ((metadata>>(8*face))&0xFF)!=0xFF;
|
||||
}
|
||||
|
||||
public static boolean faceCanBeOccluded(long metadata, int face) {
|
||||
return ((metadata>>(8*face))&0b100)==0b100;
|
||||
}
|
||||
|
||||
public static boolean faceOccludes(long metadata, int face) {
|
||||
return faceExists(metadata, face) && ((metadata>>(8*face))&0b1)==0b1;
|
||||
}
|
||||
|
||||
public static boolean faceUsesSelfLighting(long metadata, int face) {
|
||||
return ((metadata>>(8*face))&0b1000) != 0;
|
||||
}
|
||||
|
||||
public static boolean isDoubleSided(long metadata) {
|
||||
return ((metadata>>(8*6))&4) != 0;
|
||||
}
|
||||
|
||||
public static boolean isTranslucent(long metadata) {
|
||||
return ((metadata>>(8*6))&2) != 0;
|
||||
}
|
||||
|
||||
public static boolean containsFluid(long metadata) {
|
||||
return ((metadata>>(8*6))&8) != 0;
|
||||
}
|
||||
|
||||
public static boolean isFluid(long metadata) {
|
||||
return ((metadata>>(8*6))&16) != 0;
|
||||
}
|
||||
|
||||
public static boolean isBiomeColoured(long metadata) {
|
||||
return ((metadata>>(8*6))&1) != 0;
|
||||
}
|
||||
|
||||
//NOTE: this might need to be moved to per face
|
||||
public static boolean cullsSame(long metadata) {
|
||||
return ((metadata>>(8*6))&32) != 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.cortex.voxy.client.core.model;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.gl.GlTexture;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.GL_RGBA8;
|
||||
|
||||
public class ModelStore {
|
||||
public static final int MODEL_SIZE = 64;
|
||||
private final GlBuffer modelBuffer;
|
||||
private final GlBuffer modelColourBuffer;
|
||||
private final GlTexture textures;
|
||||
|
||||
public ModelStore(int modelTextureSize) {
|
||||
this.modelBuffer = new GlBuffer(MODEL_SIZE * (1<<16));
|
||||
this.modelColourBuffer = new GlBuffer(4 * (1<<16));
|
||||
this.textures = new GlTexture().store(GL_RGBA8, 4, modelTextureSize*3*256,modelTextureSize*2*256);
|
||||
}
|
||||
|
||||
|
||||
public void free() {
|
||||
this.modelBuffer.free();
|
||||
this.modelColourBuffer.free();
|
||||
this.textures.free();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package me.cortex.voxy.client.core.model;
|
||||
|
||||
import com.mojang.blaze3d.platform.GlConst;
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
|
||||
import static me.cortex.voxy.client.core.model.ModelFactory.MODEL_TEXTURE_SIZE;
|
||||
import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureSubImage2D;
|
||||
import static org.lwjgl.opengl.GL11.GL_RGBA;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_BYTE;
|
||||
|
||||
public record ModelTextureUpload(int id, int[][] data) {
|
||||
|
||||
public void upload(int textureId) {
|
||||
GlStateManager._pixelStore(GlConst.GL_UNPACK_ROW_LENGTH, 0);
|
||||
GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_PIXELS, 0);
|
||||
GlStateManager._pixelStore(GlConst.GL_UNPACK_SKIP_ROWS, 0);
|
||||
GlStateManager._pixelStore(GlConst.GL_UNPACK_ALIGNMENT, 4);
|
||||
|
||||
int i = 0;
|
||||
int X = (this.id&0xFF) * MODEL_TEXTURE_SIZE*3;
|
||||
int Y = ((this.id>>8)&0xFF) * MODEL_TEXTURE_SIZE*2;
|
||||
for (int face = 0; face < 6; face++) {
|
||||
int x = X + (face>>1)*MODEL_TEXTURE_SIZE;
|
||||
int y = Y + (face&1)*MODEL_TEXTURE_SIZE;
|
||||
for (int mip = 0; mip < 4; mip++) {
|
||||
glTextureSubImage2D(id, mip, x >> mip, y >> mip, MODEL_TEXTURE_SIZE >> mip, MODEL_TEXTURE_SIZE >> mip, GL_RGBA, GL_UNSIGNED_BYTE, this.data[i++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package me.cortex.voxy.client.core.model;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
public record NewModelBufferDelta(int modelClientId, long modelBufferChangesPtr, int biomeIndex, int[] biomeData, ModelTextureUpload textureUpload) {
|
||||
public static NewModelBufferDelta empty(int modelClientId) {
|
||||
return new NewModelBufferDelta(modelClientId, 0, -1, null, null);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return this.modelBufferChangesPtr == 0;
|
||||
}
|
||||
|
||||
public void free() {
|
||||
MemoryUtil.nmemFree(this.modelBufferChangesPtr);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
package me.cortex.voxy.client.core.model;
|
||||
|
||||
|
||||
//Uses an off thread gl context to do the model baking on
|
||||
public class OffThreadBakerySystem {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package me.cortex.voxy.client.core.model;
|
||||
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayFIFOQueue;
|
||||
import me.cortex.voxy.common.world.other.Mapper;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import org.lwjgl.glfw.GLFW;
|
||||
import org.lwjgl.opengl.GL;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
//Uses an off thread gl context to do the model baking on
|
||||
public class OffThreadModelBakerySystem {
|
||||
//NOTE: Create a static final context offthread and dont close it, just reuse the context, since context creation is expensive
|
||||
private static final long GL_CTX;
|
||||
static {
|
||||
GLFW.glfwMakeContextCurrent(0L);
|
||||
GLFW.glfwWindowHint(GLFW.GLFW_VISIBLE, 0);
|
||||
GL_CTX = GLFW.glfwCreateWindow(1, 1, "", 0, MinecraftClient.getInstance().getWindow().getHandle());
|
||||
GLFW.glfwMakeContextCurrent(GL_CTX);
|
||||
GL.createCapabilities();
|
||||
GLFW.glfwMakeContextCurrent(MinecraftClient.getInstance().getWindow().getHandle());
|
||||
}
|
||||
|
||||
|
||||
|
||||
private final ModelStore storage = new ModelStore(16);
|
||||
public final ModelFactory factory;
|
||||
private final ConcurrentLinkedDeque<NewModelBufferDelta> bufferDeltas = new ConcurrentLinkedDeque<>();
|
||||
private final IntArrayFIFOQueue blockQueue = new IntArrayFIFOQueue();
|
||||
private final Semaphore queueCounter = new Semaphore(0);
|
||||
|
||||
|
||||
|
||||
private final Thread bakingThread;
|
||||
private volatile boolean running = true;
|
||||
public OffThreadModelBakerySystem(Mapper mapper) {
|
||||
this.factory = new ModelFactory(mapper);
|
||||
this.bakingThread = new Thread(this::bakeryThread);
|
||||
this.bakingThread.setName("Baking thread");
|
||||
this.bakingThread.setPriority(3);
|
||||
this.bakingThread.start();
|
||||
}
|
||||
|
||||
private void bakeryThread() {
|
||||
GLFW.glfwMakeContextCurrent(GL_CTX);
|
||||
|
||||
//FIXME: tile entities will probably need to be baked on the main render thread
|
||||
while (true) {
|
||||
this.queueCounter.acquireUninterruptibly();
|
||||
if (!this.running) break;
|
||||
int blockId;
|
||||
synchronized (this.blockQueue) {
|
||||
blockId = this.blockQueue.dequeueInt();
|
||||
VarHandle.fullFence();//Ensure memory coherancy
|
||||
}
|
||||
|
||||
this.factory.addEntry(blockId);
|
||||
}
|
||||
}
|
||||
|
||||
//Changes to the block table/atlas must be run on the main render thread
|
||||
public void syncChanges() {
|
||||
//TODO: FIXME!! the ordering of the uploads here and the order that _both_ model AND biome are done in the bakery thread MUST be the same!!!
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
this.running = false;
|
||||
this.queueCounter.release(999);
|
||||
try {
|
||||
this.bakingThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
this.factory.free();
|
||||
this.storage.free();
|
||||
}
|
||||
|
||||
public void requestBlockBake(int blockId) {
|
||||
synchronized (this.blockQueue) {
|
||||
this.blockQueue.enqueue(blockId);
|
||||
VarHandle.fullFence();//Ensure memory coherancy
|
||||
this.queueCounter.release(1);
|
||||
}
|
||||
}
|
||||
|
||||
public void addDebugData(List<String> debug) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
|
||||
import static org.lwjgl.opengl.GL30C.GL_R8UI;
|
||||
import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER;
|
||||
import static org.lwjgl.opengl.GL42.GL_UNSIGNED_BYTE;
|
||||
import static org.lwjgl.opengl.GL45C.nglClearNamedBufferData;
|
||||
|
||||
public class Gl46MeshletViewport extends Viewport<Gl46MeshletViewport> {
|
||||
GlBuffer visibilityBuffer;
|
||||
public Gl46MeshletViewport(Gl46MeshletsFarWorldRenderer renderer) {
|
||||
this.visibilityBuffer = new GlBuffer(renderer.maxSections*4L).zero();
|
||||
}
|
||||
|
||||
protected void delete0() {
|
||||
this.visibilityBuffer.free();
|
||||
}
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.gl.shader.PrintfInjector;
|
||||
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||
import me.cortex.voxy.client.core.model.ModelManager;
|
||||
import me.cortex.voxy.client.core.rendering.building.RenderDataFactory;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import static org.lwjgl.opengl.ARBDirectStateAccess.glGetNamedFramebufferAttachmentParameteriv;
|
||||
import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureParameteri;
|
||||
import static org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate;
|
||||
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
|
||||
import static org.lwjgl.opengl.GL15.glBindBuffer;
|
||||
import static org.lwjgl.opengl.GL30.glBindBufferBase;
|
||||
import static org.lwjgl.opengl.GL30.glBindVertexArray;
|
||||
import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER;
|
||||
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
|
||||
import static org.lwjgl.opengl.GL31.glDrawElementsInstanced;
|
||||
import static org.lwjgl.opengl.GL33.glBindSampler;
|
||||
import static org.lwjgl.opengl.GL40.glDrawElementsIndirect;
|
||||
import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER;
|
||||
import static org.lwjgl.opengl.GL42.GL_FRAMEBUFFER_BARRIER_BIT;
|
||||
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
|
||||
import static org.lwjgl.opengl.GL43.*;
|
||||
import static org.lwjgl.opengl.GL45.glBindTextureUnit;
|
||||
import static org.lwjgl.opengl.GL45.nglClearNamedBufferSubData;
|
||||
import static org.lwjgl.opengl.GL45C.nglClearNamedBufferData;
|
||||
import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV;
|
||||
import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV;
|
||||
|
||||
//TODO: NOTE
|
||||
// can do "meshlet compaction"
|
||||
// that is, meshlets are refered not by the meshlet id but by an 8 byte alligned index, (the quad index)
|
||||
// this means that non full meshlets can have the tailing data be truncated and used in the next meshlet
|
||||
// as long as the number of quads in the meshlet is stored in the header
|
||||
// the shader can cull the verticies of any quad that has its index over the expected quuad count
|
||||
// this could potentially result in a fair bit of memory savings (especially if used in normal mc terrain rendering)
|
||||
public class Gl46MeshletsFarWorldRenderer extends AbstractFarWorldRenderer<Gl46MeshletViewport, DefaultGeometryManager> {
|
||||
|
||||
private final Shader lodShader = Shader.make()
|
||||
.define("QUADS_PER_MESHLET", RenderDataFactory.QUADS_PER_MESHLET)
|
||||
.add(ShaderType.VERTEX, "voxy:lod/gl46mesh/quads.vert")
|
||||
.add(ShaderType.FRAGMENT, "voxy:lod/gl46mesh/quads.frag")
|
||||
.compile();
|
||||
|
||||
private final Shader cullShader = Shader.make()
|
||||
.define("QUADS_PER_MESHLET", RenderDataFactory.QUADS_PER_MESHLET)
|
||||
.add(ShaderType.VERTEX, "voxy:lod/gl46mesh/cull.vert")
|
||||
.add(ShaderType.FRAGMENT, "voxy:lod/gl46mesh/cull.frag")
|
||||
.compile();
|
||||
|
||||
private final Shader meshletGenerator = Shader.make()
|
||||
.define("QUADS_PER_MESHLET", RenderDataFactory.QUADS_PER_MESHLET)
|
||||
.add(ShaderType.COMPUTE, "voxy:lod/gl46mesh/cmdgen.comp")
|
||||
.compile();
|
||||
|
||||
private final Shader meshletCuller = Shader.make()
|
||||
.define("QUADS_PER_MESHLET", RenderDataFactory.QUADS_PER_MESHLET)
|
||||
.add(ShaderType.COMPUTE, "voxy:lod/gl46mesh/meshletculler.comp")
|
||||
.compile();
|
||||
|
||||
private final GlBuffer glDrawIndirect;
|
||||
private final GlBuffer meshletBuffer;
|
||||
private final HiZBuffer hiZBuffer = new HiZBuffer();
|
||||
private final int hizSampler = glGenSamplers();
|
||||
|
||||
public Gl46MeshletsFarWorldRenderer(ModelManager modelManager, int geometrySize, int maxSections) {
|
||||
super(modelManager, new DefaultGeometryManager(alignUp(geometrySize*8L, 8* (RenderDataFactory.QUADS_PER_MESHLET+2)), maxSections, 8*(RenderDataFactory.QUADS_PER_MESHLET+2)));
|
||||
this.glDrawIndirect = new GlBuffer(4*(4+5)).zero();
|
||||
this.meshletBuffer = new GlBuffer(4*1000000).zero();//TODO: Make max meshlet count configurable, not just 1 million (even tho thats a max of 126 million quads per frame)
|
||||
|
||||
glSamplerParameteri(this.hizSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);//This is so that using the shadow sampler works correctly
|
||||
glTextureParameteri(this.hizSampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTextureParameteri(this.hizSampler, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
||||
glTextureParameteri(this.hizSampler, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
||||
glTextureParameteri(this.hizSampler, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTextureParameteri(this.hizSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTextureParameteri(this.hizSampler, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
|
||||
/*
|
||||
nglClearNamedBufferData(this.meshletBuffer.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
|
||||
nglClearNamedBufferData(this.glDrawIndirect.id, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
|
||||
*/
|
||||
}
|
||||
|
||||
protected void bindResources(Gl46MeshletViewport viewport, boolean bindToDrawIndirect, boolean bindToDispatchIndirect, boolean bindHiz) {
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometry.geometryId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, bindToDrawIndirect?0:this.glDrawIndirect.id);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, this.meshletBuffer.id);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.geometry.metaId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.visibilityBuffer.id);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.models.getBufferId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, this.models.getColourBufferId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, this.lightDataBuffer.id);//Lighting LUT
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, bindToDrawIndirect?this.glDrawIndirect.id:0);
|
||||
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, bindToDispatchIndirect?this.glDrawIndirect.id:0);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE_BYTE.id());
|
||||
|
||||
if (!bindHiz) {
|
||||
//Bind the texture atlas
|
||||
glBindSampler(0, this.models.getSamplerId());
|
||||
glBindTextureUnit(0, this.models.getTextureId());
|
||||
} else {
|
||||
//Bind the hiz buffer
|
||||
glBindSampler(0, this.hizSampler);
|
||||
glBindTextureUnit(0, this.hiZBuffer.getHizTextureId());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUniformBuffer(Gl46MeshletViewport viewport) {
|
||||
long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, this.uniformBuffer.size());
|
||||
|
||||
//TODO:FIXME remove this.sx/sy/sz and make it based on the viewport!!!
|
||||
|
||||
var mat = new Matrix4f(viewport.projection).mul(viewport.modelView);
|
||||
var innerTranslation = new Vector3f((float) (viewport.cameraX-(this.sx<<5)), (float) (viewport.cameraY-(this.sy<<5)), (float) (viewport.cameraZ-(this.sz<<5)));
|
||||
mat.translate(-innerTranslation.x, -innerTranslation.y, -innerTranslation.z);
|
||||
mat.getToAddress(ptr); ptr += 4*4*4;
|
||||
MemoryUtil.memPutInt(ptr, this.sx); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, this.sy); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, this.sz); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, this.geometry.getSectionCount()); ptr += 4;
|
||||
var planes = ((AccessFrustumIntersection)this.frustum).getPlanes();
|
||||
for (var plane : planes) {
|
||||
plane.getToAddress(ptr); ptr += 4*4;
|
||||
}
|
||||
innerTranslation.getToAddress(ptr); ptr += 4*3;
|
||||
MemoryUtil.memPutInt(ptr, viewport.frameId++); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, viewport.width); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, viewport.height); ptr += 4;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFarAwayOpaque(Gl46MeshletViewport viewport) {
|
||||
if (this.geometry.getSectionCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
{//Mark all of the updated sections as being visible from last frame
|
||||
for (int id : this.updatedSectionIds) {
|
||||
long ptr = UploadStream.INSTANCE.upload(viewport.visibilityBuffer, id * 4L, 4);
|
||||
MemoryUtil.memPutInt(ptr, viewport.frameId - 1);//(visible from last frame)
|
||||
}
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
RenderLayer.getCutoutMipped().startDrawing();
|
||||
|
||||
this.updateUniformBuffer(viewport);
|
||||
UploadStream.INSTANCE.commit();
|
||||
glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO);
|
||||
|
||||
nglClearNamedBufferSubData(this.glDrawIndirect.id, GL_R32UI, 0, 4*4, GL_RED_INTEGER, GL_UNSIGNED_INT, 0);
|
||||
|
||||
this.lodShader.bind();
|
||||
this.bindResources(viewport, true, false, false);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glProvokingVertex(GL_FIRST_VERTEX_CONVENTION);
|
||||
glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_BYTE, 4*4);
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT | GL_FRAMEBUFFER_BARRIER_BIT);
|
||||
|
||||
this.cullShader.bind();
|
||||
this.bindResources(viewport, false, false, false);
|
||||
glDepthMask(false);
|
||||
glColorMask(false, false, false, false);
|
||||
glDrawElementsInstanced(GL_TRIANGLES, 6 * 2 * 3, GL_UNSIGNED_BYTE, (1 << 8) * 6, this.geometry.getSectionCount());
|
||||
glColorMask(true, true, true, true);
|
||||
glDepthMask(true);
|
||||
|
||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
|
||||
this.meshletGenerator.bind();
|
||||
this.bindResources(viewport, false, false, false);
|
||||
glDispatchCompute((this.geometry.getSectionCount()+63)/64, 1, 1);
|
||||
|
||||
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT | GL_COMMAND_BARRIER_BIT);
|
||||
|
||||
var i = new int[1];
|
||||
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, i);
|
||||
this.hiZBuffer.buildMipChain(i[0], MinecraftClient.getInstance().getFramebuffer().textureWidth, MinecraftClient.getInstance().getFramebuffer().textureHeight);
|
||||
|
||||
this.meshletCuller.bind();
|
||||
this.bindResources(viewport, false, true, true);
|
||||
glDispatchComputeIndirect(0);
|
||||
|
||||
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindSampler(0, 0);
|
||||
glBindTextureUnit(0, 0);
|
||||
RenderLayer.getCutoutMipped().endDrawing();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void renderFarAwayTranslucent(Gl46MeshletViewport viewport) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Gl46MeshletViewport createViewport0() {
|
||||
return new Gl46MeshletViewport(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
super.shutdown();
|
||||
this.lodShader.free();
|
||||
this.cullShader.free();
|
||||
this.meshletGenerator.free();
|
||||
this.meshletCuller.free();
|
||||
|
||||
this.glDrawIndirect.free();
|
||||
this.meshletBuffer.free();
|
||||
|
||||
this.hiZBuffer.free();
|
||||
glDeleteSamplers(this.hizSampler);
|
||||
}
|
||||
|
||||
public static long alignUp(long n, long alignment) {
|
||||
return (n + alignment - 1) & -alignment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean generateMeshlets() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,10 @@ import me.cortex.voxy.client.core.gl.GlFramebuffer;
|
||||
import me.cortex.voxy.client.core.gl.GlTexture;
|
||||
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractFarWorldRenderer;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
import static org.lwjgl.opengl.ARBDirectStateAccess.*;
|
||||
import static org.lwjgl.opengl.GL11.glScaled;
|
||||
import static org.lwjgl.opengl.GL11C.*;
|
||||
import static org.lwjgl.opengl.GL30C.*;
|
||||
import static org.lwjgl.opengl.GL33.glBindSampler;
|
||||
@@ -17,7 +17,6 @@ import static org.lwjgl.opengl.GL33C.glSamplerParameteri;
|
||||
import static org.lwjgl.opengl.GL42C.GL_FRAMEBUFFER_BARRIER_BIT;
|
||||
import static org.lwjgl.opengl.GL42C.glMemoryBarrier;
|
||||
import static org.lwjgl.opengl.GL43C.glCopyImageSubData;
|
||||
import static org.lwjgl.opengl.GL45C.glNamedFramebufferTexture;
|
||||
import static org.lwjgl.opengl.GL45C.glTextureBarrier;
|
||||
|
||||
public class HiZBuffer {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
|
||||
|
||||
import me.cortex.voxy.client.core.rendering.hierarchical.HierarchicalOcclusionRenderer;
|
||||
import me.cortex.voxy.common.world.other.Mapper;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.Frustum;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.lwjgl.opengl.GL30.*;
|
||||
|
||||
public interface IRenderInterface <T extends Viewport> {
|
||||
|
||||
T createViewport();
|
||||
|
||||
void setupRender(Frustum frustum, Camera camera);
|
||||
|
||||
void renderFarAwayOpaque(T viewport);
|
||||
|
||||
void renderFarAwayTranslucent(T viewport);
|
||||
|
||||
void addDebugData(List<String> debug);
|
||||
|
||||
void shutdown();
|
||||
|
||||
void addBlockState(Mapper.StateEntry stateEntry);
|
||||
|
||||
void addBiome(Mapper.BiomeEntry biomeEntry);
|
||||
|
||||
boolean generateMeshlets();
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||
import me.cortex.voxy.client.core.model.ModelManager;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.Frustum;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.opengl.GL11C;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB;
|
||||
import static org.lwjgl.opengl.ARBIndirectParameters.glMultiDrawElementsIndirectCountARB;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate;
|
||||
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
|
||||
import static org.lwjgl.opengl.GL15.glBindBuffer;
|
||||
import static org.lwjgl.opengl.GL30.glBindBufferBase;
|
||||
import static org.lwjgl.opengl.GL30.glBindVertexArray;
|
||||
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
|
||||
import static org.lwjgl.opengl.GL31.glDrawElementsInstanced;
|
||||
import static org.lwjgl.opengl.GL33.glBindSampler;
|
||||
import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER;
|
||||
import static org.lwjgl.opengl.GL42.GL_FRAMEBUFFER_BARRIER_BIT;
|
||||
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
|
||||
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BARRIER_BIT;
|
||||
import static org.lwjgl.opengl.GL43.GL_SHADER_STORAGE_BUFFER;
|
||||
import static org.lwjgl.opengl.GL45.glBindTextureUnit;
|
||||
import static org.lwjgl.opengl.NVMeshShader.glDrawMeshTasksNV;
|
||||
import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV;
|
||||
|
||||
public class NvMeshFarWorldRenderer extends AbstractFarWorldRenderer<NvMeshViewport, DefaultGeometryManager> {
|
||||
private final Shader terrain = Shader.make()
|
||||
.add(ShaderType.TASK, "voxy:lod/nvmesh/primary.task")
|
||||
.add(ShaderType.MESH, "voxy:lod/nvmesh/primary.mesh")
|
||||
.add(ShaderType.FRAGMENT, "voxy:lod/nvmesh/primary.frag")
|
||||
.compile();
|
||||
|
||||
private final Shader translucent = Shader.make()
|
||||
.add(ShaderType.TASK, "voxy:lod/nvmesh/translucent.task")
|
||||
.add(ShaderType.MESH, "voxy:lod/nvmesh/translucent.mesh")
|
||||
.add(ShaderType.FRAGMENT, "voxy:lod/nvmesh/primary.frag")
|
||||
.compile();
|
||||
|
||||
private final Shader cull = Shader.make()
|
||||
.add(ShaderType.VERTEX, "voxy:lod/nvmesh/cull.vert")
|
||||
.add(ShaderType.FRAGMENT, "voxy:lod/nvmesh/cull.frag")
|
||||
.compile();
|
||||
|
||||
public NvMeshFarWorldRenderer(ModelManager modelManager, int geometrySize, int maxSections) {
|
||||
super(modelManager, new DefaultGeometryManager(geometrySize*8L, maxSections));
|
||||
}
|
||||
|
||||
|
||||
private void updateUniform(NvMeshViewport viewport) {
|
||||
long ptr = UploadStream.INSTANCE.upload(this.uniformBuffer, 0, this.uniformBuffer.size());
|
||||
|
||||
var mat = new Matrix4f(viewport.projection).mul(viewport.modelView);
|
||||
mat.getToAddress(ptr); ptr += 4*4*4;
|
||||
var innerTranslation = new Vector3f((float) (viewport.cameraX-(this.sx<<5)), (float) (viewport.cameraY-(this.sy<<5)), (float) (viewport.cameraZ-(this.sz<<5)));
|
||||
MemoryUtil.memPutInt(ptr, this.sx); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, this.sy); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, this.sz); ptr += 4;
|
||||
MemoryUtil.memPutInt(ptr, this.geometry.getSectionCount()); ptr += 4;
|
||||
innerTranslation.getToAddress(ptr); ptr += 4*3;
|
||||
MemoryUtil.memPutInt(ptr, viewport.frameId++); ptr += 4;
|
||||
}
|
||||
|
||||
private void bindResources(NvMeshViewport viewport) {
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id());
|
||||
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniformBuffer.id);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometry.geometryId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.geometry.metaId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, viewport.visibilityBuffer.id);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.models.getBufferId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, this.models.getColourBufferId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.lightDataBuffer.id);//Lighting LUT
|
||||
|
||||
//Bind the texture atlas
|
||||
glBindSampler(0, this.models.getSamplerId());
|
||||
glBindTextureUnit(0, this.models.getTextureId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFarAwayOpaque(NvMeshViewport viewport) {
|
||||
{//TODO: move all this code into a common super method renderFarAwayTranslucent and make the current method renderFarAwayTranslucent0
|
||||
if (this.geometry.getSectionCount() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
{//Mark all of the updated sections as being visible from last frame
|
||||
for (int id : this.updatedSectionIds) {
|
||||
long ptr = UploadStream.INSTANCE.upload(viewport.visibilityBuffer, id * 4L, 4);
|
||||
MemoryUtil.memPutInt(ptr, viewport.frameId - 1);//(visible from last frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
|
||||
//Update and upload data
|
||||
this.updateUniform(viewport);
|
||||
UploadStream.INSTANCE.commit();
|
||||
|
||||
|
||||
this.terrain.bind();
|
||||
|
||||
RenderLayer.getCutoutMipped().startDrawing();
|
||||
|
||||
glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO);
|
||||
this.bindResources(viewport);
|
||||
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDrawMeshTasksNV(0, this.geometry.getSectionCount());
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
||||
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);
|
||||
|
||||
this.cull.bind();
|
||||
this.bindResources(viewport);
|
||||
glColorMask(false, false, false, false);
|
||||
glDepthMask(false);
|
||||
glEnable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV);
|
||||
glDrawElementsInstanced(GL_TRIANGLES, 6 * 2 * 3, GL_UNSIGNED_BYTE, (1 << 16) * 6 * 2, this.geometry.getSectionCount());
|
||||
glDisable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV);
|
||||
glDepthMask(true);
|
||||
glColorMask(true, true, true, true);
|
||||
|
||||
glBindVertexArray(0);
|
||||
glBindSampler(0, 0);
|
||||
glBindTextureUnit(0, 0);
|
||||
RenderLayer.getCutoutMipped().endDrawing();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderFarAwayTranslucent(NvMeshViewport viewport) {
|
||||
if (this.geometry.getSectionCount()==0) {
|
||||
return;
|
||||
}
|
||||
RenderLayer.getTranslucent().startDrawing();
|
||||
glBindVertexArray(AbstractFarWorldRenderer.STATIC_VAO);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glEnable(GL_BLEND);
|
||||
|
||||
//TODO: maybe change this so the alpha isnt applied in the same way or something?? since atm the texture bakery uses a very hacky
|
||||
// blend equation to make it avoid double applying translucency
|
||||
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
|
||||
glBindSampler(0, this.models.getSamplerId());
|
||||
glBindTextureUnit(0, this.models.getTextureId());
|
||||
|
||||
this.translucent.bind();
|
||||
this.bindResources(viewport);
|
||||
|
||||
glDrawMeshTasksNV(0, this.geometry.getSectionCount());
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
glBindVertexArray(0);
|
||||
|
||||
|
||||
glBindSampler(0, 0);
|
||||
glBindTextureUnit(0, 0);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
RenderLayer.getTranslucent().endDrawing();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NvMeshViewport createViewport0() {
|
||||
return new NvMeshViewport(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
super.shutdown();
|
||||
this.terrain.free();
|
||||
this.translucent.free();
|
||||
this.cull.free();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
|
||||
import static org.lwjgl.opengl.GL30C.GL_R8UI;
|
||||
import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER;
|
||||
import static org.lwjgl.opengl.GL42.GL_UNSIGNED_BYTE;
|
||||
import static org.lwjgl.opengl.GL45C.glClearNamedBufferData;
|
||||
|
||||
public class NvMeshViewport extends Viewport<NvMeshViewport> {
|
||||
GlBuffer visibilityBuffer;
|
||||
public NvMeshViewport(NvMeshFarWorldRenderer renderer) {
|
||||
this.visibilityBuffer = new GlBuffer(renderer.maxSections*4L).zero();
|
||||
}
|
||||
|
||||
protected void delete0() {
|
||||
this.visibilityBuffer.free();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
|
||||
import me.cortex.voxy.client.config.VoxyConfig;
|
||||
import me.cortex.voxy.client.core.model.OffThreadModelBakerySystem;
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
import net.minecraft.client.render.Camera;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RenderService {
|
||||
private final OffThreadModelBakerySystem modelService;
|
||||
private final RenderGenerationService renderGen;
|
||||
|
||||
public RenderService(WorldEngine world) {
|
||||
this.modelService = new OffThreadModelBakerySystem(world.getMapper());
|
||||
this.renderGen = new RenderGenerationService(world, this.modelService, VoxyConfig.CONFIG.renderThreads, this::consumeRenderBuildResult, false);
|
||||
|
||||
this.renderGen.enqueueTask(0,0,0,0);
|
||||
}
|
||||
|
||||
private void consumeRenderBuildResult(BuiltSection section) {
|
||||
System.err.println("Section " + WorldEngine.pprintPos(section.position));
|
||||
section.free();
|
||||
}
|
||||
|
||||
public void setup(Camera camera) {
|
||||
this.modelService.syncChanges();
|
||||
}
|
||||
|
||||
public void renderFarAwayOpaque(Viewport viewport) {
|
||||
|
||||
}
|
||||
|
||||
public void renderFarAwayTranslucent(Viewport viewport) {
|
||||
|
||||
}
|
||||
|
||||
public void addDebugData(List<String> debug) {
|
||||
this.modelService.addDebugData(debug);
|
||||
this.renderGen.addDebugData(debug);
|
||||
}
|
||||
|
||||
public Viewport<?> createViewport() {
|
||||
return new RenderServiceViewport();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void shutdown() {
|
||||
this.modelService.shutdown();
|
||||
this.renderGen.shutdown();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
|
||||
public class RenderServiceViewport extends Viewport<RenderServiceViewport> {
|
||||
@Override
|
||||
protected void delete0() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,9 @@ import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractFarWorldRenderer;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
import me.cortex.voxy.common.world.WorldSection;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
//Tracks active sections, dispatches updates to the build system, everything related to rendering flows through here
|
||||
public class RenderTracker {
|
||||
|
||||
@@ -5,7 +5,7 @@ import org.joml.Matrix4f;
|
||||
public abstract class Viewport <A extends Viewport<A>> {
|
||||
public int width;
|
||||
public int height;
|
||||
int frameId;
|
||||
public int frameId;
|
||||
public Matrix4f projection;
|
||||
public Matrix4f modelView;
|
||||
public double cameraX;
|
||||
|
||||
@@ -2,22 +2,21 @@ package me.cortex.voxy.client.core.rendering.building;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import me.cortex.voxy.client.core.Capabilities;
|
||||
import me.cortex.voxy.client.core.model.ModelManager;
|
||||
import me.cortex.voxy.client.core.model.ModelFactory;
|
||||
import me.cortex.voxy.client.core.model.ModelQueries;
|
||||
import me.cortex.voxy.client.core.util.Mesher2D;
|
||||
import me.cortex.voxy.common.util.MemoryBuffer;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
import me.cortex.voxy.common.world.WorldSection;
|
||||
import me.cortex.voxy.common.world.other.Mapper;
|
||||
import net.minecraft.block.FluidBlock;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
public class RenderDataFactory {
|
||||
private final WorldEngine world;
|
||||
private final ModelManager modelMan;
|
||||
private final ModelFactory modelMan;
|
||||
|
||||
private final Mesher2D negativeMesher = new Mesher2D(5, 15);
|
||||
private final Mesher2D positiveMesher = new Mesher2D(5, 15);
|
||||
@@ -39,7 +38,7 @@ public class RenderDataFactory {
|
||||
private int maxX;
|
||||
private int maxY;
|
||||
private int maxZ;
|
||||
public RenderDataFactory(WorldEngine world, ModelManager modelManager, boolean emitMeshlets) {
|
||||
public RenderDataFactory(WorldEngine world, ModelFactory modelManager, boolean emitMeshlets) {
|
||||
this.world = world;
|
||||
this.modelMan = modelManager;
|
||||
this.generateMeshlets = emitMeshlets;
|
||||
@@ -333,14 +332,13 @@ public class RenderDataFactory {
|
||||
if (Mapper.isAir(self)) continue;
|
||||
|
||||
int selfBlockId = Mapper.getBlockId(self);
|
||||
long selfMetadata = this.modelMan.getModelMetadata(selfBlockId);
|
||||
|
||||
|
||||
int selfClientModelId = this.modelMan.getModelId(selfBlockId);
|
||||
long selfMetadata = this.modelMan.getModelMetadataFromClientId(selfClientModelId);
|
||||
|
||||
boolean putFace = false;
|
||||
|
||||
//Branch into 2 paths, the + direction and -direction, doing it at once makes it much faster as it halves the number of loops
|
||||
if (ModelManager.faceExists(selfMetadata, axisId<<1) || ModelManager.containsFluid(selfMetadata)) {//- direction
|
||||
if (ModelQueries.faceExists(selfMetadata, axisId<<1) || ModelQueries.containsFluid(selfMetadata)) {//- direction
|
||||
long facingState = Mapper.AIR;
|
||||
//Need to access the other connecting section
|
||||
if (primary == 0) {
|
||||
@@ -359,14 +357,16 @@ public class RenderDataFactory {
|
||||
facingState = this.sectionCache[WorldSection.getIndex(x-aX, y-aY, z-aZ)];
|
||||
}
|
||||
|
||||
if (!ModelManager.isFluid(selfMetadata)) {
|
||||
putFace |= this.putFaceIfCan(this.negativeMesher, (axisId << 1), (axisId << 1)|1, self, selfMetadata, selfBlockId, facingState, a, b);
|
||||
int facingClientModelId = this.modelMan.getModelId(Mapper.getBlockId(facingState));
|
||||
long facingMetadata = this.modelMan.getModelMetadataFromClientId(facingClientModelId);
|
||||
if (!ModelQueries.isFluid(selfMetadata)) {
|
||||
putFace |= this.putFaceIfCan(this.negativeMesher, (axisId << 1), (axisId << 1)|1, self, selfMetadata, selfClientModelId, selfBlockId, facingState, facingMetadata, a, b);
|
||||
}
|
||||
if (ModelManager.containsFluid(selfMetadata)) {
|
||||
putFace |= this.putFluidFaceIfCan(this.negativeFluidMesher, (axisId << 1), (axisId << 1)|1, self, selfMetadata, selfBlockId, facingState, a, b);
|
||||
if (ModelQueries.containsFluid(selfMetadata)) {
|
||||
putFace |= this.putFluidFaceIfCan(this.negativeFluidMesher, (axisId << 1), (axisId << 1)|1, self, selfMetadata, selfClientModelId, selfBlockId, facingState, facingMetadata, facingClientModelId, a, b);
|
||||
}
|
||||
}
|
||||
if (ModelManager.faceExists(selfMetadata, (axisId<<1)|1) || ModelManager.containsFluid(selfMetadata)) {//+ direction
|
||||
if (ModelQueries.faceExists(selfMetadata, (axisId<<1)|1) || ModelQueries.containsFluid(selfMetadata)) {//+ direction
|
||||
long facingState = Mapper.AIR;
|
||||
//Need to access the other connecting section
|
||||
if (primary == 31) {
|
||||
@@ -384,11 +384,14 @@ public class RenderDataFactory {
|
||||
} else {
|
||||
facingState = this.sectionCache[WorldSection.getIndex(x+aX, y+aY, z+aZ)];
|
||||
}
|
||||
if (!ModelManager.isFluid(selfMetadata)) {
|
||||
putFace |= this.putFaceIfCan(this.positiveMesher, (axisId << 1) | 1, (axisId << 1), self, selfMetadata, selfBlockId, facingState, a, b);
|
||||
|
||||
int facingClientModelId = this.modelMan.getModelId(Mapper.getBlockId(facingState));
|
||||
long facingMetadata = this.modelMan.getModelMetadataFromClientId(facingClientModelId);
|
||||
if (!ModelQueries.isFluid(selfMetadata)) {
|
||||
putFace |= this.putFaceIfCan(this.positiveMesher, (axisId << 1) | 1, (axisId << 1), self, selfMetadata, selfClientModelId, selfBlockId, facingState, facingMetadata, a, b);
|
||||
}
|
||||
if (ModelManager.containsFluid(selfMetadata)) {
|
||||
putFace |= this.putFluidFaceIfCan(this.positiveFluidMesher, (axisId << 1) | 1, (axisId << 1), self, selfMetadata, selfBlockId, facingState, a, b);
|
||||
if (ModelQueries.containsFluid(selfMetadata)) {
|
||||
putFace |= this.putFluidFaceIfCan(this.positiveFluidMesher, (axisId << 1) | 1, (axisId << 1), self, selfMetadata, selfClientModelId, selfBlockId, facingState, facingMetadata, facingClientModelId, a, b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,15 +417,13 @@ public class RenderDataFactory {
|
||||
|
||||
|
||||
//Returns true if a face was placed
|
||||
private boolean putFluidFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int selfBlockId, long facingState, int a, int b) {
|
||||
long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState));
|
||||
|
||||
int selfFluidClientId = this.modelMan.getFluidClientStateId(this.modelMan.getModelId(selfBlockId));
|
||||
private boolean putFluidFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int selfClientModelId, int selfBlockId, long facingState, long facingMetadata, int facingClientModelId, int a, int b) {
|
||||
int selfFluidClientId = this.modelMan.getFluidClientStateId(selfClientModelId);
|
||||
long selfFluidMetadata = this.modelMan.getModelMetadataFromClientId(selfFluidClientId);
|
||||
|
||||
int facingFluidClientId = -1;
|
||||
if (ModelManager.containsFluid(facingMetadata)) {
|
||||
facingFluidClientId = this.modelMan.getFluidClientStateId(this.modelMan.getModelId(Mapper.getBlockId(facingState)));
|
||||
if (ModelQueries.containsFluid(facingMetadata)) {
|
||||
facingFluidClientId = this.modelMan.getFluidClientStateId(facingClientModelId);
|
||||
}
|
||||
|
||||
//If both of the states are the same, then dont render the fluid face
|
||||
@@ -431,18 +432,19 @@ public class RenderDataFactory {
|
||||
}
|
||||
|
||||
if (facingFluidClientId != -1) {
|
||||
//TODO: OPTIMIZE
|
||||
if (this.world.getMapper().getBlockStateFromBlockId(selfBlockId).getBlock() == this.world.getMapper().getBlockStateFromBlockId(Mapper.getBlockId(facingState)).getBlock()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (ModelManager.faceOccludes(facingMetadata, opposingFace)) {
|
||||
if (ModelQueries.faceOccludes(facingMetadata, opposingFace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if the model has a fluid state but is not a liquid need to see if the solid state had a face rendered and that face is occluding, if so, dont render the fluid state face
|
||||
if ((!ModelManager.isFluid(metadata)) && ModelManager.faceOccludes(metadata, face)) {
|
||||
if ((!ModelQueries.isFluid(metadata)) && ModelQueries.faceOccludes(metadata, face)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -458,32 +460,28 @@ public class RenderDataFactory {
|
||||
|
||||
|
||||
long otherFlags = 0;
|
||||
otherFlags |= ModelManager.isTranslucent(selfFluidMetadata)?1L<<33:0;
|
||||
otherFlags |= ModelManager.isDoubleSided(selfFluidMetadata)?1L<<34:0;
|
||||
mesher.put(a, b, ((long)selfFluidClientId) | (((long) Mapper.getLightId(ModelManager.faceUsesSelfLighting(selfFluidMetadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelManager.isBiomeColoured(selfFluidMetadata)?1:0)) | otherFlags);
|
||||
otherFlags |= ModelQueries.isTranslucent(selfFluidMetadata)?1L<<33:0;
|
||||
otherFlags |= ModelQueries.isDoubleSided(selfFluidMetadata)?1L<<34:0;
|
||||
mesher.put(a, b, ((long)selfFluidClientId) | (((long) Mapper.getLightId(ModelQueries.faceUsesSelfLighting(selfFluidMetadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelQueries.isBiomeColoured(selfFluidMetadata)?1:0)) | otherFlags);
|
||||
return true;
|
||||
}
|
||||
|
||||
//Returns true if a face was placed
|
||||
private boolean putFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int selfBlockId, long facingState, int a, int b) {
|
||||
long facingMetadata = this.modelMan.getModelMetadata(Mapper.getBlockId(facingState));
|
||||
|
||||
private boolean putFaceIfCan(Mesher2D mesher, int face, int opposingFace, long self, long metadata, int clientModelId, int selfBlockId, long facingState, long facingMetadata, int a, int b) {
|
||||
//If face can be occluded and is occluded from the facing block, then dont render the face
|
||||
if (ModelManager.faceCanBeOccluded(metadata, face) && ModelManager.faceOccludes(facingMetadata, opposingFace)) {
|
||||
if (ModelQueries.faceCanBeOccluded(metadata, face) && ModelQueries.faceOccludes(facingMetadata, opposingFace)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ModelManager.cullsSame(metadata) && selfBlockId == Mapper.getBlockId(facingState)) {
|
||||
if (ModelQueries.cullsSame(metadata) && selfBlockId == Mapper.getBlockId(facingState)) {
|
||||
//If we are facing a block, and we are both the same state, dont render that face
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int clientModelId = this.modelMan.getModelId(selfBlockId);
|
||||
long otherFlags = 0;
|
||||
otherFlags |= ModelManager.isTranslucent(metadata)?1L<<33:0;
|
||||
otherFlags |= ModelManager.isDoubleSided(metadata)?1L<<34:0;
|
||||
mesher.put(a, b, ((long)clientModelId) | (((long) Mapper.getLightId(ModelManager.faceUsesSelfLighting(metadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelManager.isBiomeColoured(metadata)?1:0)) | otherFlags);
|
||||
otherFlags |= ModelQueries.isTranslucent(metadata)?1L<<33:0;
|
||||
otherFlags |= ModelQueries.isDoubleSided(metadata)?1L<<34:0;
|
||||
mesher.put(a, b, ((long)clientModelId) | (((long) Mapper.getLightId(ModelQueries.faceUsesSelfLighting(metadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelQueries.isBiomeColoured(metadata)?1:0)) | otherFlags);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
package me.cortex.voxy.client.core.rendering.building;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import me.cortex.voxy.client.core.model.IdNotYetComputedException;
|
||||
import me.cortex.voxy.client.core.model.ModelManager;
|
||||
import me.cortex.voxy.client.core.model.ModelFactory;
|
||||
import me.cortex.voxy.client.core.model.OffThreadModelBakerySystem;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
import me.cortex.voxy.common.world.WorldSection;
|
||||
import me.cortex.voxy.common.world.other.Mapper;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.ToIntFunction;
|
||||
|
||||
//TODO: Add a render cache
|
||||
public class RenderGenerationService {
|
||||
public interface TaskChecker {boolean check(int lvl, int x, int y, int z);}
|
||||
private record BuildTask(long position, Supplier<WorldSection> sectionSupplier) {}
|
||||
private record BuildTask(long position, Supplier<WorldSection> sectionSupplier, boolean[] hasDoneModelRequest) {}
|
||||
|
||||
private volatile boolean running = true;
|
||||
private final Thread[] workers;
|
||||
@@ -26,15 +29,15 @@ public class RenderGenerationService {
|
||||
|
||||
private final Semaphore taskCounter = new Semaphore(0);
|
||||
private final WorldEngine world;
|
||||
private final ModelManager modelManager;
|
||||
private final OffThreadModelBakerySystem modelBakery;
|
||||
private final Consumer<BuiltSection> resultConsumer;
|
||||
private final BuiltSectionMeshCache meshCache = new BuiltSectionMeshCache();
|
||||
private final boolean emitMeshlets;
|
||||
|
||||
public RenderGenerationService(WorldEngine world, ModelManager modelManager, int workers, Consumer<BuiltSection> consumer, boolean emitMeshlets) {
|
||||
public RenderGenerationService(WorldEngine world, OffThreadModelBakerySystem modelBakery, int workers, Consumer<BuiltSection> consumer, boolean emitMeshlets) {
|
||||
this.emitMeshlets = emitMeshlets;
|
||||
this.world = world;
|
||||
this.modelManager = modelManager;
|
||||
this.modelBakery = modelBakery;
|
||||
this.resultConsumer = consumer;
|
||||
this.workers = new Thread[workers];
|
||||
for (int i = 0; i < workers; i++) {
|
||||
@@ -45,10 +48,26 @@ public class RenderGenerationService {
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE: the biomes are always fully populated/kept up to date
|
||||
|
||||
//Asks the Model system to bake all blocks that currently dont have a model
|
||||
private void computeAndRequestRequiredModels(WorldSection section) {
|
||||
var raw = section.copyData();//TODO: replace with copyDataTo and use a "thread local"/context array to reduce allocation rates
|
||||
IntOpenHashSet seen = new IntOpenHashSet(128);
|
||||
for (long state : raw) {
|
||||
int block = Mapper.getBlockId(state);
|
||||
if (!this.modelBakery.factory.hasModelForBlockId(block)) {
|
||||
if (seen.add(block)) {
|
||||
this.modelBakery.requestBlockBake(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: add a generated render data cache
|
||||
private void renderWorker() {
|
||||
//Thread local instance of the factory
|
||||
var factory = new RenderDataFactory(this.world, this.modelManager, this.emitMeshlets);
|
||||
var factory = new RenderDataFactory(this.world, this.modelBakery.factory, this.emitMeshlets);
|
||||
while (this.running) {
|
||||
this.taskCounter.acquireUninterruptibly();
|
||||
if (!this.running) break;
|
||||
@@ -67,17 +86,28 @@ public class RenderGenerationService {
|
||||
try {
|
||||
mesh = factory.generateMesh(section);
|
||||
} catch (IdNotYetComputedException e) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
if (task.hasDoneModelRequest[0]) {
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
} else {
|
||||
this.computeAndRequestRequiredModels(section);
|
||||
}
|
||||
//We need to reinsert the build task into the queue
|
||||
//System.err.println("Render task failed to complete due to un-computed client id");
|
||||
synchronized (this.taskQueue) {
|
||||
this.taskQueue.computeIfAbsent(section.key, key->{this.taskCounter.release(); return task;});
|
||||
var queuedTask = this.taskQueue.computeIfAbsent(section.key, (a)->task);
|
||||
queuedTask.hasDoneModelRequest[0] = true;//Mark (or remark) the section as having chunks requested
|
||||
|
||||
if (queuedTask == task) {//use the == not .equal to see if we need to release a permit
|
||||
this.taskCounter.release();//Since we put in queue, release permit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: if the section was _not_ built, maybe dont release it, or release it with the hint
|
||||
section.release();
|
||||
if (mesh != null) {
|
||||
//TODO: if the mesh is null, need to clear the cache at that point
|
||||
@@ -136,7 +166,7 @@ public class RenderGenerationService {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}, new boolean[1]);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -196,4 +226,8 @@ public class RenderGenerationService {
|
||||
}
|
||||
this.meshCache.free();
|
||||
}
|
||||
|
||||
public void addDebugData(List<String> debug) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package me.cortex.voxy.client.core.rendering.geometry;
|
||||
|
||||
//The geometry renderer, takes a list of section ids to render (gpu side buffer) and renders them
|
||||
// Also manages base geometry info
|
||||
public abstract class AbstractGeometryRenderer {
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
package me.cortex.voxy.client.core.rendering.geometry.OLD;
|
||||
|
||||
//NOTE: an idea on how to do it is so that any render section, we _keep_ aquired (yes this will be very memory intensive)
|
||||
// could maybe tosomething else
|
||||
@@ -6,7 +6,8 @@ package me.cortex.voxy.client.core.rendering;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.model.ModelManager;
|
||||
import me.cortex.voxy.client.core.model.ModelFactory;
|
||||
import me.cortex.voxy.client.core.rendering.Viewport;
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
@@ -19,7 +20,6 @@ import net.minecraft.util.Identifier;
|
||||
import org.joml.FrustumIntersection;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
|
||||
@@ -35,12 +35,12 @@ import static org.lwjgl.opengl.GL30.*;
|
||||
|
||||
|
||||
//Todo: tinker with having the compute shader where each thread is a position to render? maybe idk
|
||||
public abstract class AbstractFarWorldRenderer <T extends Viewport, J extends AbstractGeometryManager> implements IRenderInterface<T> {
|
||||
public abstract class AbstractFarWorldRenderer <T extends Viewport, J extends AbstractGeometryManager> {
|
||||
public static final int STATIC_VAO = glGenVertexArrays();
|
||||
|
||||
protected final GlBuffer uniformBuffer;
|
||||
protected final J geometry;
|
||||
protected final ModelManager models;
|
||||
protected final ModelFactory models;
|
||||
protected final GlBuffer lightDataBuffer;
|
||||
|
||||
protected final int maxSections;
|
||||
@@ -58,7 +58,7 @@ public abstract class AbstractFarWorldRenderer <T extends Viewport, J extends Ab
|
||||
|
||||
private final ConcurrentLinkedDeque<Mapper.StateEntry> blockStateUpdates = new ConcurrentLinkedDeque<>();
|
||||
private final ConcurrentLinkedDeque<Mapper.BiomeEntry> biomeUpdates = new ConcurrentLinkedDeque<>();
|
||||
public AbstractFarWorldRenderer(ModelManager models, J geometry) {
|
||||
public AbstractFarWorldRenderer(ModelFactory models, J geometry) {
|
||||
this.maxSections = geometry.getMaxSections();
|
||||
this.uniformBuffer = new GlBuffer(1024);
|
||||
this.lightDataBuffer = new GlBuffer(256*4);//256 of uint
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
package me.cortex.voxy.client.core.rendering.geometry.OLD;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
package me.cortex.voxy.client.core.rendering.geometry.OLD;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap;
|
||||
@@ -12,8 +12,6 @@ import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.text.Text;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
|
||||
public class DefaultGeometryManager extends AbstractGeometryManager {
|
||||
private static final int SECTION_METADATA_SIZE = 32;
|
||||
private final Long2IntOpenHashMap pos2id = new Long2IntOpenHashMap();
|
||||
@@ -1,30 +1,23 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
package me.cortex.voxy.client.core.rendering.geometry.OLD;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||
import me.cortex.voxy.client.core.model.ModelManager;
|
||||
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
||||
import me.cortex.voxy.client.core.model.ModelFactory;
|
||||
import me.cortex.voxy.client.core.rendering.SharedIndexBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.opengl.GL11C;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.lwjgl.opengl.ARBIndirectParameters.GL_PARAMETER_BUFFER_ARB;
|
||||
import static org.lwjgl.opengl.ARBIndirectParameters.glMultiDrawElementsIndirectCountARB;
|
||||
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
|
||||
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_SHORT;
|
||||
import static org.lwjgl.opengl.GL11.glGetInteger;
|
||||
import static org.lwjgl.opengl.GL14C.glBlendFuncSeparate;
|
||||
import static org.lwjgl.opengl.GL30.glBindVertexArray;
|
||||
import static org.lwjgl.opengl.GL30C.GL_R8UI;
|
||||
import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER;
|
||||
import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER;
|
||||
import static org.lwjgl.opengl.GL42.*;
|
||||
@@ -55,7 +48,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer<Gl46Viewport,
|
||||
private final GlBuffer glCommandBuffer;
|
||||
private final GlBuffer glCommandCountBuffer;
|
||||
|
||||
public Gl46FarWorldRenderer(ModelManager modelManager, int geometryBuffer, int maxSections) {
|
||||
public Gl46FarWorldRenderer(ModelFactory modelManager, int geometryBuffer, int maxSections) {
|
||||
super(modelManager, new DefaultGeometryManager(geometryBuffer*8L, maxSections));
|
||||
this.glCommandBuffer = new GlBuffer(maxSections*5L*4 * 6);
|
||||
this.glCommandCountBuffer = new GlBuffer(4*2);
|
||||
@@ -69,8 +62,8 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer<Gl46Viewport,
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, this.glCommandCountBuffer.id);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.geometry.metaId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, viewport.visibilityBuffer.id);
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.models.getBufferId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, this.models.getColourBufferId());
|
||||
//glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, this.models.getBufferId());
|
||||
//glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 7, this.models.getColourBufferId());
|
||||
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 8, this.lightDataBuffer.id);//Lighting LUT
|
||||
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.glCommandBuffer.id);
|
||||
glBindBuffer(GL_PARAMETER_BUFFER_ARB, this.glCommandCountBuffer.id);
|
||||
@@ -78,7 +71,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer<Gl46Viewport,
|
||||
|
||||
//Bind the texture atlas
|
||||
glBindSampler(0, this.models.getSamplerId());
|
||||
glBindTextureUnit(0, this.models.getTextureId());
|
||||
//glBindTextureUnit(0, this.models.getTextureId());
|
||||
}
|
||||
|
||||
//FIXME: dont do something like this as it breaks multiviewport mods
|
||||
@@ -198,7 +191,7 @@ public class Gl46FarWorldRenderer extends AbstractFarWorldRenderer<Gl46Viewport,
|
||||
|
||||
|
||||
glBindSampler(0, this.models.getSamplerId());
|
||||
glBindTextureUnit(0, this.models.getTextureId());
|
||||
//glBindTextureUnit(0, this.models.getTextureId());
|
||||
|
||||
//RenderSystem.blendFunc(GlStateManager.SrcFactor.ONE, GlStateManager.DstFactor.ONE);
|
||||
this.lodShader.bind();
|
||||
@@ -1,14 +1,9 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
package me.cortex.voxy.client.core.rendering.geometry.OLD;
|
||||
|
||||
import com.sun.jna.NativeLibrary;
|
||||
import me.cortex.voxy.client.core.AbstractRenderWorldInteractor;
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.gl.shader.PrintfInjector;
|
||||
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||
import me.cortex.voxy.client.core.model.ModelManager;
|
||||
import me.cortex.voxy.client.core.model.ModelFactory;
|
||||
import me.cortex.voxy.client.core.rendering.building.BuiltSection;
|
||||
import me.cortex.voxy.client.core.rendering.building.RenderDataFactory;
|
||||
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||
import me.cortex.voxy.client.core.rendering.hierarchical.DebugRenderer;
|
||||
import me.cortex.voxy.client.core.rendering.hierarchical.HierarchicalOcclusionRenderer;
|
||||
@@ -16,44 +11,24 @@ import me.cortex.voxy.client.core.rendering.hierarchical.INodeInteractor;
|
||||
import me.cortex.voxy.client.core.rendering.hierarchical.MeshManager;
|
||||
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection;
|
||||
import me.cortex.voxy.common.world.WorldEngine;
|
||||
import me.cortex.voxy.common.world.WorldSection;
|
||||
import me.cortex.voxy.common.world.other.Mapper;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.Camera;
|
||||
import net.minecraft.client.render.Frustum;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureParameteri;
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
|
||||
import static org.lwjgl.opengl.GL15.glBindBuffer;
|
||||
import static org.lwjgl.opengl.GL30.glBindBufferBase;
|
||||
import static org.lwjgl.opengl.GL30.glBindVertexArray;
|
||||
import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER;
|
||||
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
|
||||
import static org.lwjgl.opengl.GL31.glDrawElementsInstanced;
|
||||
import static org.lwjgl.opengl.GL33.glBindSampler;
|
||||
import static org.lwjgl.opengl.GL40.glDrawElementsIndirect;
|
||||
import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER;
|
||||
import static org.lwjgl.opengl.GL42.GL_FRAMEBUFFER_BARRIER_BIT;
|
||||
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
|
||||
import static org.lwjgl.opengl.GL43.*;
|
||||
import static org.lwjgl.opengl.GL45.glBindTextureUnit;
|
||||
import static org.lwjgl.opengl.GL45.nglClearNamedBufferSubData;
|
||||
|
||||
public class Gl46HierarchicalRenderer implements IRenderInterface<Gl46HierarchicalViewport>, AbstractRenderWorldInteractor {
|
||||
public class Gl46HierarchicalRenderer {
|
||||
private final HierarchicalOcclusionRenderer sectionSelector;
|
||||
private final MeshManager meshManager = new MeshManager();
|
||||
|
||||
@@ -76,25 +51,25 @@ public class Gl46HierarchicalRenderer implements IRenderInterface<Gl46Hierarchic
|
||||
|
||||
protected final ConcurrentLinkedDeque<BuiltSection> buildResults = new ConcurrentLinkedDeque<>();
|
||||
|
||||
private final ModelManager modelManager;
|
||||
private final ModelFactory modelManager;
|
||||
private RenderGenerationService sectionGenerationService;
|
||||
private Consumer<BuiltSection> resultConsumer;
|
||||
|
||||
public Gl46HierarchicalRenderer(ModelManager model) {
|
||||
public Gl46HierarchicalRenderer(ModelFactory model) {
|
||||
this.modelManager = model;
|
||||
|
||||
this.sectionSelector = new HierarchicalOcclusionRenderer(new INodeInteractor() {
|
||||
@Override
|
||||
|
||||
public void watchUpdates(long pos) {
|
||||
//System.err.println("Watch: " + pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void unwatchUpdates(long pos) {
|
||||
//System.err.println("Unwatch: " + pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void requestMesh(long pos) {
|
||||
Gl46HierarchicalRenderer.this.sectionGenerationService.enqueueTask(
|
||||
WorldEngine.getLevel(pos),
|
||||
@@ -104,14 +79,13 @@ public class Gl46HierarchicalRenderer implements IRenderInterface<Gl46Hierarchic
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void setMeshUpdateCallback(Consumer<BuiltSection> mesh) {
|
||||
Gl46HierarchicalRenderer.this.resultConsumer = mesh;
|
||||
}
|
||||
}, this.meshManager, this.printf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupRender(Frustum frustum, Camera camera) {
|
||||
{//Tick upload and download queues
|
||||
UploadStream.INSTANCE.tick();
|
||||
@@ -143,7 +117,7 @@ public class Gl46HierarchicalRenderer implements IRenderInterface<Gl46Hierarchic
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void renderFarAwayOpaque(Gl46HierarchicalViewport viewport) {
|
||||
//Process all the build results
|
||||
while (!this.buildResults.isEmpty()) {
|
||||
@@ -167,12 +141,12 @@ public class Gl46HierarchicalRenderer implements IRenderInterface<Gl46Hierarchic
|
||||
this.printf.download();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void renderFarAwayTranslucent(Gl46HierarchicalViewport viewport) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void addDebugData(List<String> debug) {
|
||||
debug.add("Printf Queue: ");
|
||||
debug.addAll(this.printfQueue);
|
||||
@@ -185,12 +159,12 @@ public class Gl46HierarchicalRenderer implements IRenderInterface<Gl46Hierarchic
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
public void addBlockState(Mapper.StateEntry stateEntry) {
|
||||
this.blockStateUpdates.add(stateEntry);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public void addBiome(Mapper.BiomeEntry biomeEntry) {
|
||||
this.biomeUpdates.add(biomeEntry);
|
||||
}
|
||||
@@ -198,21 +172,10 @@ public class Gl46HierarchicalRenderer implements IRenderInterface<Gl46Hierarchic
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void processBuildResult(BuiltSection section) {
|
||||
this.buildResults.add(section);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sectionUpdated(WorldSection worldSection) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void initPosition(int X, int Z) {
|
||||
for (int x = -10; x <= 10; x++) {
|
||||
for (int z = -10; z <= 10; z++) {
|
||||
@@ -224,27 +187,19 @@ public class Gl46HierarchicalRenderer implements IRenderInterface<Gl46Hierarchic
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCenter(int x, int y, int z) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean generateMeshlets() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRenderGen(RenderGenerationService renderService) {
|
||||
this.sectionGenerationService = renderService;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
public Gl46HierarchicalViewport createViewport() {
|
||||
return new Gl46HierarchicalViewport(this);
|
||||
}
|
||||
@@ -252,7 +207,7 @@ public class Gl46HierarchicalRenderer implements IRenderInterface<Gl46Hierarchic
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
|
||||
public void shutdown() {
|
||||
this.meshManager.free();
|
||||
this.sectionSelector.free();
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
package me.cortex.voxy.client.core.rendering.geometry.OLD;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.Viewport;
|
||||
|
||||
public class Gl46HierarchicalViewport extends Viewport<Gl46HierarchicalViewport> {
|
||||
public Gl46HierarchicalViewport(Gl46HierarchicalRenderer renderer) {
|
||||
@@ -1,13 +1,10 @@
|
||||
package me.cortex.voxy.client.core.rendering;
|
||||
package me.cortex.voxy.client.core.rendering.geometry.OLD;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.Viewport;
|
||||
|
||||
import static org.lwjgl.opengl.ARBIndirectParameters.glMultiDrawElementsIndirectCountARB;
|
||||
import static org.lwjgl.opengl.GL30C.GL_R8UI;
|
||||
import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER;
|
||||
import static org.lwjgl.opengl.GL42.GL_UNSIGNED_BYTE;
|
||||
import static org.lwjgl.opengl.GL45C.glClearNamedBufferData;
|
||||
import static org.lwjgl.opengl.GL45C.nglClearNamedBufferData;
|
||||
|
||||
public class Gl46Viewport extends Viewport<Gl46Viewport> {
|
||||
GlBuffer visibilityBuffer;
|
||||
@@ -3,8 +3,8 @@ package me.cortex.voxy.client.core.rendering.hierarchical;
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||
import me.cortex.voxy.client.core.rendering.AbstractFarWorldRenderer;
|
||||
import me.cortex.voxy.client.core.rendering.Gl46HierarchicalViewport;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.AbstractFarWorldRenderer;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport;
|
||||
import me.cortex.voxy.client.core.rendering.SharedIndexBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
@@ -4,10 +4,9 @@ import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.gl.shader.PrintfInjector;
|
||||
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||
import me.cortex.voxy.client.core.rendering.Gl46HierarchicalViewport;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport;
|
||||
import me.cortex.voxy.client.core.rendering.HiZBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||
import me.cortex.voxy.client.mixin.joml.AccessFrustumIntersection;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
@@ -88,6 +87,8 @@ public class HierarchicalOcclusionRenderer {
|
||||
|
||||
UploadStream.INSTANCE.commit();
|
||||
|
||||
//FIXME: need to have the hiz respect the stencil mask aswell to mask away normal terrain, (much increase perf)
|
||||
|
||||
//Make hiz
|
||||
this.hiz.buildMipChain(depthBuffer, viewport.width, viewport.height);
|
||||
glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT);
|
||||
|
||||
@@ -2,7 +2,7 @@ package me.cortex.voxy.client.core.rendering.section;
|
||||
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.Gl46HierarchicalViewport;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport;
|
||||
|
||||
//Takes in mesh ids from the hierachical traversal and may perform more culling then renders it
|
||||
public abstract class AbstractSectionRenderer {
|
||||
|
||||
@@ -2,7 +2,7 @@ package me.cortex.voxy.client.core.rendering.section;
|
||||
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.rendering.Gl46HierarchicalViewport;
|
||||
import me.cortex.voxy.client.core.rendering.geometry.OLD.Gl46HierarchicalViewport;
|
||||
|
||||
//Uses MDIC to render the sections
|
||||
public class MDICSectionRenderer extends AbstractSectionRenderer {
|
||||
|
||||
@@ -3,6 +3,7 @@ package me.cortex.voxy.client.core.rendering.util;
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.client.core.util.AllocationArena;
|
||||
import me.cortex.voxy.common.util.MemoryBuffer;
|
||||
import me.cortex.voxy.common.util.UnsafeUtil;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
public class BufferArena {
|
||||
@@ -32,7 +33,7 @@ public class BufferArena {
|
||||
return -1;
|
||||
}
|
||||
long uploadPtr = UploadStream.INSTANCE.upload(this.buffer, addr * this.elementSize, buffer.size);
|
||||
MemoryUtil.memCopy(buffer.address, uploadPtr, buffer.size);
|
||||
UnsafeUtil.memcpy(buffer.address, uploadPtr, buffer.size);
|
||||
this.used += size;
|
||||
return addr;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.cortex.voxy.client.core.rendering.util;
|
||||
|
||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||
import me.cortex.voxy.common.util.UnsafeUtil;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
//Just a utility for making a deferred upload (make on other thread then upload on render thread)
|
||||
public final class DeferredUpload {
|
||||
public final long ptr;
|
||||
private final long size;
|
||||
private final long offset;
|
||||
private final GlBuffer buffer;
|
||||
public DeferredUpload(GlBuffer buffer, long offset, long size) {
|
||||
this.ptr = MemoryUtil.nmemAlloc(size);
|
||||
this.offset = offset;
|
||||
this.buffer = buffer;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public void upload() {
|
||||
long upPtr = UploadStream.INSTANCE.upload(this.buffer, this.offset, this.size);
|
||||
UnsafeUtil.memcpy(this.ptr, upPtr, this.size);
|
||||
MemoryUtil.nmemFree(this.ptr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package me.cortex.voxy.client.mixin.minecraft;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(RenderSystem.class)
|
||||
public class MixinRenderSystem {
|
||||
@Inject(method = {"assertOnRenderThread", "assertOnRenderThreadOrInit"}, at = @At("HEAD"), cancellable = true)
|
||||
private static void cancelAssert(CallbackInfo ci) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,9 @@ public class MixinRenderSectionManager {
|
||||
|
||||
@Inject(method = "onChunkRemoved", at = @At("HEAD"))
|
||||
private void injectIngest(int x, int z, CallbackInfo ci) {
|
||||
var core = ((IGetVoxelCore)(world.worldRenderer)).getVoxelCore();
|
||||
var core = ((IGetVoxelCore)(this.world.worldRenderer)).getVoxelCore();
|
||||
if (core != null && VoxyConfig.CONFIG.ingestEnabled) {
|
||||
core.enqueueIngest(world.getChunk(x, z));
|
||||
core.enqueueIngest(this.world.getChunk(x, z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
public abstract class StorageBackend {
|
||||
public abstract void iterateStoredSectionPositions(LongConsumer consumer);
|
||||
|
||||
public abstract ByteBuffer getSectionData(long key);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import org.apache.commons.lang3.stream.Streams;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
public class MemoryStorageBackend extends StorageBackend {
|
||||
private final Long2ObjectMap<ByteBuffer>[] maps;
|
||||
@@ -22,6 +23,19 @@ public class MemoryStorageBackend extends StorageBackend {
|
||||
this(4);
|
||||
}
|
||||
|
||||
private Long2ObjectMap<ByteBuffer> getMap(long key) {
|
||||
return this.maps[(int) (RandomSeed.mixStafford13(RandomSeed.mixStafford13(key)^key)&(this.maps.length-1))];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterateStoredSectionPositions(LongConsumer consumer) {
|
||||
for (var map : this.maps) {
|
||||
synchronized (map) {
|
||||
map.keySet().forEach(consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MemoryStorageBackend(int slicesBitCount) {
|
||||
this.maps = new Long2ObjectMap[1<<slicesBitCount];
|
||||
for (int i = 0; i < this.maps.length; i++) {
|
||||
@@ -29,10 +43,6 @@ public class MemoryStorageBackend extends StorageBackend {
|
||||
}
|
||||
}
|
||||
|
||||
private Long2ObjectMap<ByteBuffer> getMap(long key) {
|
||||
return this.maps[(int) (RandomSeed.mixStafford13(RandomSeed.mixStafford13(key)^key)&(this.maps.length-1))];
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getSectionData(long key) {
|
||||
var map = this.getMap(key);
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.nio.ByteBuffer;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.LongConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.lwjgl.util.lmdb.LMDB.*;
|
||||
@@ -82,6 +83,11 @@ public class LMDBStorageBackend extends StorageBackend {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterateStoredSectionPositions(LongConsumer consumer) {
|
||||
throw new IllegalStateException("Not yet implemented");
|
||||
}
|
||||
|
||||
//TODO: make batch get and updates
|
||||
public ByteBuffer getSectionData(long key) {
|
||||
return this.synchronizedTransaction(() -> this.sectionDatabase.transaction(MDB_RDONLY, transaction->{
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
public class DelegatingStorageAdaptor extends StorageBackend {
|
||||
protected final StorageBackend delegate;
|
||||
@@ -17,6 +18,9 @@ public class DelegatingStorageAdaptor extends StorageBackend {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterateStoredSectionPositions(LongConsumer consumer) {this.delegate.iterateStoredSectionPositions(consumer);}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getSectionData(long key) {
|
||||
return this.delegate.getSectionData(key);
|
||||
|
||||
@@ -12,6 +12,7 @@ import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
//Segments the section data into multiple dbs
|
||||
public class FragmentedStorageBackendAdaptor extends StorageBackend {
|
||||
@@ -29,6 +30,11 @@ public class FragmentedStorageBackendAdaptor extends StorageBackend {
|
||||
return (int) (RandomSeed.mixStafford13(RandomSeed.mixStafford13(key)^key)&(this.backends.length-1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterateStoredSectionPositions(LongConsumer consumer) {
|
||||
throw new IllegalStateException("Not yet implemented");
|
||||
}
|
||||
|
||||
//TODO: reencode the key to be shifted one less OR
|
||||
// use like a mix64 to shuffle the key in getSegmentId so that
|
||||
// multiple layers of spliced storage backends can be stacked
|
||||
|
||||
@@ -7,6 +7,7 @@ import me.cortex.voxy.common.storage.config.StorageConfig;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
public class ReadonlyCachingLayer extends StorageBackend {
|
||||
private final StorageBackend cache;
|
||||
@@ -30,6 +31,11 @@ public class ReadonlyCachingLayer extends StorageBackend {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterateStoredSectionPositions(LongConsumer consumer) {
|
||||
throw new IllegalStateException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSectionData(long key, ByteBuffer data) {
|
||||
this.cache.setSectionData(key, data);
|
||||
|
||||
@@ -9,6 +9,7 @@ import redis.clients.jedis.JedisPool;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
public class RedisStorageBackend extends StorageBackend {
|
||||
private final JedisPool pool;
|
||||
@@ -29,6 +30,11 @@ public class RedisStorageBackend extends StorageBackend {
|
||||
this.MAPPINGS = (prefix+"id_mappings").getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterateStoredSectionPositions(LongConsumer consumer) {
|
||||
throw new IllegalStateException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getSectionData(long key) {
|
||||
try (var jedis = this.pool.getResource()) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.LongConsumer;
|
||||
|
||||
public class RocksDBStorageBackend extends StorageBackend {
|
||||
private final RocksDB db;
|
||||
@@ -80,6 +81,11 @@ public class RocksDBStorageBackend extends StorageBackend {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void iterateStoredSectionPositions(LongConsumer consumer) {
|
||||
throw new IllegalStateException("Not yet implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getSectionData(long key) {
|
||||
try {
|
||||
|
||||
@@ -13,7 +13,7 @@ public class MemoryBuffer extends TrackedObject {
|
||||
|
||||
public void cpyTo(long dst) {
|
||||
super.assertNotFreed();
|
||||
MemoryUtil.memCopy(this.address, dst, this.size);
|
||||
UnsafeUtil.memcpy(this.address, dst, this.size);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
21
src/main/java/me/cortex/voxy/common/util/UnsafeUtil.java
Normal file
21
src/main/java/me/cortex/voxy/common/util/UnsafeUtil.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package me.cortex.voxy.common.util;
|
||||
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
public class UnsafeUtil {
|
||||
private static final Unsafe UNSAFE;
|
||||
static {
|
||||
try {
|
||||
Field field = Unsafe.class.getDeclaredField("theUnsafe");
|
||||
field.setAccessible(true);
|
||||
UNSAFE = (Unsafe)field.get(null);
|
||||
} catch (Exception e) {throw new RuntimeException(e);}
|
||||
}
|
||||
|
||||
public static void memcpy(long src, long dst, long length) {
|
||||
UNSAFE.copyMemory(src, dst, length);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.cortex.voxy.common.world;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import me.cortex.voxy.common.util.VolatileHolder;
|
||||
import me.cortex.voxy.common.world.other.Mapper;
|
||||
@@ -14,7 +15,10 @@ public class ActiveSectionTracker {
|
||||
|
||||
private final Long2ObjectOpenHashMap<VolatileHolder<WorldSection>>[] loadedSectionCache;
|
||||
private final SectionLoader loader;
|
||||
//private final SectionDataCache dataCache;
|
||||
|
||||
//private static final int SECONDARY_CACHE_CAPACITY = 256;
|
||||
//private final Long2ObjectLinkedOpenHashMap<long[]> secondaryDataCache = new Long2ObjectLinkedOpenHashMap<>(SECONDARY_CACHE_CAPACITY*2);//Its x2 due to race conditions
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ActiveSectionTracker(int numSlicesBits, SectionLoader loader) {
|
||||
@@ -43,6 +47,7 @@ public class ActiveSectionTracker {
|
||||
return section;
|
||||
}
|
||||
}
|
||||
|
||||
//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);
|
||||
@@ -84,11 +89,13 @@ public class ActiveSectionTracker {
|
||||
|
||||
void tryUnload(WorldSection section) {
|
||||
var cache = this.loadedSectionCache[this.getCacheArrayIndex(section.key)];
|
||||
boolean removed = false;
|
||||
synchronized (cache) {
|
||||
if (section.trySetFreed()) {
|
||||
if (cache.remove(section.key).obj != section) {
|
||||
throw new IllegalStateException("Removed section not the same as the referenced section in the cache");
|
||||
}
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package me.cortex.voxy.common.world;
|
||||
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2LongOpenHashMap;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.SoftReference;
|
||||
|
||||
public class SectionDataCache {
|
||||
private record MapPair(Reference2LongOpenHashMap<SoftReference<long[]>> ref2pos, Long2ObjectMap<SoftReference<long[]>> pos2ref) {
|
||||
public MapPair() {
|
||||
this(new Reference2LongOpenHashMap<>(), new Long2ObjectOpenHashMap<>());
|
||||
}
|
||||
}
|
||||
private final MapPair[] maps;
|
||||
private final ReferenceQueue<long[]> cleanupQueue;
|
||||
public SectionDataCache(int sliceBits) {
|
||||
this.cleanupQueue = new ReferenceQueue<>();
|
||||
this.maps = new MapPair[1<<sliceBits];
|
||||
for (int i = 0; i < this.maps.length; i++) {
|
||||
this.maps[i] = new MapPair();
|
||||
}
|
||||
}
|
||||
|
||||
private MapPair getMap(long pos) {
|
||||
return this.maps[(int) (ActiveSectionTracker.mixStafford13(pos)&(this.maps.length-1))];
|
||||
}
|
||||
|
||||
public int loadAndPut(WorldSection section) {
|
||||
var map = this.getMap(section.key);
|
||||
synchronized (map) {
|
||||
var entry = map.pos2ref.get(section.key);
|
||||
if (entry == null) {//No entry in cache so put it in cache
|
||||
map.pos2ref.put(section.key, new SoftReference<>(section.data));
|
||||
return -1;
|
||||
}
|
||||
//var data = entry.data.get();
|
||||
//if (data == null) {
|
||||
// map.remove(section.key);
|
||||
// return -1;
|
||||
//}
|
||||
//System.arraycopy(data, 0, section.data, 0, data.length);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,6 +95,10 @@ public class WorldEngine {
|
||||
return (int) ((id<<12)>>40);
|
||||
}
|
||||
|
||||
public static String pprintPos(long pos) {
|
||||
return getLevel(pos)+"@["+getX(pos)+", "+getY(pos)+", " + getZ(pos)+"]";
|
||||
}
|
||||
|
||||
//Marks a section as dirty, enqueuing it for saving and or render data rebuilding
|
||||
public void markDirty(WorldSection section) {
|
||||
if (this.dirtyCallback != null) {
|
||||
|
||||
@@ -64,6 +64,7 @@ public final class WorldSection {
|
||||
return this.atomicState.get()>>1;
|
||||
}
|
||||
|
||||
//TODO: add the ability to hint to the tracker that yes the section is unloaded, try to cache it in a secondary cache since it will be reused/needed later
|
||||
public int release() {
|
||||
int state = this.atomicState.addAndGet(-2);
|
||||
if (state < 1) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"minecraft.MixinClientChunkManager",
|
||||
"minecraft.MixinDebugHud",
|
||||
"minecraft.MixinMinecraftClient",
|
||||
"minecraft.MixinRenderSystem",
|
||||
"minecraft.MixinWorldRenderer",
|
||||
"nvidium.MixinRenderPipeline",
|
||||
"sodium.MixinDefaultChunkRenderer",
|
||||
|
||||
Reference in New Issue
Block a user