0.1.4, added render distance slider, disabled caching until better solution can be found
This commit is contained in:
@@ -91,11 +91,11 @@ public class VoxyConfigScreenFactory implements ModMenuApi {
|
|||||||
.setDefaultValue(DEFAULT.maxSections)
|
.setDefaultValue(DEFAULT.maxSections)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
//category.addEntry(entryBuilder.startIntSlider(Text.translatable("voxy.config.general.renderDistance"), config.maxSections, 16, 2048)
|
category.addEntry(entryBuilder.startIntField(Text.translatable("voxy.config.general.renderDistance"), config.renderDistance)
|
||||||
// .setTooltip(Text.translatable("voxy.config.general.renderDistance.tooltip"))
|
.setTooltip(Text.translatable("voxy.config.general.renderDistance.tooltip"))
|
||||||
// .setSaveConsumer(val -> config.renderDistance = val)
|
.setSaveConsumer(val -> config.renderDistance = val)
|
||||||
// .setDefaultValue(DEFAULT.renderDistance)
|
.setDefaultValue(DEFAULT.renderDistance)
|
||||||
// .build());
|
.build());
|
||||||
|
|
||||||
//category.addEntry(entryBuilder.startIntSlider(Text.translatable("voxy.config.general.compression"), config.savingCompressionLevel, 1, 21)
|
//category.addEntry(entryBuilder.startIntSlider(Text.translatable("voxy.config.general.compression"), config.savingCompressionLevel, 1, 21)
|
||||||
// .setTooltip(Text.translatable("voxy.config.general.compression.tooltip"))
|
// .setTooltip(Text.translatable("voxy.config.general.compression.tooltip"))
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import me.cortex.voxy.client.core.rendering.RenderTracker;
|
|||||||
import me.cortex.voxy.client.core.util.RingUtil;
|
import me.cortex.voxy.client.core.util.RingUtil;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
|
||||||
import java.util.stream.IntStream;
|
|
||||||
|
|
||||||
//Can use ring logic
|
//Can use ring logic
|
||||||
// i.e. when a player moves the rings of each lod change (how it was doing in the original attempt)
|
// i.e. when a player moves the rings of each lod change (how it was doing in the original attempt)
|
||||||
// also have it do directional quad culling and rebuild the chunk if needed (this shouldent happen very often) (the reason is to significantly reduce draw calls)
|
// also have it do directional quad culling and rebuild the chunk if needed (this shouldent happen very often) (the reason is to significantly reduce draw calls)
|
||||||
@@ -19,12 +17,13 @@ public class DistanceTracker {
|
|||||||
private final TransitionRing2D[] loDRings;
|
private final TransitionRing2D[] loDRings;
|
||||||
private final TransitionRing2D[] cacheLoadRings;
|
private final TransitionRing2D[] cacheLoadRings;
|
||||||
private final TransitionRing2D[] cacheUnloadRings;
|
private final TransitionRing2D[] cacheUnloadRings;
|
||||||
|
private final TransitionRing2D mostOuterNonClampedRing;
|
||||||
private final RenderTracker tracker;
|
private final RenderTracker tracker;
|
||||||
private final int minYSection;
|
private final int minYSection;
|
||||||
private final int maxYSection;
|
private final int maxYSection;
|
||||||
private final int renderDistance;
|
private final int renderDistance;
|
||||||
|
|
||||||
public DistanceTracker(RenderTracker tracker, int[] lodRingScales, int renderDistance, int cacheLoadDistance, int cacheUnloadDistance) {
|
public DistanceTracker(RenderTracker tracker, int[] lodRingScales, int renderDistance, int cacheDistance) {
|
||||||
this.loDRings = new TransitionRing2D[lodRingScales.length];
|
this.loDRings = new TransitionRing2D[lodRingScales.length];
|
||||||
this.cacheLoadRings = new TransitionRing2D[lodRingScales.length];
|
this.cacheLoadRings = new TransitionRing2D[lodRingScales.length];
|
||||||
this.cacheUnloadRings = new TransitionRing2D[lodRingScales.length];
|
this.cacheUnloadRings = new TransitionRing2D[lodRingScales.length];
|
||||||
@@ -34,30 +33,87 @@ public class DistanceTracker {
|
|||||||
this.renderDistance = renderDistance;
|
this.renderDistance = renderDistance;
|
||||||
|
|
||||||
|
|
||||||
|
boolean wasRdClamped = false;
|
||||||
//The rings 0+ start at 64 vanilla rd, no matter what the game is set at, that is if the game is set to 32 rd
|
//The rings 0+ start at 64 vanilla rd, no matter what the game is set at, that is if the game is set to 32 rd
|
||||||
// there will still be 32 chunks untill the first lod drop
|
// there will still be 32 chunks untill the first lod drop
|
||||||
// if the game is set to 16, then there will be 48 chunks until the drop
|
// if the game is set to 16, then there will be 48 chunks until the drop
|
||||||
for (int i = 0; i < this.loDRings.length; i++) {
|
for (int i = 0; i < this.loDRings.length; i++) {
|
||||||
|
int scaleP = lodRingScales[i];
|
||||||
|
boolean isTerminatingRing = ((lodRingScales[i]+2)<<(1+i) >= renderDistance)&&renderDistance>0;
|
||||||
|
if (isTerminatingRing) {
|
||||||
|
scaleP = Math.max(renderDistance >> (1+i), 1);
|
||||||
|
wasRdClamped = true;
|
||||||
|
}
|
||||||
|
int scale = scaleP;
|
||||||
|
|
||||||
//TODO: FIXME: check that the level shift is right when inc/dec
|
//TODO: FIXME: check that the level shift is right when inc/dec
|
||||||
int capRing = i;
|
int capRing = i;
|
||||||
this.loDRings[i] = new TransitionRing2D(6+i, lodRingScales[i], (x, z) -> this.dec(capRing+1, x, z), (x, z) -> this.inc(capRing+1, x, z));
|
this.loDRings[i] = new TransitionRing2D((isTerminatingRing?5:6)+i, isTerminatingRing?scale<<1:scale, (x, z) -> {
|
||||||
|
if (isTerminatingRing) {
|
||||||
|
add(capRing, x, z);
|
||||||
|
} else
|
||||||
|
this.dec(capRing+1, x, z);
|
||||||
|
}, (x, z) -> {
|
||||||
|
if (isTerminatingRing) {
|
||||||
|
remove(capRing, x, z);
|
||||||
|
//remove(capRing, (x<<1), (z<<1));
|
||||||
|
} else
|
||||||
|
this.inc(capRing+1, x, z);
|
||||||
|
});
|
||||||
|
|
||||||
//TODO:FIXME i think the radius is wrong and (lodRingScales[i]) needs to be (lodRingScales[i]<<1) since the transition ring (the thing above)
|
//TODO:FIXME i think the radius is wrong and (lodRingScales[i]) needs to be (lodRingScales[i]<<1) since the transition ring (the thing above)
|
||||||
// acts on LoD level + 1
|
// acts on LoD level + 1
|
||||||
|
|
||||||
//TODO: check this is actually working lmao and make it generate parent level lods on the exit instead of entry so it looks correct when flying backwards
|
//TODO: check this is actually working lmao and make it generate parent level lods on the exit instead of entry so it looks correct when flying backwards
|
||||||
this.cacheLoadRings[i] = new TransitionRing2D(5+i, (lodRingScales[i]<<1) + cacheLoadDistance, (x, z) -> {
|
if (!isTerminatingRing) {
|
||||||
|
//TODO: COMPLETLY REDO THE CACHING SYSTEM CAUSE THE LOGIC IS COMPLETLY INCORRECT
|
||||||
|
// we want basicly 2 rings offset by an amount such that when a position is near an lod transition point
|
||||||
|
// it will be meshed (both the higher and lower quality lods), enabling semless loading
|
||||||
|
// the issue is when to uncache these methods
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
this.cacheLoadRings[i] = new TransitionRing2D(5 + i, (scale << 1) + cacheDistance, (x, z) -> {
|
||||||
//When entering a cache ring, trigger a mesh op and inject into cache
|
//When entering a cache ring, trigger a mesh op and inject into cache
|
||||||
for (int y = this.minYSection >> capRing; y <= this.maxYSection >> capRing; y++) {
|
for (int y = this.minYSection >> capRing; y <= this.maxYSection >> capRing; y++) {
|
||||||
this.tracker.addCache(capRing, x, y, z);
|
this.tracker.addCache(capRing, x, y, z);
|
||||||
}
|
}
|
||||||
}, (x, z) -> {});
|
}, (x, z) -> {
|
||||||
this.cacheUnloadRings[i] = new TransitionRing2D(5+i, (lodRingScales[i]<<1) + cacheUnloadDistance, (x, z) -> {}, (x, z) -> {
|
int shift = capRing+1;
|
||||||
|
if (shift <= this.loDRings.length) {
|
||||||
|
for (int y = this.minYSection >> shift; y <= this.maxYSection >> shift; y++) {
|
||||||
|
this.tracker.removeCache(shift, x>>1, y, z>>1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.cacheUnloadRings[i] = new TransitionRing2D(5 + i, Math.max(1, (scale << 1) + cacheDistance), (x, z) -> {
|
||||||
|
int shift = capRing+1;
|
||||||
|
if (shift <= this.loDRings.length) {
|
||||||
|
for (int y = this.minYSection >> shift; y <= this.maxYSection >> shift; y++) {
|
||||||
|
this.tracker.addCache(shift, x>>1, y, z>>1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, (x, z) -> {
|
||||||
//When exiting the cache unload ring, tell the cache to dump whatever mesh it has cached and not add any mesh from that position
|
//When exiting the cache unload ring, tell the cache to dump whatever mesh it has cached and not add any mesh from that position
|
||||||
for (int y = this.minYSection >> capRing; y <= this.maxYSection >> capRing; y++) {
|
for (int y = this.minYSection >> capRing; y <= this.maxYSection >> capRing; y++) {
|
||||||
this.tracker.removeCache(capRing, x, y, z);
|
this.tracker.removeCache(capRing, x, y, z);
|
||||||
}
|
}
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isTerminatingRing) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!wasRdClamped) {
|
||||||
|
this.mostOuterNonClampedRing = new TransitionRing2D(5+this.loDRings.length, Math.max(renderDistance, 2048)>>this.loDRings.length, (x,z)->
|
||||||
|
add(this.loDRings.length, x, z), (x,z)->{
|
||||||
|
if (renderDistance > 0) {
|
||||||
|
remove(this.loDRings.length,x,z);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
this.mostOuterNonClampedRing = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,6 +129,19 @@ public class DistanceTracker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void add(int lvl, int x, int z) {
|
||||||
|
for (int y = this.minYSection>>lvl; y <= this.maxYSection>>lvl; y++) {
|
||||||
|
this.tracker.add(lvl, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void remove(int lvl, int x, int z) {
|
||||||
|
for (int y = this.minYSection>>lvl; y <= this.maxYSection>>lvl; y++) {
|
||||||
|
this.tracker.remove(lvl, x, y, z);
|
||||||
|
this.tracker.removeCache(lvl, x, y, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//How it works is there are N ring zones (one zone for each lod boundary)
|
//How it works is there are N ring zones (one zone for each lod boundary)
|
||||||
// the transition zone is what determines what lods are rendered etc (and it biases higher lod levels cause its easier)
|
// the transition zone is what determines what lods are rendered etc (and it biases higher lod levels cause its easier)
|
||||||
// the transition zone is only ever checked when the player moves 1<<(4+lodlvl) blocks, its position is set
|
// the transition zone is only ever checked when the player moves 1<<(4+lodlvl) blocks, its position is set
|
||||||
@@ -84,9 +153,14 @@ public class DistanceTracker {
|
|||||||
if (ring!=null)
|
if (ring!=null)
|
||||||
ring.update(x, z);
|
ring.update(x, z);
|
||||||
}
|
}
|
||||||
|
if (this.mostOuterNonClampedRing!=null)
|
||||||
|
this.mostOuterNonClampedRing.update(x, z);
|
||||||
|
|
||||||
//Update in reverse order (biggest lod to smallest lod)
|
//Update in reverse order (biggest lod to smallest lod)
|
||||||
for (int i = this.loDRings.length-1; -1<i; i-- ) {
|
for (int i = this.loDRings.length-1; -1<i; i-- ) {
|
||||||
this.loDRings[i].update(x, z);
|
var ring = this.loDRings[i];
|
||||||
|
if (ring != null)
|
||||||
|
ring.update(x, z);
|
||||||
}
|
}
|
||||||
for (var ring : this.cacheUnloadRings) {
|
for (var ring : this.cacheUnloadRings) {
|
||||||
if (ring!=null)
|
if (ring!=null)
|
||||||
@@ -109,18 +183,10 @@ public class DistanceTracker {
|
|||||||
if (ring != null)
|
if (ring != null)
|
||||||
ring.setCenter(x, z);
|
ring.setCenter(x, z);
|
||||||
}
|
}
|
||||||
|
if (this.mostOuterNonClampedRing!=null)
|
||||||
|
this.mostOuterNonClampedRing.setCenter(x, z);
|
||||||
|
|
||||||
var thread = new Thread(()-> {
|
var thread = new Thread(()-> {
|
||||||
//Radius of chunks to enqueue
|
|
||||||
int SIZE = 128;
|
|
||||||
//Insert highest LOD level
|
|
||||||
for (int ox = -SIZE; ox <= SIZE; ox++) {
|
|
||||||
for (int oz = -SIZE; oz <= SIZE; oz++) {
|
|
||||||
this.inc(4, (x >> (5 + this.loDRings.length)) + ox, (z >> (5 + this.loDRings.length)) + oz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (var ring : this.cacheLoadRings) {
|
for (var ring : this.cacheLoadRings) {
|
||||||
if (ring != null)
|
if (ring != null)
|
||||||
ring.fill(x, z);
|
ring.fill(x, z);
|
||||||
@@ -131,6 +197,14 @@ public class DistanceTracker {
|
|||||||
ring.fill(x, z);
|
ring.fill(x, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//This is an ungodly terrible hack to make the lods load in a semi ok order
|
||||||
|
for (var ring : this.loDRings)
|
||||||
|
if (ring != null)
|
||||||
|
ring.fill(x, z);
|
||||||
|
|
||||||
|
if (this.mostOuterNonClampedRing!=null)
|
||||||
|
this.mostOuterNonClampedRing.fill(x, z);
|
||||||
|
|
||||||
for (int i = this.loDRings.length - 1; 0 <= i; i--) {
|
for (int i = this.loDRings.length - 1; 0 <= i; i--) {
|
||||||
if (this.loDRings[i] != null) {
|
if (this.loDRings[i] != null) {
|
||||||
this.loDRings[i].fill(x, z);
|
this.loDRings[i].fill(x, z);
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ public class VoxelCore {
|
|||||||
//To get to chunk scale multiply the scale by 2, the scale is after how many chunks does the lods halve
|
//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 q = VoxyConfig.CONFIG.qualityScale;
|
||||||
//TODO: add an option for cache load and unload distance
|
//TODO: add an option for cache load and unload distance
|
||||||
this.distanceTracker = new DistanceTracker(this.renderTracker, new int[]{q,q,q,q}, VoxyConfig.CONFIG.renderDistance/2, 6, 6);
|
this.distanceTracker = new DistanceTracker(this.renderTracker, new int[]{q,q,q,q}, (VoxyConfig.CONFIG.renderDistance<0?VoxyConfig.CONFIG.renderDistance:((VoxyConfig.CONFIG.renderDistance+1)/2)), 3);
|
||||||
System.out.println("Distance tracker initialized");
|
System.out.println("Distance tracker initialized");
|
||||||
|
|
||||||
this.postProcessing = new PostProcessing();
|
this.postProcessing = new PostProcessing();
|
||||||
@@ -182,6 +182,7 @@ public class VoxelCore {
|
|||||||
debug.add("Saving service tasks: " + this.world.savingService.getTaskCount());
|
debug.add("Saving service tasks: " + this.world.savingService.getTaskCount());
|
||||||
debug.add("Render service tasks: " + this.renderGen.getTaskCount());
|
debug.add("Render service tasks: " + this.renderGen.getTaskCount());
|
||||||
debug.add("Loaded cache sizes: " + Arrays.toString(this.world.getLoadedSectionCacheSizes()));
|
debug.add("Loaded cache sizes: " + Arrays.toString(this.world.getLoadedSectionCacheSizes()));
|
||||||
|
debug.add("Mesh cache count: " + this.renderGen.getMeshCacheCount());
|
||||||
this.renderer.addDebugData(debug);
|
this.renderer.addDebugData(debug);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,16 +88,17 @@ public class GeometryManager {
|
|||||||
long ptr = UploadStream.INSTANCE.upload(this.sectionMetaBuffer, (long)SECTION_METADATA_SIZE * id, SECTION_METADATA_SIZE);
|
long ptr = UploadStream.INSTANCE.upload(this.sectionMetaBuffer, (long)SECTION_METADATA_SIZE * id, SECTION_METADATA_SIZE);
|
||||||
meta.writeMetadata(ptr);
|
meta.writeMetadata(ptr);
|
||||||
} else {
|
} else {
|
||||||
//Add to the end of the array
|
|
||||||
id = this.sectionCount++;
|
|
||||||
this.pos2id.put(result.position, id);
|
|
||||||
this.id2pos.add(result.position);
|
|
||||||
|
|
||||||
//Create the new meta
|
//Create the new meta
|
||||||
var meta = this.createMeta(result);
|
var meta = this.createMeta(result);
|
||||||
if (meta == null) {
|
if (meta == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Add to the end of the array
|
||||||
|
id = this.sectionCount++;
|
||||||
|
this.pos2id.put(result.position, id);
|
||||||
|
this.id2pos.add(result.position);
|
||||||
|
|
||||||
this.sectionMetadata.add(meta);
|
this.sectionMetadata.add(meta);
|
||||||
long ptr = UploadStream.INSTANCE.upload(this.sectionMetaBuffer, (long)SECTION_METADATA_SIZE * id, SECTION_METADATA_SIZE);
|
long ptr = UploadStream.INSTANCE.upload(this.sectionMetaBuffer, (long)SECTION_METADATA_SIZE * id, SECTION_METADATA_SIZE);
|
||||||
meta.writeMetadata(ptr);
|
meta.writeMetadata(ptr);
|
||||||
|
|||||||
@@ -150,6 +150,15 @@ public class RenderTracker {
|
|||||||
this.renderGen.unmarkCache(lvl, x, y, z);
|
this.renderGen.unmarkCache(lvl, x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void remove(int lvl, int x, int y, int z) {
|
||||||
|
this.remove(WorldEngine.getWorldSectionId(lvl, x, y, z));
|
||||||
|
this.renderer.enqueueResult(new BuiltSection(WorldEngine.getWorldSectionId(lvl, x, y, z)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(int lvl, int x, int y, int z) {
|
||||||
|
this.put(WorldEngine.getWorldSectionId(lvl, x, y, z));
|
||||||
|
this.renderGen.enqueueTask(lvl, x, y, z, this::shouldStillBuild);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//Called by the world engine when a section gets dirtied
|
//Called by the world engine when a section gets dirtied
|
||||||
|
|||||||
@@ -62,4 +62,8 @@ public class BuiltSectionMeshCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getCount() {
|
||||||
|
return this.renderCache.size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import java.util.function.ToIntFunction;
|
|||||||
|
|
||||||
//TODO: Add a render cache
|
//TODO: Add a render cache
|
||||||
public class RenderGenerationService {
|
public class RenderGenerationService {
|
||||||
|
|
||||||
public interface TaskChecker {boolean check(int lvl, int x, int y, int z);}
|
public interface TaskChecker {boolean check(int lvl, int x, int y, int z);}
|
||||||
private record BuildTask(Supplier<WorldSection> sectionSupplier) {}
|
private record BuildTask(Supplier<WorldSection> sectionSupplier) {}
|
||||||
|
|
||||||
@@ -82,6 +83,10 @@ public class RenderGenerationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getMeshCacheCount() {
|
||||||
|
return this.meshCache.getCount();
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: Add a priority system, higher detail sections must always be updated before lower detail
|
//TODO: Add a priority system, higher detail sections must always be updated before lower detail
|
||||||
// e.g. priorities NONE->lvl0 and lvl1 -> lvl0 over lvl0 -> lvl1
|
// e.g. priorities NONE->lvl0 and lvl1 -> lvl0 over lvl0 -> lvl1
|
||||||
|
|
||||||
@@ -102,6 +107,7 @@ public class RenderGenerationService {
|
|||||||
this.enqueueTask(lvl, x, y, z, (l,x1,y1,z1)->true);
|
this.enqueueTask(lvl, x, y, z, (l,x1,y1,z1)->true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void enqueueTask(int lvl, int x, int y, int z, TaskChecker checker) {
|
public void enqueueTask(int lvl, int x, int y, int z, TaskChecker checker) {
|
||||||
long ikey = WorldEngine.getWorldSectionId(lvl, x, y, z);
|
long ikey = WorldEngine.getWorldSectionId(lvl, x, y, z);
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
// holds a 32x32x32 region of detail
|
// holds a 32x32x32 region of detail
|
||||||
public final class WorldSection {
|
public final class WorldSection {
|
||||||
private static final int ARRAY_REUSE_CACHE_SIZE = 256;
|
private static final int ARRAY_REUSE_CACHE_SIZE = 256;
|
||||||
|
//TODO: maybe just swap this to a ConcurrentLinkedDeque
|
||||||
private static final Deque<long[]> ARRAY_REUSE_CACHE = new ArrayDeque<>(1024);
|
private static final Deque<long[]> ARRAY_REUSE_CACHE = new ArrayDeque<>(1024);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"voxy.config.general.maxSections": "Max Sections",
|
"voxy.config.general.maxSections": "Max Sections",
|
||||||
"voxy.config.general.maxSections.tooltip": "The max number of sections the renderer can contain",
|
"voxy.config.general.maxSections.tooltip": "The max number of sections the renderer can contain",
|
||||||
"voxy.config.general.renderDistance": "Render Distance",
|
"voxy.config.general.renderDistance": "Render Distance",
|
||||||
"voxy.config.general.renderDistance.tooltip": "The render distance in chunks",
|
"voxy.config.general.renderDistance.tooltip": "The render distance in chunks (set to -1 to disable chunk unloading)",
|
||||||
|
|
||||||
"voxy.config.threads.ingest": "Ingest",
|
"voxy.config.threads.ingest": "Ingest",
|
||||||
"voxy.config.threads.ingest.tooltip": "How many threads voxy will use for ingesting new chunks",
|
"voxy.config.threads.ingest.tooltip": "How many threads voxy will use for ingesting new chunks",
|
||||||
|
|||||||
Reference in New Issue
Block a user