World seperation, path config, faster importing/ingesting, fixed render error when client model id hadnt been computed, version bump

This commit is contained in:
mcrcortex
2024-02-12 22:14:54 +10:00
parent 99d821e1ec
commit 3096745982
15 changed files with 232 additions and 131 deletions

View File

@@ -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

View File

@@ -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() {

View File

@@ -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());
} }
} }

View File

@@ -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);
}
}

View File

@@ -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;
} }

View File

@@ -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,15 +57,25 @@ 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();
} }
} }
} }
}
//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

View File

@@ -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 {

View File

@@ -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;
} }

View File

@@ -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";
}
}

View File

@@ -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";
} }

View File

@@ -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);
}
}

View File

@@ -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);
} }
} }

View File

@@ -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);
block = state;
}
}
data[G(x, y, z)] = Mapper.composeMappingId(light, blockId, biomeId);
} }
} }
} }
} }
if (nonAir != 0) { }
{//Generate mipping }
}
return section;
}
private static int G(int x, int y, int z) {
return ((y<<8)|(z<<4)|x);
}
private static int H(int x, int y, int z) {
return ((y<<6)|(z<<3)|x) + 16*16*16;
}
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 //Mip L1
int i = 0; int i = 0;
for (int y = 0; y < 4; y += 2) { for (int y = 0; y < 16; y+=2) {
for (int z = 0; z < 4; z += 2) { for (int z = 0; z < 16; z += 2) {
for (int x = 0; x < 4; x += 2) { for (int x = 0; x < 16; x += 2) {
current[4 * 4 * 4 + i++] = Mipper.mip( data[16*16*16 + i++] =
current[I(x, y, z)], current[I(x+1, y, z)], current[I(x, y, z+1)], current[I(x+1, y, z+1)], Mipper.mip(
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)], data[G(x, y, z)], data[G(x+1, y, z)], data[G(x, y, z+1)], data[G(x+1, y, z+1)],
stateMapper); 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 L2 //Mip L2
section[I(ox, oy, oz)] = Mipper.mip( i = 0;
current[J(0,0,0)], current[J(1,0,0)], current[J(0,0,1)], current[J(1,0,1)], for (int y = 0; y < 8; y+=2) {
current[J(0,1,0)], current[J(1,1,0)], current[J(0,1,1)], current[J(1,1,1)], for (int z = 0; z < 8; z += 2) {
stateMapper); for (int x = 0; x < 8; x += 2) {
} data[16*16*16 + 8*8*8 + i++] =
Mipper.mip(
//Update existence mask data[H(x, y, z)], data[H(x+1, y, z)], data[H(x, y, z+1)], data[H(x+1, y, z+1)],
msk |= 1L<<I(ox, oy, oz); 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)],
subSections[I(ox, oy, oz)] = current; mapper);
current = new long[4*4*4+2*2*2+1];
}
} }
} }
} }
{//Generate mipping
//Mip L3 //Mip L3
int i = 0; i = 0;
for (int y = 0; y < 4; y+=2) { for (int y = 0; y < 4; y+=2) {
for (int z = 0; z < 4; z += 2) { for (int z = 0; z < 4; z += 2) {
for (int x = 0; x < 4; x += 2) { for (int x = 0; x < 4; x += 2) {
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)], data[16*16*16 + 8*8*8 + 4*4*4 + i++] =
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)], Mipper.mip(
stateMapper); 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
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 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);
} }
} }

View File

@@ -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));

View File

@@ -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);
} }
} }