World seperation, path config, faster importing/ingesting, fixed render error when client model id hadnt been computed, version bump
This commit is contained in:
@@ -8,7 +8,7 @@ yarn_mappings=1.20.4+build.1
|
|||||||
loader_version=0.15.0
|
loader_version=0.15.0
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version = 0.0.6-alpha
|
mod_version = 0.0.7-alpha
|
||||||
maven_group = me.cortex
|
maven_group = me.cortex
|
||||||
archives_base_name = voxy
|
archives_base_name = voxy
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ public class VoxyConfig {
|
|||||||
public int ingestThreads = 5;
|
public int ingestThreads = 5;
|
||||||
public int savingThreads = 10;
|
public int savingThreads = 10;
|
||||||
public int renderThreads = 5;
|
public int renderThreads = 5;
|
||||||
public int savingCompressionLevel = 7;
|
|
||||||
public String storagePath = "voxy_db";
|
|
||||||
|
|
||||||
|
|
||||||
public static VoxyConfig loadOrCreate() {
|
public static VoxyConfig loadOrCreate() {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class VoxyConfigScreenFactory implements ModMenuApi {
|
|||||||
VoxyConfig.CONFIG.save();
|
VoxyConfig.CONFIG.save();
|
||||||
});
|
});
|
||||||
|
|
||||||
return ClothConfigDemo.getConfigBuilderWithDemo().build();
|
return builder.build();//ClothConfigDemo.getConfigBuilderWithDemo().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addGeneralCategory(ConfigBuilder builder, VoxyConfig config) {
|
private static void addGeneralCategory(ConfigBuilder builder, VoxyConfig config) {
|
||||||
@@ -84,11 +84,11 @@ public class VoxyConfigScreenFactory implements ModMenuApi {
|
|||||||
.setDefaultValue(DEFAULT.maxSections)
|
.setDefaultValue(DEFAULT.maxSections)
|
||||||
.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"))
|
||||||
.setSaveConsumer(val -> config.savingCompressionLevel = val)
|
// .setSaveConsumer(val -> config.savingCompressionLevel = val)
|
||||||
.setDefaultValue(DEFAULT.savingCompressionLevel)
|
// .setDefaultValue(DEFAULT.savingCompressionLevel)
|
||||||
.build());
|
// .build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addThreadsCategory(ConfigBuilder builder, VoxyConfig config) {
|
private static void addThreadsCategory(ConfigBuilder builder, VoxyConfig config) {
|
||||||
@@ -118,12 +118,12 @@ public class VoxyConfigScreenFactory implements ModMenuApi {
|
|||||||
ConfigCategory category = builder.getOrCreateCategory(Text.translatable("voxy.config.storage"));
|
ConfigCategory category = builder.getOrCreateCategory(Text.translatable("voxy.config.storage"));
|
||||||
ConfigEntryBuilder entryBuilder = builder.entryBuilder();
|
ConfigEntryBuilder entryBuilder = builder.entryBuilder();
|
||||||
|
|
||||||
//Temporary until i figure out how to do more complex multi layer configuration for storage
|
////Temporary until i figure out how to do more complex multi layer configuration for storage
|
||||||
category.addEntry(entryBuilder.startStrField(Text.translatable("voxy.config.storage.path"), config.storagePath)
|
//category.addEntry(entryBuilder.startStrField(Text.translatable("voxy.config.storage.path"), config.storagePath)
|
||||||
.setTooltip(Text.translatable("voxy.config.storage.path.tooltip"))
|
// .setTooltip(Text.translatable("voxy.config.storage.path.tooltip"))
|
||||||
.setSaveConsumer(val -> config.storagePath = val)
|
// .setSaveConsumer(val -> config.storagePath = val)
|
||||||
.setDefaultValue(DEFAULT.storagePath)
|
// .setDefaultValue(DEFAULT.storagePath)
|
||||||
.build());
|
// .build());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package me.cortex.voxy.client.core.model;
|
||||||
|
|
||||||
|
public class IdNotYetComputedException extends RuntimeException {
|
||||||
|
public IdNotYetComputedException(int id) {
|
||||||
|
super("Id not yet computed: " + id);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -523,7 +523,7 @@ public class ModelManager {
|
|||||||
map = this.idMappings[blockId];
|
map = this.idMappings[blockId];
|
||||||
}
|
}
|
||||||
if (map == -1) {
|
if (map == -1) {
|
||||||
throw new IllegalArgumentException("Id hasnt been computed yet: " + blockId);
|
throw new IdNotYetComputedException(blockId);
|
||||||
}
|
}
|
||||||
return this.metadataCache[map];
|
return this.metadataCache[map];
|
||||||
//int map = 0;
|
//int map = 0;
|
||||||
@@ -541,7 +541,7 @@ public class ModelManager {
|
|||||||
public int getModelId(int blockId) {
|
public int getModelId(int blockId) {
|
||||||
int map = this.idMappings[blockId];
|
int map = this.idMappings[blockId];
|
||||||
if (map == -1) {
|
if (map == -1) {
|
||||||
throw new IllegalArgumentException("Id hasnt been computed yet: " + blockId);
|
throw new IdNotYetComputedException(blockId);
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package me.cortex.voxy.client.core.rendering.building;
|
package me.cortex.voxy.client.core.rendering.building;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
|
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.ModelManager;
|
||||||
import me.cortex.voxy.common.world.WorldEngine;
|
import me.cortex.voxy.common.world.WorldEngine;
|
||||||
import me.cortex.voxy.common.world.WorldSection;
|
import me.cortex.voxy.common.world.WorldSection;
|
||||||
@@ -56,12 +57,22 @@ public class RenderGenerationService {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
section.assertNotFree();
|
section.assertNotFree();
|
||||||
var mesh = factory.generateMesh(section);
|
BuiltSection mesh = null;
|
||||||
|
try {
|
||||||
|
mesh = factory.generateMesh(section);
|
||||||
|
} catch (IdNotYetComputedException e) {
|
||||||
|
//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;});
|
||||||
|
}
|
||||||
|
}
|
||||||
section.release();
|
section.release();
|
||||||
|
if (mesh != null) {
|
||||||
this.resultConsumer.accept(mesh.clone());
|
this.resultConsumer.accept(mesh.clone());
|
||||||
if (!this.meshCache.putMesh(mesh)) {
|
if (!this.meshCache.putMesh(mesh)) {
|
||||||
mesh.free();
|
mesh.free();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,15 @@ import me.cortex.voxy.client.core.util.ByteBufferBackedInputStream;
|
|||||||
import me.cortex.voxy.common.voxelization.VoxelizedSection;
|
import me.cortex.voxy.common.voxelization.VoxelizedSection;
|
||||||
import me.cortex.voxy.common.voxelization.WorldConversionFactory;
|
import me.cortex.voxy.common.voxelization.WorldConversionFactory;
|
||||||
import me.cortex.voxy.common.world.WorldEngine;
|
import me.cortex.voxy.common.world.WorldEngine;
|
||||||
|
import me.cortex.voxy.common.world.other.Mipper;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.nbt.*;
|
import net.minecraft.nbt.*;
|
||||||
|
import net.minecraft.network.PacketByteBuf;
|
||||||
import net.minecraft.registry.RegistryKeys;
|
import net.minecraft.registry.RegistryKeys;
|
||||||
import net.minecraft.registry.entry.RegistryEntry;
|
import net.minecraft.registry.entry.RegistryEntry;
|
||||||
|
import net.minecraft.util.collection.IndexedIterable;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import net.minecraft.world.biome.Biome;
|
import net.minecraft.world.biome.Biome;
|
||||||
import net.minecraft.world.biome.BiomeKeys;
|
import net.minecraft.world.biome.BiomeKeys;
|
||||||
@@ -28,14 +31,16 @@ import java.nio.file.StandardOpenOption;
|
|||||||
import java.util.concurrent.ForkJoinPool;
|
import java.util.concurrent.ForkJoinPool;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
public class WorldImporter {
|
public class WorldImporter {
|
||||||
public record ImportUpdate(){}
|
public record ImportUpdate(){}
|
||||||
|
|
||||||
private final WorldEngine world;
|
private final WorldEngine world;
|
||||||
private final World mcWorld;
|
private final World mcWorld;
|
||||||
private final RegistryEntry<Biome> defaultBiome;
|
private final ReadableContainer<RegistryEntry<Biome>> defaultBiomeProvider;
|
||||||
private final Codec<ReadableContainer<RegistryEntry<Biome>>> biomeCodec;
|
private final Codec<ReadableContainer<RegistryEntry<Biome>>> biomeCodec;
|
||||||
private final AtomicInteger totalRegions = new AtomicInteger();
|
private final AtomicInteger totalRegions = new AtomicInteger();
|
||||||
private final AtomicInteger regionsProcessed = new AtomicInteger();
|
private final AtomicInteger regionsProcessed = new AtomicInteger();
|
||||||
@@ -46,7 +51,49 @@ public class WorldImporter {
|
|||||||
this.mcWorld = mcWorld;
|
this.mcWorld = mcWorld;
|
||||||
|
|
||||||
var biomeRegistry = mcWorld.getRegistryManager().get(RegistryKeys.BIOME);
|
var biomeRegistry = mcWorld.getRegistryManager().get(RegistryKeys.BIOME);
|
||||||
this.defaultBiome = biomeRegistry.entryOf(BiomeKeys.PLAINS);
|
var defaultBiome = biomeRegistry.entryOf(BiomeKeys.PLAINS);
|
||||||
|
this.defaultBiomeProvider = new ReadableContainer<RegistryEntry<Biome>>() {
|
||||||
|
@Override
|
||||||
|
public RegistryEntry<Biome> get(int x, int y, int z) {
|
||||||
|
return defaultBiome;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void forEachValue(Consumer<RegistryEntry<Biome>> action) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writePacket(PacketByteBuf buf) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPacketSize() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAny(Predicate<RegistryEntry<Biome>> predicate) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void count(PalettedContainer.Counter<RegistryEntry<Biome>> counter) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PalettedContainer<RegistryEntry<Biome>> slice() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Serialized<RegistryEntry<Biome>> serialize(IndexedIterable<RegistryEntry<Biome>> idList, PalettedContainer.PaletteProvider paletteProvider) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
this.biomeCodec = PalettedContainer.createReadableContainerCodec(biomeRegistry.getIndexedEntries(), biomeRegistry.createEntryCodec(), PalettedContainer.PaletteProvider.BIOME, biomeRegistry.entryOf(BiomeKeys.PLAINS));
|
this.biomeCodec = PalettedContainer.createReadableContainerCodec(biomeRegistry.getIndexedEntries(), biomeRegistry.createEntryCodec(), PalettedContainer.PaletteProvider.BIOME, biomeRegistry.entryOf(BiomeKeys.PLAINS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +256,7 @@ public class WorldImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var blockStates = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, section.getCompound("block_states")).result().get();
|
var blockStates = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, section.getCompound("block_states")).result().get();
|
||||||
var biomes = this.biomeCodec.parse(NbtOps.INSTANCE, section.getCompound("biomes")).result().orElse(null);
|
var biomes = this.biomeCodec.parse(NbtOps.INSTANCE, section.getCompound("biomes")).result().orElse(this.defaultBiomeProvider);
|
||||||
VoxelizedSection csec = WorldConversionFactory.convert(
|
VoxelizedSection csec = WorldConversionFactory.convert(
|
||||||
this.world.getMapper(),
|
this.world.getMapper(),
|
||||||
blockStates,
|
blockStates,
|
||||||
@@ -228,10 +275,11 @@ public class WorldImporter {
|
|||||||
},
|
},
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
z,
|
z
|
||||||
this.defaultBiome
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
WorldConversionFactory.mipSection(csec, this.world.getMapper());
|
||||||
|
|
||||||
this.world.insertUpdate(csec);
|
this.world.insertUpdate(csec);
|
||||||
while (this.world.savingService.getTaskCount() > 4000) {
|
while (this.world.savingService.getTaskCount() > 4000) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -14,14 +14,17 @@ import java.io.File;
|
|||||||
|
|
||||||
public class WorldImportCommand {
|
public class WorldImportCommand {
|
||||||
public static LiteralArgumentBuilder<FabricClientCommandSource> register() {
|
public static LiteralArgumentBuilder<FabricClientCommandSource> register() {
|
||||||
return ClientCommandManager.literal("voxy").then(ClientCommandManager.literal("import").then(ClientCommandManager.literal("world").then(ClientCommandManager.argument("world_name", StringArgumentType.string()).executes(WorldImportCommand::importWorld))));
|
return ClientCommandManager.literal("voxy").then(
|
||||||
|
ClientCommandManager.literal("import")
|
||||||
|
.then(ClientCommandManager.literal("world")
|
||||||
|
.then(ClientCommandManager.argument("world_name", StringArgumentType.string()).executes(WorldImportCommand::importWorld))));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static WorldImporter importerInstance;
|
public static WorldImporter importerInstance;
|
||||||
|
|
||||||
private static int importWorld(CommandContext<FabricClientCommandSource> ctx) {
|
private static int importWorld(CommandContext<FabricClientCommandSource> ctx) {
|
||||||
var instance = MinecraftClient.getInstance();
|
var instance = MinecraftClient.getInstance();
|
||||||
var file = new File(ctx.getArgument("world_name", String.class));
|
var file = new File("saves").toPath().resolve(ctx.getArgument("world_name", String.class)).resolve("region").toFile();
|
||||||
importerInstance = ((IGetVoxelCore)instance.worldRenderer).getVoxelCore().createWorldImporter(MinecraftClient.getInstance().player.clientWorld, file);
|
importerInstance = ((IGetVoxelCore)instance.worldRenderer).getVoxelCore().createWorldImporter(MinecraftClient.getInstance().player.clientWorld, file);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package me.cortex.voxy.common.storage.other;
|
||||||
|
|
||||||
|
import me.cortex.voxy.common.storage.StorageBackend;
|
||||||
|
import me.cortex.voxy.common.storage.config.ConfigBuildCtx;
|
||||||
|
|
||||||
|
//Very simple config that adds a path to the config builder
|
||||||
|
public class BasicPathInsertionConfig extends DelegateStorageConfig {
|
||||||
|
public String path = "";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StorageBackend build(ConfigBuildCtx ctx) {
|
||||||
|
ctx.pushPath(this.path);
|
||||||
|
var storage = this.delegate.build(ctx);
|
||||||
|
ctx.popPath();
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getConfigTypeName() {
|
||||||
|
return "BasicPathConfig";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -43,20 +43,14 @@ public class CompressionStorageAdaptor extends DelegatingStorageAdaptor {
|
|||||||
super.close();
|
super.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Config extends StorageConfig {
|
public static class Config extends DelegateStorageConfig {
|
||||||
public CompressorConfig compressor;
|
public CompressorConfig compressor;
|
||||||
public StorageConfig delegate;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StorageBackend build(ConfigBuildCtx ctx) {
|
public StorageBackend build(ConfigBuildCtx ctx) {
|
||||||
return new CompressionStorageAdaptor(this.compressor.build(ctx), this.delegate.build(ctx));
|
return new CompressionStorageAdaptor(this.compressor.build(ctx), this.delegate.build(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<StorageConfig> getChildStorageConfigs() {
|
|
||||||
return List.of(this.delegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getConfigTypeName() {
|
public static String getConfigTypeName() {
|
||||||
return "CompressionAdaptor";
|
return "CompressionAdaptor";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package me.cortex.voxy.common.storage.other;
|
||||||
|
|
||||||
|
import me.cortex.voxy.common.storage.config.StorageConfig;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class DelegateStorageConfig extends StorageConfig {
|
||||||
|
public StorageConfig delegate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<StorageConfig> getChildStorageConfigs() {
|
||||||
|
return List.of(this.delegate);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,13 +8,9 @@ public class VoxelizedSection {
|
|||||||
public final int x;
|
public final int x;
|
||||||
public final int y;
|
public final int y;
|
||||||
public final int z;
|
public final int z;
|
||||||
private final long[] section;
|
final long[] section;
|
||||||
private final long populationMsk;
|
public VoxelizedSection(long[] section, int x, int y, int z) {
|
||||||
private final long[][] subSections;
|
|
||||||
public VoxelizedSection(long[] section, long populationMsk, long[][] subSections, int x, int y, int z) {
|
|
||||||
this.section = section;
|
this.section = section;
|
||||||
this.populationMsk = populationMsk;
|
|
||||||
this.subSections = subSections;
|
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.z = z;
|
this.z = z;
|
||||||
@@ -29,31 +25,14 @@ public class VoxelizedSection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long get(int lvl, int x, int y, int z) {
|
public long get(int lvl, int x, int y, int z) {
|
||||||
if (lvl < 2) {
|
int offset = lvl==1?(1<<12):0;
|
||||||
int subIdx = getIdx(x,y,z,2-lvl,2);
|
offset |= lvl==2?(1<<12)|(1<<9):0;
|
||||||
var subSec = this.subSections[subIdx];
|
offset |= lvl==3?(1<<12)|(1<<9)|(1<<6):0;
|
||||||
if (subSec == null) {
|
offset |= lvl==4?(1<<12)|(1<<9)|(1<<6)|(1<<3):0;
|
||||||
return Mapper.AIR;
|
return this.section[getIdx(x, y, z, 0, 4-lvl) + offset];
|
||||||
}
|
|
||||||
|
|
||||||
if (lvl == 0) {
|
|
||||||
return subSec[getIdx(x,y,z,0,2)];
|
|
||||||
} else if (lvl == 1) {
|
|
||||||
return subSec[4*4*4+getIdx(x,y,z,0,1)];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (lvl == 2) {
|
|
||||||
return section[getIdx(x,y,z,0,2)];
|
|
||||||
} else if (lvl == 3) {
|
|
||||||
return section[4*4*4+getIdx(x,y,z,0,1)];
|
|
||||||
} else if (lvl == 4) {
|
|
||||||
return section[4*4*4+2*2*2];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Mapper.UNKNOWN_MAPPING;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static VoxelizedSection createEmpty(int x, int y, int z) {
|
public static VoxelizedSection createEmpty(int x, int y, int z) {
|
||||||
return new VoxelizedSection(new long[4*4*4+2*2*2+1], 0, new long[4*4*4][], x, y, z);
|
return new VoxelizedSection(new long[16*16*16 + 8*8*8 + 4*4*4 + 2*2*2 + 1], x, y, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package me.cortex.voxy.common.voxelization;
|
package me.cortex.voxy.common.voxelization;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
|
||||||
import me.cortex.voxy.common.world.other.Mipper;
|
import me.cortex.voxy.common.world.other.Mipper;
|
||||||
import me.cortex.voxy.common.world.other.Mapper;
|
import me.cortex.voxy.common.world.other.Mapper;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
@@ -9,14 +10,7 @@ import net.minecraft.world.chunk.PalettedContainer;
|
|||||||
import net.minecraft.world.chunk.ReadableContainer;
|
import net.minecraft.world.chunk.ReadableContainer;
|
||||||
|
|
||||||
public class WorldConversionFactory {
|
public class WorldConversionFactory {
|
||||||
|
private static final ThreadLocal<Reference2IntOpenHashMap<BlockState>> BLOCK_CACHE = ThreadLocal.withInitial(Reference2IntOpenHashMap::new);
|
||||||
private static int I(int x, int y, int z) {
|
|
||||||
return (y<<4)|(z<<2)|x;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int J(int x, int y, int z) {
|
|
||||||
return ((y<<2)|(z<<1)|x) + 4*4*4;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: add a local mapper cache since it should be smaller and faster
|
//TODO: add a local mapper cache since it should be smaller and faster
|
||||||
public static VoxelizedSection convert(Mapper stateMapper,
|
public static VoxelizedSection convert(Mapper stateMapper,
|
||||||
@@ -25,20 +19,20 @@ public class WorldConversionFactory {
|
|||||||
ILightingSupplier lightSupplier,
|
ILightingSupplier lightSupplier,
|
||||||
int sx,
|
int sx,
|
||||||
int sy,
|
int sy,
|
||||||
int sz,
|
int sz) {
|
||||||
RegistryEntry<Biome> defaultBiome) {
|
var blockCache = BLOCK_CACHE.get();
|
||||||
long[] section = new long[4*4*4+2*2*2+1];//Mipping
|
|
||||||
long[][] subSections = new long[4*4*4][];
|
var section = VoxelizedSection.createEmpty(sx, sy, sz);
|
||||||
long[] current = new long[4*4*4+2*2*2];
|
var data = section.section;
|
||||||
long msk = 0;
|
|
||||||
|
int blockId = -1;
|
||||||
|
BlockState block = null;
|
||||||
|
|
||||||
for (int oy = 0; oy < 4; oy++) {
|
for (int oy = 0; oy < 4; oy++) {
|
||||||
for (int oz = 0; oz < 4; oz++) {
|
for (int oz = 0; oz < 4; oz++) {
|
||||||
for (int ox = 0; ox < 4; ox++) {
|
for (int ox = 0; ox < 4; ox++) {
|
||||||
RegistryEntry<Biome> biome = defaultBiome;
|
int biomeId = stateMapper.getIdForBiome(biomeContainer.get(ox, oy, oz));
|
||||||
if (biomeContainer != null) {
|
|
||||||
biome = biomeContainer.get(ox, oy, oz);
|
|
||||||
}
|
|
||||||
int nonAir = 0;
|
|
||||||
for (int iy = 0; iy < 4; iy++) {
|
for (int iy = 0; iy < 4; iy++) {
|
||||||
for (int iz = 0; iz < 4; iz++) {
|
for (int iz = 0; iz < 4; iz++) {
|
||||||
for (int ix = 0; ix < 4; ix++) {
|
for (int ix = 0; ix < 4; ix++) {
|
||||||
@@ -47,61 +41,94 @@ public class WorldConversionFactory {
|
|||||||
int z = (oz<<2)|iz;
|
int z = (oz<<2)|iz;
|
||||||
var state = blockContainer.get(x, y, z);
|
var state = blockContainer.get(x, y, z);
|
||||||
byte light = lightSupplier.supply(x,y,z,state);
|
byte light = lightSupplier.supply(x,y,z,state);
|
||||||
if (!(state.isAir() && (light==0))) {//TODO:FIXME:optimize this in such a way that having skylight access/no skylight means that an entire section is created, WHICH IS VERY BAD FOR PERFORMANCE!!!!
|
if (!(state.isAir() && (light==0))) {
|
||||||
nonAir++;
|
if (block != state) {
|
||||||
current[I(ix, iy, iz)] = stateMapper.getBaseId(light, state, biome);
|
if (state.isAir()) {
|
||||||
}
|
block = state;
|
||||||
}
|
blockId = 0;
|
||||||
}
|
} else {
|
||||||
}
|
blockId = blockCache.computeIfAbsent(state, stateMapper::getIdForBlockState);
|
||||||
if (nonAir != 0) {
|
block = state;
|
||||||
{//Generate mipping
|
}
|
||||||
//Mip L1
|
|
||||||
int i = 0;
|
|
||||||
for (int y = 0; y < 4; y += 2) {
|
|
||||||
for (int z = 0; z < 4; z += 2) {
|
|
||||||
for (int x = 0; x < 4; x += 2) {
|
|
||||||
current[4 * 4 * 4 + i++] = Mipper.mip(
|
|
||||||
current[I(x, y, z)], current[I(x+1, y, z)], current[I(x, y, z+1)], current[I(x+1, y, z+1)],
|
|
||||||
current[I(x, y+1, z)], current[I(x+1, y+1, z)], current[I(x, y+1, z+1)], current[I(x+1, y+1, z+1)],
|
|
||||||
stateMapper);
|
|
||||||
}
|
}
|
||||||
|
data[G(x, y, z)] = Mapper.composeMappingId(light, blockId, biomeId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Mip L2
|
|
||||||
section[I(ox, oy, oz)] = Mipper.mip(
|
|
||||||
current[J(0,0,0)], current[J(1,0,0)], current[J(0,0,1)], current[J(1,0,1)],
|
|
||||||
current[J(0,1,0)], current[J(1,1,0)], current[J(0,1,1)], current[J(1,1,1)],
|
|
||||||
stateMapper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update existence mask
|
|
||||||
msk |= 1L<<I(ox, oy, oz);
|
|
||||||
subSections[I(ox, oy, oz)] = current;
|
|
||||||
current = new long[4*4*4+2*2*2+1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return section;
|
||||||
|
}
|
||||||
|
|
||||||
{//Generate mipping
|
private static int G(int x, int y, int z) {
|
||||||
//Mip L3
|
return ((y<<8)|(z<<4)|x);
|
||||||
int i = 0;
|
}
|
||||||
for (int y = 0; y < 4; y+=2) {
|
|
||||||
for (int z = 0; z < 4; z += 2) {
|
private static int H(int x, int y, int z) {
|
||||||
for (int x = 0; x < 4; x += 2) {
|
return ((y<<6)|(z<<3)|x) + 16*16*16;
|
||||||
section[4 * 4 * 4 + i++] = Mipper.mip(section[I(x, y, z)], section[I(x+1, y, z)], section[I(x, y, z+1)], section[I(x+1, y, z+1)],
|
}
|
||||||
section[I(x, y+1, z)], section[I(x+1, y+1, z)], section[I(x, y+1, z+1)], section[I(x+1, y+1, z+1)],
|
|
||||||
stateMapper);
|
private static int I(int x, int y, int z) {
|
||||||
}
|
return ((y<<4)|(z<<2)|x) + 8*8*8 + 16*16*16;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int J(int x, int y, int z) {
|
||||||
|
return ((y<<2)|(z<<1)|x) + 4*4*4 + 8*8*8 + 16*16*16;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Instead of this mip section as we are updating the data in the world
|
||||||
|
public static void mipSection(VoxelizedSection section, Mapper mapper) {
|
||||||
|
var data = section.section;
|
||||||
|
|
||||||
|
//Mip L1
|
||||||
|
int i = 0;
|
||||||
|
for (int y = 0; y < 16; y+=2) {
|
||||||
|
for (int z = 0; z < 16; z += 2) {
|
||||||
|
for (int x = 0; x < 16; x += 2) {
|
||||||
|
data[16*16*16 + i++] =
|
||||||
|
Mipper.mip(
|
||||||
|
data[G(x, y, z)], data[G(x+1, y, z)], data[G(x, y, z+1)], data[G(x+1, y, z+1)],
|
||||||
|
data[G(x, y+1, z)], data[G(x+1, y+1, z)], data[G(x, y+1, z+1)], data[G(x+1, y+1, z+1)],
|
||||||
|
mapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Mip L4
|
|
||||||
section[4*4*4+2*2*2] = Mipper.mip(section[J(0, 0, 0)], section[J(1, 0, 0)], section[J(0, 0, 1)], section[J(1, 0, 1)],
|
|
||||||
section[J(0, 1, 0)], section[J(1, 1, 0)], section[J(0, 1, 1)], section[J(1, 1, 1)],
|
|
||||||
stateMapper);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new VoxelizedSection(section, msk, subSections, sx, sy, sz);
|
//Mip L2
|
||||||
|
i = 0;
|
||||||
|
for (int y = 0; y < 8; y+=2) {
|
||||||
|
for (int z = 0; z < 8; z += 2) {
|
||||||
|
for (int x = 0; x < 8; x += 2) {
|
||||||
|
data[16*16*16 + 8*8*8 + i++] =
|
||||||
|
Mipper.mip(
|
||||||
|
data[H(x, y, z)], data[H(x+1, y, z)], data[H(x, y, z+1)], data[H(x+1, y, z+1)],
|
||||||
|
data[H(x, y+1, z)], data[H(x+1, y+1, z)], data[H(x, y+1, z+1)], data[H(x+1, y+1, z+1)],
|
||||||
|
mapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Mip L3
|
||||||
|
i = 0;
|
||||||
|
for (int y = 0; y < 4; y+=2) {
|
||||||
|
for (int z = 0; z < 4; z += 2) {
|
||||||
|
for (int x = 0; x < 4; x += 2) {
|
||||||
|
data[16*16*16 + 8*8*8 + 4*4*4 + i++] =
|
||||||
|
Mipper.mip(
|
||||||
|
data[I(x, y, z)], data[I(x+1, y, z)], data[I(x, y, z+1)], data[I(x+1, y, z+1)],
|
||||||
|
data[I(x, y+1, z)], data[I(x+1, y+1, z)], data[I(x, y+1, z+1)], data[I(x+1, y+1, z+1)],
|
||||||
|
mapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Mip L4
|
||||||
|
data[16*16*16 + 8*8*8 + 4*4*4 + 2*2*2] =
|
||||||
|
Mipper.mip(
|
||||||
|
data[J(0, 0, 0)], data[J(1, 0, 0)], data[J(0, 0, 1)], data[J(1, 0, 1)],
|
||||||
|
data[J(0, 1, 0)], data[J(1, 1, 0)], data[J(0, 1, 1)], data[J(1, 1, 1)],
|
||||||
|
mapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ public class WorldEngine {
|
|||||||
|
|
||||||
//TODO: move this to auxilery class so that it can take into account larger than 4 mip levels
|
//TODO: move this to auxilery class so that it can take into account larger than 4 mip levels
|
||||||
//Executes an update to the world and automatically updates all the parent mip layers up to level 4 (e.g. where 1 chunk section is 1 block big)
|
//Executes an update to the world and automatically updates all the parent mip layers up to level 4 (e.g. where 1 chunk section is 1 block big)
|
||||||
public void insertUpdate(VoxelizedSection section) {
|
public void insertUpdate(VoxelizedSection section) {//TODO: add a bitset of levels to update and if it should force update
|
||||||
//The >>1 is cause the world sections size is 32x32x32 vs the 16x16x16 of the voxelized section
|
//The >>1 is cause the world sections size is 32x32x32 vs the 16x16x16 of the voxelized section
|
||||||
for (int lvl = 0; lvl < this.maxMipLevels; lvl++) {
|
for (int lvl = 0; lvl < this.maxMipLevels; lvl++) {
|
||||||
var worldSection = this.acquire(lvl, section.x >> (lvl + 1), section.y >> (lvl + 1), section.z >> (lvl + 1));
|
var worldSection = this.acquire(lvl, section.x >> (lvl + 1), section.y >> (lvl + 1), section.z >> (lvl + 1));
|
||||||
|
|||||||
@@ -69,10 +69,9 @@ public class VoxelIngestService {
|
|||||||
},
|
},
|
||||||
chunk.getPos().x,
|
chunk.getPos().x,
|
||||||
i,
|
i,
|
||||||
chunk.getPos().z,
|
chunk.getPos().z
|
||||||
null
|
|
||||||
);
|
);
|
||||||
|
WorldConversionFactory.mipSection(csec, this.world.getMapper());
|
||||||
this.world.insertUpdate(csec);
|
this.world.insertUpdate(csec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user