This commit is contained in:
mcrcortex
2023-12-27 11:23:13 +10:00
parent 6ef2902e0c
commit 8d7fc753b8
8 changed files with 244 additions and 80 deletions

View File

@@ -57,6 +57,6 @@ public class Test {
//WorldEngine engine = new WorldEngine(new File("storagefile2.db"), 5);
WorldImporter importer = new WorldImporter(null, null);
//importer.importWorld(new File("run/saves/Drehmal 2.2 Apotheosis Beta - 1.0.0/region/"));
importer.importWorldAsyncStart(new File("D:\\PrismLauncher-Windows-MSVC-Portable-7.1\\instances\\1.20.1(3)\\.minecraft\\.bobby\\build.docm77.de\\-8149132374211427218\\minecraft\\overworld\\"));
importer.importWorldAsyncStart(new File("D:\\PrismLauncher-Windows-MSVC-Portable-7.1\\instances\\1.20.1(3)\\.minecraft\\.bobby\\build.docm77.de\\-8149132374211427218\\minecraft\\overworld\\"), 2,null, null);
}
}

View File

@@ -1,6 +1,7 @@
package me.cortex.voxelmon;
import me.cortex.voxelmon.terrain.TestSparseGenCommand;
//import me.cortex.voxelmon.terrain.WorldImportCommand;
import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
@@ -8,6 +9,6 @@ import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
public class Voxelmon implements ClientModInitializer {
@Override
public void onInitializeClient() {
//CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> TestSparseGenCommand.register(dispatcher));
//CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> WorldImportCommand.register(dispatcher));
}
}

View File

@@ -22,6 +22,7 @@ import net.minecraft.client.render.Frustum;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import net.minecraft.world.chunk.WorldChunk;
import org.lwjgl.system.MemoryUtil;
@@ -72,7 +73,7 @@ public class VoxelCore {
//Trigger the shared index buffer loading
SharedIndexBuffer.INSTANCE.id();
this.renderer = new Gl46FarWorldRenderer();
this.world = new WorldEngine(new File("storagefile2.db"), 20, 5);//"storagefile.db"//"ethoslab.db"
this.world = new WorldEngine(new File("storagefile.db"), 20, 5);//"storagefile.db"//"ethoslab.db"
this.renderTracker = new RenderTracker(this.world, this.renderer);
this.renderGen = new RenderGenerationService(this.world,4, this.renderTracker::processBuildResult);
@@ -196,4 +197,12 @@ public class VoxelCore {
try {this.renderer.shutdown();} catch (Exception e) {System.err.println(e);}
try {this.postProcessing.shutdown();} catch (Exception e) {System.err.println(e);}
}
public WorldImporter createWorldImporter(World mcWorld, File worldPath) {
var importer = new WorldImporter(this.world, mcWorld);
importer.importWorldAsyncStart(worldPath, 10, null, ()->{
System.err.println("DONE IMPORT");
});
return importer;
}
}

View File

@@ -3,6 +3,7 @@ package me.cortex.voxelmon.core.rendering.building;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import me.cortex.voxelmon.core.util.MemoryBuffer;
import me.cortex.voxelmon.core.util.Mesher2D;
import me.cortex.voxelmon.core.util.Mesher2Dv2;
import me.cortex.voxelmon.core.world.WorldEngine;
import me.cortex.voxelmon.core.world.WorldSection;
import me.cortex.voxelmon.core.world.other.Mapper;
@@ -11,7 +12,7 @@ import org.lwjgl.system.MemoryUtil;
public class RenderDataFactory {
private final Mesher2D mesher = new Mesher2D(5,15);//15
private final Mesher2Dv2 mesher = new Mesher2Dv2(5,15);//15
private final LongArrayList outData = new LongArrayList(1000);
private final WorldEngine world;
public RenderDataFactory(WorldEngine world) {

View File

@@ -0,0 +1,175 @@
package me.cortex.voxelmon.core.util;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Random;
public class Mesher2Dv2 {
private final int size;
private final int maxSize;
private final long[] data;
private final BitSet setset;
private int[] quadCache;
public Mesher2Dv2(int sizeBits, int maxSize) {
this.size = sizeBits;
this.maxSize = maxSize;
this.data = new long[1<<(sizeBits<<1)];
this.setset = new BitSet(1<<(sizeBits<<1));
this.quadCache = new int[128];
}
private int getIdx(int x, int z) {
int M = (1<<this.size)-1;
if (x>M || z>M) {
throw new IllegalStateException();
}
return ((z&M)<<this.size)|(x&M);
}
public Mesher2Dv2 put(int x, int z, long data) {
int idx = this.getIdx(x, z);
this.data[idx] = data;
this.setset.set(idx);
return this;
}
public static int getX(int data) {
return (data>>24)&0xFF;
}
public static int getZ(int data) {
return (data>>16)&0xFF;
}
public static int getH(int data) {
return (data>>8)&0xFF;
}
public static int getW(int data) {
return data&0xFF;
}
//
private static int encodeQuad(int x, int z, int sx, int sz) {
return ((x&0xFF)<<24)|((z&0xFF)<<16)|((sx&0xFF)<<8)|((sz&0xFF)<<0);
}
private boolean canMerge(int x, int z, long match) {
int id = this.getIdx(x, z);
return this.setset.get(id) && this.data[id] == match;
}
public int[] process() {
int[] quads = this.quadCache;
int idxCount = 0;
//TODO: add different strategies/ways to mesh
int posId = this.data[0] == 0?this.setset.nextSetBit(0):0;
while (posId < this.data.length && posId != -1) {
int idx = posId;
long data = this.data[idx];
int M = (1<<this.size)-1;
int x = idx&M;
int z = (idx>>>this.size)&M;
boolean ex = x != ((1<<this.size)-1);
boolean ez = z != ((1<<this.size)-1);
int endX = x;
int endZ = z;
while (ex || ez) {
//Expand in the x direction
if (ex) {
if (endX + 1 > this.maxSize || endX+1 == (1 << this.size) - 1) {
ex = false;
}
}
if (ex) {
for (int tz = z; tz < endZ+1; tz++) {
if (!this.canMerge(endX + 1, tz, data)) {
ex = false;
}
}
}
if (ex) {
endX++;
}
if (ez) {
if (endZ + 1 > this.maxSize || endZ+1 == (1<<this.size)-1) {
ez = false;
}
}
if (ez) {
for (int tx = x; tx < endX+1; tx++) {
if (!this.canMerge(tx, endZ + 1, data)) {
ez = false;
}
}
}
if (ez) {
endZ++;
}
}
//Mark the sections as meshed
for (int mx = x; mx <= endX; mx++) {
for (int mz = z; mz <= endZ; mz++) {
this.setset.clear(this.getIdx(mx, mz));
}
}
int encodedQuad = encodeQuad(x, z, endX - x + 1, endZ - z + 1);
{
int pIdx = idxCount++;
if (pIdx == quads.length) {
var newArray = new int[quads.length + 64];
System.arraycopy(quads, 0, newArray, 0, quads.length);
quads = newArray;
}
quads[pIdx] = encodedQuad;
}
posId = this.setset.nextSetBit(posId);
}
var out = new int[idxCount];
System.arraycopy(quads, 0, out, 0, idxCount);
this.quadCache = quads;
return out;
}
public static void main(String[] args) {
var r = new Random(123451);
int a = 0;
long total = 0;
for (int i = 0; i < 200000; i++) {
var mesh = new Mesher2Dv2(5,16);
for (int j = 0; j < 512; j++) {
mesh.put(r.nextInt(32), r.nextInt(32), r.nextInt(100));
}
long s = System.nanoTime();
var result = mesh.process();
total += System.nanoTime() - s;
a += result.hashCode();
}
System.out.println(total/(1e+6));
System.out.println((double) (total/(1e+6))/200000);
//mesh.put(0,0,1);
}
public void reset() {
this.setset.clear();
Arrays.fill(this.data, 0);
}
public long getDataFromQuad(int quad) {
return this.getData(getX(quad), getZ(quad));
}
public long getData(int x, int z) {
return this.data[this.getIdx(x, z)];
}
}

View File

@@ -1,65 +0,0 @@
package me.cortex.voxelmon.importers;
import me.cortex.voxelmon.core.world.WorldEngine;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.world.World;
import org.lwjgl.system.MemoryUtil;
import java.io.IOException;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class BobbyImporter {
private final WorldEngine world;
private final World mcWorld;
public BobbyImporter(WorldEngine worldEngine, World mcWorld) {
this.world = worldEngine;
this.mcWorld = mcWorld;
}
//TODO: make the importer run in another thread with a progress callback
public void importBobby(Path directory) {
directory.forEach(child->{
var file = child.toFile();
if (!file.isFile()) {
return;
}
var name = file.getName();
var sections = name.split("\\.");
if (sections.length != 4 || (!sections[0].equals("r")) || (!sections[3].equals("mca"))) {
throw new IllegalStateException();
}
int rx = Integer.parseInt(sections[1]);
int rz = Integer.parseInt(sections[2]);
});
}
private void importRegionFile(Path file, int x, int z) throws IOException {
try (var fileStream = FileChannel.open(file, StandardOpenOption.READ)) {
var sectorsSavesBB = MemoryUtil.memAlloc(8192);
if (fileStream.read(sectorsSavesBB) != 8192) {
throw new IllegalStateException("Header of region file invalid");
}
var sectorsSaves = sectorsSavesBB.asIntBuffer();
//Find and load all saved chunks
for (int idx = 0; idx < 1024; idx++) {
int sectorMeta = sectorsSaves.get(idx);
if (sectorMeta == 0) {
//Empty chunk
continue;
}
int sectorStart = sectorMeta>>>8;
int sectorCount = sectorMeta&((1<<8)-1);
}
}
}
private void importChunkNBT(ChunkPos pos, NbtCompound chunk) {
}
}

View File

@@ -30,8 +30,14 @@ import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
public class WorldImporter {
public record ImportUpdate(){}
private final WorldEngine world;
private final World mcWorld;
private final Codec<ReadableContainer<RegistryEntry<Biome>>> biomeCodec;
@@ -45,27 +51,35 @@ public class WorldImporter {
}
private Thread worker;
public void importWorldAsyncStart(File directory) {
public void importWorldAsyncStart(File directory, int threads, Function<ImportUpdate, Boolean> updateCallback, Runnable onCompletion) {
this.worker = new Thread(() -> {
Arrays.stream(directory.listFiles()).parallel().forEach(file -> {
var workers = new ForkJoinPool(threads);
for (var file : directory.listFiles()) {
if (!file.isFile()) {
return;
continue;
}
var name = file.getName();
var sections = name.split("\\.");
if (sections.length != 4 || (!sections[0].equals("r")) || (!sections[3].equals("mca"))) {
System.err.println("Unknown file: " + name);
return;
continue;
}
int rx = Integer.parseInt(sections[1]);
int rz = Integer.parseInt(sections[2]);
try {
this.importRegionFile(file.toPath(), rx, rz);
} catch (Exception e) {
e.printStackTrace();
}
});
System.err.println("Done");
workers.submit(() -> {
try {
this.importRegionFile(file.toPath(), rx, rz);
} catch (
Exception e) {
e.printStackTrace();
}
});
}
workers.shutdown();
try {
workers.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {}
onCompletion.run();
});
this.worker.setName("World importer");
this.worker.start();

View File

@@ -0,0 +1,29 @@
package me.cortex.voxelmon.terrain;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import me.cortex.voxelmon.core.VoxelCore;
import me.cortex.voxelmon.importers.WorldImporter;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientCommandSource;
import net.minecraft.server.command.ServerCommandSource;
import static com.mojang.brigadier.builder.LiteralArgumentBuilder.literal;
import static com.mojang.brigadier.builder.RequiredArgumentBuilder.argument;
/*
public class WorldImportCommand {
public static void register(CommandDispatcher<ClientCommandSource> dispatcher) {
dispatcher.register(literal("zenith")
.then(literal("import").then(literal("world").then(argument("world_name", StringArgumentType.string()).executes(WorldImportCommand::importWorld)))));
}
private static int importWorld(CommandContext<ClientCommandSource> ctx) {
VoxelCore.INSTANCE.createWorldImporter(MinecraftClient.getInstance().world, ctx.getArgument("world_name", String.class));
return 0;
}
}
*/