Changes and removals
This commit is contained in:
@@ -1,45 +1,18 @@
|
|||||||
package me.cortex.voxy.client.core;
|
package me.cortex.voxy.client.core;
|
||||||
|
|
||||||
import com.mojang.blaze3d.systems.RenderSystem;
|
|
||||||
import me.cortex.voxy.client.config.VoxyConfig;
|
import me.cortex.voxy.client.config.VoxyConfig;
|
||||||
import me.cortex.voxy.client.core.gl.Capabilities;
|
|
||||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
|
||||||
import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
|
import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
|
||||||
import me.cortex.voxy.client.core.rendering.*;
|
import me.cortex.voxy.client.core.rendering.building.RenderDataFactory45;
|
||||||
import me.cortex.voxy.client.core.rendering.building.RenderDataFactory4;
|
|
||||||
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||||
import me.cortex.voxy.client.core.rendering.post.PostProcessing;
|
|
||||||
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
|
||||||
import me.cortex.voxy.client.core.util.IrisUtil;
|
|
||||||
import me.cortex.voxy.client.saver.ContextSelectionSystem;
|
import me.cortex.voxy.client.saver.ContextSelectionSystem;
|
||||||
import me.cortex.voxy.client.taskbar.Taskbar;
|
|
||||||
import me.cortex.voxy.common.Logger;
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.util.MemoryBuffer;
|
|
||||||
import me.cortex.voxy.common.world.WorldEngine;
|
import me.cortex.voxy.common.world.WorldEngine;
|
||||||
import me.cortex.voxy.commonImpl.importers.WorldImporter;
|
|
||||||
import me.cortex.voxy.common.thread.ServiceThreadPool;
|
import me.cortex.voxy.common.thread.ServiceThreadPool;
|
||||||
import me.cortex.voxy.common.world.WorldSection;
|
import me.cortex.voxy.common.world.WorldSection;
|
||||||
import me.cortex.voxy.common.world.other.Mapper;
|
import me.cortex.voxy.common.world.other.Mapper;
|
||||||
import me.cortex.voxy.commonImpl.VoxyCommon;
|
|
||||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
|
||||||
import net.minecraft.client.MinecraftClient;
|
|
||||||
import net.minecraft.client.gui.hud.ClientBossBar;
|
|
||||||
import net.minecraft.client.render.Camera;
|
|
||||||
import net.minecraft.client.render.Frustum;
|
|
||||||
import net.minecraft.client.util.math.MatrixStack;
|
|
||||||
import net.minecraft.entity.boss.BossBar;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
import net.minecraft.util.math.MathHelper;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import net.minecraft.world.chunk.WorldChunk;
|
|
||||||
import org.joml.Matrix4f;
|
|
||||||
import org.lwjgl.opengl.GL11;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.Predicate;
|
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL30C.*;
|
import static org.lwjgl.opengl.GL30C.*;
|
||||||
|
|
||||||
@@ -162,60 +135,6 @@ public class VoxelCore {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void testMeshingPerformance() {
|
|
||||||
var modelService = new ModelBakerySubsystem(this.world.getMapper());
|
|
||||||
var factory = new RenderDataFactory4(this.world, modelService.factory, false);
|
|
||||||
|
|
||||||
List<WorldSection> sections = new ArrayList<>();
|
|
||||||
|
|
||||||
System.out.println("Loading sections");
|
|
||||||
for (int x = -17; x <= 17; x++) {
|
|
||||||
for (int z = -17; z <= 17; z++) {
|
|
||||||
for (int y = -1; y <= 4; y++) {
|
|
||||||
var section = this.world.acquire(0, x, y, z);
|
|
||||||
|
|
||||||
int nonAir = 0;
|
|
||||||
for (long state : section.copyData()) {
|
|
||||||
nonAir += Mapper.isAir(state)?0:1;
|
|
||||||
modelService.requestBlockBake(Mapper.getBlockId(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nonAir > 500 && Math.abs(x) <= 16 && Math.abs(z) <= 16) {
|
|
||||||
sections.add(section);
|
|
||||||
} else {
|
|
||||||
section.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Baking models");
|
|
||||||
{
|
|
||||||
//Bake everything
|
|
||||||
while (!modelService.areQueuesEmpty()) {
|
|
||||||
modelService.tick();
|
|
||||||
glFinish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Ready!");
|
|
||||||
|
|
||||||
{
|
|
||||||
int iteration = 0;
|
|
||||||
while (true) {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
for (var section : sections) {
|
|
||||||
var mesh = factory.generateMesh(section);
|
|
||||||
|
|
||||||
mesh.free();
|
|
||||||
}
|
|
||||||
long delta = System.currentTimeMillis() - start;
|
|
||||||
System.out.println("Iteration: " + (iteration++) + " took " + delta + "ms, for an average of " + ((float)delta/sections.size()) + "ms per section");
|
|
||||||
//System.out.println("Quad count: " + factory.quadCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void testDbPerformance() {
|
private void testDbPerformance() {
|
||||||
@@ -238,71 +157,4 @@ public class VoxelCore {
|
|||||||
long delta = System.currentTimeMillis() - start;
|
long delta = System.currentTimeMillis() - start;
|
||||||
System.out.println("Total "+delta+"ms " + ((double)delta/c) + "ms average" );
|
System.out.println("Total "+delta+"ms " + ((double)delta/c) + "ms average" );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private void testFullMesh() {
|
|
||||||
var modelService = new ModelBakerySubsystem(this.world.getMapper());
|
|
||||||
var completedCounter = new AtomicInteger();
|
|
||||||
var generationService = new RenderGenerationService(this.world, modelService, this.serviceThreadPool, a-> {completedCounter.incrementAndGet(); a.free();}, false);
|
|
||||||
|
|
||||||
|
|
||||||
var r = new Random(12345);
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 10_000; i++) {
|
|
||||||
int x = (r.nextInt(256*2+2)-256)>>1;//-32
|
|
||||||
int z = (r.nextInt(256*2+2)-256)>>1;//-32
|
|
||||||
int y = r.nextInt(10)-2;
|
|
||||||
int lvl = 0;//r.nextInt(5);
|
|
||||||
long key = WorldEngine.getWorldSectionId(lvl, x>>lvl, y>>lvl, z>>lvl);
|
|
||||||
generationService.enqueueTask(key);
|
|
||||||
}
|
|
||||||
int i = 0;
|
|
||||||
while (true) {
|
|
||||||
modelService.tick();
|
|
||||||
if (i++%5000==0)
|
|
||||||
System.out.println(completedCounter.get());
|
|
||||||
glFinish();
|
|
||||||
List<String> a = new ArrayList<>();
|
|
||||||
generationService.addDebugData(a);
|
|
||||||
if (a.getFirst().endsWith(" 0")) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
System.out.println("Running benchmark");
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
completedCounter.set(0);
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
int C = 200_000;
|
|
||||||
for (int i = 0; i < C; i++) {
|
|
||||||
int x = (r.nextInt(256 * 2 + 2) - 256) >> 1;//-32
|
|
||||||
int z = (r.nextInt(256 * 2 + 2) - 256) >> 1;//-32
|
|
||||||
int y = r.nextInt(10) - 2;
|
|
||||||
int lvl = 0;//r.nextInt(5);
|
|
||||||
long key = WorldEngine.getWorldSectionId(lvl, x >> lvl, y >> lvl, z >> lvl);
|
|
||||||
generationService.enqueueTask(key);
|
|
||||||
}
|
|
||||||
//int i = 0;
|
|
||||||
while (true) {
|
|
||||||
//if (i++%5000==0)
|
|
||||||
// System.out.println(completedCounter.get());
|
|
||||||
modelService.tick();
|
|
||||||
glFinish();
|
|
||||||
List<String> a = new ArrayList<>();
|
|
||||||
generationService.addDebugData(a);
|
|
||||||
if (a.getFirst().endsWith(" 0")) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
long delta = (System.currentTimeMillis()-start);
|
|
||||||
System.out.println("Time "+delta+"ms count: " + completedCounter.get() + " avg per mesh: " + ((double)delta/completedCounter.get()) + "ms");
|
|
||||||
if (false)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
generationService.shutdown();
|
|
||||||
modelService.shutdown();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ import me.cortex.voxy.client.config.VoxyConfig;
|
|||||||
import me.cortex.voxy.client.core.gl.Capabilities;
|
import me.cortex.voxy.client.core.gl.Capabilities;
|
||||||
import me.cortex.voxy.client.core.gl.GlBuffer;
|
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||||
import me.cortex.voxy.client.core.model.ColourDepthTextureData;
|
import me.cortex.voxy.client.core.model.ColourDepthTextureData;
|
||||||
|
import me.cortex.voxy.client.core.model.ModelBakerySubsystem;
|
||||||
import me.cortex.voxy.client.core.model.ModelTextureBakery;
|
import me.cortex.voxy.client.core.model.ModelTextureBakery;
|
||||||
|
import me.cortex.voxy.client.core.rendering.building.RenderDataFactory45;
|
||||||
|
import me.cortex.voxy.client.core.rendering.building.RenderGenerationService;
|
||||||
import me.cortex.voxy.client.core.rendering.post.PostProcessing;
|
import me.cortex.voxy.client.core.rendering.post.PostProcessing;
|
||||||
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
||||||
import me.cortex.voxy.client.core.rendering.util.RawDownloadStream;
|
import me.cortex.voxy.client.core.rendering.util.RawDownloadStream;
|
||||||
@@ -13,6 +16,9 @@ import me.cortex.voxy.client.core.util.IrisUtil;
|
|||||||
import me.cortex.voxy.common.Logger;
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.thread.ServiceThreadPool;
|
import me.cortex.voxy.common.thread.ServiceThreadPool;
|
||||||
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.other.Mapper;
|
||||||
|
import me.cortex.voxy.commonImpl.VoxyCommon;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.client.render.Camera;
|
import net.minecraft.client.render.Camera;
|
||||||
@@ -22,19 +28,25 @@ import org.joml.Matrix4f;
|
|||||||
import org.lwjgl.opengl.GL11;
|
import org.lwjgl.opengl.GL11;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import static org.lwjgl.opengl.GL11C.glFinish;
|
||||||
import static org.lwjgl.opengl.GL30C.GL_DRAW_FRAMEBUFFER_BINDING;
|
import static org.lwjgl.opengl.GL30C.GL_DRAW_FRAMEBUFFER_BINDING;
|
||||||
|
|
||||||
public class VoxyRenderSystem {
|
public class VoxyRenderSystem {
|
||||||
private final RenderService renderer;
|
private final RenderService renderer;
|
||||||
private final PostProcessing postProcessing;
|
private final PostProcessing postProcessing;
|
||||||
|
private final WorldEngine worldIn;
|
||||||
|
|
||||||
public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) {
|
public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) {
|
||||||
//Trigger the shared index buffer loading
|
//Trigger the shared index buffer loading
|
||||||
SharedIndexBuffer.INSTANCE.id();
|
SharedIndexBuffer.INSTANCE.id();
|
||||||
Capabilities.init();//Ensure clinit is called
|
Capabilities.init();//Ensure clinit is called
|
||||||
|
|
||||||
|
this.worldIn = world;
|
||||||
this.renderer = new RenderService(world, threadPool);
|
this.renderer = new RenderService(world, threadPool);
|
||||||
this.postProcessing = new PostProcessing();
|
this.postProcessing = new PostProcessing();
|
||||||
}
|
}
|
||||||
@@ -173,4 +185,129 @@ public class VoxyRenderSystem {
|
|||||||
Logger.info("Shutting down post processor");
|
Logger.info("Shutting down post processor");
|
||||||
if (this.postProcessing!=null){try {this.postProcessing.shutdown();} catch (Exception e) {Logger.error("Error shutting down post processor", e);}}
|
if (this.postProcessing!=null){try {this.postProcessing.shutdown();} catch (Exception e) {Logger.error("Error shutting down post processor", e);}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void testMeshingPerformance() {
|
||||||
|
var modelService = new ModelBakerySubsystem(this.worldIn.getMapper());
|
||||||
|
var factory = new RenderDataFactory45(this.worldIn, modelService.factory, false);
|
||||||
|
|
||||||
|
List<WorldSection> sections = new ArrayList<>();
|
||||||
|
|
||||||
|
System.out.println("Loading sections");
|
||||||
|
for (int x = -17; x <= 17; x++) {
|
||||||
|
for (int z = -17; z <= 17; z++) {
|
||||||
|
for (int y = -1; y <= 4; y++) {
|
||||||
|
var section = this.worldIn.acquire(0, x, y, z);
|
||||||
|
|
||||||
|
int nonAir = 0;
|
||||||
|
for (long state : section.copyData()) {
|
||||||
|
nonAir += Mapper.isAir(state)?0:1;
|
||||||
|
modelService.requestBlockBake(Mapper.getBlockId(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nonAir > 500 && Math.abs(x) <= 16 && Math.abs(z) <= 16) {
|
||||||
|
sections.add(section);
|
||||||
|
} else {
|
||||||
|
section.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Baking models");
|
||||||
|
{
|
||||||
|
//Bake everything
|
||||||
|
while (!modelService.areQueuesEmpty()) {
|
||||||
|
modelService.tick();
|
||||||
|
glFinish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Ready!");
|
||||||
|
|
||||||
|
{
|
||||||
|
int iteration = 0;
|
||||||
|
while (true) {
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
for (var section : sections) {
|
||||||
|
var mesh = factory.generateMesh(section);
|
||||||
|
|
||||||
|
mesh.free();
|
||||||
|
}
|
||||||
|
long delta = System.currentTimeMillis() - start;
|
||||||
|
System.out.println("Iteration: " + (iteration++) + " took " + delta + "ms, for an average of " + ((float)delta/sections.size()) + "ms per section");
|
||||||
|
//System.out.println("Quad count: " + factory.quadCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void testFullMesh() {
|
||||||
|
var modelService = new ModelBakerySubsystem(this.worldIn.getMapper());
|
||||||
|
var completedCounter = new AtomicInteger();
|
||||||
|
var generationService = new RenderGenerationService(this.worldIn, modelService, VoxyCommon.getInstance().getThreadPool(), a-> {completedCounter.incrementAndGet(); a.free();}, false);
|
||||||
|
|
||||||
|
|
||||||
|
var r = new Random(12345);
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 10_000; i++) {
|
||||||
|
int x = (r.nextInt(256*2+2)-256)>>1;//-32
|
||||||
|
int z = (r.nextInt(256*2+2)-256)>>1;//-32
|
||||||
|
int y = r.nextInt(10)-2;
|
||||||
|
int lvl = 0;//r.nextInt(5);
|
||||||
|
long key = WorldEngine.getWorldSectionId(lvl, x>>lvl, y>>lvl, z>>lvl);
|
||||||
|
generationService.enqueueTask(key);
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
modelService.tick();
|
||||||
|
if (i++%5000==0)
|
||||||
|
System.out.println(completedCounter.get());
|
||||||
|
glFinish();
|
||||||
|
List<String> a = new ArrayList<>();
|
||||||
|
generationService.addDebugData(a);
|
||||||
|
if (a.getFirst().endsWith(" 0")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Running benchmark");
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
completedCounter.set(0);
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
int C = 200_000;
|
||||||
|
for (int i = 0; i < C; i++) {
|
||||||
|
int x = (r.nextInt(256 * 2 + 2) - 256) >> 1;//-32
|
||||||
|
int z = (r.nextInt(256 * 2 + 2) - 256) >> 1;//-32
|
||||||
|
int y = r.nextInt(10) - 2;
|
||||||
|
int lvl = 0;//r.nextInt(5);
|
||||||
|
long key = WorldEngine.getWorldSectionId(lvl, x >> lvl, y >> lvl, z >> lvl);
|
||||||
|
generationService.enqueueTask(key);
|
||||||
|
}
|
||||||
|
//int i = 0;
|
||||||
|
while (true) {
|
||||||
|
//if (i++%5000==0)
|
||||||
|
// System.out.println(completedCounter.get());
|
||||||
|
modelService.tick();
|
||||||
|
glFinish();
|
||||||
|
List<String> a = new ArrayList<>();
|
||||||
|
generationService.addDebugData(a);
|
||||||
|
if (a.getFirst().endsWith(" 0")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long delta = (System.currentTimeMillis()-start);
|
||||||
|
System.out.println("Time "+delta+"ms count: " + completedCounter.get() + " avg per mesh: " + ((double)delta/completedCounter.get()) + "ms");
|
||||||
|
if (false)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
generationService.shutdown();
|
||||||
|
modelService.shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,670 +0,0 @@
|
|||||||
package me.cortex.voxy.client.core.rendering.building;
|
|
||||||
|
|
||||||
import me.cortex.voxy.client.core.gl.Capabilities;
|
|
||||||
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.client.core.util.ScanMesher2D;
|
|
||||||
import me.cortex.voxy.common.util.MemoryBuffer;
|
|
||||||
import me.cortex.voxy.common.util.UnsafeUtil;
|
|
||||||
import me.cortex.voxy.common.world.WorldEngine;
|
|
||||||
import me.cortex.voxy.common.world.WorldSection;
|
|
||||||
import me.cortex.voxy.common.world.other.Mapper;
|
|
||||||
import me.cortex.voxy.commonImpl.VoxyCommon;
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
|
|
||||||
public class RenderDataFactory4 {
|
|
||||||
private static final boolean VERIFY_MESHING = VoxyCommon.isVerificationFlagOn("verifyMeshing");
|
|
||||||
|
|
||||||
private final WorldEngine world;
|
|
||||||
private final ModelFactory modelMan;
|
|
||||||
|
|
||||||
//private final long[] sectionData = new long[32*32*32*2];
|
|
||||||
private final long[] sectionData = new long[32*32*32*2];
|
|
||||||
|
|
||||||
private final int[] opaqueMasks = new int[32*32];
|
|
||||||
|
|
||||||
//TODO: emit directly to memory buffer instead of long arrays
|
|
||||||
|
|
||||||
//Each axis gets a max quad count of 2^16 (65536 quads) since that is the max the basic geometry manager can handle
|
|
||||||
private final MemoryBuffer directionalQuadBuffer = new MemoryBuffer(6*(8*(1<<16)));
|
|
||||||
private final long directionalQuadBufferPtr = this.directionalQuadBuffer.address;
|
|
||||||
private final int[] directionalQuadCounters = new int[6];//Maybe change to short? (or long /w raw pointers)
|
|
||||||
|
|
||||||
|
|
||||||
private int minX;
|
|
||||||
private int minY;
|
|
||||||
private int minZ;
|
|
||||||
private int maxX;
|
|
||||||
private int maxY;
|
|
||||||
private int maxZ;
|
|
||||||
|
|
||||||
private int quadCount = 0;
|
|
||||||
|
|
||||||
|
|
||||||
//Wont work for double sided quads
|
|
||||||
private final class Mesher extends ScanMesher2D {
|
|
||||||
public int auxiliaryPosition = 0;
|
|
||||||
public boolean doAuxiliaryFaceOffset = true;
|
|
||||||
public int axis = 0;
|
|
||||||
|
|
||||||
//Note x, z are in top right
|
|
||||||
@Override
|
|
||||||
protected void emitQuad(int x, int z, int length, int width, long data) {
|
|
||||||
RenderDataFactory4.this.quadCount++;
|
|
||||||
|
|
||||||
if (VERIFY_MESHING) {
|
|
||||||
if (length<1||length>16) {
|
|
||||||
throw new IllegalStateException("length out of bounds: " + length);
|
|
||||||
}
|
|
||||||
if (width<1||width>16) {
|
|
||||||
throw new IllegalStateException("width out of bounds: " + width);
|
|
||||||
}
|
|
||||||
if (x<0||x>31) {
|
|
||||||
throw new IllegalStateException("x out of bounds: " + x);
|
|
||||||
}
|
|
||||||
if (z<0||z>31) {
|
|
||||||
throw new IllegalStateException("z out of bounds: " + z);
|
|
||||||
}
|
|
||||||
if (x-(length-1)<0 || z-(width-1)<0) {
|
|
||||||
throw new IllegalStateException("dim out of bounds: " + (x-(length-1))+", " + (z-(width-1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
x -= length-1;
|
|
||||||
z -= width-1;
|
|
||||||
|
|
||||||
if (this.axis == 2) {
|
|
||||||
//Need to swizzle the data if on x axis
|
|
||||||
int tmp = x;
|
|
||||||
x = z;
|
|
||||||
z = tmp;
|
|
||||||
|
|
||||||
tmp = length;
|
|
||||||
length = width;
|
|
||||||
width = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Lower 26 bits can be auxiliary data since that is where quad position information goes;
|
|
||||||
int auxData = (int) (data&((1<<26)-1));
|
|
||||||
int faceSide = auxData&1;
|
|
||||||
data &= ~(data&((1<<26)-1));
|
|
||||||
|
|
||||||
final int axis = this.axis;
|
|
||||||
int face = (axis<<1)|faceSide;
|
|
||||||
int encodedPosition = face;
|
|
||||||
|
|
||||||
//Shift up if is negative axis
|
|
||||||
int auxPos = this.auxiliaryPosition;
|
|
||||||
auxPos += this.doAuxiliaryFaceOffset?(1-faceSide):0;//Shift
|
|
||||||
|
|
||||||
if (VERIFY_MESHING) {
|
|
||||||
if (auxPos > 31) {
|
|
||||||
throw new IllegalStateException("OOB face: " + auxPos + ", " + faceSide);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
encodedPosition |= ((width - 1) << 7) | ((length - 1) << 3);
|
|
||||||
|
|
||||||
encodedPosition |= x << (axis==2?16:21);
|
|
||||||
encodedPosition |= z << (axis==1?16:11);
|
|
||||||
encodedPosition |= auxPos << (axis==0?16:(axis==1?11:21));
|
|
||||||
|
|
||||||
long quad = data | encodedPosition;
|
|
||||||
|
|
||||||
|
|
||||||
MemoryUtil.memPutLong(RenderDataFactory4.this.directionalQuadBufferPtr + (RenderDataFactory4.this.directionalQuadCounters[face]++)*8L + face*8L*(1<<16), quad);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private final Mesher blockMesher = new Mesher();
|
|
||||||
|
|
||||||
public RenderDataFactory4(WorldEngine world, ModelFactory modelManager, boolean emitMeshlets) {
|
|
||||||
this.world = world;
|
|
||||||
this.modelMan = modelManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//TODO: MAKE a render cache that caches each WorldSection directional face generation, cause then can just pull that directly
|
|
||||||
// instead of needing to regen the entire thing
|
|
||||||
|
|
||||||
|
|
||||||
//Ok so the idea for fluid rendering is to make it use a seperate mesher and use a different code path for it
|
|
||||||
// since fluid states are explicitly overlays over the base block
|
|
||||||
// can do funny stuff like double rendering
|
|
||||||
|
|
||||||
private static final boolean USE_UINT64 = Capabilities.INSTANCE.INT64_t;
|
|
||||||
public static final int QUADS_PER_MESHLET = 14;
|
|
||||||
private static void writePos(long ptr, long pos) {
|
|
||||||
if (USE_UINT64) {
|
|
||||||
MemoryUtil.memPutLong(ptr, pos);
|
|
||||||
} else {
|
|
||||||
MemoryUtil.memPutInt(ptr, (int) (pos>>32));
|
|
||||||
MemoryUtil.memPutInt(ptr + 4, (int)pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void prepareSectionData() {
|
|
||||||
final var sectionData = this.sectionData;
|
|
||||||
int opaque = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 32*32*32;) {
|
|
||||||
long block = sectionData[i + 32 * 32 * 32];//Get the block mapping
|
|
||||||
|
|
||||||
int modelId = this.modelMan.getModelId(Mapper.getBlockId(block));
|
|
||||||
long modelMetadata = this.modelMan.getModelMetadataFromClientId(modelId);
|
|
||||||
|
|
||||||
sectionData[i * 2] = modelId | ((long) (Mapper.getLightId(block)) << 16) | (ModelQueries.isBiomeColoured(modelMetadata)?(((long) (Mapper.getBiomeId(block))) << 24):0);
|
|
||||||
sectionData[i * 2 + 1] = modelMetadata;
|
|
||||||
|
|
||||||
boolean isFullyOpaque = ModelQueries.isFullyOpaque(modelMetadata);
|
|
||||||
opaque |= (isFullyOpaque ? 1:0) << (i & 31);
|
|
||||||
|
|
||||||
//TODO: here also do bitmask of what neighboring sections are needed to compute (may be getting rid of this in future)
|
|
||||||
|
|
||||||
//Do increment here
|
|
||||||
i++;
|
|
||||||
|
|
||||||
if ((i & 31) == 0) {
|
|
||||||
this.opaqueMasks[(i >> 5) - 1] = opaque;
|
|
||||||
opaque = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void generateYZFaces() {
|
|
||||||
for (int axis = 0; axis < 2; axis++) {
|
|
||||||
this.blockMesher.axis = axis;
|
|
||||||
for (int layer = 0; layer < 31; layer++) {
|
|
||||||
this.blockMesher.auxiliaryPosition = layer;
|
|
||||||
for (int other = 0; other < 32; other++) {//TODO: need to do the faces that border sections
|
|
||||||
int pidx = axis==0 ?(layer*32+other):(other*32+layer);
|
|
||||||
int skipAmount = axis==0?32:1;
|
|
||||||
|
|
||||||
int current = this.opaqueMasks[pidx];
|
|
||||||
int next = this.opaqueMasks[pidx + skipAmount];
|
|
||||||
|
|
||||||
int msk = current ^ next;
|
|
||||||
if (msk == 0) {
|
|
||||||
this.blockMesher.skip(32);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: For boarder sections, should NOT EMIT neighbors faces
|
|
||||||
int faceForwardMsk = msk & current;
|
|
||||||
int cIdx = -1;
|
|
||||||
while (msk != 0) {
|
|
||||||
int index = Integer.numberOfTrailingZeros(msk);//Is also the x-axis index
|
|
||||||
int delta = index - cIdx - 1;
|
|
||||||
cIdx = index; //index--;
|
|
||||||
if (delta != 0) this.blockMesher.skip(delta);
|
|
||||||
msk &= ~Integer.lowestOneBit(msk);
|
|
||||||
|
|
||||||
int facingForward = ((faceForwardMsk >> index) & 1);
|
|
||||||
|
|
||||||
{
|
|
||||||
int idx = index + (pidx*32);
|
|
||||||
|
|
||||||
//TODO: swap this out for something not getting the next entry
|
|
||||||
long A = this.sectionData[idx * 2];
|
|
||||||
long B = this.sectionData[(idx + skipAmount * 32) * 2];
|
|
||||||
|
|
||||||
//Flip data with respect to facing direction
|
|
||||||
long selfModel = facingForward == 1 ? A : B;
|
|
||||||
long nextModel = facingForward == 1 ? B : A;
|
|
||||||
|
|
||||||
//Example thing thats just wrong but as example
|
|
||||||
this.blockMesher.putNext(((long) facingForward) |//Facing
|
|
||||||
((selfModel & 0xFFFF) << 26) | //ModelId
|
|
||||||
(((nextModel>>16)&0xFF) << 55) |//Lighting
|
|
||||||
((selfModel&(0x1FFL<<24))<<(46-24))//biomeId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.blockMesher.endRow();
|
|
||||||
}
|
|
||||||
this.blockMesher.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (true) {
|
|
||||||
this.blockMesher.doAuxiliaryFaceOffset = false;
|
|
||||||
//Hacky generate section side faces (without check neighbor section)
|
|
||||||
for (int side = 0; side < 2; side++) {
|
|
||||||
int layer = side == 0 ? 0 : 31;
|
|
||||||
this.blockMesher.auxiliaryPosition = layer;
|
|
||||||
for (int other = 0; other < 32; other++) {
|
|
||||||
int pidx = axis == 0 ? (layer * 32 + other) : (other * 32 + layer);
|
|
||||||
int msk = this.opaqueMasks[pidx];
|
|
||||||
if (msk == 0) {
|
|
||||||
this.blockMesher.skip(32);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cIdx = -1;
|
|
||||||
while (msk != 0) {
|
|
||||||
int index = Integer.numberOfTrailingZeros(msk);//Is also the x-axis index
|
|
||||||
int delta = index - cIdx - 1;
|
|
||||||
cIdx = index; //index--;
|
|
||||||
if (delta != 0) this.blockMesher.skip(delta);
|
|
||||||
msk &= ~Integer.lowestOneBit(msk);
|
|
||||||
|
|
||||||
{
|
|
||||||
int idx = index + (pidx * 32);
|
|
||||||
|
|
||||||
//TODO: swap this out for something not getting the next entry
|
|
||||||
long A = this.sectionData[idx * 2];
|
|
||||||
|
|
||||||
//Example thing thats just wrong but as example
|
|
||||||
this.blockMesher.putNext((long) (side == 0 ? 0L : 1L) |
|
|
||||||
((A & 0xFFFFL) << 26) |
|
|
||||||
(((0xFFL) & 0xFF) << 55) |
|
|
||||||
((A&(0x1FFL<<24))<<(46-24))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.blockMesher.endRow();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.blockMesher.finish();
|
|
||||||
}
|
|
||||||
this.blockMesher.doAuxiliaryFaceOffset = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private final Mesher[] xAxisMeshers = new Mesher[32];
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
var mesher = new Mesher();
|
|
||||||
mesher.auxiliaryPosition = i;
|
|
||||||
mesher.axis = 2;//X axis
|
|
||||||
this.xAxisMeshers[i] = mesher;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final long X_I_MSK = 0x4210842108421L;
|
|
||||||
private void generateXFaces() {
|
|
||||||
for (int y = 0; y < 32; y++) {
|
|
||||||
long sumA = 0;
|
|
||||||
long sumB = 0;
|
|
||||||
long sumC = 0;
|
|
||||||
int partialHasCount = -1;
|
|
||||||
int msk = 0;
|
|
||||||
for (int z = 0; z < 32; z++) {
|
|
||||||
int lMsk = this.opaqueMasks[y*32+z];
|
|
||||||
msk = (lMsk^(lMsk>>>1));
|
|
||||||
msk &= -1>>>1;//Remove top bit as we dont actually know/have the data for that slice
|
|
||||||
|
|
||||||
//Always increment cause can do funny trick (i.e. -1 on skip amount)
|
|
||||||
sumA += X_I_MSK;
|
|
||||||
sumB += X_I_MSK;
|
|
||||||
sumC += X_I_MSK;
|
|
||||||
|
|
||||||
partialHasCount &= ~msk;
|
|
||||||
|
|
||||||
if (z == 30 && partialHasCount != 0) {//Hackfix for incremental count overflow issue
|
|
||||||
int cmsk = partialHasCount;
|
|
||||||
while (cmsk!=0) {
|
|
||||||
int index = Integer.numberOfTrailingZeros(cmsk);
|
|
||||||
cmsk &= ~Integer.lowestOneBit(cmsk);
|
|
||||||
//TODO: fixme! check this is correct or if should be 30
|
|
||||||
this.xAxisMeshers[index].skip(31);
|
|
||||||
}
|
|
||||||
//Clear the sum
|
|
||||||
sumA &= ~(Long.expand(Integer.toUnsignedLong(partialHasCount), X_I_MSK)*0x1F);
|
|
||||||
sumB &= ~(Long.expand(Integer.toUnsignedLong(partialHasCount)>>11, X_I_MSK)*0x1F);
|
|
||||||
sumC &= ~(Long.expand(Integer.toUnsignedLong(partialHasCount)>>22, X_I_MSK)*0x1F);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msk == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
{//Dont need this as can just increment everything then -1 in mask
|
|
||||||
//Compute and increment skips for indexes
|
|
||||||
long imsk = Integer.toUnsignedLong(~msk);// we only want to increment where there isnt a face
|
|
||||||
sumA += Long.expand(imsk, X_I_MSK);
|
|
||||||
sumB += Long.expand(imsk>>11, X_I_MSK);
|
|
||||||
sumC += Long.expand(imsk>>22, X_I_MSK);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
int faceForwardMsk = msk&lMsk;
|
|
||||||
int iter = msk;
|
|
||||||
while (iter!=0) {
|
|
||||||
int index = Integer.numberOfTrailingZeros(iter);
|
|
||||||
iter &= ~Integer.lowestOneBit(iter);
|
|
||||||
|
|
||||||
var mesher = this.xAxisMeshers[index];
|
|
||||||
|
|
||||||
int skipCount;//Compute the skip count
|
|
||||||
{//TODO: Branch-less
|
|
||||||
//Compute skip and clear
|
|
||||||
if (index<11) {
|
|
||||||
skipCount = (int) (sumA>>(index*5));
|
|
||||||
sumA &= ~(0x1FL<<(index*5));
|
|
||||||
} else if (index<22) {
|
|
||||||
skipCount = (int) (sumB>>((index-11)*5));
|
|
||||||
sumB &= ~(0x1FL<<((index-11)*5));
|
|
||||||
} else {
|
|
||||||
skipCount = (int) (sumC>>((index-22)*5));
|
|
||||||
sumC &= ~(0x1FL<<((index-22)*5));
|
|
||||||
}
|
|
||||||
skipCount &= 0x1F;
|
|
||||||
skipCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skipCount != 0) {
|
|
||||||
mesher.skip(skipCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
int facingForward = ((faceForwardMsk>>index)&1);
|
|
||||||
{
|
|
||||||
int idx = index + (z * 32) + (y * 32 * 32);
|
|
||||||
//TODO: swap this out for something not getting the next entry
|
|
||||||
long A = this.sectionData[idx * 2];
|
|
||||||
long B = this.sectionData[(idx + 1) * 2];
|
|
||||||
|
|
||||||
//Flip data with respect to facing direction
|
|
||||||
long selfModel = facingForward==1?A:B;
|
|
||||||
long nextModel = facingForward==1?B:A;
|
|
||||||
|
|
||||||
//Example thing thats just wrong but as example
|
|
||||||
mesher.putNext(((long) facingForward) |//Facing
|
|
||||||
((selfModel & 0xFFFF) << 26) | //ModelId
|
|
||||||
(((nextModel>>16)&0xFF) << 55) |//Lighting
|
|
||||||
((selfModel&(0x1FFL<<24))<<(46-24))//biomeId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Need to skip the remaining entries in the skip array
|
|
||||||
{
|
|
||||||
msk = ~msk;//Invert the mask as we only need to set stuff that isnt 0
|
|
||||||
while (msk!=0) {
|
|
||||||
int index = Integer.numberOfTrailingZeros(msk);
|
|
||||||
msk &= ~Integer.lowestOneBit(msk);
|
|
||||||
int skipCount;
|
|
||||||
if (index < 11) {
|
|
||||||
skipCount = (int) (sumA>>(index*5));
|
|
||||||
} else if (index<22) {
|
|
||||||
skipCount = (int) (sumB>>((index-11)*5));
|
|
||||||
} else {
|
|
||||||
skipCount = (int) (sumC>>((index-22)*5));
|
|
||||||
}
|
|
||||||
skipCount &= 0x1F;
|
|
||||||
|
|
||||||
if (skipCount != 0) {
|
|
||||||
this.xAxisMeshers[index].skip(skipCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Generate the side faces, hackily, using 0 and 1 mesher
|
|
||||||
if (true) {
|
|
||||||
var ma = this.xAxisMeshers[0];
|
|
||||||
var mb = this.xAxisMeshers[31];
|
|
||||||
ma.finish();
|
|
||||||
mb.finish();
|
|
||||||
ma.doAuxiliaryFaceOffset = false;
|
|
||||||
mb.doAuxiliaryFaceOffset = false;
|
|
||||||
for (int y = 0; y < 32; y++) {
|
|
||||||
int skipA = 0;
|
|
||||||
int skipB = 0;
|
|
||||||
for (int z = 0; z < 32; z++) {
|
|
||||||
int i = y*32+z;
|
|
||||||
int msk = this.opaqueMasks[i];
|
|
||||||
if ((msk & 1) != 0) {
|
|
||||||
ma.skip(skipA); skipA = 0;
|
|
||||||
|
|
||||||
long A = this.sectionData[(i<<5) * 2];
|
|
||||||
|
|
||||||
ma.putNext(0L | ((A&0xFFFF)<<26) | (((0xFFL)&0xFF)<<55)|((A&(0x1FFL<<24))<<(46-24)));
|
|
||||||
} else {skipA++;}
|
|
||||||
|
|
||||||
if ((msk & (1<<31)) != 0) {
|
|
||||||
mb.skip(skipB); skipB = 0;
|
|
||||||
|
|
||||||
long A = this.sectionData[(i*32+31) * 2];
|
|
||||||
|
|
||||||
mb.putNext(1L | ((A&0xFFFF)<<26) | (((0xFFL)&0xFF)<<55)|((A&(0x1FFL<<24))<<(46-24)));
|
|
||||||
} else {skipB++;}
|
|
||||||
}
|
|
||||||
ma.skip(skipA);
|
|
||||||
mb.skip(skipB);
|
|
||||||
}
|
|
||||||
ma.finish();
|
|
||||||
mb.finish();
|
|
||||||
ma.doAuxiliaryFaceOffset = true;
|
|
||||||
mb.doAuxiliaryFaceOffset = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var mesher : this.xAxisMeshers) {
|
|
||||||
mesher.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
private static long createQuad() {
|
|
||||||
((long)clientModelId) | (((long) Mapper.getLightId(ModelQueries.faceUsesSelfLighting(metadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelQueries.isBiomeColoured(metadata)?1:0)) | otherFlags
|
|
||||||
|
|
||||||
|
|
||||||
long data = Integer.toUnsignedLong(array[i*3+1]);
|
|
||||||
data |= ((long) array[i*3+2])<<32;
|
|
||||||
long encodedQuad = Integer.toUnsignedLong(QuadEncoder.encodePosition(face, otherAxis, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//section is already acquired and gets released by the parent
|
|
||||||
public BuiltSection generateMesh(WorldSection section) {
|
|
||||||
//Copy section data to end of array so that can mutate array while reading safely
|
|
||||||
section.copyDataTo(this.sectionData, 32*32*32);
|
|
||||||
|
|
||||||
this.quadCount = 0;
|
|
||||||
|
|
||||||
this.minX = Integer.MAX_VALUE;
|
|
||||||
this.minY = Integer.MAX_VALUE;
|
|
||||||
this.minZ = Integer.MAX_VALUE;
|
|
||||||
this.maxX = Integer.MIN_VALUE;
|
|
||||||
this.maxY = Integer.MIN_VALUE;
|
|
||||||
this.maxZ = Integer.MIN_VALUE;
|
|
||||||
|
|
||||||
Arrays.fill(this.directionalQuadCounters, (short) 0);
|
|
||||||
|
|
||||||
|
|
||||||
this.world.acquire(section.lvl, section.x+1, section.y, section.z).release();
|
|
||||||
this.world.acquire(section.lvl, section.x-1, section.y, section.z).release();
|
|
||||||
|
|
||||||
this.world.acquire(section.lvl, section.x, section.y+1, section.z).release();
|
|
||||||
this.world.acquire(section.lvl, section.x, section.y-1, section.z).release();
|
|
||||||
|
|
||||||
this.world.acquire(section.lvl, section.x, section.y, section.z+1).release();
|
|
||||||
this.world.acquire(section.lvl, section.x, section.y, section.z-1).release();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Prepare everything
|
|
||||||
this.prepareSectionData();
|
|
||||||
|
|
||||||
|
|
||||||
this.generateYZFaces();
|
|
||||||
this.generateXFaces();
|
|
||||||
|
|
||||||
|
|
||||||
//TODO:NOTE! when doing face culling of translucent blocks,
|
|
||||||
// if the connecting type of the translucent block is the same AND the face is full, discard it
|
|
||||||
// this stops e.g. multiple layers of glass (and ocean) from having 3000 layers of quads etc
|
|
||||||
|
|
||||||
if (this.quadCount == 0) {
|
|
||||||
return BuiltSection.emptyWithChildren(section.key, section.getNonEmptyChildren());
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: FIXME AND OPTIMIZE, get rid of the stupid quad collector bullshit
|
|
||||||
|
|
||||||
int[] offsets = new int[8];
|
|
||||||
var buff = new MemoryBuffer(this.quadCount * 8L);
|
|
||||||
long ptr = buff.address;
|
|
||||||
int coff = 0;
|
|
||||||
|
|
||||||
for (int face = 0; face < 6; face++) {
|
|
||||||
offsets[face + 2] = coff;
|
|
||||||
int size = this.directionalQuadCounters[face];
|
|
||||||
UnsafeUtil.memcpy(this.directionalQuadBufferPtr + (face*(8*(1<<16))), ptr + coff*8L, (size* 8L));
|
|
||||||
coff += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int aabb = 0;
|
|
||||||
aabb |= 0;
|
|
||||||
aabb |= 0<<5;
|
|
||||||
aabb |= 0<<10;
|
|
||||||
aabb |= (31)<<15;
|
|
||||||
aabb |= (31)<<20;
|
|
||||||
aabb |= (31)<<25;
|
|
||||||
|
|
||||||
return new BuiltSection(section.key, section.getNonEmptyChildren(), aabb, buff, offsets);
|
|
||||||
|
|
||||||
/*
|
|
||||||
buff = new MemoryBuffer(bufferSize * 8L);
|
|
||||||
long ptr = buff.address;
|
|
||||||
int coff = 0;
|
|
||||||
|
|
||||||
//Ordering is: translucent, double sided quads, directional quads
|
|
||||||
offsets[0] = coff;
|
|
||||||
int size = this.translucentQuadCollector.size();
|
|
||||||
LongArrayList arrayList = this.translucentQuadCollector;
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
long data = arrayList.getLong(i);
|
|
||||||
MemoryUtil.memPutLong(ptr + ((coff++) * 8L), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
offsets[1] = coff;
|
|
||||||
size = this.doubleSidedQuadCollector.size();
|
|
||||||
arrayList = this.doubleSidedQuadCollector;
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
long data = arrayList.getLong(i);
|
|
||||||
MemoryUtil.memPutLong(ptr + ((coff++) * 8L), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int face = 0; face < 6; face++) {
|
|
||||||
offsets[face + 2] = coff;
|
|
||||||
final LongArrayList faceArray = this.directionalQuadCollectors[face];
|
|
||||||
size = faceArray.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
long data = faceArray.getLong(i);
|
|
||||||
MemoryUtil.memPutLong(ptr + ((coff++) * 8L), data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int aabb = 0;
|
|
||||||
aabb |= this.minX;
|
|
||||||
aabb |= this.minY<<5;
|
|
||||||
aabb |= this.minZ<<10;
|
|
||||||
aabb |= (this.maxX-this.minX)<<15;
|
|
||||||
aabb |= (this.maxY-this.minY)<<20;
|
|
||||||
aabb |= (this.maxZ-this.minZ)<<25;
|
|
||||||
|
|
||||||
return new BuiltSection(section.key, section.getNonEmptyChildren(), aabb, buff, offsets);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
public void free() {
|
|
||||||
this.directionalQuadBuffer.free();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Returns true if a face was placed
|
|
||||||
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 (ModelQueries.containsFluid(facingMetadata)) {
|
|
||||||
facingFluidClientId = this.modelMan.getFluidClientStateId(facingClientModelId);
|
|
||||||
}
|
|
||||||
|
|
||||||
//If both of the states are the same, then dont render the fluid face
|
|
||||||
if (selfFluidClientId == facingFluidClientId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (facingFluidClientId != -1) {
|
|
||||||
//TODO: OPTIMIZE
|
|
||||||
if (this.world.getMapper().getBlockStateFromBlockId(selfBlockId).getBlock() == this.world.getMapper().getBlockStateFromBlockId(Mapper.getBlockId(facingState)).getBlock()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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 ((!ModelQueries.isFluid(metadata)) && ModelQueries.faceOccludes(metadata, face)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//TODO:FIXME SOMEHOW THIS IS CRITICAL!!!!!!!!!!!!!!!!!!
|
|
||||||
// so there is one more issue need to be fixed, if water is layered ontop of eachother, the side faces depend on the water state ontop
|
|
||||||
// this has been hackfixed in the model texture bakery but a proper solution that doesnt explode the sides of the water textures needs to be done
|
|
||||||
// the issue is that the fluid rendering depends on the up state aswell not just the face state which is really really painful to account for
|
|
||||||
// e.g the sides of a full water is 8 high or something, not the full block height, this results in a gap between water layers
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
long otherFlags = 0;
|
|
||||||
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 metadata, int clientModelId, int selfBiome, long facingModelId, long facingMetadata, int selfLight, int facingLight, int a, int b) {
|
|
||||||
if (ModelQueries.cullsSame(metadata) && clientModelId == facingModelId) {
|
|
||||||
//If we are facing a block, and we are both the same state, dont render that face
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//If face can be occluded and is occluded from the facing block, then dont render the face
|
|
||||||
if (ModelQueries.faceCanBeOccluded(metadata, face) && ModelQueries.faceOccludes(facingMetadata, opposingFace)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
long otherFlags = 0;
|
|
||||||
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)?selfLight:facingLight))<<16) | (ModelQueries.isBiomeColoured(metadata)?(((long) Mapper.getBiomeId(selfBiome))<<24):0) | otherFlags);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getMeshletHoldingCount(int quads, int quadsPerMeshlet, int meshletSize) {
|
|
||||||
return ((quads+(quadsPerMeshlet-1))/quadsPerMeshlet)*meshletSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int alignUp(int n, int alignment) {
|
|
||||||
return (n + alignment - 1) & -alignment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,721 +0,0 @@
|
|||||||
package me.cortex.voxy.client.core.rendering.building;
|
|
||||||
|
|
||||||
import me.cortex.voxy.client.core.gl.Capabilities;
|
|
||||||
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.client.core.util.ScanMesher2D;
|
|
||||||
import me.cortex.voxy.common.util.MemoryBuffer;
|
|
||||||
import me.cortex.voxy.common.util.UnsafeUtil;
|
|
||||||
import me.cortex.voxy.common.world.WorldEngine;
|
|
||||||
import me.cortex.voxy.common.world.WorldSection;
|
|
||||||
import me.cortex.voxy.common.world.other.Mapper;
|
|
||||||
import me.cortex.voxy.commonImpl.VoxyCommon;
|
|
||||||
import org.lwjgl.system.MemoryUtil;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
|
|
||||||
public class RenderDataFactory5 {
|
|
||||||
private static final boolean VERIFY_MESHING = VoxyCommon.isVerificationFlagOn("verifyMeshing");
|
|
||||||
|
|
||||||
private final WorldEngine world;
|
|
||||||
private final ModelFactory modelMan;
|
|
||||||
|
|
||||||
//private final long[] sectionData = new long[32*32*32*2];
|
|
||||||
private final long[] sectionData = new long[32*32*32*2];
|
|
||||||
|
|
||||||
private final int[] opaqueMasks = new int[32*32];
|
|
||||||
private final int[] nonOpaqueMasks = new int[32*32];
|
|
||||||
|
|
||||||
|
|
||||||
//TODO: emit directly to memory buffer instead of long arrays
|
|
||||||
|
|
||||||
//Each axis gets a max quad count of 2^16 (65536 quads) since that is the max the basic geometry manager can handle
|
|
||||||
private final MemoryBuffer directionalQuadBuffer = new MemoryBuffer(6*(8*(1<<16)));
|
|
||||||
private final long directionalQuadBufferPtr = this.directionalQuadBuffer.address;
|
|
||||||
private final int[] directionalQuadCounters = new int[6];//Maybe change to short? (or long /w raw pointers)
|
|
||||||
|
|
||||||
|
|
||||||
private int minX;
|
|
||||||
private int minY;
|
|
||||||
private int minZ;
|
|
||||||
private int maxX;
|
|
||||||
private int maxY;
|
|
||||||
private int maxZ;
|
|
||||||
|
|
||||||
private int quadCount = 0;
|
|
||||||
|
|
||||||
|
|
||||||
//Wont work for double sided quads
|
|
||||||
private final class Mesher extends ScanMesher2D {
|
|
||||||
public int auxiliaryPosition = 0;
|
|
||||||
public boolean doAuxiliaryFaceOffset = true;
|
|
||||||
public int axis = 0;
|
|
||||||
|
|
||||||
//Note x, z are in top right
|
|
||||||
@Override
|
|
||||||
protected void emitQuad(int x, int z, int length, int width, long data) {
|
|
||||||
RenderDataFactory5.this.quadCount++;
|
|
||||||
|
|
||||||
if (VERIFY_MESHING) {
|
|
||||||
if (length<1||length>16) {
|
|
||||||
throw new IllegalStateException("length out of bounds: " + length);
|
|
||||||
}
|
|
||||||
if (width<1||width>16) {
|
|
||||||
throw new IllegalStateException("width out of bounds: " + width);
|
|
||||||
}
|
|
||||||
if (x<0||x>31) {
|
|
||||||
throw new IllegalStateException("x out of bounds: " + x);
|
|
||||||
}
|
|
||||||
if (z<0||z>31) {
|
|
||||||
throw new IllegalStateException("z out of bounds: " + z);
|
|
||||||
}
|
|
||||||
if (x-(length-1)<0 || z-(width-1)<0) {
|
|
||||||
throw new IllegalStateException("dim out of bounds: " + (x-(length-1))+", " + (z-(width-1)));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
x -= length-1;
|
|
||||||
z -= width-1;
|
|
||||||
|
|
||||||
if (this.axis == 2) {
|
|
||||||
//Need to swizzle the data if on x axis
|
|
||||||
int tmp = x;
|
|
||||||
x = z;
|
|
||||||
z = tmp;
|
|
||||||
|
|
||||||
tmp = length;
|
|
||||||
length = width;
|
|
||||||
width = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Lower 26 bits can be auxiliary data since that is where quad position information goes;
|
|
||||||
int auxData = (int) (data&((1<<26)-1));
|
|
||||||
int faceSide = auxData&1;
|
|
||||||
data &= ~(data&((1<<26)-1));
|
|
||||||
|
|
||||||
final int axis = this.axis;
|
|
||||||
int face = (axis<<1)|faceSide;
|
|
||||||
int encodedPosition = face;
|
|
||||||
|
|
||||||
//Shift up if is negative axis
|
|
||||||
int auxPos = this.auxiliaryPosition;
|
|
||||||
auxPos += this.doAuxiliaryFaceOffset?(1-faceSide):0;//Shift
|
|
||||||
|
|
||||||
if (VERIFY_MESHING) {
|
|
||||||
if (auxPos > 31) {
|
|
||||||
throw new IllegalStateException("OOB face: " + auxPos + ", " + faceSide);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
encodedPosition |= ((width - 1) << 7) | ((length - 1) << 3);
|
|
||||||
|
|
||||||
encodedPosition |= x << (axis==2?16:21);
|
|
||||||
encodedPosition |= z << (axis==1?16:11);
|
|
||||||
encodedPosition |= auxPos << (axis==0?16:(axis==1?11:21));
|
|
||||||
|
|
||||||
long quad = data | encodedPosition;
|
|
||||||
|
|
||||||
|
|
||||||
MemoryUtil.memPutLong(RenderDataFactory5.this.directionalQuadBufferPtr + (RenderDataFactory5.this.directionalQuadCounters[face]++)*8L + face*8L*(1<<16), quad);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private final Mesher blockMesher = new Mesher();
|
|
||||||
private final Mesher partiallyOpaqueMesher = new Mesher();
|
|
||||||
|
|
||||||
public RenderDataFactory5(WorldEngine world, ModelFactory modelManager, boolean emitMeshlets) {
|
|
||||||
this.world = world;
|
|
||||||
this.modelMan = modelManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int prepareSectionData() {
|
|
||||||
final var sectionData = this.sectionData;
|
|
||||||
int opaque = 0;
|
|
||||||
int notEmpty = 0;
|
|
||||||
|
|
||||||
int neighborAcquireMsk = 0;//-+x, -+y, -+Z
|
|
||||||
for (int i = 0; i < 32*32*32;) {
|
|
||||||
long block = sectionData[i + 32 * 32 * 32];//Get the block mapping
|
|
||||||
|
|
||||||
int modelId = this.modelMan.getModelId(Mapper.getBlockId(block));
|
|
||||||
long modelMetadata = this.modelMan.getModelMetadataFromClientId(modelId);
|
|
||||||
|
|
||||||
sectionData[i * 2] = modelId | ((long) (Mapper.getLightId(block)) << 16) | (ModelQueries.isBiomeColoured(modelMetadata)?(((long) (Mapper.getBiomeId(block))) << 24):0);
|
|
||||||
sectionData[i * 2 + 1] = modelMetadata;
|
|
||||||
|
|
||||||
boolean isFullyOpaque = ModelQueries.isFullyOpaque(modelMetadata);
|
|
||||||
opaque |= (isFullyOpaque ? 1:0) << (i & 31);
|
|
||||||
notEmpty |= (modelId!=0 ? 1:0) << (i & 31);
|
|
||||||
|
|
||||||
//TODO: here also do bitmask of what neighboring sections are needed to compute (may be getting rid of this in future)
|
|
||||||
|
|
||||||
//Do increment here
|
|
||||||
i++;
|
|
||||||
|
|
||||||
if ((i & 31) == 0) {
|
|
||||||
this.opaqueMasks[(i >> 5) - 1] = opaque;
|
|
||||||
this.nonOpaqueMasks[(i >> 5) - 1] = notEmpty^opaque;
|
|
||||||
|
|
||||||
int neighborMsk = 0;
|
|
||||||
//-+x
|
|
||||||
neighborMsk |= notEmpty&1;
|
|
||||||
neighborMsk |= (notEmpty>>>30)&0b10;
|
|
||||||
|
|
||||||
//notEmpty = (notEmpty != 0)?1:0;
|
|
||||||
neighborMsk |= notEmpty!=0&&((i>>5)&0x1F)==0?0b100:0;//-z
|
|
||||||
neighborMsk |= notEmpty!=0&&((i>>5)&0x1F)==31?0b1000:0;//+z
|
|
||||||
neighborMsk |= notEmpty!=0&&(i>>10)==0?0b10000:0;//-y
|
|
||||||
neighborMsk |= notEmpty!=0&&(i>>10)==31?0b100000:0;//+y
|
|
||||||
|
|
||||||
neighborAcquireMsk |= neighborMsk;
|
|
||||||
|
|
||||||
|
|
||||||
opaque = 0;
|
|
||||||
notEmpty = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return neighborAcquireMsk;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void generateYZFaces() {
|
|
||||||
for (int axis = 0; axis < 2; axis++) {
|
|
||||||
this.blockMesher.axis = axis;
|
|
||||||
for (int layer = 0; layer < 31; layer++) {
|
|
||||||
this.blockMesher.auxiliaryPosition = layer;
|
|
||||||
for (int other = 0; other < 32; other++) {//TODO: need to do the faces that border sections
|
|
||||||
int pidx = axis==0 ?(layer*32+other):(other*32+layer);
|
|
||||||
int skipAmount = axis==0?32:1;
|
|
||||||
|
|
||||||
int current = this.opaqueMasks[pidx];
|
|
||||||
int next = this.opaqueMasks[pidx + skipAmount];
|
|
||||||
|
|
||||||
int msk = current ^ next;
|
|
||||||
if (msk == 0) {
|
|
||||||
this.blockMesher.skip(32);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: For boarder sections, should NOT EMIT neighbors faces
|
|
||||||
int faceForwardMsk = msk & current;
|
|
||||||
int cIdx = -1;
|
|
||||||
while (msk != 0) {
|
|
||||||
int index = Integer.numberOfTrailingZeros(msk);//Is also the x-axis index
|
|
||||||
int delta = index - cIdx - 1;
|
|
||||||
cIdx = index; //index--;
|
|
||||||
if (delta != 0) this.blockMesher.skip(delta);
|
|
||||||
msk &= ~Integer.lowestOneBit(msk);
|
|
||||||
|
|
||||||
int facingForward = ((faceForwardMsk >> index) & 1);
|
|
||||||
|
|
||||||
{
|
|
||||||
int idx = index + (pidx*32);
|
|
||||||
|
|
||||||
//TODO: swap this out for something not getting the next entry
|
|
||||||
long A = this.sectionData[idx * 2];
|
|
||||||
long B = this.sectionData[(idx + skipAmount * 32) * 2];
|
|
||||||
|
|
||||||
//Flip data with respect to facing direction
|
|
||||||
long selfModel = facingForward == 1 ? A : B;
|
|
||||||
long nextModel = facingForward == 1 ? B : A;
|
|
||||||
|
|
||||||
//Example thing thats just wrong but as example
|
|
||||||
this.blockMesher.putNext(((long) facingForward) |//Facing
|
|
||||||
((selfModel & 0xFFFF) << 26) | //ModelId
|
|
||||||
(((nextModel>>16)&0xFF) << 55) |//Lighting
|
|
||||||
((selfModel&(0x1FFL<<24))<<(46-24))//biomeId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.blockMesher.endRow();
|
|
||||||
}
|
|
||||||
this.blockMesher.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (true) {
|
|
||||||
this.blockMesher.doAuxiliaryFaceOffset = false;
|
|
||||||
//Hacky generate section side faces (without check neighbor section)
|
|
||||||
for (int side = 0; side < 2; side++) {
|
|
||||||
int layer = side == 0 ? 0 : 31;
|
|
||||||
this.blockMesher.auxiliaryPosition = layer;
|
|
||||||
for (int other = 0; other < 32; other++) {
|
|
||||||
int pidx = axis == 0 ? (layer * 32 + other) : (other * 32 + layer);
|
|
||||||
int msk = this.opaqueMasks[pidx];
|
|
||||||
if (msk == 0) {
|
|
||||||
this.blockMesher.skip(32);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cIdx = -1;
|
|
||||||
while (msk != 0) {
|
|
||||||
int index = Integer.numberOfTrailingZeros(msk);//Is also the x-axis index
|
|
||||||
int delta = index - cIdx - 1;
|
|
||||||
cIdx = index; //index--;
|
|
||||||
if (delta != 0) this.blockMesher.skip(delta);
|
|
||||||
msk &= ~Integer.lowestOneBit(msk);
|
|
||||||
|
|
||||||
{
|
|
||||||
int idx = index + (pidx * 32);
|
|
||||||
|
|
||||||
//TODO: swap this out for something not getting the next entry
|
|
||||||
long A = this.sectionData[idx * 2];
|
|
||||||
|
|
||||||
//Example thing thats just wrong but as example
|
|
||||||
this.blockMesher.putNext((long) (side == 0 ? 0L : 1L) |
|
|
||||||
((A & 0xFFFFL) << 26) |
|
|
||||||
(((0xFFL) & 0xFF) << 55) |
|
|
||||||
((A&(0x1FFL<<24))<<(46-24))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.blockMesher.endRow();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.blockMesher.finish();
|
|
||||||
}
|
|
||||||
this.blockMesher.doAuxiliaryFaceOffset = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{//Non fully opaque geometry
|
|
||||||
//Note: think is ok to just reuse.. blockMesher
|
|
||||||
this.blockMesher.axis = axis;
|
|
||||||
for (int layer = 0; layer < 32; layer++) {
|
|
||||||
this.blockMesher.auxiliaryPosition = layer;
|
|
||||||
for (int other = 0; other < 32; other++) {//TODO: need to do the faces that border sections
|
|
||||||
int pidx = axis == 0 ? (layer * 32 + other) : (other * 32 + layer);
|
|
||||||
|
|
||||||
int msk = this.nonOpaqueMasks[pidx];
|
|
||||||
|
|
||||||
if (msk == 0) {
|
|
||||||
this.blockMesher.skip(32);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int cIdx = -1;
|
|
||||||
while (msk != 0) {
|
|
||||||
int index = Integer.numberOfTrailingZeros(msk);//Is also the x-axis index
|
|
||||||
int delta = index - cIdx - 1;
|
|
||||||
cIdx = index; //index--;
|
|
||||||
if (delta != 0) this.blockMesher.skip(delta);
|
|
||||||
msk &= ~Integer.lowestOneBit(msk);
|
|
||||||
|
|
||||||
{
|
|
||||||
int idx = index + (pidx * 32);
|
|
||||||
|
|
||||||
//TODO: swap this out for something not getting the next entry
|
|
||||||
long A = this.sectionData[idx * 2];
|
|
||||||
|
|
||||||
//Example thing thats just wrong but as example
|
|
||||||
this.blockMesher.putNext((long) (false ? 0L : 1L) |
|
|
||||||
((A & 0xFFFFL) << 26) |
|
|
||||||
(((0xFFL) & 0xFF) << 55) |
|
|
||||||
((A&(0x1FFL<<24))<<(46-24))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.blockMesher.endRow();
|
|
||||||
}
|
|
||||||
this.blockMesher.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private final Mesher[] xAxisMeshers = new Mesher[32];
|
|
||||||
{
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
var mesher = new Mesher();
|
|
||||||
mesher.auxiliaryPosition = i;
|
|
||||||
mesher.axis = 2;//X axis
|
|
||||||
this.xAxisMeshers[i] = mesher;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final long X_I_MSK = 0x4210842108421L;
|
|
||||||
private void generateXFaces() {
|
|
||||||
for (int y = 0; y < 32; y++) {
|
|
||||||
long sumA = 0;
|
|
||||||
long sumB = 0;
|
|
||||||
long sumC = 0;
|
|
||||||
int partialHasCount = -1;
|
|
||||||
int msk = 0;
|
|
||||||
for (int z = 0; z < 32; z++) {
|
|
||||||
int lMsk = this.opaqueMasks[y*32+z];
|
|
||||||
msk = (lMsk^(lMsk>>>1));
|
|
||||||
msk &= -1>>>1;//Remove top bit as we dont actually know/have the data for that slice
|
|
||||||
|
|
||||||
//Always increment cause can do funny trick (i.e. -1 on skip amount)
|
|
||||||
sumA += X_I_MSK;
|
|
||||||
sumB += X_I_MSK;
|
|
||||||
sumC += X_I_MSK;
|
|
||||||
|
|
||||||
partialHasCount &= ~msk;
|
|
||||||
|
|
||||||
if (z == 30 && partialHasCount != 0) {//Hackfix for incremental count overflow issue
|
|
||||||
int cmsk = partialHasCount;
|
|
||||||
while (cmsk!=0) {
|
|
||||||
int index = Integer.numberOfTrailingZeros(cmsk);
|
|
||||||
cmsk &= ~Integer.lowestOneBit(cmsk);
|
|
||||||
//TODO: fixme! check this is correct or if should be 30
|
|
||||||
this.xAxisMeshers[index].skip(31);
|
|
||||||
}
|
|
||||||
//Clear the sum
|
|
||||||
sumA &= ~(Long.expand(Integer.toUnsignedLong(partialHasCount), X_I_MSK)*0x1F);
|
|
||||||
sumB &= ~(Long.expand(Integer.toUnsignedLong(partialHasCount)>>11, X_I_MSK)*0x1F);
|
|
||||||
sumC &= ~(Long.expand(Integer.toUnsignedLong(partialHasCount)>>22, X_I_MSK)*0x1F);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msk == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
{//Dont need this as can just increment everything then -1 in mask
|
|
||||||
//Compute and increment skips for indexes
|
|
||||||
long imsk = Integer.toUnsignedLong(~msk);// we only want to increment where there isnt a face
|
|
||||||
sumA += Long.expand(imsk, X_I_MSK);
|
|
||||||
sumB += Long.expand(imsk>>11, X_I_MSK);
|
|
||||||
sumC += Long.expand(imsk>>22, X_I_MSK);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
int faceForwardMsk = msk&lMsk;
|
|
||||||
int iter = msk;
|
|
||||||
while (iter!=0) {
|
|
||||||
int index = Integer.numberOfTrailingZeros(iter);
|
|
||||||
iter &= ~Integer.lowestOneBit(iter);
|
|
||||||
|
|
||||||
var mesher = this.xAxisMeshers[index];
|
|
||||||
|
|
||||||
int skipCount;//Compute the skip count
|
|
||||||
{//TODO: Branch-less
|
|
||||||
//Compute skip and clear
|
|
||||||
if (index<11) {
|
|
||||||
skipCount = (int) (sumA>>(index*5));
|
|
||||||
sumA &= ~(0x1FL<<(index*5));
|
|
||||||
} else if (index<22) {
|
|
||||||
skipCount = (int) (sumB>>((index-11)*5));
|
|
||||||
sumB &= ~(0x1FL<<((index-11)*5));
|
|
||||||
} else {
|
|
||||||
skipCount = (int) (sumC>>((index-22)*5));
|
|
||||||
sumC &= ~(0x1FL<<((index-22)*5));
|
|
||||||
}
|
|
||||||
skipCount &= 0x1F;
|
|
||||||
skipCount--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skipCount != 0) {
|
|
||||||
mesher.skip(skipCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
int facingForward = ((faceForwardMsk>>index)&1);
|
|
||||||
{
|
|
||||||
int idx = index + (z * 32) + (y * 32 * 32);
|
|
||||||
//TODO: swap this out for something not getting the next entry
|
|
||||||
long A = this.sectionData[idx * 2];
|
|
||||||
long B = this.sectionData[(idx + 1) * 2];
|
|
||||||
|
|
||||||
//Flip data with respect to facing direction
|
|
||||||
long selfModel = facingForward==1?A:B;
|
|
||||||
long nextModel = facingForward==1?B:A;
|
|
||||||
|
|
||||||
//Example thing thats just wrong but as example
|
|
||||||
mesher.putNext(((long) facingForward) |//Facing
|
|
||||||
((selfModel & 0xFFFF) << 26) | //ModelId
|
|
||||||
(((nextModel>>16)&0xFF) << 55) |//Lighting
|
|
||||||
((selfModel&(0x1FFL<<24))<<(46-24))//biomeId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Need to skip the remaining entries in the skip array
|
|
||||||
{
|
|
||||||
msk = ~msk;//Invert the mask as we only need to set stuff that isnt 0
|
|
||||||
while (msk!=0) {
|
|
||||||
int index = Integer.numberOfTrailingZeros(msk);
|
|
||||||
msk &= ~Integer.lowestOneBit(msk);
|
|
||||||
int skipCount;
|
|
||||||
if (index < 11) {
|
|
||||||
skipCount = (int) (sumA>>(index*5));
|
|
||||||
} else if (index<22) {
|
|
||||||
skipCount = (int) (sumB>>((index-11)*5));
|
|
||||||
} else {
|
|
||||||
skipCount = (int) (sumC>>((index-22)*5));
|
|
||||||
}
|
|
||||||
skipCount &= 0x1F;
|
|
||||||
|
|
||||||
if (skipCount != 0) {
|
|
||||||
this.xAxisMeshers[index].skip(skipCount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Generate the side faces, hackily, using 0 and 1 mesher
|
|
||||||
if (true) {
|
|
||||||
var ma = this.xAxisMeshers[0];
|
|
||||||
var mb = this.xAxisMeshers[31];
|
|
||||||
ma.finish();
|
|
||||||
mb.finish();
|
|
||||||
ma.doAuxiliaryFaceOffset = false;
|
|
||||||
mb.doAuxiliaryFaceOffset = false;
|
|
||||||
for (int y = 0; y < 32; y++) {
|
|
||||||
int skipA = 0;
|
|
||||||
int skipB = 0;
|
|
||||||
for (int z = 0; z < 32; z++) {
|
|
||||||
int i = y*32+z;
|
|
||||||
int msk = this.opaqueMasks[i];
|
|
||||||
if ((msk & 1) != 0) {
|
|
||||||
ma.skip(skipA); skipA = 0;
|
|
||||||
|
|
||||||
long A = this.sectionData[(i<<5) * 2];
|
|
||||||
|
|
||||||
ma.putNext(0L | ((A&0xFFFF)<<26) | (((0xFFL)&0xFF)<<55)|((A&(0x1FFL<<24))<<(46-24)));
|
|
||||||
} else {skipA++;}
|
|
||||||
|
|
||||||
if ((msk & (1<<31)) != 0) {
|
|
||||||
mb.skip(skipB); skipB = 0;
|
|
||||||
|
|
||||||
long A = this.sectionData[(i*32+31) * 2];
|
|
||||||
|
|
||||||
mb.putNext(1L | ((A&0xFFFF)<<26) | (((0xFFL)&0xFF)<<55)|((A&(0x1FFL<<24))<<(46-24)));
|
|
||||||
} else {skipB++;}
|
|
||||||
}
|
|
||||||
ma.skip(skipA);
|
|
||||||
mb.skip(skipB);
|
|
||||||
}
|
|
||||||
ma.finish();
|
|
||||||
mb.finish();
|
|
||||||
ma.doAuxiliaryFaceOffset = true;
|
|
||||||
mb.doAuxiliaryFaceOffset = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var mesher : this.xAxisMeshers) {
|
|
||||||
mesher.finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
private static long createQuad() {
|
|
||||||
((long)clientModelId) | (((long) Mapper.getLightId(ModelQueries.faceUsesSelfLighting(metadata, face)?self:facingState))<<16) | ((((long) Mapper.getBiomeId(self))<<24) * (ModelQueries.isBiomeColoured(metadata)?1:0)) | otherFlags
|
|
||||||
|
|
||||||
|
|
||||||
long data = Integer.toUnsignedLong(array[i*3+1]);
|
|
||||||
data |= ((long) array[i*3+2])<<32;
|
|
||||||
long encodedQuad = Integer.toUnsignedLong(QuadEncoder.encodePosition(face, otherAxis, quad)) | ((data&0xFFFF)<<26) | (((data>>16)&0xFF)<<55) | (((data>>24)&0x1FF)<<46);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
//section is already acquired and gets released by the parent
|
|
||||||
public BuiltSection generateMesh(WorldSection section) {
|
|
||||||
//Copy section data to end of array so that can mutate array while reading safely
|
|
||||||
section.copyDataTo(this.sectionData, 32*32*32);
|
|
||||||
|
|
||||||
this.quadCount = 0;
|
|
||||||
|
|
||||||
this.minX = Integer.MAX_VALUE;
|
|
||||||
this.minY = Integer.MAX_VALUE;
|
|
||||||
this.minZ = Integer.MAX_VALUE;
|
|
||||||
this.maxX = Integer.MIN_VALUE;
|
|
||||||
this.maxY = Integer.MIN_VALUE;
|
|
||||||
this.maxZ = Integer.MIN_VALUE;
|
|
||||||
|
|
||||||
Arrays.fill(this.directionalQuadCounters, (short) 0);
|
|
||||||
|
|
||||||
|
|
||||||
this.world.acquire(section.lvl, section.x+1, section.y, section.z).release();
|
|
||||||
this.world.acquire(section.lvl, section.x-1, section.y, section.z).release();
|
|
||||||
|
|
||||||
this.world.acquire(section.lvl, section.x, section.y+1, section.z).release();
|
|
||||||
this.world.acquire(section.lvl, section.x, section.y-1, section.z).release();
|
|
||||||
|
|
||||||
this.world.acquire(section.lvl, section.x, section.y, section.z+1).release();
|
|
||||||
this.world.acquire(section.lvl, section.x, section.y, section.z-1).release();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Prepare everything
|
|
||||||
this.prepareSectionData();
|
|
||||||
|
|
||||||
|
|
||||||
this.generateYZFaces();
|
|
||||||
this.generateXFaces();
|
|
||||||
|
|
||||||
|
|
||||||
//TODO:NOTE! when doing face culling of translucent blocks,
|
|
||||||
// if the connecting type of the translucent block is the same AND the face is full, discard it
|
|
||||||
// this stops e.g. multiple layers of glass (and ocean) from having 3000 layers of quads etc
|
|
||||||
|
|
||||||
if (this.quadCount == 0) {
|
|
||||||
return BuiltSection.emptyWithChildren(section.key, section.getNonEmptyChildren());
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: FIXME AND OPTIMIZE, get rid of the stupid quad collector bullshit
|
|
||||||
|
|
||||||
int[] offsets = new int[8];
|
|
||||||
var buff = new MemoryBuffer(this.quadCount * 8L);
|
|
||||||
long ptr = buff.address;
|
|
||||||
int coff = 0;
|
|
||||||
|
|
||||||
for (int face = 0; face < 6; face++) {
|
|
||||||
offsets[face + 2] = coff;
|
|
||||||
int size = this.directionalQuadCounters[face];
|
|
||||||
UnsafeUtil.memcpy(this.directionalQuadBufferPtr + (face*(8*(1<<16))), ptr + coff*8L, (size* 8L));
|
|
||||||
coff += size;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int aabb = 0;
|
|
||||||
aabb |= 0;
|
|
||||||
aabb |= 0<<5;
|
|
||||||
aabb |= 0<<10;
|
|
||||||
aabb |= (31)<<15;
|
|
||||||
aabb |= (31)<<20;
|
|
||||||
aabb |= (31)<<25;
|
|
||||||
|
|
||||||
return new BuiltSection(section.key, section.getNonEmptyChildren(), aabb, buff, offsets);
|
|
||||||
|
|
||||||
/*
|
|
||||||
buff = new MemoryBuffer(bufferSize * 8L);
|
|
||||||
long ptr = buff.address;
|
|
||||||
int coff = 0;
|
|
||||||
|
|
||||||
//Ordering is: translucent, double sided quads, directional quads
|
|
||||||
offsets[0] = coff;
|
|
||||||
int size = this.translucentQuadCollector.size();
|
|
||||||
LongArrayList arrayList = this.translucentQuadCollector;
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
long data = arrayList.getLong(i);
|
|
||||||
MemoryUtil.memPutLong(ptr + ((coff++) * 8L), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
offsets[1] = coff;
|
|
||||||
size = this.doubleSidedQuadCollector.size();
|
|
||||||
arrayList = this.doubleSidedQuadCollector;
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
long data = arrayList.getLong(i);
|
|
||||||
MemoryUtil.memPutLong(ptr + ((coff++) * 8L), data);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int face = 0; face < 6; face++) {
|
|
||||||
offsets[face + 2] = coff;
|
|
||||||
final LongArrayList faceArray = this.directionalQuadCollectors[face];
|
|
||||||
size = faceArray.size();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
long data = faceArray.getLong(i);
|
|
||||||
MemoryUtil.memPutLong(ptr + ((coff++) * 8L), data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int aabb = 0;
|
|
||||||
aabb |= this.minX;
|
|
||||||
aabb |= this.minY<<5;
|
|
||||||
aabb |= this.minZ<<10;
|
|
||||||
aabb |= (this.maxX-this.minX)<<15;
|
|
||||||
aabb |= (this.maxY-this.minY)<<20;
|
|
||||||
aabb |= (this.maxZ-this.minZ)<<25;
|
|
||||||
|
|
||||||
return new BuiltSection(section.key, section.getNonEmptyChildren(), aabb, buff, offsets);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
public void free() {
|
|
||||||
this.directionalQuadBuffer.free();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Returns true if a face was placed
|
|
||||||
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 (ModelQueries.containsFluid(facingMetadata)) {
|
|
||||||
facingFluidClientId = this.modelMan.getFluidClientStateId(facingClientModelId);
|
|
||||||
}
|
|
||||||
|
|
||||||
//If both of the states are the same, then dont render the fluid face
|
|
||||||
if (selfFluidClientId == facingFluidClientId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (facingFluidClientId != -1) {
|
|
||||||
//TODO: OPTIMIZE
|
|
||||||
if (this.world.getMapper().getBlockStateFromBlockId(selfBlockId).getBlock() == this.world.getMapper().getBlockStateFromBlockId(Mapper.getBlockId(facingState)).getBlock()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
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 ((!ModelQueries.isFluid(metadata)) && ModelQueries.faceOccludes(metadata, face)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//TODO:FIXME SOMEHOW THIS IS CRITICAL!!!!!!!!!!!!!!!!!!
|
|
||||||
// so there is one more issue need to be fixed, if water is layered ontop of eachother, the side faces depend on the water state ontop
|
|
||||||
// this has been hackfixed in the model texture bakery but a proper solution that doesnt explode the sides of the water textures needs to be done
|
|
||||||
// the issue is that the fluid rendering depends on the up state aswell not just the face state which is really really painful to account for
|
|
||||||
// e.g the sides of a full water is 8 high or something, not the full block height, this results in a gap between water layers
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
long otherFlags = 0;
|
|
||||||
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 metadata, int clientModelId, int selfBiome, long facingModelId, long facingMetadata, int selfLight, int facingLight, int a, int b) {
|
|
||||||
if (ModelQueries.cullsSame(metadata) && clientModelId == facingModelId) {
|
|
||||||
//If we are facing a block, and we are both the same state, dont render that face
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//If face can be occluded and is occluded from the facing block, then dont render the face
|
|
||||||
if (ModelQueries.faceCanBeOccluded(metadata, face) && ModelQueries.faceOccludes(facingMetadata, opposingFace)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
long otherFlags = 0;
|
|
||||||
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)?selfLight:facingLight))<<16) | (ModelQueries.isBiomeColoured(metadata)?(((long) Mapper.getBiomeId(selfBiome))<<24):0) | otherFlags);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getMeshletHoldingCount(int quads, int quadsPerMeshlet, int meshletSize) {
|
|
||||||
return ((quads+(quadsPerMeshlet-1))/quadsPerMeshlet)*meshletSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int alignUp(int n, int alignment) {
|
|
||||||
return (n + alignment - 1) & -alignment;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,6 +5,8 @@ import me.cortex.voxy.common.config.section.SectionStorage;
|
|||||||
import me.cortex.voxy.common.util.TrackedObject;
|
import me.cortex.voxy.common.util.TrackedObject;
|
||||||
import me.cortex.voxy.common.voxelization.VoxelizedSection;
|
import me.cortex.voxy.common.voxelization.VoxelizedSection;
|
||||||
import me.cortex.voxy.common.world.other.Mapper;
|
import me.cortex.voxy.common.world.other.Mapper;
|
||||||
|
import me.cortex.voxy.commonImpl.VoxyInstance;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
public class WorldEngine {
|
public class WorldEngine {
|
||||||
@@ -24,7 +26,6 @@ public class WorldEngine {
|
|||||||
private final ActiveSectionTracker sectionTracker;
|
private final ActiveSectionTracker sectionTracker;
|
||||||
private ISectionChangeCallback dirtyCallback;
|
private ISectionChangeCallback dirtyCallback;
|
||||||
private ISectionSaveCallback saveCallback;
|
private ISectionSaveCallback saveCallback;
|
||||||
private final int maxMipLevels;
|
|
||||||
private volatile boolean isLive = true;
|
private volatile boolean isLive = true;
|
||||||
|
|
||||||
public void setDirtyCallback(ISectionChangeCallback callback) {
|
public void setDirtyCallback(ISectionChangeCallback callback) {
|
||||||
@@ -37,12 +38,16 @@ public class WorldEngine {
|
|||||||
|
|
||||||
public Mapper getMapper() {return this.mapper;}
|
public Mapper getMapper() {return this.mapper;}
|
||||||
public boolean isLive() {return this.isLive;}
|
public boolean isLive() {return this.isLive;}
|
||||||
|
|
||||||
|
public final @Nullable VoxyInstance instanceIn;
|
||||||
|
|
||||||
public WorldEngine(SectionStorage storage, int cacheCount) {
|
public WorldEngine(SectionStorage storage, int cacheCount) {
|
||||||
this(storage, MAX_LOD_LAYER+1, cacheCount);//The +1 is because its from 1 not from 0
|
this(storage, cacheCount, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private WorldEngine(SectionStorage storage, int maxMipLayers, int cacheCount) {
|
public WorldEngine(SectionStorage storage, int cacheCount, @Nullable VoxyInstance instance) {
|
||||||
this.maxMipLevels = maxMipLayers;
|
this.instanceIn = instance;
|
||||||
|
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.mapper = new Mapper(this.storage);
|
this.mapper = new Mapper(this.storage);
|
||||||
//5 cache size bits means that the section tracker has 32 separate maps that it uses
|
//5 cache size bits means that the section tracker has 32 separate maps that it uses
|
||||||
@@ -119,7 +124,7 @@ public class WorldEngine {
|
|||||||
boolean shouldCheckEmptiness = false;
|
boolean shouldCheckEmptiness = false;
|
||||||
WorldSection previousSection = null;
|
WorldSection previousSection = null;
|
||||||
|
|
||||||
for (int lvl = 0; lvl < this.maxMipLevels; lvl++) {
|
for (int lvl = 0; lvl < MAX_LOD_LAYER+1; 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));
|
||||||
|
|
||||||
int emptinessStateChange = 0;
|
int emptinessStateChange = 0;
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ public class VoxyInstance {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected WorldEngine createWorld(SectionStorage storage) {
|
protected WorldEngine createWorld(SectionStorage storage) {
|
||||||
var world = new WorldEngine(storage, 2048);
|
var world = new WorldEngine(storage, 2048, this);
|
||||||
world.setSaveCallback(this.savingService::enqueueSave);
|
world.setSaveCallback(this.savingService::enqueueSave);
|
||||||
this.activeWorlds.add(world);
|
this.activeWorlds.add(world);
|
||||||
return world;
|
return world;
|
||||||
|
|||||||
Reference in New Issue
Block a user