Compare commits
9 Commits
mc_1217
...
mc_1217_me
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5458d9f9b8 | ||
|
|
0b5183e196 | ||
|
|
8c49b42aa6 | ||
|
|
5657018a98 | ||
|
|
711d3c44d3 | ||
|
|
864f798f19 | ||
|
|
92bb0fbb5c | ||
|
|
6cf1f7f8ae | ||
|
|
d8c5f08f06 |
3
.github/workflows/check-does-build-pr.yml
vendored
3
.github/workflows/check-does-build-pr.yml
vendored
@@ -9,6 +9,9 @@ jobs:
|
|||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Verify wrapper
|
||||||
|
uses: gradle/actions/wrapper-validation@v3
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
|
|||||||
13
.github/workflows/check-does-build.yml
vendored
13
.github/workflows/check-does-build.yml
vendored
@@ -9,24 +9,19 @@ jobs:
|
|||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Verify wrapper
|
||||||
|
uses: gradle/actions/wrapper-validation@v3
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 21
|
java-version: 21
|
||||||
|
|
||||||
- name: Loom Cache
|
|
||||||
uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: "**/.gradle/loom-cache"
|
|
||||||
key: "${{ runner.os }}-gradle-${{ hashFiles('**/libs.versions.*', '**/*.gradle*', '**/gradle-wrapper.properties') }}"
|
|
||||||
restore-keys: "${{ runner.os }}-gradle-"
|
|
||||||
|
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v4
|
||||||
with:
|
with:
|
||||||
cache-read-only: false
|
cache-read-only: false
|
||||||
cache-cleanup: never
|
|
||||||
|
|
||||||
- name: Gradle build
|
- name: Gradle build
|
||||||
run: ./gradlew -I init.gradle build
|
run: ./gradlew build
|
||||||
4
.github/workflows/manual-artifact.yml
vendored
4
.github/workflows/manual-artifact.yml
vendored
@@ -9,6 +9,9 @@ jobs:
|
|||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Verify wrapper
|
||||||
|
uses: gradle/actions/wrapper-validation@v3
|
||||||
|
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v4
|
uses: actions/setup-java@v4
|
||||||
with:
|
with:
|
||||||
@@ -19,7 +22,6 @@ jobs:
|
|||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v4
|
||||||
with:
|
with:
|
||||||
cache-read-only: false
|
cache-read-only: false
|
||||||
cache-cleanup: always
|
|
||||||
|
|
||||||
- name: Gradle build
|
- name: Gradle build
|
||||||
run: ./gradlew build
|
run: ./gradlew build
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ dependencies {
|
|||||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||||
|
|
||||||
//TODO: this is to eventually not need sodium installed as atm its just used for parsing shaders
|
//TODO: this is to eventually not need sodium installed as atm its just used for parsing shaders
|
||||||
modRuntimeOnlyMsk "maven.modrinth:sodium:mc1.21.8-0.7.0-fabric"
|
modRuntimeOnlyMsk "maven.modrinth:sodium:mc1.21.6-0.6.13-fabric"
|
||||||
modCompileOnly "maven.modrinth:sodium:mc1.21.8-0.7.0-fabric"
|
modCompileOnly "maven.modrinth:sodium:mc1.21.6-0.6.13-fabric"
|
||||||
|
|
||||||
modImplementation("maven.modrinth:lithium:mc1.21.8-0.18.0-fabric")
|
modImplementation("maven.modrinth:lithium:mc1.21.8-0.18.0-fabric")
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@ dependencies {
|
|||||||
modRuntimeOnlyMsk("maven.modrinth:modmenu:15.0.0-beta.3")
|
modRuntimeOnlyMsk("maven.modrinth:modmenu:15.0.0-beta.3")
|
||||||
|
|
||||||
modCompileOnly("maven.modrinth:iris:1.9.1+1.21.7-fabric")
|
modCompileOnly("maven.modrinth:iris:1.9.1+1.21.7-fabric")
|
||||||
//modRuntimeOnlyMsk("maven.modrinth:iris:1.9.1+1.21.7-fabric")
|
modRuntimeOnlyMsk("maven.modrinth:iris:1.9.1+1.21.7-fabric")
|
||||||
|
|
||||||
//modCompileOnly("maven.modrinth:starlight:1.1.3+1.20.4")
|
//modCompileOnly("maven.modrinth:starlight:1.1.3+1.20.4")
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ org.gradle.jvmargs=-Xmx2G
|
|||||||
|
|
||||||
org.gradle.caching=true
|
org.gradle.caching=true
|
||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
org.gradle.daemon = false
|
|
||||||
|
|
||||||
# Fabric Properties
|
# Fabric Properties
|
||||||
# check these on https://modmuss50.me/fabric.html
|
# check these on https://modmuss50.me/fabric.html
|
||||||
|
|||||||
16
init.gradle
16
init.gradle
@@ -1,16 +0,0 @@
|
|||||||
if (System.getenv("GITHUB_ACTIONS") == "true") {
|
|
||||||
beforeSettings { settings ->
|
|
||||||
def cleanupTime = Long.parseLong(System.getProperty("CLEANUP_TIME", Long.toString(System.currentTimeMillis()-(60_000*10))));//Remove unused entries from more then 10 min old
|
|
||||||
|
|
||||||
settings.caches {
|
|
||||||
//Note: this could be Cleanup.DEFAULT
|
|
||||||
cleanup = Cleanup.ALWAYS
|
|
||||||
|
|
||||||
releasedWrappers.setRemoveUnusedEntriesOlderThan(cleanupTime)
|
|
||||||
snapshotWrappers.setRemoveUnusedEntriesOlderThan(cleanupTime)
|
|
||||||
downloadedResources.setRemoveUnusedEntriesOlderThan(cleanupTime)
|
|
||||||
createdResources.setRemoveUnusedEntriesOlderThan(cleanupTime)
|
|
||||||
buildCache.setRemoveUnusedEntriesOlderThan(cleanupTime)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -57,4 +57,4 @@ public class VoxyClient implements ClientModInitializer {
|
|||||||
public static boolean isFrexActive() {
|
public static boolean isFrexActive() {
|
||||||
return !FREX.isEmpty();
|
return !FREX.isEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
|
|||||||
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.command.CommandSource;
|
import net.minecraft.command.CommandSource;
|
||||||
import net.minecraft.text.PlainTextContent;
|
|
||||||
import net.minecraft.text.Text;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -54,7 +52,7 @@ public class VoxyCommands {
|
|||||||
.executes(VoxyCommands::importDistantHorizons)));
|
.executes(VoxyCommands::importDistantHorizons)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ClientCommandManager.literal("voxy")//.requires((ctx)-> VoxyCommon.getInstance() != null)
|
return ClientCommandManager.literal("voxy").requires((ctx)-> VoxyCommon.getInstance() != null)
|
||||||
.then(ClientCommandManager.literal("reload")
|
.then(ClientCommandManager.literal("reload")
|
||||||
.executes(VoxyCommands::reloadInstance))
|
.executes(VoxyCommands::reloadInstance))
|
||||||
.then(imports);
|
.then(imports);
|
||||||
@@ -63,7 +61,6 @@ public class VoxyCommands {
|
|||||||
private static int reloadInstance(CommandContext<FabricClientCommandSource> ctx) {
|
private static int reloadInstance(CommandContext<FabricClientCommandSource> ctx) {
|
||||||
var instance = (VoxyClientInstance)VoxyCommon.getInstance();
|
var instance = (VoxyClientInstance)VoxyCommon.getInstance();
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
var wr = MinecraftClient.getInstance().worldRenderer;
|
var wr = MinecraftClient.getInstance().worldRenderer;
|
||||||
@@ -85,7 +82,6 @@ public class VoxyCommands {
|
|||||||
private static int importDistantHorizons(CommandContext<FabricClientCommandSource> ctx) {
|
private static int importDistantHorizons(CommandContext<FabricClientCommandSource> ctx) {
|
||||||
var instance = (VoxyClientInstance)VoxyCommon.getInstance();
|
var instance = (VoxyClientInstance)VoxyCommon.getInstance();
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
var dbFile = new File(ctx.getArgument("sqlDbPath", String.class));
|
var dbFile = new File(ctx.getArgument("sqlDbPath", String.class));
|
||||||
@@ -100,10 +96,10 @@ public class VoxyCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
File dbFile_ = dbFile;
|
File dbFile_ = dbFile;
|
||||||
var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().world);
|
var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().player.clientWorld);
|
||||||
if (engine==null)return 1;
|
if (engine==null)return 1;
|
||||||
return instance.getImportManager().makeAndRunIfNone(engine, ()->
|
return instance.getImportManager().makeAndRunIfNone(engine, ()->
|
||||||
new DHImporter(dbFile_, engine, MinecraftClient.getInstance().world, instance.getThreadPool(), instance.savingServiceRateLimiter))?0:1;
|
new DHImporter(dbFile_, engine, MinecraftClient.getInstance().player.clientWorld, instance.getThreadPool(), instance.savingServiceRateLimiter))?0:1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean fileBasedImporter(File directory) {
|
private static boolean fileBasedImporter(File directory) {
|
||||||
@@ -112,30 +108,20 @@ public class VoxyCommands {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().world);
|
var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().player.clientWorld);
|
||||||
if (engine==null) return false;
|
if (engine==null) return false;
|
||||||
return instance.getImportManager().makeAndRunIfNone(engine, ()->{
|
return instance.getImportManager().makeAndRunIfNone(engine, ()->{
|
||||||
var importer = new WorldImporter(engine, MinecraftClient.getInstance().world, instance.getThreadPool(), instance.savingServiceRateLimiter);
|
var importer = new WorldImporter(engine, MinecraftClient.getInstance().player.clientWorld, instance.getThreadPool(), instance.savingServiceRateLimiter);
|
||||||
importer.importRegionDirectoryAsync(directory);
|
importer.importRegionDirectoryAsync(directory);
|
||||||
return importer;
|
return importer;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int importRaw(CommandContext<FabricClientCommandSource> ctx) {
|
private static int importRaw(CommandContext<FabricClientCommandSource> ctx) {
|
||||||
if (VoxyCommon.getInstance() == null) {
|
|
||||||
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fileBasedImporter(new File(ctx.getArgument("path", String.class)))?0:1;
|
return fileBasedImporter(new File(ctx.getArgument("path", String.class)))?0:1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int importBobby(CommandContext<FabricClientCommandSource> ctx) {
|
private static int importBobby(CommandContext<FabricClientCommandSource> ctx) {
|
||||||
if (VoxyCommon.getInstance() == null) {
|
|
||||||
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var file = new File(".bobby").toPath().resolve(ctx.getArgument("world_name", String.class)).toFile();
|
var file = new File(".bobby").toPath().resolve(ctx.getArgument("world_name", String.class)).toFile();
|
||||||
return fileBasedImporter(file)?0:1;
|
return fileBasedImporter(file)?0:1;
|
||||||
}
|
}
|
||||||
@@ -190,11 +176,6 @@ public class VoxyCommands {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int importWorld(CommandContext<FabricClientCommandSource> ctx) {
|
private static int importWorld(CommandContext<FabricClientCommandSource> ctx) {
|
||||||
if (VoxyCommon.getInstance() == null) {
|
|
||||||
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = ctx.getArgument("world_name", String.class);
|
var name = ctx.getArgument("world_name", String.class);
|
||||||
var file = new File("saves").toPath().resolve(name);
|
var file = new File("saves").toPath().resolve(name);
|
||||||
name = name.toLowerCase();
|
name = name.toLowerCase();
|
||||||
@@ -216,15 +197,14 @@ public class VoxyCommands {
|
|||||||
|
|
||||||
var instance = (VoxyClientInstance)VoxyCommon.getInstance();
|
var instance = (VoxyClientInstance)VoxyCommon.getInstance();
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
String finalInnerDir = innerDir;
|
String finalInnerDir = innerDir;
|
||||||
|
|
||||||
var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().world);
|
var engine = WorldIdentifier.ofEngine(MinecraftClient.getInstance().player.clientWorld);
|
||||||
if (engine != null) {
|
if (engine != null) {
|
||||||
return instance.getImportManager().makeAndRunIfNone(engine, () -> {
|
return instance.getImportManager().makeAndRunIfNone(engine, () -> {
|
||||||
var importer = new WorldImporter(engine, MinecraftClient.getInstance().world, instance.getThreadPool(), instance.savingServiceRateLimiter);
|
var importer = new WorldImporter(engine, MinecraftClient.getInstance().player.clientWorld, instance.getThreadPool(), instance.savingServiceRateLimiter);
|
||||||
importer.importZippedRegionDirectoryAsync(zip, finalInnerDir);
|
importer.importZippedRegionDirectoryAsync(zip, finalInnerDir);
|
||||||
return importer;
|
return importer;
|
||||||
}) ? 0 : 1;
|
}) ? 0 : 1;
|
||||||
@@ -232,13 +212,12 @@ public class VoxyCommands {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int cancelImport(CommandContext<FabricClientCommandSource> ctx) {
|
private static int cancelImport(CommandContext<FabricClientCommandSource> fabricClientCommandSourceCommandContext) {
|
||||||
var instance = (VoxyClientInstance)VoxyCommon.getInstance();
|
var instance = (VoxyClientInstance)VoxyCommon.getInstance();
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
ctx.getSource().sendError(Text.translatable("Voxy must be enabled in settings to use this"));
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
var world = WorldIdentifier.ofEngineNullable(MinecraftClient.getInstance().world);
|
var world = WorldIdentifier.ofEngineNullable(MinecraftClient.getInstance().player.clientWorld);
|
||||||
if (world != null) {
|
if (world != null) {
|
||||||
return instance.getImportManager().cancelImport(world)?0:1;
|
return instance.getImportManager().cancelImport(world)?0:1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTra
|
|||||||
import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner;
|
import me.cortex.voxy.client.core.rendering.hierachical.NodeCleaner;
|
||||||
import me.cortex.voxy.client.core.rendering.section.AbstractSectionRenderer;
|
import me.cortex.voxy.client.core.rendering.section.AbstractSectionRenderer;
|
||||||
import me.cortex.voxy.client.core.rendering.section.MDICSectionRenderer;
|
import me.cortex.voxy.client.core.rendering.section.MDICSectionRenderer;
|
||||||
|
import me.cortex.voxy.client.core.rendering.section.MeshEXTSectionRenderer;
|
||||||
import me.cortex.voxy.client.core.rendering.section.geometry.BasicSectionGeometryData;
|
import me.cortex.voxy.client.core.rendering.section.geometry.BasicSectionGeometryData;
|
||||||
import me.cortex.voxy.client.core.rendering.section.geometry.IGeometryData;
|
import me.cortex.voxy.client.core.rendering.section.geometry.IGeometryData;
|
||||||
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
||||||
@@ -70,7 +71,8 @@ public class VoxyRenderSystem {
|
|||||||
|
|
||||||
private static AbstractSectionRenderer<?,?> createSectionRenderer(AbstractRenderPipeline pipeline, ModelStore modelStore, IGeometryData geometryData) {
|
private static AbstractSectionRenderer<?,?> createSectionRenderer(AbstractRenderPipeline pipeline, ModelStore modelStore, IGeometryData geometryData) {
|
||||||
//TODO: need todo a thing where selects optimal section render based on if supports the pipeline and geometry data type
|
//TODO: need todo a thing where selects optimal section render based on if supports the pipeline and geometry data type
|
||||||
return new MDICSectionRenderer(pipeline, modelStore, (BasicSectionGeometryData) geometryData);//We only have MDIC backend... for now
|
//return new MDICSectionRenderer(pipeline, modelStore, (BasicSectionGeometryData) geometryData);//We only have MDIC backend... for now
|
||||||
|
return new MeshEXTSectionRenderer(pipeline, modelStore, (BasicSectionGeometryData) geometryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) {
|
public VoxyRenderSystem(WorldEngine world, ServiceThreadPool threadPool) {
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package me.cortex.voxy.client.core.gl;
|
||||||
|
|
||||||
|
import org.lwjgl.opengl.GL;
|
||||||
|
import org.lwjgl.system.JNI;
|
||||||
|
|
||||||
|
public class EXTMeshShader {
|
||||||
|
public static final int
|
||||||
|
GL_MESH_SHADER_EXT = 0x9559,
|
||||||
|
GL_TASK_SHADER_EXT = 0x955A;
|
||||||
|
|
||||||
|
private static final long glDrawMeshTasksIndirectEXT_ptr;
|
||||||
|
static {
|
||||||
|
if (GL.getFunctionProvider() == null) {
|
||||||
|
throw new IllegalStateException("Class must be initalized after gl context has been created");
|
||||||
|
}
|
||||||
|
glDrawMeshTasksIndirectEXT_ptr = GL.getFunctionProvider().getFunctionAddress("glDrawMeshTasksIndirectEXT");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void glDrawMeshTasksIndirectEXT(long indirect) {
|
||||||
|
if (glDrawMeshTasksIndirectEXT_ptr == 0) {
|
||||||
|
throw new IllegalStateException("glDrawMeshTasksIndirectEXT not supported");
|
||||||
|
}
|
||||||
|
JNI.callPV(indirect, glDrawMeshTasksIndirectEXT_ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,6 @@ import static org.lwjgl.opengl.GL45C.*;
|
|||||||
public class GlBuffer extends TrackedObject {
|
public class GlBuffer extends TrackedObject {
|
||||||
public final int id;
|
public final int id;
|
||||||
private final long size;
|
private final long size;
|
||||||
private final int flags;
|
|
||||||
|
|
||||||
private static int COUNT;
|
private static int COUNT;
|
||||||
private static long TOTAL_SIZE;
|
private static long TOTAL_SIZE;
|
||||||
@@ -29,7 +28,6 @@ public class GlBuffer extends TrackedObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public GlBuffer(long size, int flags, boolean zero) {
|
public GlBuffer(long size, int flags, boolean zero) {
|
||||||
this.flags = flags;
|
|
||||||
this.id = glCreateBuffers();
|
this.id = glCreateBuffers();
|
||||||
this.size = size;
|
this.size = size;
|
||||||
glNamedBufferStorage(this.id, size, flags);
|
glNamedBufferStorage(this.id, size, flags);
|
||||||
@@ -50,10 +48,6 @@ public class GlBuffer extends TrackedObject {
|
|||||||
TOTAL_SIZE -= this.size;
|
TOTAL_SIZE -= this.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSparse() {
|
|
||||||
return (this.flags&GL_SPARSE_STORAGE_BIT_ARB)!=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long size() {
|
public long size() {
|
||||||
return this.size;
|
return this.size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package me.cortex.voxy.client.core.gl;
|
|||||||
import me.cortex.voxy.common.util.TrackedObject;
|
import me.cortex.voxy.common.util.TrackedObject;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL45C.*;
|
import static org.lwjgl.opengl.GL45C.*;
|
||||||
import static org.lwjgl.opengl.GL45C.glNamedFramebufferDrawBuffers;
|
|
||||||
|
|
||||||
public class GlFramebuffer extends TrackedObject {
|
public class GlFramebuffer extends TrackedObject {
|
||||||
public final int id;
|
public final int id;
|
||||||
@@ -25,11 +24,6 @@ public class GlFramebuffer extends TrackedObject {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlFramebuffer setDrawBuffers(int... buffers) {
|
|
||||||
glNamedFramebufferDrawBuffers(this.id, buffers);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void free() {
|
public void free() {
|
||||||
super.free0();
|
super.free0();
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ public class GlTexture extends TrackedObject {
|
|||||||
private long getEstimatedSize() {
|
private long getEstimatedSize() {
|
||||||
this.assertAllocated();
|
this.assertAllocated();
|
||||||
long elemSize = switch (this.format) {
|
long elemSize = switch (this.format) {
|
||||||
case GL_R32UI, GL_RGBA8, GL_DEPTH24_STENCIL8, GL_R32F -> 4;
|
case GL_RGBA8, GL_DEPTH24_STENCIL8, GL_R32F -> 4;
|
||||||
case GL_DEPTH_COMPONENT24 -> 4;//TODO: check this is right????
|
case GL_DEPTH_COMPONENT24 -> 4;//TODO: check this is right????
|
||||||
case GL_DEPTH_COMPONENT32F -> 4;
|
case GL_DEPTH_COMPONENT32F -> 4;
|
||||||
case GL_DEPTH_COMPONENT32 -> 4;
|
case GL_DEPTH_COMPONENT32 -> 4;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import net.caffeinemc.mods.sodium.client.gl.shader.ShaderParser;
|
|||||||
|
|
||||||
public class ShaderLoader {
|
public class ShaderLoader {
|
||||||
public static String parse(String id) {
|
public static String parse(String id) {
|
||||||
return "#version 460 core\n"+ShaderParser.parseShader("\n#import <" + id + ">\n//beans", ShaderConstants.builder().build()).src().replaceAll("\r\n", "\n").replaceFirst("\n#version .+\n", "\n");
|
return ShaderParser.parseShader("#import <" + id + ">", ShaderConstants.builder().build());
|
||||||
//return me.jellysquid.mods.sodium.client.gl.shader.ShaderLoader.getShaderSource(new Identifier(id));
|
//return me.jellysquid.mods.sodium.client.gl.shader.ShaderLoader.getShaderSource(new Identifier(id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -345,8 +345,6 @@ public class ModelFactory {
|
|||||||
|
|
||||||
boolean fullyOpaque = true;
|
boolean fullyOpaque = true;
|
||||||
|
|
||||||
//TODO: FIXME faces that have the same "alignment depth" e.g. (sizes[0]+sizes[1])~=1 can be merged into a double faced single quad
|
|
||||||
|
|
||||||
//TODO: add a bunch of control config options for overriding/setting options of metadata for each face of each type
|
//TODO: add a bunch of control config options for overriding/setting options of metadata for each face of each type
|
||||||
for (int face = 5; face != -1; face--) {//In reverse order to make indexing into the metadata long easier
|
for (int face = 5; face != -1; face--) {//In reverse order to make indexing into the metadata long easier
|
||||||
long faceUploadPtr = uploadPtr + 4L * face;//Each face gets 4 bytes worth of data
|
long faceUploadPtr = uploadPtr + 4L * face;//Each face gets 4 bytes worth of data
|
||||||
@@ -396,19 +394,14 @@ public class ModelFactory {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (MODEL_TEXTURE_SIZE-1 != 15) {
|
//Scale face size from 0->this.modelTextureSize-1 to 0->15
|
||||||
//Scale face size from 0->this.modelTextureSize-1 to 0->15
|
for (int i = 0; i < 4; i++) {
|
||||||
for (int i = 0; i < 4; i++) {
|
faceSize[i] = Math.round((((float)faceSize[i])/(MODEL_TEXTURE_SIZE-1))*15);
|
||||||
faceSize[i] = Math.round((((float) faceSize[i]) / (MODEL_TEXTURE_SIZE - 1)) * 15);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int faceModelData = 0;
|
int faceModelData = 0;
|
||||||
faceModelData |= faceSize[0] | (faceSize[1]<<4) | (faceSize[2]<<8) | (faceSize[3]<<12);
|
faceModelData |= faceSize[0] | (faceSize[1]<<4) | (faceSize[2]<<8) | (faceSize[3]<<12);
|
||||||
//Change the scale from 0->1 (ends inclusive)
|
faceModelData |= Math.round(offset*63)<<16;//Change the scale from 0->1 (ends inclusive) float to 0->63 (6 bits) NOTE! that 63 == 1.0f meaning its shifted all the way to the other side of the model
|
||||||
// this is cursed also warning stuff at 63 (i.e half a pixel from the end will be clamped to the end)
|
|
||||||
int enc = Math.round(offset*64);
|
|
||||||
faceModelData |= Math.min(enc,63)<<16;
|
|
||||||
//Still have 11 bits free
|
//Still have 11 bits free
|
||||||
|
|
||||||
//Stuff like fences are solid, however they have extra side piece that mean it needs to have discard on
|
//Stuff like fences are solid, however they have extra side piece that mean it needs to have discard on
|
||||||
@@ -421,15 +414,7 @@ public class ModelFactory {
|
|||||||
|
|
||||||
faceModelData |= ((!faceCoversFullBlock)&&blockRenderLayer != BlockRenderLayer.TRANSLUCENT)?1<<23:0;//Alpha discard override, translucency doesnt have alpha discard
|
faceModelData |= ((!faceCoversFullBlock)&&blockRenderLayer != BlockRenderLayer.TRANSLUCENT)?1<<23:0;//Alpha discard override, translucency doesnt have alpha discard
|
||||||
|
|
||||||
//Bits 24,25 are tint metadata
|
|
||||||
if (colourProvider!=null) {//We have a tint
|
|
||||||
int tintState = TextureUtils.computeFaceTint(textureData[face], checkMode);
|
|
||||||
if (tintState == 2) {//Partial tint
|
|
||||||
faceModelData |= 1<<24;
|
|
||||||
} else if (tintState == 3) {//Full tint
|
|
||||||
faceModelData |= 2<<24;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MemoryUtil.memPutInt(faceUploadPtr, faceModelData);
|
MemoryUtil.memPutInt(faceUploadPtr, faceModelData);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,37 +38,6 @@ public class TextureUtils {
|
|||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//0: nothing written
|
|
||||||
//1: none tinted
|
|
||||||
//2: some tinted
|
|
||||||
//3: all tinted
|
|
||||||
public static int computeFaceTint(ColourDepthTextureData texture, int checkMode) {
|
|
||||||
boolean allTinted = true;
|
|
||||||
boolean someTinted = false;
|
|
||||||
boolean wasWriten = false;
|
|
||||||
|
|
||||||
final var colourData = texture.colour();
|
|
||||||
final var depthData = texture.depth();
|
|
||||||
for (int i = 0; i < colourData.length; i++) {
|
|
||||||
if (!wasPixelWritten(texture, checkMode, i)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ((colourData[i]&0xFFFFFF) == 0 || (colourData[i]>>>24)==0) {//If the pixel is fully black (or translucent)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
boolean pixelTinited = (depthData[i]&(1<<7))!=0;
|
|
||||||
wasWriten |= true;
|
|
||||||
allTinted &= pixelTinited;
|
|
||||||
someTinted |= pixelTinited;
|
|
||||||
|
|
||||||
}
|
|
||||||
if (!wasWriten) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return someTinted?(allTinted?3:2):1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final int DEPTH_MODE_AVG = 1;
|
public static final int DEPTH_MODE_AVG = 1;
|
||||||
public static final int DEPTH_MODE_MAX = 2;
|
public static final int DEPTH_MODE_MAX = 2;
|
||||||
public static final int DEPTH_MODE_MIN = 3;
|
public static final int DEPTH_MODE_MIN = 3;
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
|||||||
import org.lwjgl.system.MemoryStack;
|
import org.lwjgl.system.MemoryStack;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.ARBDirectStateAccess.*;
|
import static org.lwjgl.opengl.ARBDirectStateAccess.glTextureParameteri;
|
||||||
|
import static org.lwjgl.opengl.ARBDirectStateAccess.nglClearNamedFramebufferfv;
|
||||||
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BIT;
|
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_FRAMEBUFFER_BARRIER_BIT;
|
||||||
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_PIXEL_BUFFER_BARRIER_BIT;
|
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_PIXEL_BUFFER_BARRIER_BIT;
|
||||||
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
|
import static org.lwjgl.opengl.ARBShaderImageLoadStore.GL_SHADER_IMAGE_ACCESS_BARRIER_BIT;
|
||||||
@@ -24,14 +25,12 @@ public class GlViewCapture {
|
|||||||
private final GlTexture colourTex;
|
private final GlTexture colourTex;
|
||||||
private final GlTexture depthTex;
|
private final GlTexture depthTex;
|
||||||
private final GlTexture stencilTex;
|
private final GlTexture stencilTex;
|
||||||
private final GlTexture metaTex;
|
|
||||||
final GlFramebuffer framebuffer;
|
final GlFramebuffer framebuffer;
|
||||||
private final Shader copyOutShader;
|
private final Shader copyOutShader;
|
||||||
|
|
||||||
public GlViewCapture(int width, int height) {
|
public GlViewCapture(int width, int height) {
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.metaTex = new GlTexture().store(GL_R32UI, 1, width*3, height*2).name("ModelBakeryMetadata");
|
|
||||||
this.colourTex = new GlTexture().store(GL_RGBA8, 1, width*3, height*2).name("ModelBakeryColour");
|
this.colourTex = new GlTexture().store(GL_RGBA8, 1, width*3, height*2).name("ModelBakeryColour");
|
||||||
this.depthTex = new GlTexture().store(GL_DEPTH24_STENCIL8, 1, width*3, height*2).name("ModelBakeryDepth");
|
this.depthTex = new GlTexture().store(GL_DEPTH24_STENCIL8, 1, width*3, height*2).name("ModelBakeryDepth");
|
||||||
//TODO: FIXME: Mesa is broken when trying to read from a sampler of GL_STENCIL_INDEX
|
//TODO: FIXME: Mesa is broken when trying to read from a sampler of GL_STENCIL_INDEX
|
||||||
@@ -40,27 +39,22 @@ public class GlViewCapture {
|
|||||||
this.stencilTex = this.depthTex.createView();
|
this.stencilTex = this.depthTex.createView();
|
||||||
glTextureParameteri(this.depthTex.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
|
glTextureParameteri(this.depthTex.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_DEPTH_COMPONENT);
|
||||||
|
|
||||||
this.framebuffer = new GlFramebuffer().bind(GL_COLOR_ATTACHMENT0, this.colourTex).bind(GL_COLOR_ATTACHMENT1, this.metaTex).setDrawBuffers(GL_COLOR_ATTACHMENT0,GL_COLOR_ATTACHMENT1).bind(GL_DEPTH_STENCIL_ATTACHMENT, this.depthTex).verify().name("ModelFramebuffer");
|
this.framebuffer = new GlFramebuffer().bind(GL_COLOR_ATTACHMENT0, this.colourTex).bind(GL_DEPTH_STENCIL_ATTACHMENT, this.depthTex).verify().name("ModelFramebuffer");
|
||||||
|
|
||||||
glTextureParameteri(this.stencilTex.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
|
glTextureParameteri(this.stencilTex.id, GL_DEPTH_STENCIL_TEXTURE_MODE, GL_STENCIL_INDEX);
|
||||||
glTextureParameteri(this.stencilTex.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTextureParameteri(this.stencilTex.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
glTextureParameteri(this.stencilTex.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTextureParameteri(this.stencilTex.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
glTextureParameteri(this.metaTex.id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTextureParameteri(this.metaTex.id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
|
|
||||||
this.copyOutShader = Shader.makeAuto()
|
this.copyOutShader = Shader.makeAuto()
|
||||||
.define("WIDTH", width)
|
.define("WIDTH", width)
|
||||||
.define("HEIGHT", height)
|
.define("HEIGHT", height)
|
||||||
.define("COLOUR_IN_BINDING", 0)
|
.define("COLOUR_IN_BINDING", 0)
|
||||||
.define("DEPTH_IN_BINDING", 1)
|
.define("DEPTH_IN_BINDING", 1)
|
||||||
.define("STENCIL_IN_BINDING", 2)
|
.define("STENCIL_IN_BINDING", 2)
|
||||||
.define("META_IN_BINDING", 3)
|
.define("BUFFER_OUT_BINDING", 3)
|
||||||
.define("BUFFER_OUT_BINDING", 4)
|
|
||||||
.add(ShaderType.COMPUTE, "voxy:bakery/bufferreorder.comp")
|
.add(ShaderType.COMPUTE, "voxy:bakery/bufferreorder.comp")
|
||||||
.compile()
|
.compile()
|
||||||
.name("ModelBakeryOut")
|
.name("ModelBakeryOut")
|
||||||
.texture("META_IN_BINDING", 0, this.metaTex)
|
|
||||||
.texture("COLOUR_IN_BINDING", 0, this.colourTex)
|
.texture("COLOUR_IN_BINDING", 0, this.colourTex)
|
||||||
.texture("DEPTH_IN_BINDING", 0, this.depthTex)
|
.texture("DEPTH_IN_BINDING", 0, this.depthTex)
|
||||||
.texture("STENCIL_IN_BINDING", 0, this.stencilTex);
|
.texture("STENCIL_IN_BINDING", 0, this.stencilTex);
|
||||||
@@ -68,10 +62,10 @@ public class GlViewCapture {
|
|||||||
|
|
||||||
public void emitToStream(int buffer, int offset) {
|
public void emitToStream(int buffer, int offset) {
|
||||||
this.copyOutShader.bind();
|
this.copyOutShader.bind();
|
||||||
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 4, buffer, offset, (this.width*3L)*(this.height*2L)*4L*2);//its 2*4 because colour + depth stencil
|
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, buffer, offset, (this.width*3L)*(this.height*2L)*4L*2);//its 2*4 because colour + depth stencil
|
||||||
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_TEXTURE_UPDATE_BARRIER_BIT|GL_PIXEL_BUFFER_BARRIER_BIT|GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);//Am not sure if barriers are right
|
glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT|GL_TEXTURE_UPDATE_BARRIER_BIT|GL_PIXEL_BUFFER_BARRIER_BIT|GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);//Am not sure if barriers are right
|
||||||
glDispatchCompute(3, 2, 1);
|
glDispatchCompute(3, 2, 1);
|
||||||
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 4, 0, 0, 4);//WHY DOES THIS FIX FUCKING BINDING ISSUES HERE WHEN DOING THIS IN THE RENDER SYSTEM DOESNT
|
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 3, 0, 0, 4);//WHY DOES THIS FIX FUCKING BINDING ISSUES HERE WHEN DOING THIS IN THE RENDER SYSTEM DOESNT
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
@@ -80,9 +74,6 @@ public class GlViewCapture {
|
|||||||
MemoryUtil.memPutLong(ptr, 0);
|
MemoryUtil.memPutLong(ptr, 0);
|
||||||
MemoryUtil.memPutLong(ptr+8, 0);
|
MemoryUtil.memPutLong(ptr+8, 0);
|
||||||
nglClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, ptr);
|
nglClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, ptr);
|
||||||
nglClearNamedFramebufferuiv(this.framebuffer.id, GL_COLOR, 1, ptr);
|
|
||||||
//TODO: fix the draw buffer thing maybe? it might need todo multiple clears
|
|
||||||
//nglClearNamedFramebufferfv(this.framebuffer.id, GL_COLOR, 0, ptr);
|
|
||||||
}
|
}
|
||||||
glClearNamedFramebufferfi(this.framebuffer.id, GL_DEPTH_STENCIL, 0, 1.0f, 0);
|
glClearNamedFramebufferfi(this.framebuffer.id, GL_DEPTH_STENCIL, 0, 1.0f, 0);
|
||||||
}
|
}
|
||||||
@@ -92,7 +83,6 @@ public class GlViewCapture {
|
|||||||
this.colourTex.free();
|
this.colourTex.free();
|
||||||
this.stencilTex.free();
|
this.stencilTex.free();
|
||||||
this.depthTex.free();
|
this.depthTex.free();
|
||||||
this.metaTex.free();
|
|
||||||
this.copyOutShader.free();
|
this.copyOutShader.free();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,11 +67,13 @@ public class ModelTextureBakery {
|
|||||||
|
|
||||||
int meta = getMetaFromLayer(layer);
|
int meta = getMetaFromLayer(layer);
|
||||||
|
|
||||||
for (var part : model.getParts(new LocalRandom(42L))) {
|
for (Direction direction : new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST, null}) {
|
||||||
for (Direction direction : new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST, null}) {
|
for (var part : model.getParts(new LocalRandom(42L))) {
|
||||||
var quads = part.getQuads(direction);
|
var quads = part.getQuads(direction);
|
||||||
for (var quad : quads) {
|
for (var quad : quads) {
|
||||||
this.vc.quad(quad, meta|(quad.hasTint()?4:0));
|
//TODO: add meta specifiying quad has a tint
|
||||||
|
//quad.hasTint()
|
||||||
|
this.vc.quad(quad, meta);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,14 +81,7 @@ public class ModelTextureBakery {
|
|||||||
|
|
||||||
|
|
||||||
private void bakeFluidState(BlockState state, BlockRenderLayer layer, int face) {
|
private void bakeFluidState(BlockState state, BlockRenderLayer layer, int face) {
|
||||||
{
|
this.vc.setDefaultMeta(getMetaFromLayer(layer));//Set the meta while baking
|
||||||
//TODO: somehow set the tint flag per quad or something?
|
|
||||||
int metadata = getMetaFromLayer(layer);
|
|
||||||
//Just assume all fluids are tinted, if they arnt it should be implicitly culled in the model baking phase
|
|
||||||
// since it wont have the colour provider
|
|
||||||
metadata |= 4;//Has tint
|
|
||||||
this.vc.setDefaultMeta(metadata);//Set the meta while baking
|
|
||||||
}
|
|
||||||
MinecraftClient.getInstance().getBlockRenderManager().renderFluid(BlockPos.ORIGIN, new BlockRenderView() {
|
MinecraftClient.getInstance().getBlockRenderManager().renderFluid(BlockPos.ORIGIN, new BlockRenderView() {
|
||||||
@Override
|
@Override
|
||||||
public float getBrightness(Direction direction, boolean shaded) {
|
public float getBrightness(Direction direction, boolean shaded) {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ public class SectionUpdateRouter implements ISectionWatcher {
|
|||||||
}
|
}
|
||||||
lock.unlock(stamp);
|
lock.unlock(stamp);
|
||||||
}
|
}
|
||||||
if (((delta&types)&UPDATE_TYPE_BLOCK_BIT)!=0) {
|
if ((delta&UPDATE_TYPE_BLOCK_BIT)!=0) {
|
||||||
//If we added it, immediately invoke for an update
|
//If we added it, immediately invoke for an update
|
||||||
this.initialRenderMeshGen.accept(position);
|
this.initialRenderMeshGen.accept(position);
|
||||||
}
|
}
|
||||||
@@ -138,32 +138,19 @@ public class SectionUpdateRouter implements ISectionWatcher {
|
|||||||
var lock = this.locks[idx];
|
var lock = this.locks[idx];
|
||||||
|
|
||||||
long stamp = lock.readLock();
|
long stamp = lock.readLock();
|
||||||
byte types = (byte) (set.getOrDefault(position, (byte) 0)&type);
|
byte types = set.getOrDefault(position, (byte) 0);
|
||||||
lock.unlockRead(stamp);
|
lock.unlockRead(stamp);
|
||||||
|
|
||||||
if (types!=0) {
|
if (types!=0) {
|
||||||
if ((types&WorldEngine.UPDATE_TYPE_CHILD_EXISTENCE_BIT)!=0) {
|
if ((type&WorldEngine.UPDATE_TYPE_CHILD_EXISTENCE_BIT)!=0) {
|
||||||
this.childUpdateCallback.accept(section);
|
this.childUpdateCallback.accept(section);
|
||||||
}
|
}
|
||||||
if ((types&UPDATE_TYPE_BLOCK_BIT)!=0) {
|
if ((type& UPDATE_TYPE_BLOCK_BIT)!=0) {
|
||||||
this.renderMeshGen.accept(section.key);
|
this.renderMeshGen.accept(section.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void triggerRemesh(long position) {
|
|
||||||
int idx = getSliceIndex(position);
|
|
||||||
var set = this.slices[idx];
|
|
||||||
var lock = this.locks[idx];
|
|
||||||
|
|
||||||
long stamp = lock.readLock();
|
|
||||||
byte types = set.getOrDefault(position, (byte) 0);
|
|
||||||
lock.unlockRead(stamp);
|
|
||||||
if ((types&UPDATE_TYPE_BLOCK_BIT)!=0) {
|
|
||||||
this.renderMeshGen.accept(position);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int getSliceIndex(long value) {
|
private static int getSliceIndex(long value) {
|
||||||
value = (value ^ value >>> 30) * -4658895280553007687L;
|
value = (value ^ value >>> 30) * -4658895280553007687L;
|
||||||
value = (value ^ value >>> 27) * -7723592293110705685L;
|
value = (value ^ value >>> 27) * -7723592293110705685L;
|
||||||
|
|||||||
@@ -111,9 +111,7 @@ public abstract class Viewport <A extends Viewport<A>> {
|
|||||||
(float) (this.cameraY-(sy<<5)),
|
(float) (this.cameraY-(sy<<5)),
|
||||||
(float) (this.cameraZ-(sz<<5)));
|
(float) (this.cameraZ-(sz<<5)));
|
||||||
|
|
||||||
if (this.depthBoundingBuffer.resize(this.width, this.height)) {
|
this.depthBoundingBuffer.resize(this.width, this.height);
|
||||||
this.depthBoundingBuffer.clear(0.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (A) this;
|
return (A) this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
public class RenderDataFactory {
|
public class RenderDataFactory {
|
||||||
private static final boolean CHECK_NEIGHBOR_FACE_OCCLUSION = true;
|
private static final boolean CHECK_NEIGHBOR_FACE_OCCLUSION = true;
|
||||||
private static final boolean DISABLE_CULL_SAME_OCCLUDES = false;//TODO: FIX TRANSLUCENTS (e.g. stained glass) breaking on chunk boarders with this set to false (it might be something else????)
|
private static final boolean DISABLE_CULL_SAME_OCCLUDES = false;
|
||||||
|
|
||||||
private static final boolean VERIFY_MESHING = VoxyCommon.isVerificationFlagOn("verifyMeshing");
|
private static final boolean VERIFY_MESHING = VoxyCommon.isVerificationFlagOn("verifyMeshing");
|
||||||
|
|
||||||
@@ -341,7 +341,7 @@ public class RenderDataFactory {
|
|||||||
private static final long LM = (0xFFL<<55);
|
private static final long LM = (0xFFL<<55);
|
||||||
|
|
||||||
private static boolean shouldMeshNonOpaqueBlockFace(int face, long quad, long meta, long neighborQuad, long neighborMeta) {
|
private static boolean shouldMeshNonOpaqueBlockFace(int face, long quad, long meta, long neighborQuad, long neighborMeta) {
|
||||||
if (((quad^neighborQuad)&(0xFFFFL<<26))==0 && (DISABLE_CULL_SAME_OCCLUDES || (ModelQueries.cullsSame(meta)||ModelQueries.faceOccludes(meta, face)))) return false;//This is a hack, if the neigbor and this are the same, dont mesh the face// TODO: FIXME
|
if (((quad^neighborQuad)&(0xFFFFL<<26))==0 && (DISABLE_CULL_SAME_OCCLUDES || ModelQueries.faceOccludes(meta, face))) return false;//This is a hack, if the neigbor and this are the same, dont mesh the face// TODO: FIXME
|
||||||
if (!ModelQueries.faceExists(meta, face)) return false;//Dont mesh if no face
|
if (!ModelQueries.faceExists(meta, face)) return false;//Dont mesh if no face
|
||||||
//if (ModelQueries.faceCanBeOccluded(meta, face)) //TODO: maybe enable this
|
//if (ModelQueries.faceCanBeOccluded(meta, face)) //TODO: maybe enable this
|
||||||
if (ModelQueries.faceOccludes(neighborMeta, face^1)) return false;
|
if (ModelQueries.faceOccludes(neighborMeta, face^1)) return false;
|
||||||
@@ -397,21 +397,16 @@ public class RenderDataFactory {
|
|||||||
int iA = idx * 2 + (facingForward == 1 ? 0 : shift);
|
int iA = idx * 2 + (facingForward == 1 ? 0 : shift);
|
||||||
int iB = idx * 2 + (facingForward == 1 ? shift : 0);
|
int iB = idx * 2 + (facingForward == 1 ? shift : 0);
|
||||||
|
|
||||||
long selfModel = this.sectionData[iA];
|
|
||||||
long nextModel = this.sectionData[iB];
|
|
||||||
|
|
||||||
//Check if next culls this face
|
//Check if next culls this face
|
||||||
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
||||||
long neighbor = this.sectionData[iB + 1];
|
if (ModelQueries.faceOccludes(this.sectionData[iB + 1], (axis << 1) | (1 - facingForward))) {
|
||||||
boolean culls = false;
|
|
||||||
culls |= ((selfModel^nextModel)&(0xFFFFL<<26))==0&&ModelQueries.cullsSame(neighbor);
|
|
||||||
culls |= ModelQueries.faceOccludes(neighbor, (axis << 1) | (1 - facingForward));
|
|
||||||
if (culls) {
|
|
||||||
this.blockMesher.skip(1);
|
this.blockMesher.skip(1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long selfModel = this.sectionData[iA];
|
||||||
|
long nextModel = this.sectionData[iB];
|
||||||
this.blockMesher.putNext(((long) facingForward) |//Facing
|
this.blockMesher.putNext(((long) facingForward) |//Facing
|
||||||
(selfModel&~LM) |
|
(selfModel&~LM) |
|
||||||
(nextModel&LM)//Apply lighting
|
(nextModel&LM)//Apply lighting
|
||||||
@@ -457,10 +452,8 @@ public class RenderDataFactory {
|
|||||||
|
|
||||||
int neighborIdx = ((axis+1)*32*32 * 2)+(side)*32*32;
|
int neighborIdx = ((axis+1)*32*32 * 2)+(side)*32*32;
|
||||||
long neighborId = this.neighboringFaces[neighborIdx + (other*32) + index];
|
long neighborId = this.neighboringFaces[neighborIdx + (other*32) + index];
|
||||||
long A = this.sectionData[idx * 2];
|
|
||||||
|
|
||||||
int nib = Mapper.getBlockId(neighborId);
|
if (Mapper.getBlockId(neighborId) != 0) {//Not air
|
||||||
if (nib != 0) {//Not air
|
|
||||||
long meta = this.modelMan.getModelMetadataFromClientId(this.modelMan.getModelId(Mapper.getBlockId(neighborId)));
|
long meta = this.modelMan.getModelMetadataFromClientId(this.modelMan.getModelId(Mapper.getBlockId(neighborId)));
|
||||||
if (ModelQueries.isFullyOpaque(meta)) {//Dont mesh this face
|
if (ModelQueries.isFullyOpaque(meta)) {//Dont mesh this face
|
||||||
this.blockMesher.skip(1);
|
this.blockMesher.skip(1);
|
||||||
@@ -470,10 +463,7 @@ public class RenderDataFactory {
|
|||||||
//This very funnily causes issues when not combined with meshing non full opaque geometry
|
//This very funnily causes issues when not combined with meshing non full opaque geometry
|
||||||
//TODO:FIXME, when non opaque geometry is added
|
//TODO:FIXME, when non opaque geometry is added
|
||||||
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
||||||
boolean culls = false;
|
if (ModelQueries.faceOccludes(meta, (axis << 1) | (1 - side))) {
|
||||||
culls |= nib==((A>>26)&0xFFFF)&&ModelQueries.cullsSame(meta);
|
|
||||||
culls |= ModelQueries.faceOccludes(meta, (axis << 1) | (1 - side));
|
|
||||||
if (culls) {
|
|
||||||
this.blockMesher.skip(1);
|
this.blockMesher.skip(1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -481,6 +471,7 @@ public class RenderDataFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
long A = this.sectionData[idx * 2];
|
||||||
|
|
||||||
this.blockMesher.putNext(((side == 0) ? 0L : 1L) |
|
this.blockMesher.putNext(((side == 0) ? 0L : 1L) |
|
||||||
(A&~LM) |
|
(A&~LM) |
|
||||||
@@ -540,7 +531,7 @@ public class RenderDataFactory {
|
|||||||
int bi = facingForward == 1 ? b : a;
|
int bi = facingForward == 1 ? b : a;
|
||||||
|
|
||||||
//TODO: check if must cull against next entries face
|
//TODO: check if must cull against next entries face
|
||||||
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {//TODO:SELF OCCLUSION
|
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
||||||
if (ModelQueries.faceOccludes(this.sectionData[bi + 1], (axis << 1) | (1 - facingForward))) {
|
if (ModelQueries.faceOccludes(this.sectionData[bi + 1], (axis << 1) | (1 - facingForward))) {
|
||||||
this.blockMesher.skip(1);
|
this.blockMesher.skip(1);
|
||||||
continue;
|
continue;
|
||||||
@@ -763,7 +754,6 @@ public class RenderDataFactory {
|
|||||||
if (Mapper.getBlockId(neighborId) != 0) {//Not air
|
if (Mapper.getBlockId(neighborId) != 0) {//Not air
|
||||||
int modelId = this.modelMan.getModelId(Mapper.getBlockId(neighborId));
|
int modelId = this.modelMan.getModelId(Mapper.getBlockId(neighborId));
|
||||||
if (modelId == ((A>>26)&0xFFFF)) {//TODO: FIXME, this technically isnt correct as need to check self occulsion, thinks?
|
if (modelId == ((A>>26)&0xFFFF)) {//TODO: FIXME, this technically isnt correct as need to check self occulsion, thinks?
|
||||||
//TODO: check self occlsuion in the if statment
|
|
||||||
fail = true;
|
fail = true;
|
||||||
} else {
|
} else {
|
||||||
long meta = this.modelMan.getModelMetadataFromClientId(modelId);
|
long meta = this.modelMan.getModelMetadataFromClientId(modelId);
|
||||||
@@ -778,7 +768,6 @@ public class RenderDataFactory {
|
|||||||
long nB = this.sectionData[(idx+skipAmount) * 2 + 1];
|
long nB = this.sectionData[(idx+skipAmount) * 2 + 1];
|
||||||
boolean failB = false;
|
boolean failB = false;
|
||||||
if ((nA&(0xFFFFL<<26)) == (A&(0xFFFFL<<26))) {//TODO: FIXME, this technically isnt correct as need to check self occulsion, thinks?
|
if ((nA&(0xFFFFL<<26)) == (A&(0xFFFFL<<26))) {//TODO: FIXME, this technically isnt correct as need to check self occulsion, thinks?
|
||||||
//TODO: check self occlsuion in the if statment
|
|
||||||
failB = true;
|
failB = true;
|
||||||
} else {
|
} else {
|
||||||
if (ModelQueries.faceOccludes(nB, (axis << 1) | (side))) {
|
if (ModelQueries.faceOccludes(nB, (axis << 1) | (side))) {
|
||||||
@@ -941,7 +930,6 @@ public class RenderDataFactory {
|
|||||||
|
|
||||||
//Check if next culls this face
|
//Check if next culls this face
|
||||||
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
||||||
//TODO: check self occlsuion
|
|
||||||
if (ModelQueries.faceOccludes(this.sectionData[iB + 1], (2 << 1) | (1 - facingForward))) {
|
if (ModelQueries.faceOccludes(this.sectionData[iB + 1], (2 << 1) | (1 - facingForward))) {
|
||||||
mesher.skip(1);
|
mesher.skip(1);
|
||||||
continue;
|
continue;
|
||||||
@@ -1008,7 +996,6 @@ public class RenderDataFactory {
|
|||||||
if (ModelQueries.isFullyOpaque(meta)) {
|
if (ModelQueries.isFullyOpaque(meta)) {
|
||||||
oki = false;
|
oki = false;
|
||||||
} else if (CHECK_NEIGHBOR_FACE_OCCLUSION && ModelQueries.faceOccludes(meta, (2 << 1) | (1 - 1))) {
|
} else if (CHECK_NEIGHBOR_FACE_OCCLUSION && ModelQueries.faceOccludes(meta, (2 << 1) | (1 - 1))) {
|
||||||
//TODO check self occlsion
|
|
||||||
oki = false;
|
oki = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1030,7 +1017,6 @@ public class RenderDataFactory {
|
|||||||
if (ModelQueries.isFullyOpaque(meta)) {
|
if (ModelQueries.isFullyOpaque(meta)) {
|
||||||
oki = false;
|
oki = false;
|
||||||
} else if (CHECK_NEIGHBOR_FACE_OCCLUSION && ModelQueries.faceOccludes(meta, (2 << 1) | (1 - 0))) {
|
} else if (CHECK_NEIGHBOR_FACE_OCCLUSION && ModelQueries.faceOccludes(meta, (2 << 1) | (1 - 0))) {
|
||||||
//TODO check self occlsion
|
|
||||||
oki = false;
|
oki = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1136,8 +1122,7 @@ public class RenderDataFactory {
|
|||||||
|
|
||||||
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
||||||
if (ModelQueries.faceOccludes(this.sectionData[bi + 1], (2 << 1) | (1 - facingForward))) {
|
if (ModelQueries.faceOccludes(this.sectionData[bi + 1], (2 << 1) | (1 - facingForward))) {
|
||||||
//TODO check self occlsion
|
this.blockMesher.skip(1);
|
||||||
mesher.skip(1);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1606,9 +1591,7 @@ public class RenderDataFactory {
|
|||||||
if (neighborMsk>>31!=0) {//We failed to get everything so throw exception
|
if (neighborMsk>>31!=0) {//We failed to get everything so throw exception
|
||||||
throw new IdNotYetComputedException(neighborMsk&(~(1<<31)), true);
|
throw new IdNotYetComputedException(neighborMsk&(~(1<<31)), true);
|
||||||
}
|
}
|
||||||
if (CHECK_NEIGHBOR_FACE_OCCLUSION) {
|
this.acquireNeighborData(section, neighborMsk);
|
||||||
this.acquireNeighborData(section, neighborMsk);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.generateYZFaces();
|
this.generateYZFaces();
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import me.cortex.voxy.common.Logger;
|
|||||||
import me.cortex.voxy.common.util.AllocationArena;
|
import me.cortex.voxy.common.util.AllocationArena;
|
||||||
import me.cortex.voxy.common.util.MemoryBuffer;
|
import me.cortex.voxy.common.util.MemoryBuffer;
|
||||||
import me.cortex.voxy.common.util.UnsafeUtil;
|
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.WorldSection;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
@@ -189,7 +188,7 @@ public class AsyncNodeManager {
|
|||||||
}
|
}
|
||||||
//This is a funny thing, wait a bit, this allows for better batching, but this thread is independent of everything else so waiting a bit should be mostly ok
|
//This is a funny thing, wait a bit, this allows for better batching, but this thread is independent of everything else so waiting a bit should be mostly ok
|
||||||
try {
|
try {
|
||||||
Thread.sleep(10);
|
Thread.sleep(25);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -514,7 +513,6 @@ public class AsyncNodeManager {
|
|||||||
|
|
||||||
var upload = results.geometryUpload;
|
var upload = results.geometryUpload;
|
||||||
if (!upload.dataUploadPoints.isEmpty()) {
|
if (!upload.dataUploadPoints.isEmpty()) {
|
||||||
((BasicSectionGeometryData)this.geometryData).ensureAccessable(upload.maxElementAccess);
|
|
||||||
TimingStatistics.A.start();
|
TimingStatistics.A.start();
|
||||||
|
|
||||||
int copies = upload.dataUploadPoints.size();
|
int copies = upload.dataUploadPoints.size();
|
||||||
@@ -761,20 +759,11 @@ public class AsyncNodeManager {
|
|||||||
return this.workCounter.get()!=0 || RESULT_HANDLE.get(this) != null;
|
return this.workCounter.get()!=0 || RESULT_HANDLE.get(this) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void worldEvent(WorldSection section, int flags, int neighborMask) {
|
public void worldEvent(WorldSection section, int flags) {
|
||||||
//If there is any change, we need to clear the geometry cache before emitting update
|
//If there is any change, we need to clear the geometry cache before emitting update
|
||||||
this.geometryCache.clear(section.key);
|
this.geometryCache.clear(section.key);
|
||||||
|
|
||||||
this.router.forwardEvent(section, flags);
|
this.router.forwardEvent(section, flags);
|
||||||
|
|
||||||
if (neighborMask != 0) {//trigger rebuilds for neighbors
|
|
||||||
if ((neighborMask&0b000001)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x, section.y-1, section.z));//-y
|
|
||||||
if ((neighborMask&0b000010)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x, section.y+1, section.z));//+y
|
|
||||||
if ((neighborMask&0b000100)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x-1, section.y, section.z));//-x
|
|
||||||
if ((neighborMask&0b001000)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x+1, section.y, section.z));//+x
|
|
||||||
if ((neighborMask&0b010000)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x, section.y, section.z-1));//-z
|
|
||||||
if ((neighborMask&0b100000)!=0) this.router.triggerRemesh(WorldEngine.getWorldSectionId(section.lvl, section.x, section.y, section.z+1));//+z
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Results object, which is to be synced between the render thread and worker thread
|
//Results object, which is to be synced between the render thread and worker thread
|
||||||
@@ -859,7 +848,6 @@ public class AsyncNodeManager {
|
|||||||
|
|
||||||
private static class ComputeMemoryCopy {
|
private static class ComputeMemoryCopy {
|
||||||
public int currentElemCopyAmount;
|
public int currentElemCopyAmount;
|
||||||
public int maxElementAccess;
|
|
||||||
private MemoryBuffer scratchHeaderBuffer = new MemoryBuffer(1<<16);
|
private MemoryBuffer scratchHeaderBuffer = new MemoryBuffer(1<<16);
|
||||||
private MemoryBuffer scratchDataBuffer = new MemoryBuffer(1<<20);
|
private MemoryBuffer scratchDataBuffer = new MemoryBuffer(1<<20);
|
||||||
|
|
||||||
@@ -911,7 +899,6 @@ public class AsyncNodeManager {
|
|||||||
public void upload(int point, MemoryBuffer data) {
|
public void upload(int point, MemoryBuffer data) {
|
||||||
if ((data.size%8)!=0) throw new IllegalStateException("Data must be of size multiple 8");
|
if ((data.size%8)!=0) throw new IllegalStateException("Data must be of size multiple 8");
|
||||||
int elemSize = (int) (data.size / 8);
|
int elemSize = (int) (data.size / 8);
|
||||||
this.maxElementAccess = Math.max(this.maxElementAccess, point + elemSize);
|
|
||||||
int header = this.dataUploadPoints.get(point);
|
int header = this.dataUploadPoints.get(point);
|
||||||
if (header != -1) {
|
if (header != -1) {
|
||||||
//If we already have a header location, we just need to reallocate the data
|
//If we already have a header location, we just need to reallocate the data
|
||||||
@@ -986,7 +973,6 @@ public class AsyncNodeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
this.maxElementAccess = 0;
|
|
||||||
this.currentElemCopyAmount = 0;
|
this.currentElemCopyAmount = 0;
|
||||||
this.dataUploadPoints.clear();
|
this.dataUploadPoints.clear();
|
||||||
this.arena.reset();
|
this.arena.reset();
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ public class NodeManager {
|
|||||||
|
|
||||||
var request = new SingleNodeRequest(pos);
|
var request = new SingleNodeRequest(pos);
|
||||||
int id = this.singleRequests.put(request);
|
int id = this.singleRequests.put(request);
|
||||||
this.watcher.watch(pos, WorldEngine.DEFAULT_UPDATE_FLAGS);
|
this.watcher.watch(pos, WorldEngine.UPDATE_FLAGS);
|
||||||
this.activeSectionMap.put(pos, id|NODE_TYPE_REQUEST|REQUEST_TYPE_SINGLE);
|
this.activeSectionMap.put(pos, id|NODE_TYPE_REQUEST|REQUEST_TYPE_SINGLE);
|
||||||
this.topLevelNodes.add(pos);
|
this.topLevelNodes.add(pos);
|
||||||
}
|
}
|
||||||
@@ -353,7 +353,7 @@ public class NodeManager {
|
|||||||
throw new IllegalStateException("Child pos was in a request but not in active section map");
|
throw new IllegalStateException("Child pos was in a request but not in active section map");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.watcher.unwatch(cPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
|
if (!this.watcher.unwatch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Child pos was not being watched");
|
throw new IllegalStateException("Child pos was not being watched");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,7 +371,7 @@ public class NodeManager {
|
|||||||
if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) {
|
if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) {
|
||||||
throw new IllegalStateException("Child pos was already in active section tracker but was part of a request");
|
throw new IllegalStateException("Child pos was already in active section tracker but was part of a request");
|
||||||
}
|
}
|
||||||
if (!this.watcher.watch(cPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
|
if (!this.watcher.watch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Child pos update router issue");
|
throw new IllegalStateException("Child pos update router issue");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -428,7 +428,7 @@ public class NodeManager {
|
|||||||
if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) {
|
if (this.activeSectionMap.put(cPos, requestId|NODE_TYPE_REQUEST|REQUEST_TYPE_CHILD) != -1) {
|
||||||
throw new IllegalStateException("Child pos was already in active section tracker but was part of a request");
|
throw new IllegalStateException("Child pos was already in active section tracker but was part of a request");
|
||||||
}
|
}
|
||||||
if (!this.watcher.watch(cPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
|
if (!this.watcher.watch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Child pos update router issue");
|
throw new IllegalStateException("Child pos update router issue");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -467,7 +467,7 @@ public class NodeManager {
|
|||||||
if (cnid == -1 || (cnid&NODE_TYPE_MSK) != NODE_TYPE_REQUEST) {//TODO: verify the removed section is a request type of child and the request id matches this
|
if (cnid == -1 || (cnid&NODE_TYPE_MSK) != NODE_TYPE_REQUEST) {//TODO: verify the removed section is a request type of child and the request id matches this
|
||||||
throw new IllegalStateException("Child pos was in a request but not in active section map");
|
throw new IllegalStateException("Child pos was in a request but not in active section map");
|
||||||
}
|
}
|
||||||
if (!this.watcher.unwatch(cPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
|
if (!this.watcher.unwatch(cPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Child pos was not being watched");
|
throw new IllegalStateException("Child pos was not being watched");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -663,7 +663,7 @@ public class NodeManager {
|
|||||||
if ((cId&NODE_TYPE_MSK) != NODE_TYPE_REQUEST || (cId&REQUEST_TYPE_MSK) != REQUEST_TYPE_CHILD || (cId&NODE_ID_MSK) != reqId) {
|
if ((cId&NODE_TYPE_MSK) != NODE_TYPE_REQUEST || (cId&REQUEST_TYPE_MSK) != REQUEST_TYPE_CHILD || (cId&NODE_ID_MSK) != reqId) {
|
||||||
throw new IllegalStateException("Invalid child active state map: " + cId);
|
throw new IllegalStateException("Invalid child active state map: " + cId);
|
||||||
}
|
}
|
||||||
if (!this.watcher.unwatch(childPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
|
if (!this.watcher.unwatch(childPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Pos was not being watched");
|
throw new IllegalStateException("Pos was not being watched");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -785,7 +785,7 @@ public class NodeManager {
|
|||||||
this.invalidateNode(nodeId);
|
this.invalidateNode(nodeId);
|
||||||
|
|
||||||
//Unwatch position
|
//Unwatch position
|
||||||
if (!this.watcher.unwatch(pos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
|
if (!this.watcher.unwatch(pos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Pos was not being watched");
|
throw new IllegalStateException("Pos was not being watched");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -795,7 +795,7 @@ public class NodeManager {
|
|||||||
this.invalidateNode(nodeId);
|
this.invalidateNode(nodeId);
|
||||||
}
|
}
|
||||||
} else if (type == NODE_TYPE_REQUEST) {
|
} else if (type == NODE_TYPE_REQUEST) {
|
||||||
if (!this.watcher.unwatch(pos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
|
if (!this.watcher.unwatch(pos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Pos was not being watched");
|
throw new IllegalStateException("Pos was not being watched");
|
||||||
}
|
}
|
||||||
if ((nodeId&REQUEST_TYPE_MSK) == REQUEST_TYPE_SINGLE) {
|
if ((nodeId&REQUEST_TYPE_MSK) == REQUEST_TYPE_SINGLE) {
|
||||||
@@ -1072,8 +1072,7 @@ public class NodeManager {
|
|||||||
public void processRequest(long pos) {
|
public void processRequest(long pos) {
|
||||||
int nodeId = this.activeSectionMap.get(pos);
|
int nodeId = this.activeSectionMap.get(pos);
|
||||||
if (nodeId == -1) {
|
if (nodeId == -1) {
|
||||||
//TODO: make into timing thing
|
Logger.warn("Got request for pos " + WorldEngine.pprintPos(pos) + " but it was not in active map, ignoring!");
|
||||||
//Logger.warn("Got request for pos " + WorldEngine.pprintPos(pos) + " but it was not in active map, ignoring!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int nodeType = nodeId&NODE_TYPE_MSK;
|
int nodeType = nodeId&NODE_TYPE_MSK;
|
||||||
@@ -1184,7 +1183,7 @@ public class NodeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Watch and request the child node at the given position
|
//Watch and request the child node at the given position
|
||||||
if (!this.watcher.watch(childPos, WorldEngine.DEFAULT_UPDATE_FLAGS)) {
|
if (!this.watcher.watch(childPos, WorldEngine.UPDATE_FLAGS)) {
|
||||||
throw new IllegalStateException("Failed to watch childPos");
|
throw new IllegalStateException("Failed to watch childPos");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1238,8 +1237,7 @@ public class NodeManager {
|
|||||||
int nodeType = nodeId&NODE_TYPE_MSK;
|
int nodeType = nodeId&NODE_TYPE_MSK;
|
||||||
nodeId &= NODE_ID_MSK;
|
nodeId &= NODE_ID_MSK;
|
||||||
if (nodeType == NODE_TYPE_REQUEST) {
|
if (nodeType == NODE_TYPE_REQUEST) {
|
||||||
//TODO: only log a specific number of times
|
Logger.warn("Tried removing geometry for pos: " + WorldEngine.pprintPos(pos) + " but its type was a request, ignoring!");
|
||||||
//Logger.warn("Tried removing geometry for pos: " + WorldEngine.pprintPos(pos) + " but its type was a request, ignoring!");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//this.clearId(nodeId);
|
//this.clearId(nodeId);
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ public class TestNodeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String[] getPrettyTypes(int msk) {
|
private static String[] getPrettyTypes(int msk) {
|
||||||
if ((msk&~(DEFAULT_UPDATE_FLAGS|UPDATE_TYPE_DONT_SAVE))!=0) {
|
if ((msk&~UPDATE_FLAGS)!=0) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
String[] types = new String[Integer.bitCount(msk)];
|
String[] types = new String[Integer.bitCount(msk)];
|
||||||
@@ -150,9 +150,6 @@ public class TestNodeManager {
|
|||||||
if ((msk&UPDATE_TYPE_CHILD_EXISTENCE_BIT)!=0) {
|
if ((msk&UPDATE_TYPE_CHILD_EXISTENCE_BIT)!=0) {
|
||||||
types[i++] = "CHILD";
|
types[i++] = "CHILD";
|
||||||
}
|
}
|
||||||
if ((msk&UPDATE_TYPE_DONT_SAVE)!=0) {
|
|
||||||
types[i++] = "DONT_SAVE";
|
|
||||||
}
|
|
||||||
return types;
|
return types;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
|
|||||||
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||||
import me.cortex.voxy.common.Logger;
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.world.WorldEngine;
|
import me.cortex.voxy.common.world.WorldEngine;
|
||||||
import net.minecraft.client.MinecraftClient;
|
|
||||||
import org.joml.Matrix4f;
|
import org.joml.Matrix4f;
|
||||||
import org.lwjgl.system.MemoryUtil;
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
@@ -103,9 +102,6 @@ public class MDICSectionRenderer extends AbstractSectionRenderer<MDICViewport, B
|
|||||||
var builder = Shader.make()
|
var builder = Shader.make()
|
||||||
.defineIf("TAA_PATCH", taa != null)
|
.defineIf("TAA_PATCH", taa != null)
|
||||||
.defineIf("DEBUG_RENDER", false)
|
.defineIf("DEBUG_RENDER", false)
|
||||||
|
|
||||||
.defineIf("DARKENED_TINTING", MinecraftClient.getInstance().world.getDimensionEffects().isDarkened())//TODO: FIXME: this is really jank atm
|
|
||||||
|
|
||||||
.addSource(ShaderType.VERTEX, vertex);
|
.addSource(ShaderType.VERTEX, vertex);
|
||||||
|
|
||||||
String frag = ShaderLoader.parse("voxy:lod/gl46/quads.frag");
|
String frag = ShaderLoader.parse("voxy:lod/gl46/quads.frag");
|
||||||
|
|||||||
@@ -0,0 +1,231 @@
|
|||||||
|
package me.cortex.voxy.client.core.rendering.section;
|
||||||
|
|
||||||
|
|
||||||
|
import me.cortex.voxy.client.RenderStatistics;
|
||||||
|
import me.cortex.voxy.client.core.AbstractRenderPipeline;
|
||||||
|
import me.cortex.voxy.client.core.gl.Capabilities;
|
||||||
|
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||||
|
import me.cortex.voxy.client.core.gl.GlTexture;
|
||||||
|
import me.cortex.voxy.client.core.gl.GlVertexArray;
|
||||||
|
import me.cortex.voxy.client.core.gl.shader.Shader;
|
||||||
|
import me.cortex.voxy.client.core.gl.shader.ShaderType;
|
||||||
|
import me.cortex.voxy.client.core.model.ModelStore;
|
||||||
|
import me.cortex.voxy.client.core.rendering.section.geometry.BasicSectionGeometryData;
|
||||||
|
import me.cortex.voxy.client.core.rendering.util.DownloadStream;
|
||||||
|
import me.cortex.voxy.client.core.rendering.util.LightMapHelper;
|
||||||
|
import me.cortex.voxy.client.core.rendering.util.SharedIndexBuffer;
|
||||||
|
import me.cortex.voxy.client.core.rendering.util.UploadStream;
|
||||||
|
import me.cortex.voxy.common.Logger;
|
||||||
|
import me.cortex.voxy.common.world.WorldEngine;
|
||||||
|
import org.joml.Matrix4f;
|
||||||
|
import org.lwjgl.system.MemoryUtil;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static me.cortex.voxy.client.core.gl.EXTMeshShader.glDrawMeshTasksIndirectEXT;
|
||||||
|
import static org.lwjgl.opengl.GL11.*;
|
||||||
|
import static org.lwjgl.opengl.GL11C.GL_UNSIGNED_INT;
|
||||||
|
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
|
||||||
|
import static org.lwjgl.opengl.GL15.glBindBuffer;
|
||||||
|
import static org.lwjgl.opengl.GL30.glBindBufferBase;
|
||||||
|
import static org.lwjgl.opengl.GL30.glBindVertexArray;
|
||||||
|
import static org.lwjgl.opengl.GL30C.GL_R32UI;
|
||||||
|
import static org.lwjgl.opengl.GL30C.GL_RED_INTEGER;
|
||||||
|
import static org.lwjgl.opengl.GL31.GL_UNIFORM_BUFFER;
|
||||||
|
import static org.lwjgl.opengl.GL33.glBindSampler;
|
||||||
|
import static org.lwjgl.opengl.GL40C.GL_DRAW_INDIRECT_BUFFER;
|
||||||
|
import static org.lwjgl.opengl.GL42.glMemoryBarrier;
|
||||||
|
import static org.lwjgl.opengl.GL43.*;
|
||||||
|
import static org.lwjgl.opengl.GL45.*;
|
||||||
|
import static org.lwjgl.opengl.NVRepresentativeFragmentTest.GL_REPRESENTATIVE_FRAGMENT_TEST_NV;
|
||||||
|
|
||||||
|
//Uses MDIC to render the sections
|
||||||
|
public class MeshEXTSectionRenderer extends AbstractSectionRenderer<MeshViewport, BasicSectionGeometryData> {
|
||||||
|
private static final int STATISTICS_BUFFER_BINDING = 8;
|
||||||
|
private final Shader terrainShader = Shader.make()
|
||||||
|
.define("MESH_SIZE", 32)//16
|
||||||
|
|
||||||
|
.defineIf("HAS_STATISTICS", RenderStatistics.enabled)
|
||||||
|
.defineIf("STATISTICS_BUFFER_BINDING", RenderStatistics.enabled, STATISTICS_BUFFER_BINDING)
|
||||||
|
|
||||||
|
.add(ShaderType.TASK, "voxy:lod/meshext/task.glsl")
|
||||||
|
.add(ShaderType.MESH, "voxy:lod/meshext/mesh.glsl")
|
||||||
|
.add(ShaderType.FRAGMENT, "voxy:lod/meshext/frag.glsl")
|
||||||
|
.compile();
|
||||||
|
|
||||||
|
private final Shader cullShader = Shader.make()
|
||||||
|
.add(ShaderType.VERTEX, "voxy:lod/gl46/cull/raster.vert")
|
||||||
|
.add(ShaderType.FRAGMENT, "voxy:lod/gl46/cull/raster.frag")
|
||||||
|
.compile();
|
||||||
|
|
||||||
|
private final GlBuffer uniform = new GlBuffer(1024).zero();
|
||||||
|
private final GlBuffer cullAndMeshDrawCommand = new GlBuffer(8*4).zero();//TODO: this needs tobe in the viewport
|
||||||
|
|
||||||
|
//Statistics
|
||||||
|
private final GlBuffer statisticsBuffer = new GlBuffer(1024).zero();
|
||||||
|
|
||||||
|
private final AbstractRenderPipeline pipeline;
|
||||||
|
public MeshEXTSectionRenderer(AbstractRenderPipeline pipeline, ModelStore modelStore, BasicSectionGeometryData geometryData) {
|
||||||
|
super(modelStore, geometryData);
|
||||||
|
this.pipeline = pipeline;
|
||||||
|
glClearNamedBufferSubData(this.cullAndMeshDrawCommand.id, GL_R32UI,0, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{6*2*3});//count
|
||||||
|
glClearNamedBufferSubData(this.cullAndMeshDrawCommand.id, GL_R32UI,8, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{(1<<16)*6*2});//firstIndex
|
||||||
|
glClearNamedBufferSubData(this.cullAndMeshDrawCommand.id, GL_R32UI,5*4+4, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{1});//y
|
||||||
|
glClearNamedBufferSubData(this.cullAndMeshDrawCommand.id, GL_R32UI,5*4+8, 4, GL_RED_INTEGER, GL_UNSIGNED_INT, new int[]{1});//z
|
||||||
|
}
|
||||||
|
|
||||||
|
private void uploadUniformBuffer(MeshViewport viewport) {
|
||||||
|
long ptr = UploadStream.INSTANCE.upload(this.uniform, 0, 1024);
|
||||||
|
|
||||||
|
var mat = new Matrix4f(viewport.MVP);
|
||||||
|
mat.translate(-viewport.innerTranslation.x, -viewport.innerTranslation.y, -viewport.innerTranslation.z);
|
||||||
|
mat.getToAddress(ptr); ptr += 4*4*4;
|
||||||
|
|
||||||
|
viewport.section.getToAddress(ptr); ptr += 4*3;
|
||||||
|
|
||||||
|
if (viewport.frameId<0) {
|
||||||
|
Logger.error("Frame ID negative, this will cause things to break, wrapping around");
|
||||||
|
viewport.frameId &= 0x7fffffff;
|
||||||
|
}
|
||||||
|
MemoryUtil.memPutInt(ptr, viewport.frameId&0x7fffffff); ptr += 4;
|
||||||
|
viewport.innerTranslation.getToAddress(ptr); ptr += 4*3;
|
||||||
|
|
||||||
|
ptr += 4;// padd
|
||||||
|
|
||||||
|
MemoryUtil.memPutFloat(ptr, viewport.width); ptr += 4;
|
||||||
|
MemoryUtil.memPutFloat(ptr, viewport.height); ptr += 4;
|
||||||
|
|
||||||
|
UploadStream.INSTANCE.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void bindRenderingBuffers(MeshViewport viewport) {
|
||||||
|
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, viewport.getRenderList().id);
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, this.geometryManager.getMetadataBuffer().id);
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, viewport.visibilityBuffer.id);
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, this.geometryManager.getGeometryBuffer().id);
|
||||||
|
this.modelStore.bind(5, 6, 0);
|
||||||
|
LightMapHelper.bind(1);
|
||||||
|
glBindTextureUnit(2, viewport.depthBoundingBuffer.getDepthTex().id);
|
||||||
|
|
||||||
|
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.cullAndMeshDrawCommand.id);
|
||||||
|
|
||||||
|
if (RenderStatistics.enabled) {
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, STATISTICS_BUFFER_BINDING, this.statisticsBuffer.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderTerrain(MeshViewport viewport) {
|
||||||
|
//RenderLayer.getCutoutMipped().startDrawing();
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
this.terrainShader.bind();
|
||||||
|
this.pipeline.setupAndBindOpaque(viewport);
|
||||||
|
this.bindRenderingBuffers(viewport);
|
||||||
|
|
||||||
|
glMemoryBarrier(GL_COMMAND_BARRIER_BIT|GL_SHADER_STORAGE_BARRIER_BIT);//Barrier everything is needed
|
||||||
|
|
||||||
|
glDrawMeshTasksIndirectEXT(20);
|
||||||
|
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glBindSampler(0, 0);
|
||||||
|
glBindTextureUnit(0, 0);
|
||||||
|
glBindSampler(1, 0);
|
||||||
|
glBindTextureUnit(1, 0);
|
||||||
|
|
||||||
|
//RenderLayer.getCutoutMipped().endDrawing();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderOpaque(MeshViewport viewport) {
|
||||||
|
if (this.geometryManager.getSectionCount() == 0) return;
|
||||||
|
|
||||||
|
this.uploadUniformBuffer(viewport);
|
||||||
|
|
||||||
|
this.renderTerrain(viewport);
|
||||||
|
|
||||||
|
//We need todo the statistics here as rastering is part of them, download then clear
|
||||||
|
if (RenderStatistics.enabled) {
|
||||||
|
DownloadStream.INSTANCE.download(this.statisticsBuffer, down->{
|
||||||
|
final int LAYERS = WorldEngine.MAX_LOD_LAYER+1;
|
||||||
|
for (int i = 0; i < LAYERS; i++) {
|
||||||
|
RenderStatistics.visibleSections[i] = MemoryUtil.memGetInt(down.address+i*4L);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < LAYERS; i++) {
|
||||||
|
RenderStatistics.quadCount[i] = MemoryUtil.memGetInt(down.address+LAYERS*4L+i*4L);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.statisticsBuffer.zero();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderTranslucent(MeshViewport viewport) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void buildDrawCalls(MeshViewport viewport) {
|
||||||
|
if (this.geometryManager.getSectionCount() == 0) return;
|
||||||
|
this.uploadUniformBuffer(viewport);
|
||||||
|
//Can do a sneeky trick, since the sectionRenderList is a list to things to render, it invokes the culler
|
||||||
|
// which only marks visible sections
|
||||||
|
|
||||||
|
|
||||||
|
{//Test occlusion
|
||||||
|
glCopyNamedBufferSubData(viewport.getRenderList().id, this.cullAndMeshDrawCommand.id, 0, 4, 4);//Copy counts to the draw buffer
|
||||||
|
glCopyNamedBufferSubData(viewport.getRenderList().id, this.cullAndMeshDrawCommand.id, 0, 20, 4);//Copy counts to the draw buffer
|
||||||
|
|
||||||
|
this.cullShader.bind();
|
||||||
|
if (Capabilities.INSTANCE.repFragTest) {
|
||||||
|
glEnable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV);
|
||||||
|
}
|
||||||
|
glBindVertexArray(GlVertexArray.STATIC_VAO);
|
||||||
|
glBindBufferBase(GL_UNIFORM_BUFFER, 0, this.uniform.id);
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, this.geometryManager.getMetadataBuffer().id);
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, viewport.visibilityBuffer.id);
|
||||||
|
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, viewport.getRenderList().id);
|
||||||
|
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, this.cullAndMeshDrawCommand.id);
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedIndexBuffer.INSTANCE.id());
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glColorMask(false, false, false, false);
|
||||||
|
glDepthMask(false);
|
||||||
|
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT|GL_COMMAND_BARRIER_BIT);
|
||||||
|
glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_BYTE, 0);
|
||||||
|
glDepthMask(true);
|
||||||
|
glColorMask(true, true, true, true);
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
if (Capabilities.INSTANCE.repFragTest) {
|
||||||
|
glDisable(GL_REPRESENTATIVE_FRAGMENT_TEST_NV);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void renderTemporal(MeshViewport viewport) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addDebug(List<String> lines) {
|
||||||
|
super.addDebug(lines);
|
||||||
|
//lines.add("SC/GS: " + this.geometryManager.getSectionCount() + "/" + (this.geometryManager.getGeometryUsed()/(1024*1024)));//section count/geometry size (MB)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MeshViewport createViewport() {
|
||||||
|
return new MeshViewport(this.geometryManager.getMaxSectionCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void free() {
|
||||||
|
this.cullAndMeshDrawCommand.free();
|
||||||
|
this.uniform.free();
|
||||||
|
this.terrainShader.free();
|
||||||
|
this.cullShader.free();
|
||||||
|
this.statisticsBuffer.free();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package me.cortex.voxy.client.core.rendering.section;
|
||||||
|
|
||||||
|
import me.cortex.voxy.client.core.gl.GlBuffer;
|
||||||
|
import me.cortex.voxy.client.core.rendering.Viewport;
|
||||||
|
import me.cortex.voxy.client.core.rendering.hierachical.HierarchicalOcclusionTraverser;
|
||||||
|
|
||||||
|
public class MeshViewport extends Viewport<MeshViewport> {
|
||||||
|
public final GlBuffer indirectLookupBuffer = new GlBuffer(HierarchicalOcclusionTraverser.MAX_QUEUE_SIZE *4+4);
|
||||||
|
public final GlBuffer visibilityBuffer;
|
||||||
|
|
||||||
|
public MeshViewport(int maxSectionCount) {
|
||||||
|
this.visibilityBuffer = new GlBuffer(maxSectionCount*4L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void delete0() {
|
||||||
|
super.delete0();
|
||||||
|
this.visibilityBuffer.free();
|
||||||
|
this.indirectLookupBuffer.free();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GlBuffer getRenderList() {
|
||||||
|
return this.indirectLookupBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,12 +34,12 @@ public class BasicSectionGeometryData implements IGeometryData {
|
|||||||
Logger.info("if your game crashes/exits here without any other log message, try manually decreasing the geometry capacity");
|
Logger.info("if your game crashes/exits here without any other log message, try manually decreasing the geometry capacity");
|
||||||
glGetError();//Clear any errors
|
glGetError();//Clear any errors
|
||||||
GlBuffer buffer = null;
|
GlBuffer buffer = null;
|
||||||
if (!(Capabilities.INSTANCE.isNvidia)) {// && ThreadUtils.isWindows
|
if (!(Capabilities.INSTANCE.isNvidia && ThreadUtils.isWindows)) {
|
||||||
buffer = new GlBuffer(geometryCapacity, false);//Only do this if we are not on nvidia
|
buffer = new GlBuffer(geometryCapacity, false);//Only do this if we are not on nvidia
|
||||||
//TODO: FIXME: TEST, see if the issue is that we are trying to zero the entire buffer, try only zeroing increments
|
//TODO: FIXME: TEST, see if the issue is that we are trying to zero the entire buffer, try only zeroing increments
|
||||||
// or dont zero it at all
|
// or dont zero it at all
|
||||||
} else {
|
} else {
|
||||||
Logger.info("Running on nvidia, using workaround sparse buffer allocation");
|
Logger.info("Running on windows nvidia, using workaround sparse buffer allocation");
|
||||||
}
|
}
|
||||||
int error = glGetError();
|
int error = glGetError();
|
||||||
if (error != GL_NO_ERROR || buffer == null) {
|
if (error != GL_NO_ERROR || buffer == null) {
|
||||||
@@ -49,6 +49,9 @@ public class BasicSectionGeometryData implements IGeometryData {
|
|||||||
buffer.free();
|
buffer.free();
|
||||||
}
|
}
|
||||||
buffer = new GlBuffer(geometryCapacity, GL_SPARSE_STORAGE_BIT_ARB);
|
buffer = new GlBuffer(geometryCapacity, GL_SPARSE_STORAGE_BIT_ARB);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, buffer.id);
|
||||||
|
glBufferPageCommitmentARB(GL_ARRAY_BUFFER, 0, geometryCapacity, true);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
//buffer.zero();
|
//buffer.zero();
|
||||||
error = glGetError();
|
error = glGetError();
|
||||||
if (error != GL_NO_ERROR) {
|
if (error != GL_NO_ERROR) {
|
||||||
@@ -64,20 +67,6 @@ public class BasicSectionGeometryData implements IGeometryData {
|
|||||||
Logger.info("Successfully allocated the geometry buffer in " + delta + "ms");
|
Logger.info("Successfully allocated the geometry buffer in " + delta + "ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
private long sparseCommitment = 0;//Tracks the current range of the allocated sparse buffer
|
|
||||||
public void ensureAccessable(int maxElementAccess) {
|
|
||||||
long size = (Integer.toUnsignedLong(maxElementAccess)*8L+65535L)&~65535L;
|
|
||||||
//If we are a sparse buffer, ensure the memory upto the requested size is allocated
|
|
||||||
if (this.geometryBuffer.isSparse()) {
|
|
||||||
if (this.sparseCommitment < size) {//if we try to access memory outside the allocation range, allocate it
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, this.geometryBuffer.id);
|
|
||||||
glBufferPageCommitmentARB(GL_ARRAY_BUFFER, this.sparseCommitment, size-this.sparseCommitment, true);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
||||||
this.sparseCommitment = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlBuffer getGeometryBuffer() {
|
public GlBuffer getGeometryBuffer() {
|
||||||
return this.geometryBuffer;
|
return this.geometryBuffer;
|
||||||
}
|
}
|
||||||
@@ -111,20 +100,11 @@ public class BasicSectionGeometryData implements IGeometryData {
|
|||||||
glFinish();
|
glFinish();
|
||||||
gpuMemory = Capabilities.INSTANCE.getFreeDedicatedGpuMemory();
|
gpuMemory = Capabilities.INSTANCE.getFreeDedicatedGpuMemory();
|
||||||
}
|
}
|
||||||
if (this.geometryBuffer.isSparse()) {
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, this.geometryBuffer.id);
|
|
||||||
glBufferPageCommitmentARB(GL_ARRAY_BUFFER, 0, this.sparseCommitment, false);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
glFinish();
|
glFinish();
|
||||||
this.geometryBuffer.free();
|
this.geometryBuffer.free();
|
||||||
glFinish();
|
glFinish();
|
||||||
if (Capabilities.INSTANCE.canQueryGpuMemory) {
|
if (Capabilities.INSTANCE.canQueryGpuMemory) {
|
||||||
long releaseSize = (long) (this.geometryBuffer.size()*0.75);//if gpu memory usage drops by 75% of the expected value assume we freed it
|
long releaseSize = (long) (this.geometryBuffer.size()*0.75);//if gpu memory usage drops by 75% of the expected value assume we freed it
|
||||||
if (this.geometryBuffer.isSparse()) {//If we are using sparse buffers, use the commited size instead
|
|
||||||
releaseSize = (long)(this.sparseCommitment*0.75);
|
|
||||||
}
|
|
||||||
if (Capabilities.INSTANCE.getFreeDedicatedGpuMemory()-gpuMemory<=releaseSize) {
|
if (Capabilities.INSTANCE.getFreeDedicatedGpuMemory()-gpuMemory<=releaseSize) {
|
||||||
Logger.info("Attempting to wait for gpu memory to release");
|
Logger.info("Attempting to wait for gpu memory to release");
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
|
|||||||
@@ -22,16 +22,14 @@ public class DepthFramebuffer {
|
|||||||
this.depthType = depthType;
|
this.depthType = depthType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean resize(int width, int height) {
|
public void resize(int width, int height) {
|
||||||
if (this.depthBuffer == null || this.depthBuffer.getWidth() != width || this.depthBuffer.getHeight() != height) {
|
if (this.depthBuffer == null || this.depthBuffer.getWidth() != width || this.depthBuffer.getHeight() != height) {
|
||||||
if (this.depthBuffer != null) {
|
if (this.depthBuffer != null) {
|
||||||
this.depthBuffer.free();
|
this.depthBuffer.free();
|
||||||
}
|
}
|
||||||
this.depthBuffer = new GlTexture().store(this.depthType, 1, width, height);
|
this.depthBuffer = new GlTexture().store(this.depthType, 1, width, height);
|
||||||
this.framebuffer.bind(this.depthType == GL_DEPTH24_STENCIL8?GL_DEPTH_STENCIL_ATTACHMENT: GL_DEPTH_ATTACHMENT, this.depthBuffer).verify();
|
this.framebuffer.bind(this.depthType == GL_DEPTH24_STENCIL8?GL_DEPTH_STENCIL_ATTACHMENT: GL_DEPTH_ATTACHMENT, this.depthBuffer).verify();
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class HiZBuffer2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void alloc(int width, int height) {
|
private void alloc(int width, int height) {
|
||||||
this.levels = Math.min(7,(int)Math.ceil(Math.log(Math.max(width, height))/Math.log(2)));
|
this.levels = (int)Math.ceil(Math.log(Math.max(width, height))/Math.log(2));
|
||||||
//We dont care about e.g. 1x1 size texture since you dont get meshlets that big to cover such a large area
|
//We dont care about e.g. 1x1 size texture since you dont get meshlets that big to cover such a large area
|
||||||
//this.levels -= 1;//Arbitrary size, shinks the max level by alot and saves a significant amount of processing time
|
//this.levels -= 1;//Arbitrary size, shinks the max level by alot and saves a significant amount of processing time
|
||||||
// (could probably increase it to be defined by a max meshlet coverage computation thing)
|
// (could probably increase it to be defined by a max meshlet coverage computation thing)
|
||||||
|
|||||||
@@ -6,19 +6,21 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
|
|||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import me.cortex.voxy.client.core.util.IrisUtil;
|
||||||
import me.cortex.voxy.common.Logger;
|
import me.cortex.voxy.common.Logger;
|
||||||
|
import net.irisshaders.iris.api.v0.IrisApi;
|
||||||
import net.irisshaders.iris.shaderpack.ShaderPack;
|
import net.irisshaders.iris.shaderpack.ShaderPack;
|
||||||
import net.irisshaders.iris.shaderpack.include.AbsolutePackPath;
|
import net.irisshaders.iris.shaderpack.include.AbsolutePackPath;
|
||||||
import org.lwjgl.opengl.ARBDrawBuffersBlend;
|
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.IntSupplier;
|
import java.util.function.IntSupplier;
|
||||||
|
|
||||||
import static org.lwjgl.opengl.GL11.*;
|
import static org.lwjgl.opengl.GL11.*;
|
||||||
import static org.lwjgl.opengl.GL33.*;
|
import static org.lwjgl.opengl.GL33.*;
|
||||||
|
import static org.lwjgl.opengl.GL40.glBlendFuncSeparatei;
|
||||||
|
|
||||||
public class IrisShaderPatch {
|
public class IrisShaderPatch {
|
||||||
public static final int VERSION = ((IntSupplier)()->1).getAsInt();
|
public static final int VERSION = ((IntSupplier)()->1).getAsInt();
|
||||||
@@ -77,7 +79,7 @@ public class IrisShaderPatch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public record BlendState(int buffer, boolean off, int sRGB, int dRGB, int sA, int dA) {
|
public record BlendState(int buffer, boolean off, int sRBG, int dRGb, int sA, int dA) {
|
||||||
public static BlendState ALL_OFF = new BlendState(-1, true, 0,0,0,0);
|
public static BlendState ALL_OFF = new BlendState(-1, true, 0,0,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,9 +87,6 @@ public class IrisShaderPatch {
|
|||||||
private static final class BlendStateDeserializer implements JsonDeserializer<Int2ObjectMap<BlendState>> {
|
private static final class BlendStateDeserializer implements JsonDeserializer<Int2ObjectMap<BlendState>> {
|
||||||
private static int parseType(String type) {
|
private static int parseType(String type) {
|
||||||
type = type.toUpperCase();
|
type = type.toUpperCase();
|
||||||
if (!type.startsWith("GL_")) {
|
|
||||||
type = "GL_"+type;
|
|
||||||
}
|
|
||||||
return switch (type) {
|
return switch (type) {
|
||||||
case "GL_ZERO" -> GL_ZERO;
|
case "GL_ZERO" -> GL_ZERO;
|
||||||
case "GL_ONE" -> GL_ONE;
|
case "GL_ONE" -> GL_ONE;
|
||||||
@@ -103,10 +102,7 @@ public class IrisShaderPatch {
|
|||||||
case "GL_SRC1_COLOR" -> GL_SRC1_COLOR;
|
case "GL_SRC1_COLOR" -> GL_SRC1_COLOR;
|
||||||
case "GL_ONE_MINUS_SRC1_COLOR" -> GL_ONE_MINUS_SRC1_COLOR;
|
case "GL_ONE_MINUS_SRC1_COLOR" -> GL_ONE_MINUS_SRC1_COLOR;
|
||||||
case "GL_ONE_MINUS_SRC1_ALPHA" -> GL_ONE_MINUS_SRC1_ALPHA;
|
case "GL_ONE_MINUS_SRC1_ALPHA" -> GL_ONE_MINUS_SRC1_ALPHA;
|
||||||
default -> {
|
default -> -1;
|
||||||
Logger.error("Unknown blend option " + type);
|
|
||||||
yield -1;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
@@ -122,30 +118,20 @@ public class IrisShaderPatch {
|
|||||||
} else if (json.isJsonObject()) {
|
} else if (json.isJsonObject()) {
|
||||||
for (var entry : json.getAsJsonObject().entrySet()) {
|
for (var entry : json.getAsJsonObject().entrySet()) {
|
||||||
int buffer = Integer.parseInt(entry.getKey());
|
int buffer = Integer.parseInt(entry.getKey());
|
||||||
BlendState state = null;
|
BlendState state;
|
||||||
var val = entry.getValue();
|
var val = entry.getValue();
|
||||||
List<String> bs = null;
|
|
||||||
if (val.isJsonArray()) {
|
if (val.isJsonArray()) {
|
||||||
bs = val.getAsJsonArray().asList().stream().map(JsonElement::getAsString).toList();
|
int[] v = val.getAsJsonArray().asList().stream().mapToInt(a->parseType(a.getAsString())).toArray();
|
||||||
|
state = new BlendState(buffer, false, v[0], v[1], v[2], v[3]);
|
||||||
} else if (val.isJsonPrimitive()) {
|
} else if (val.isJsonPrimitive()) {
|
||||||
var str = val.getAsString();
|
if (val.getAsString().equalsIgnoreCase("off")) {
|
||||||
if (str.equalsIgnoreCase("off")) {
|
|
||||||
state = new BlendState(buffer, true, 0,0,0,0);
|
state = new BlendState(buffer, true, 0,0,0,0);
|
||||||
} else {
|
} else {
|
||||||
var parts = str.split(" ");
|
state = new BlendState(buffer, true, -1,-1,-1,-1);
|
||||||
if (parts.length < 4) {
|
|
||||||
state = new BlendState(buffer, true, -1, -1, -1, -1);
|
|
||||||
} else {
|
|
||||||
bs = List.of(parts);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
state = null;
|
state = null;
|
||||||
}
|
}
|
||||||
if (bs != null) {
|
|
||||||
int[] v = bs.stream().mapToInt(BlendStateDeserializer::parseType).toArray();
|
|
||||||
state = new BlendState(buffer, false, v[0], v[1], v[2], v[3]);
|
|
||||||
}
|
|
||||||
ret.put(buffer, state);
|
ret.put(buffer, state);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -176,14 +162,6 @@ public class IrisShaderPatch {
|
|||||||
public float[] renderScale;
|
public float[] renderScale;
|
||||||
public boolean useViewportDims;
|
public boolean useViewportDims;
|
||||||
public boolean checkValid() {
|
public boolean checkValid() {
|
||||||
if (this.blending != null) {
|
|
||||||
for (BlendState state : this.blending.values()) {
|
|
||||||
if (state.buffer != -1 && (state.buffer<0||this.translucentDrawBuffers.length<=state.buffer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.opaqueDrawBuffers != null && this.translucentDrawBuffers != null && this.uniforms != null && this.opaquePatchData != null;
|
return this.opaqueDrawBuffers != null && this.translucentDrawBuffers != null && this.uniforms != null && this.opaquePatchData != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,7 +241,7 @@ public class IrisShaderPatch {
|
|||||||
glDisable(GL_BLEND);
|
glDisable(GL_BLEND);
|
||||||
} else {
|
} else {
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
glBlendFuncSeparate(init.sRGB, init.dRGB, init.sA, init.dA);
|
glBlendFuncSeparate(init.sRBG, init.dRGb, init.sA, init.dA);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (var entry:BS.int2ObjectEntrySet()) {
|
for (var entry:BS.int2ObjectEntrySet()) {
|
||||||
@@ -273,8 +251,7 @@ public class IrisShaderPatch {
|
|||||||
glDisablei(GL_BLEND, s.buffer);
|
glDisablei(GL_BLEND, s.buffer);
|
||||||
} else {
|
} else {
|
||||||
glEnablei(GL_BLEND, s.buffer);
|
glEnablei(GL_BLEND, s.buffer);
|
||||||
//_sigh_ thanks nvidia
|
glBlendFuncSeparatei(s.buffer, s.sRBG, s.dRGb, s.sA, s.dA);
|
||||||
ARBDrawBuffersBlend.glBlendFuncSeparateiARB(s.buffer, s.sRGB, s.dRGB, s.sA, s.dA);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -319,7 +296,7 @@ public class IrisShaderPatch {
|
|||||||
}
|
}
|
||||||
patchData = GSON.fromJson(voxyPatchData, PatchGson.class);
|
patchData = GSON.fromJson(voxyPatchData, PatchGson.class);
|
||||||
if (patchData == null) {
|
if (patchData == null) {
|
||||||
throw new IllegalStateException("Voxy patch json returned null");
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
{//Inject data from the auxilery files if they are present
|
{//Inject data from the auxilery files if they are present
|
||||||
@@ -331,11 +308,6 @@ public class IrisShaderPatch {
|
|||||||
if (translucent != null) {
|
if (translucent != null) {
|
||||||
patchData.translucentPatchData = translucent;
|
patchData.translucentPatchData = translucent;
|
||||||
}
|
}
|
||||||
//This might be ok? not.. sure if is nice or not
|
|
||||||
var taa = sourceProvider.apply(directory.resolve("voxy_taa.glsl"));
|
|
||||||
if (taa != null) {
|
|
||||||
patchData.taaOffset = taa;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!patchData.checkValid()) {
|
if (!patchData.checkValid()) {
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
package me.cortex.voxy.client.mixin.minecraft;
|
|
||||||
|
|
||||||
import me.cortex.voxy.client.config.VoxyConfig;
|
|
||||||
import me.cortex.voxy.client.core.IGetVoxyRenderSystem;
|
|
||||||
import me.cortex.voxy.common.world.service.VoxelIngestService;
|
|
||||||
import me.cortex.voxy.commonImpl.VoxyCommon;
|
|
||||||
import me.cortex.voxy.commonImpl.WorldIdentifier;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.client.network.ClientPlayNetworkHandler;
|
|
||||||
import net.minecraft.client.render.WorldRenderer;
|
|
||||||
import net.minecraft.client.world.ClientChunkManager;
|
|
||||||
import net.minecraft.client.world.ClientWorld;
|
|
||||||
import net.minecraft.registry.RegistryKey;
|
|
||||||
import net.minecraft.registry.entry.RegistryEntry;
|
|
||||||
import net.minecraft.util.math.BlockPos;
|
|
||||||
import net.minecraft.util.math.ChunkSectionPos;
|
|
||||||
import net.minecraft.world.LightType;
|
|
||||||
import net.minecraft.world.World;
|
|
||||||
import net.minecraft.world.dimension.DimensionType;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
|
||||||
import org.spongepowered.asm.mixin.Unique;
|
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|
||||||
|
|
||||||
@Mixin(ClientWorld.class)
|
|
||||||
public abstract class MixinClientWorld {
|
|
||||||
|
|
||||||
@Unique
|
|
||||||
private int bottomSectionY;
|
|
||||||
|
|
||||||
@Shadow @Final public WorldRenderer worldRenderer;
|
|
||||||
|
|
||||||
@Shadow public abstract ClientChunkManager getChunkManager();
|
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("TAIL"))
|
|
||||||
private void voxy$getBottom(
|
|
||||||
ClientPlayNetworkHandler networkHandler,
|
|
||||||
ClientWorld.Properties properties,
|
|
||||||
RegistryKey<World> registryRef,
|
|
||||||
RegistryEntry<DimensionType> dimensionType,
|
|
||||||
int loadDistance,
|
|
||||||
int simulationDistance,
|
|
||||||
WorldRenderer worldRenderer,
|
|
||||||
boolean debugWorld,
|
|
||||||
long seed,
|
|
||||||
int seaLevel,
|
|
||||||
CallbackInfo cir) {
|
|
||||||
this.bottomSectionY = ((World)(Object)this).getBottomY()>>4;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "scheduleBlockRerenderIfNeeded", at = @At("TAIL"))
|
|
||||||
private void voxy$injectIngestOnStateChange(BlockPos pos, BlockState old, BlockState updated, CallbackInfo cir) {
|
|
||||||
if (old == updated) return;
|
|
||||||
|
|
||||||
//TODO: is this _really_ needed, we should have enough processing power to not need todo it if its only a
|
|
||||||
// block removal
|
|
||||||
if (!updated.isAir()) return;
|
|
||||||
|
|
||||||
if (!VoxyConfig.CONFIG.ingestEnabled) return;//Only ingest if setting enabled
|
|
||||||
|
|
||||||
var self = (World)(Object)this;
|
|
||||||
var wi = WorldIdentifier.of(self);
|
|
||||||
if (wi == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int x = pos.getX()&15;
|
|
||||||
int y = pos.getY()&15;
|
|
||||||
int z = pos.getZ()&15;
|
|
||||||
if (x == 0 || x==15 || y==0 || y==15 || z==0||z==15) {//Update if there is a statechange on the boarder
|
|
||||||
var csp = ChunkSectionPos.from(pos);
|
|
||||||
|
|
||||||
var section = self.getChunk(pos).getSection(csp.getSectionY()-this.bottomSectionY);
|
|
||||||
var lp = self.getLightingProvider();
|
|
||||||
|
|
||||||
var blp = lp.get(LightType.BLOCK).getLightSection(csp);
|
|
||||||
var slp = lp.get(LightType.SKY).getLightSection(csp);
|
|
||||||
|
|
||||||
VoxelIngestService.rawIngest(wi, section, csp.getSectionX(), csp.getSectionY(), csp.getSectionZ(), blp==null?null:blp.copy(), slp==null?null:slp.copy());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,9 +8,6 @@ import org.spongepowered.asm.mixin.Mixin;
|
|||||||
import org.spongepowered.asm.mixin.Unique;
|
import org.spongepowered.asm.mixin.Unique;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.io.StringWriter;
|
|
||||||
|
|
||||||
@Mixin(GlDebug.class)
|
@Mixin(GlDebug.class)
|
||||||
public class MixinGlDebug {
|
public class MixinGlDebug {
|
||||||
@WrapOperation(method = "onDebugMessage", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;info(Ljava/lang/String;Ljava/lang/Object;)V", remap = false))
|
@WrapOperation(method = "onDebugMessage", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;info(Ljava/lang/String;Ljava/lang/Object;)V", remap = false))
|
||||||
@@ -18,7 +15,7 @@ public class MixinGlDebug {
|
|||||||
if (msgObj instanceof GlDebug.DebugMessage msg) {
|
if (msgObj instanceof GlDebug.DebugMessage msg) {
|
||||||
var throwable = new Throwable(msg.toString());
|
var throwable = new Throwable(msg.toString());
|
||||||
if (isCausedByVoxy(throwable.getStackTrace())) {
|
if (isCausedByVoxy(throwable.getStackTrace())) {
|
||||||
original.call(instance, base+"\n"+getStackTraceAsString(throwable), throwable);
|
original.call(instance, base, throwable);
|
||||||
} else {
|
} else {
|
||||||
original.call(instance, base, msg);
|
original.call(instance, base, msg);
|
||||||
}
|
}
|
||||||
@@ -27,14 +24,6 @@ public class MixinGlDebug {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Unique
|
|
||||||
private static String getStackTraceAsString(Throwable throwable) {
|
|
||||||
StringWriter sw = new StringWriter();
|
|
||||||
PrintWriter pw = new PrintWriter(sw);
|
|
||||||
throwable.printStackTrace(pw);
|
|
||||||
return sw.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Unique
|
@Unique
|
||||||
private boolean isCausedByVoxy(StackTraceElement[] trace) {
|
private boolean isCausedByVoxy(StackTraceElement[] trace) {
|
||||||
for (var elem : trace) {
|
for (var elem : trace) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
public class MixinWindow {
|
public class MixinWindow {
|
||||||
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/Window;throwOnGlError()V"))
|
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/Window;throwOnGlError()V"))
|
||||||
private void injectInitWindow(WindowEventHandler eventHandler, MonitorTracker monitorTracker, WindowSettings settings, String fullscreenVideoMode, String title, CallbackInfo ci) {
|
private void injectInitWindow(WindowEventHandler eventHandler, MonitorTracker monitorTracker, WindowSettings settings, String fullscreenVideoMode, String title, CallbackInfo ci) {
|
||||||
|
//System.load("C:\\Users\\Cortex\\Desktop\\minecraft\\mesabuild\\mesa\\builddir\\src\\gallium\\targets\\wgl\\libgallium_wgl.dll");
|
||||||
//System.load("C:\\Program Files\\RenderDoc\\renderdoc.dll");
|
//System.load("C:\\Program Files\\RenderDoc\\renderdoc.dll");
|
||||||
var prop = System.getProperty("voxy.forceGpuSelectionIndex", "NO");
|
var prop = System.getProperty("voxy.forceGpuSelectionIndex", "NO");
|
||||||
if (!prop.equals("NO")) {
|
if (!prop.equals("NO")) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
|
|
||||||
@Mixin(WorldRenderer.class)
|
@Mixin(WorldRenderer.class)
|
||||||
public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem {
|
public abstract class MixinWorldRenderer implements IGetVoxyRenderSystem {
|
||||||
|
@Shadow private Frustum frustum;
|
||||||
@Shadow private @Nullable ClientWorld world;
|
@Shadow private @Nullable ClientWorld world;
|
||||||
@Unique private VoxyRenderSystem renderer;
|
@Unique private VoxyRenderSystem renderer;
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||||||
public class MixinDefaultChunkRenderer {
|
public class MixinDefaultChunkRenderer {
|
||||||
|
|
||||||
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/caffeinemc/mods/sodium/client/render/chunk/ShaderChunkRenderer;end(Lnet/caffeinemc/mods/sodium/client/render/chunk/terrain/TerrainRenderPass;)V", shift = At.Shift.BEFORE))
|
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/caffeinemc/mods/sodium/client/render/chunk/ShaderChunkRenderer;end(Lnet/caffeinemc/mods/sodium/client/render/chunk/terrain/TerrainRenderPass;)V", shift = At.Shift.BEFORE))
|
||||||
private void injectRender(ChunkRenderMatrices matrices, CommandList commandList, ChunkRenderListIterable renderLists, TerrainRenderPass renderPass, CameraTransform camera, FogParameters fogParameters, boolean indexedRenderingEnabled, CallbackInfo ci) {
|
private void injectRender(ChunkRenderMatrices matrices, CommandList commandList, ChunkRenderListIterable renderLists, TerrainRenderPass renderPass, CameraTransform camera, FogParameters fogParameters, CallbackInfo ci) {
|
||||||
if (renderPass == DefaultTerrainRenderPasses.CUTOUT) {
|
if (renderPass == DefaultTerrainRenderPasses.CUTOUT) {
|
||||||
var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem();
|
var renderer = ((IGetVoxyRenderSystem) MinecraftClient.getInstance().worldRenderer).getVoxyRenderSystem();
|
||||||
if (renderer != null) {
|
if (renderer != null) {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import net.caffeinemc.mods.sodium.client.render.chunk.RenderSection;
|
|||||||
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
|
import net.caffeinemc.mods.sodium.client.render.chunk.RenderSectionManager;
|
||||||
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionInfo;
|
import net.caffeinemc.mods.sodium.client.render.chunk.data.BuiltSectionInfo;
|
||||||
import net.caffeinemc.mods.sodium.client.render.chunk.map.ChunkTrackerHolder;
|
import net.caffeinemc.mods.sodium.client.render.chunk.map.ChunkTrackerHolder;
|
||||||
import net.caffeinemc.mods.sodium.client.render.chunk.translucent_sorting.SortBehavior;
|
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.minecraft.client.world.ClientWorld;
|
import net.minecraft.client.world.ClientWorld;
|
||||||
import net.minecraft.util.math.ChunkPos;
|
import net.minecraft.util.math.ChunkPos;
|
||||||
@@ -35,7 +34,7 @@ public class MixinRenderSectionManager {
|
|||||||
@Shadow @Final private ClientWorld level;
|
@Shadow @Final private ClientWorld level;
|
||||||
|
|
||||||
@Inject(method = "<init>", at = @At("TAIL"))
|
@Inject(method = "<init>", at = @At("TAIL"))
|
||||||
private void voxy$resetChunkTracker(ClientWorld level, int renderDistance, SortBehavior sortBehavior, CommandList commandList, CallbackInfo ci) {
|
private void voxy$resetChunkTracker(ClientWorld level, int renderDistance, CommandList commandList, CallbackInfo ci) {
|
||||||
if (level.worldRenderer != null) {
|
if (level.worldRenderer != null) {
|
||||||
var system = ((IGetVoxyRenderSystem)(level.worldRenderer)).getVoxyRenderSystem();
|
var system = ((IGetVoxyRenderSystem)(level.worldRenderer)).getVoxyRenderSystem();
|
||||||
if (system != null) {
|
if (system != null) {
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ public class ActiveSectionTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public WorldSection acquire(long key, boolean nullOnEmpty) {
|
public WorldSection acquire(long key, boolean nullOnEmpty) {
|
||||||
//TODO: add optional verification check to ensure this (or other critical systems) arnt being called on the render or server thread
|
|
||||||
if (this.engine != null) this.engine.lastActiveTime = System.currentTimeMillis();
|
if (this.engine != null) this.engine.lastActiveTime = System.currentTimeMillis();
|
||||||
int index = this.getCacheArrayIndex(key);
|
int index = this.getCacheArrayIndex(key);
|
||||||
var cache = this.loadedSectionCache[index];
|
var cache = this.loadedSectionCache[index];
|
||||||
|
|||||||
@@ -16,10 +16,9 @@ public class WorldEngine {
|
|||||||
|
|
||||||
public static final int UPDATE_TYPE_BLOCK_BIT = 1;
|
public static final int UPDATE_TYPE_BLOCK_BIT = 1;
|
||||||
public static final int UPDATE_TYPE_CHILD_EXISTENCE_BIT = 2;
|
public static final int UPDATE_TYPE_CHILD_EXISTENCE_BIT = 2;
|
||||||
public static final int UPDATE_TYPE_DONT_SAVE = 4;
|
public static final int UPDATE_FLAGS = UPDATE_TYPE_BLOCK_BIT | UPDATE_TYPE_CHILD_EXISTENCE_BIT;
|
||||||
public static final int DEFAULT_UPDATE_FLAGS = UPDATE_TYPE_BLOCK_BIT | UPDATE_TYPE_CHILD_EXISTENCE_BIT;
|
|
||||||
|
|
||||||
public interface ISectionChangeCallback {void accept(WorldSection section, int updateFlags, int neighborMsk);}
|
public interface ISectionChangeCallback {void accept(WorldSection section, int updateFlags);}
|
||||||
public interface ISectionSaveCallback {void save(WorldEngine engine, WorldSection section);}
|
public interface ISectionSaveCallback {void save(WorldEngine engine, WorldSection section);}
|
||||||
|
|
||||||
private final TrackedObject thisTracker = TrackedObject.createTrackedObject(this);
|
private final TrackedObject thisTracker = TrackedObject.createTrackedObject(this);
|
||||||
@@ -113,18 +112,18 @@ public class WorldEngine {
|
|||||||
|
|
||||||
//Marks a section as dirty, enqueuing it for saving and or render data rebuilding
|
//Marks a section as dirty, enqueuing it for saving and or render data rebuilding
|
||||||
public void markDirty(WorldSection section) {
|
public void markDirty(WorldSection section) {
|
||||||
this.markDirty(section, DEFAULT_UPDATE_FLAGS, 0);
|
this.markDirty(section, UPDATE_FLAGS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markDirty(WorldSection section, int changeState, int neighborMsk) {
|
public void markDirty(WorldSection section, int changeState) {
|
||||||
if (!this.isLive) throw new IllegalStateException("World is not live");
|
if (!this.isLive) throw new IllegalStateException("World is not live");
|
||||||
if (section.tracker != this.sectionTracker) {
|
if (section.tracker != this.sectionTracker) {
|
||||||
throw new IllegalStateException("Section is not from here");
|
throw new IllegalStateException("Section is not from here");
|
||||||
}
|
}
|
||||||
if (this.dirtyCallback != null) {
|
if (this.dirtyCallback != null) {
|
||||||
this.dirtyCallback.accept(section, changeState, neighborMsk);
|
this.dirtyCallback.accept(section, changeState);
|
||||||
}
|
}
|
||||||
if ((!section.inSaveQueue)&&(changeState&UPDATE_TYPE_DONT_SAVE)==0) {
|
if (!section.inSaveQueue) {
|
||||||
section.markDirty();
|
section.markDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,21 +99,7 @@ public class WorldUpdater {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (didStateChange||(emptinessStateChange!=0)) {
|
if (didStateChange||(emptinessStateChange!=0)) {
|
||||||
//TODO: somehow foward the neighbors that are facing the updated area, this allows forwarding to the dirty consumer
|
into.markDirty(worldSection, (didStateChange?UPDATE_TYPE_BLOCK_BIT:0)|(emptinessStateChange!=0?UPDATE_TYPE_CHILD_EXISTENCE_BIT:0));
|
||||||
// which can decide wether to dispatch mesh rebuilds to the surounding sections
|
|
||||||
//Bitmask of neighboring sections
|
|
||||||
//Note, this may be zero (this is more likely to occure at higher lod levels) if it doesnt face any neighbors
|
|
||||||
int neighbors = 0;
|
|
||||||
if (didStateChange) {
|
|
||||||
neighbors |= ((section.y^(section.y-1))>>(lvl+1))==0?0:1<<0;//Down
|
|
||||||
neighbors |= ((section.y^(section.y+1))>>(lvl+1))==0?0:1<<1;//Up
|
|
||||||
neighbors |= ((section.x^(section.x-1))>>(lvl+1))==0?0:1<<2;//-x
|
|
||||||
neighbors |= ((section.x^(section.x+1))>>(lvl+1))==0?0:1<<3;//+x
|
|
||||||
neighbors |= ((section.z^(section.z-1))>>(lvl+1))==0?0:1<<4;//-z
|
|
||||||
neighbors |= ((section.z^(section.z+1))>>(lvl+1))==0?0:1<<5;//+z
|
|
||||||
}
|
|
||||||
|
|
||||||
into.markDirty(worldSection, (didStateChange?UPDATE_TYPE_BLOCK_BIT:0)|(emptinessStateChange!=0?UPDATE_TYPE_CHILD_EXISTENCE_BIT:0), neighbors);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Need to release the section after using it
|
//Need to release the section after using it
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
package me.cortex.voxy.common.world.other;
|
package me.cortex.voxy.common.world.other;
|
||||||
|
|
||||||
import com.mojang.serialization.Dynamic;
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
import me.cortex.voxy.common.Logger;
|
import me.cortex.voxy.common.Logger;
|
||||||
import me.cortex.voxy.common.config.IMappingStorage;
|
import me.cortex.voxy.common.config.IMappingStorage;
|
||||||
import net.minecraft.SharedConstants;
|
|
||||||
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.block.LeavesBlock;
|
import net.minecraft.block.LeavesBlock;
|
||||||
import net.minecraft.datafixer.Schemas;
|
|
||||||
import net.minecraft.datafixer.TypeReferences;
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.nbt.NbtIo;
|
import net.minecraft.nbt.NbtIo;
|
||||||
import net.minecraft.nbt.NbtOps;
|
import net.minecraft.nbt.NbtOps;
|
||||||
@@ -99,31 +95,26 @@ public class Mapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadFromStorage() {
|
private void loadFromStorage() {
|
||||||
//TODO: FIXME: have/store the minecraft version the mappings are from (the data version)
|
|
||||||
// SharedConstants.getGameVersion().dataVersion().id()
|
|
||||||
// then use this to create an update path instead
|
|
||||||
|
|
||||||
var mappings = this.storage.getIdMappingsData();
|
var mappings = this.storage.getIdMappingsData();
|
||||||
List<StateEntry> sentries = new ArrayList<>();
|
List<StateEntry> sentries = new ArrayList<>();
|
||||||
List<BiomeEntry> bentries = new ArrayList<>();
|
List<BiomeEntry> bentries = new ArrayList<>();
|
||||||
List<Pair<byte[], Integer>> sentryErrors = new ArrayList<>();
|
List<Pair<byte[], Integer>> sentryErrors = new ArrayList<>();
|
||||||
|
|
||||||
boolean[] forceResave = new boolean[1];
|
|
||||||
for (var entry : mappings.int2ObjectEntrySet()) {
|
for (var entry : mappings.int2ObjectEntrySet()) {
|
||||||
int entryType = entry.getIntKey()>>>30;
|
int entryType = entry.getIntKey()>>>30;
|
||||||
int id = entry.getIntKey() & ((1<<30)-1);
|
int id = entry.getIntKey() & ((1<<30)-1);
|
||||||
if (entryType == BLOCK_STATE_TYPE) {
|
if (entryType == BLOCK_STATE_TYPE) {
|
||||||
var sentry = StateEntry.deserialize(id, entry.getValue(), forceResave);
|
var sentry = StateEntry.deserialize(id, entry.getValue());
|
||||||
if (sentry.state.isAir()) {
|
if (sentry.state.isAir()) {
|
||||||
Logger.error("Deserialization was air, removed block");
|
Logger.error("Deserialization was air, removed block");
|
||||||
sentryErrors.add(new Pair<>(entry.getValue(), id));
|
sentryErrors.add(new Pair<>(entry.getValue(), id));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
sentries.add(sentry);
|
sentries.add(sentry);
|
||||||
var oldEntry = this.block2stateEntry.putIfAbsent(sentry.state, sentry);
|
var oldEntry = this.block2stateEntry.put(sentry.state, sentry);
|
||||||
if (oldEntry != null) {
|
if (oldEntry != null) {
|
||||||
//forceResave[0] |= true;
|
throw new IllegalStateException("Multiple mappings for blockstate");
|
||||||
Logger.warn("Multiple mappings for blockstate, using old state, expect things to possibly go really badly. " + oldEntry.id + ":" + sentry.id + ":" + sentry.state );
|
|
||||||
}
|
}
|
||||||
} else if (entryType == BIOME_TYPE) {
|
} else if (entryType == BIOME_TYPE) {
|
||||||
var bentry = BiomeEntry.deserialize(id, entry.getValue());
|
var bentry = BiomeEntry.deserialize(id, entry.getValue());
|
||||||
@@ -136,8 +127,7 @@ public class Mapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sentryErrors.isEmpty()) {
|
{
|
||||||
forceResave[0] |= true;
|
|
||||||
//Insert garbage types into the mapping for those blocks, TODO:FIXME: Need to upgrade the type or have a solution to error blocks
|
//Insert garbage types into the mapping for those blocks, TODO:FIXME: Need to upgrade the type or have a solution to error blocks
|
||||||
var rand = new Random();
|
var rand = new Random();
|
||||||
for (var error : sentryErrors) {
|
for (var error : sentryErrors) {
|
||||||
@@ -166,10 +156,6 @@ public class Mapper {
|
|||||||
this.biomeId2biomeEntry.add(entry);
|
this.biomeId2biomeEntry.add(entry);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (forceResave[0]) {
|
|
||||||
Logger.warn("Forced state resave triggered");
|
|
||||||
this.forceResaveStates();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final int getBlockStateCount() {
|
public final int getBlockStateCount() {
|
||||||
@@ -371,29 +357,14 @@ public class Mapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StateEntry deserialize(int id, byte[] data, boolean[] forceResave) {
|
public static StateEntry deserialize(int id, byte[] data) {
|
||||||
try {
|
try {
|
||||||
var compound = NbtIo.readCompressed(new ByteArrayInputStream(data), NbtSizeTracker.ofUnlimitedBytes());
|
var compound = NbtIo.readCompressed(new ByteArrayInputStream(data), NbtSizeTracker.ofUnlimitedBytes());
|
||||||
if (compound.getInt("id", -1) != id) {
|
if (compound.getInt("id", -1) != id) {
|
||||||
throw new IllegalStateException("Encoded id != expected id");
|
throw new IllegalStateException("Encoded id != expected id");
|
||||||
}
|
}
|
||||||
var bsc = compound.getCompound("block_state").orElseThrow();
|
BlockState state = BlockState.CODEC.parse(NbtOps.INSTANCE, compound.getCompound("block_state").orElseThrow()).getOrThrow();
|
||||||
var state = BlockState.CODEC.parse(NbtOps.INSTANCE, bsc);
|
return new StateEntry(id, state);
|
||||||
if (state.isError()) {
|
|
||||||
Logger.info("Could not decode blockstate, attempting fixes, error: "+ state.error().get().message());
|
|
||||||
bsc = (NbtCompound) Schemas.getFixer().update(TypeReferences.BLOCK_STATE, new Dynamic<>(NbtOps.INSTANCE,bsc),0, SharedConstants.getGameVersion().dataVersion().id()).getValue();
|
|
||||||
state = BlockState.CODEC.parse(NbtOps.INSTANCE, bsc);
|
|
||||||
if (state.isError()) {
|
|
||||||
Logger.error("Could not decode blockstate setting to air. id:" + id + " error: " + state.error().get().message());
|
|
||||||
return new StateEntry(id, Blocks.AIR.getDefaultState());
|
|
||||||
} else {
|
|
||||||
Logger.info("Fixed blockstate to: " + state.getOrThrow());
|
|
||||||
forceResave[0] |= true;
|
|
||||||
return new StateEntry(id, state.getOrThrow());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return new StateEntry(id, state.getOrThrow());
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -200,13 +200,6 @@ public class VoxelIngestService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean rawIngest(WorldIdentifier id, ChunkSection section, int x, int y, int z, ChunkNibbleArray bl, ChunkNibbleArray sl) {
|
|
||||||
if (id == null) return false;
|
|
||||||
var engine = id.getOrCreateEngine();
|
|
||||||
if (engine == null) return false;
|
|
||||||
return rawIngest(engine, section, x, y, z, bl, sl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean rawIngest(WorldEngine engine, ChunkSection section, int x, int y, int z, ChunkNibbleArray bl, ChunkNibbleArray sl) {
|
public static boolean rawIngest(WorldEngine engine, ChunkSection section, int x, int y, int z, ChunkNibbleArray bl, ChunkNibbleArray sl) {
|
||||||
if (!shouldIngestSection(section, x, y, z)) return false;
|
if (!shouldIngestSection(section, x, y, z)) return false;
|
||||||
if (engine.instanceIn == null) return false;
|
if (engine.instanceIn == null) return false;
|
||||||
|
|||||||
@@ -86,5 +86,6 @@ public class VoxyCommon implements ModInitializer {
|
|||||||
return FACTORY != null;
|
return FACTORY != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final boolean IS_MINE_IN_ABYSS = false;
|
public static final boolean IS_MINE_IN_ABYSS = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
layout(local_size_x = WIDTH, local_size_y = HEIGHT) in;
|
layout(local_size_x = WIDTH, local_size_y = HEIGHT) in;
|
||||||
|
|
||||||
layout(binding = META_IN_BINDING) uniform usampler2D metaTexIn;
|
|
||||||
layout(binding = COLOUR_IN_BINDING) uniform sampler2D colourTexIn;
|
layout(binding = COLOUR_IN_BINDING) uniform sampler2D colourTexIn;
|
||||||
layout(binding = DEPTH_IN_BINDING) uniform sampler2D depthTexIn;
|
layout(binding = DEPTH_IN_BINDING) uniform sampler2D depthTexIn;
|
||||||
layout(binding = STENCIL_IN_BINDING) uniform usampler2D stencilTexIn;
|
layout(binding = STENCIL_IN_BINDING) uniform usampler2D stencilTexIn;
|
||||||
@@ -25,11 +24,8 @@ void main() {
|
|||||||
|
|
||||||
float depth = clamp(texelFetch(depthTexIn, samplePoint, 0).r, 0, 1);//Opengl grumble grumble
|
float depth = clamp(texelFetch(depthTexIn, samplePoint, 0).r, 0, 1);//Opengl grumble grumble
|
||||||
uint stencil = texelFetch(stencilTexIn, samplePoint, 0).r;
|
uint stencil = texelFetch(stencilTexIn, samplePoint, 0).r;
|
||||||
uint metadata = texelFetch(metaTexIn, samplePoint, 0).r;
|
|
||||||
uint value = uint(depth*((1<<24)-1))<<8;
|
uint value = uint(depth*((1<<24)-1))<<8;
|
||||||
value |= stencil&0xFFu;
|
value |= stencil;
|
||||||
//Use the very top bit of the stencil to mask if its tinted
|
|
||||||
value |= (metadata&1u)<<7;
|
|
||||||
outPoint.y = value;
|
outPoint.y = value;
|
||||||
|
|
||||||
outBuffer[globalOutIndex] = outPoint;
|
outBuffer[globalOutIndex] = outPoint;
|
||||||
|
|||||||
@@ -3,13 +3,11 @@
|
|||||||
layout(location=0) uniform sampler2D tex;
|
layout(location=0) uniform sampler2D tex;
|
||||||
in vec2 texCoord;
|
in vec2 texCoord;
|
||||||
in flat uint metadata;
|
in flat uint metadata;
|
||||||
layout(location=0) out vec4 colour;
|
out vec4 colour;
|
||||||
layout(location=1) out uvec4 metaOut;
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
colour = texture(tex, texCoord, ((~metadata>>1)&1u)*-16.0f);
|
colour = texture(tex, texCoord, ((~metadata>>1)&1u)*-16.0f);
|
||||||
if (colour.a < 0.001f && ((metadata&1u)!=0)) {
|
if (colour.a < 0.001f && ((metadata&1u)!=0)) {
|
||||||
discard;
|
discard;
|
||||||
}
|
}
|
||||||
metaOut = uvec4((metadata>>2)&1u);//Write if it is or isnt tinted
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ struct BlockModel {
|
|||||||
//TODO: FIXME: this isnt actually correct cause depending on the face (i think) it could be 1/64 th of a position off
|
//TODO: FIXME: this isnt actually correct cause depending on the face (i think) it could be 1/64 th of a position off
|
||||||
// but im going to assume that since we are dealing with huge render distances, this shouldent matter that much
|
// but im going to assume that since we are dealing with huge render distances, this shouldent matter that much
|
||||||
float extractFaceIndentation(uint faceData) {
|
float extractFaceIndentation(uint faceData) {
|
||||||
uint enc = (faceData>>16)&63u;
|
return float((faceData>>16)&63u)/63.0;
|
||||||
enc += uint(enc==63u);//convert 63 to 64 cause of pain reasons
|
|
||||||
return float(enc)/64.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vec4 extractFaceSizes(uint faceData) {
|
vec4 extractFaceSizes(uint faceData) {
|
||||||
@@ -27,10 +25,6 @@ uint faceHasAlphaCuttoutOverride(uint faceData) {
|
|||||||
return (faceData>>23)&1u;
|
return (faceData>>23)&1u;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint faceTintState(uint faceData) {
|
|
||||||
return (faceData>>24)&3u;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool modelHasBiomeLUT(BlockModel model) {
|
bool modelHasBiomeLUT(BlockModel model) {
|
||||||
return ((model.flagsA)&2u) != 0;
|
return ((model.flagsA)&2u) != 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ bool useMipmaps() {
|
|||||||
return (interData.x&2u)==0u;
|
return (interData.x&2u)==0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint tintingState() {
|
bool useTinting() {
|
||||||
return (interData.x>>2)&3u;
|
return (interData.x&4u)!=0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool useCutout() {
|
bool useCutout() {
|
||||||
@@ -91,19 +91,13 @@ void voxy_emitFragment(VoxyFragmentParameters parameters);
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
vec4 computeColour(vec2 texturePos, vec4 colour) {
|
vec4 computeColour(vec2 texturePos, vec4 colour) {
|
||||||
//Conditional tinting, TODO: FIXME: this is better but still not great, try encode data into the top bit of alpha so its per pixel
|
//Conditional tinting, TODO: FIXME: REPLACE WITH MASK OR SOMETHING, like encode data into the top bit of alpha
|
||||||
|
if (useTinting()) {
|
||||||
uint tintingFunction = tintingState();
|
|
||||||
bool doTint = tintingFunction==2;//Always tint if function == 2
|
|
||||||
if (tintingFunction == 1) {//partial tint
|
|
||||||
vec4 tintTest = textureLod(blockModelAtlas, texturePos, 0);
|
vec4 tintTest = textureLod(blockModelAtlas, texturePos, 0);
|
||||||
if (abs(tintTest.r-tintTest.g) < 0.02f && abs(tintTest.g-tintTest.b) < 0.02f) {
|
if (abs(tintTest.r-tintTest.g) < 0.02f && abs(tintTest.g-tintTest.b) < 0.02f) {
|
||||||
doTint = true;
|
colour *= uint2vec4RGBA(interData.z).yzwx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (doTint) {
|
|
||||||
colour *= uint2vec4RGBA(interData.z).yzwx;
|
|
||||||
}
|
|
||||||
return (colour * uint2vec4RGBA(interData.y)) + vec4(0,0,0,float(interData.w&0xFFu)/255);
|
return (colour * uint2vec4RGBA(interData.y)) + vec4(0,0,0,float(interData.w&0xFFu)/255);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,13 +133,11 @@ void main() {
|
|||||||
|
|
||||||
if (any(notEqual(clamp(tile, vec2(0), vec2((interData.x>>8)&0xFu, (interData.x>>12)&0xFu)), tile))) {
|
if (any(notEqual(clamp(tile, vec2(0), vec2((interData.x>>8)&0xFu, (interData.x>>12)&0xFu)), tile))) {
|
||||||
discard;
|
discard;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check the minimum bounding texture and ensure we are greater than it
|
//Check the minimum bounding texture and ensure we are greater than it
|
||||||
if (gl_FragCoord.z < texelFetch(depthTex, ivec2(gl_FragCoord.xy), 0).r) {
|
if (gl_FragCoord.z < texelFetch(depthTex, ivec2(gl_FragCoord.xy), 0).r) {
|
||||||
discard;
|
discard;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -155,16 +147,9 @@ void main() {
|
|||||||
//TODO: FIXME, basicly what this do is sample the exact pixel (no lod) for discarding, this stops mipmapping fucking it over
|
//TODO: FIXME, basicly what this do is sample the exact pixel (no lod) for discarding, this stops mipmapping fucking it over
|
||||||
#ifndef DEBUG_RENDER
|
#ifndef DEBUG_RENDER
|
||||||
discard;
|
discard;
|
||||||
return;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef PATCHED_SHADER_ALLOW_DERIVATIVES
|
|
||||||
if (gl_HelperInvocation) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef PATCHED_SHADER
|
#ifndef PATCHED_SHADER
|
||||||
colour = computeColour(texPos, colour);
|
colour = computeColour(texPos, colour);
|
||||||
outColour = colour;
|
outColour = colour;
|
||||||
@@ -181,18 +166,13 @@ void main() {
|
|||||||
#else
|
#else
|
||||||
uint modelId = getModelId();
|
uint modelId = getModelId();
|
||||||
BlockModel model = modelData[modelId];
|
BlockModel model = modelData[modelId];
|
||||||
uint tintingFunction = tintingState();
|
vec4 tint = vec4(1);
|
||||||
bool doTint = tintingFunction==2;//Always tint if function == 2
|
if (useTinting()) {
|
||||||
if (tintingFunction==1) {//Partial tint
|
|
||||||
vec4 tintTest = texture(blockModelAtlas, texPos, -2);
|
vec4 tintTest = texture(blockModelAtlas, texPos, -2);
|
||||||
if (abs(tintTest.r-tintTest.g) < 0.02f && abs(tintTest.g-tintTest.b) < 0.02f) {
|
if (abs(tintTest.r-tintTest.g) < 0.02f && abs(tintTest.g-tintTest.b) < 0.02f) {
|
||||||
doTint = true;
|
tint = uint2vec4RGBA(interData.z).yzwx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
vec4 tint = vec4(1);
|
|
||||||
if (doTint) {
|
|
||||||
tint = uint2vec4RGBA(interData.z).yzwx;
|
|
||||||
}
|
|
||||||
|
|
||||||
voxy_emitFragment(VoxyFragmentParameters(colour, tile, texPos, getFace(), modelId, getLightmap().yx, tint, model.customId));
|
voxy_emitFragment(VoxyFragmentParameters(colour, tile, texPos, getFace(), modelId, getLightmap().yx, tint, model.customId));
|
||||||
|
|
||||||
|
|||||||
@@ -139,16 +139,13 @@ void main() {
|
|||||||
|
|
||||||
//Apply model colour tinting
|
//Apply model colour tinting
|
||||||
uint tintColour = model.colourTint;
|
uint tintColour = model.colourTint;
|
||||||
|
|
||||||
if (modelHasBiomeLUT(model)) {
|
if (modelHasBiomeLUT(model)) {
|
||||||
tintColour = colourData[tintColour + extractBiomeId(quad)];
|
tintColour = colourData[tintColour + extractBiomeId(quad)];
|
||||||
}
|
}
|
||||||
|
|
||||||
uint tintState = faceTintState(faceData);
|
|
||||||
|
|
||||||
uint conditionalTinting = 0;
|
uint conditionalTinting = 0;
|
||||||
if (tintColour != uint(-1)) {
|
if (tintColour != uint(-1)) {
|
||||||
flags |= tintState<<2;
|
flags |= 1u<<2;
|
||||||
conditionalTinting = tintColour;
|
conditionalTinting = tintColour;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,21 +164,6 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Apply face tint
|
//Apply face tint
|
||||||
#ifdef DARKENED_TINTING
|
|
||||||
if (isShaded) {
|
|
||||||
//TODO: make branchless, infact apply ahead of time to the texture itself in ModelManager since that is
|
|
||||||
// per face
|
|
||||||
if ((face>>1) == 1) {//NORTH, SOUTH
|
|
||||||
tinting.xyz *= 0.8f;
|
|
||||||
} else if ((face>>1) == 2) {//EAST, WEST
|
|
||||||
tinting.xyz *= 0.6f;
|
|
||||||
} else {//UP DOWN
|
|
||||||
tinting.xyz *= 0.9f;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tinting.xyz *= 0.9f;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (isShaded) {
|
if (isShaded) {
|
||||||
//TODO: make branchless, infact apply ahead of time to the texture itself in ModelManager since that is
|
//TODO: make branchless, infact apply ahead of time to the texture itself in ModelManager since that is
|
||||||
// per face
|
// per face
|
||||||
@@ -193,7 +175,6 @@ void main() {
|
|||||||
tinting.xyz *= 0.5f;
|
tinting.xyz *= 0.5f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
setTintingAndExtra(tinting, conditionalTinting, addin|(face<<8));
|
setTintingAndExtra(tinting, conditionalTinting, addin|(face<<8));
|
||||||
#else
|
#else
|
||||||
|
|||||||
99
src/main/resources/assets/voxy/shaders/lod/meshext/frag.glsl
Normal file
99
src/main/resources/assets/voxy/shaders/lod/meshext/frag.glsl
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
#version 460 core
|
||||||
|
|
||||||
|
#extension GL_EXT_mesh_shader : require
|
||||||
|
|
||||||
|
layout(binding = 0) uniform sampler2D blockModelAtlas;
|
||||||
|
layout(binding = 2) uniform sampler2D depthTex;
|
||||||
|
|
||||||
|
layout(location=1) perprimitiveEXT in PerPrimData {
|
||||||
|
uvec4 data;
|
||||||
|
} primIn;
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 outColour;
|
||||||
|
|
||||||
|
vec4 uint2vec4RGBA(uint colour) {
|
||||||
|
return vec4((uvec4(colour)>>uvec4(24,16,8,0))&uvec4(0xFF))/255.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useMipmaps() {
|
||||||
|
return (primIn.data.x&2u)==0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useTinting() {
|
||||||
|
return (primIn.data.x&4u)!=0u;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool useCutout() {
|
||||||
|
return (primIn.data.x&1u)==1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 computeColour(vec4 colour) {
|
||||||
|
//Conditional tinting, TODO: FIXME: REPLACE WITH MASK OR SOMETHING, like encode data into the top bit of alpha
|
||||||
|
if (useTinting() && abs(colour.r-colour.g) < 0.02f && abs(colour.g-colour.b) < 0.02f) {
|
||||||
|
colour *= uint2vec4RGBA(primIn.data.z).yzwx;
|
||||||
|
}
|
||||||
|
return (colour * uint2vec4RGBA(primIn.data.y)) + vec4(0,0,0,float(primIn.data.w&0xFFu)/255);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint getFace() {
|
||||||
|
return (primIn.data.w>>8)&7u;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 getBaseUV() {
|
||||||
|
uint face = getFace();
|
||||||
|
uint modelId = primIn.data.x>>16;
|
||||||
|
vec2 modelUV = vec2(modelId&0xFFu, (modelId>>8)&0xFFu)*(1.0/(256.0));
|
||||||
|
return modelUV + (vec2(face>>1, face&1u) * (1.0/(vec2(3.0, 2.0)*256.0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
|
||||||
|
vec2 uv = vec2(0);
|
||||||
|
|
||||||
|
//Tile is the tile we are in
|
||||||
|
vec2 tile;
|
||||||
|
vec2 uv2 = modf(uv, tile)*(1.0/(vec2(3.0,2.0)*256.0));
|
||||||
|
vec4 colour;
|
||||||
|
vec2 texPos = uv2 + getBaseUV();
|
||||||
|
if (useMipmaps()) {
|
||||||
|
vec2 uvSmol = uv*(1.0/(vec2(3.0,2.0)*256.0));
|
||||||
|
vec2 dx = dFdx(uvSmol);//vec2(lDx, dDx);
|
||||||
|
vec2 dy = dFdy(uvSmol);//vec2(lDy, dDy);
|
||||||
|
colour = textureGrad(blockModelAtlas, texPos, dx, dy);
|
||||||
|
} else {
|
||||||
|
colour = textureLod(blockModelAtlas, texPos, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (any(notEqual(clamp(tile, vec2(0), vec2((primIn.data.x>>8)&0xFu, (primIn.data.x>>12)&0xFu)), tile))) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Check the minimum bounding texture and ensure we are greater than it
|
||||||
|
if (gl_FragCoord.z < texelFetch(depthTex, ivec2(gl_FragCoord.xy), 0).r) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Also, small quad is really fking over the mipping level somehow
|
||||||
|
if (useCutout() && (textureLod(blockModelAtlas, texPos, 0).a <= 0.1f)) {
|
||||||
|
//This is stupidly stupidly bad for divergence
|
||||||
|
//TODO: FIXME, basicly what this do is sample the exact pixel (no lod) for discarding, this stops mipmapping fucking it over
|
||||||
|
#ifndef DEBUG_RENDER
|
||||||
|
discard;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
colour = computeColour(colour);
|
||||||
|
outColour = colour;
|
||||||
|
|
||||||
|
/*
|
||||||
|
uint quadDebug = gl_PrimitiveID;
|
||||||
|
uint hash = quadDebug*1231421+123141;
|
||||||
|
hash ^= hash>>16;
|
||||||
|
hash = hash*1231421+123141;
|
||||||
|
hash ^= hash>>16;
|
||||||
|
hash = hash * 1827364925 + 123325621;
|
||||||
|
outColour = vec4(float(hash&15u)/15, float((hash>>4)&15u)/15, float((hash>>8)&15u)/15, 1);*/
|
||||||
|
}
|
||||||
340
src/main/resources/assets/voxy/shaders/lod/meshext/mesh.glsl
Normal file
340
src/main/resources/assets/voxy/shaders/lod/meshext/mesh.glsl
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
#version 460 core
|
||||||
|
|
||||||
|
#extension GL_EXT_mesh_shader : require
|
||||||
|
|
||||||
|
#extension GL_ARB_gpu_shader_int64 : enable
|
||||||
|
|
||||||
|
#extension GL_KHR_shader_subgroup_arithmetic: require
|
||||||
|
#extension GL_KHR_shader_subgroup_basic : require
|
||||||
|
#extension GL_KHR_shader_subgroup_ballot : require
|
||||||
|
#extension GL_KHR_shader_subgroup_vote : require
|
||||||
|
|
||||||
|
|
||||||
|
//TODO: finetune the local size and emission size
|
||||||
|
layout(local_size_x = MESH_SIZE, local_size_y=1, local_size_z=1) in;
|
||||||
|
layout(triangles, max_vertices=(MESH_SIZE*4), max_primitives=(MESH_SIZE*2)) out;
|
||||||
|
|
||||||
|
//Tightly packed, prefix sum + offset
|
||||||
|
//uvec4 binA;
|
||||||
|
//uvec4 binB;
|
||||||
|
struct Task {
|
||||||
|
uint bins[8];
|
||||||
|
vec3 cameraOffset;
|
||||||
|
uint lodLvl;
|
||||||
|
|
||||||
|
uint baseQuad;
|
||||||
|
uint quadCount;
|
||||||
|
uint padddd[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
taskPayloadSharedEXT Task task;
|
||||||
|
|
||||||
|
layout(location=1) perprimitiveEXT out PerPrimData {
|
||||||
|
uvec4 data;
|
||||||
|
} primOut[];
|
||||||
|
|
||||||
|
|
||||||
|
uint getQuadId() {
|
||||||
|
uint mid = gl_GlobalInvocationID.x;
|
||||||
|
uint cv = (mid<<16)|0xFFFFu;
|
||||||
|
/*
|
||||||
|
//Funny method
|
||||||
|
uvec4 a = mix(uvec4(0), uvec4( 1, 2, 4, 8), lessThanEqual(uvec4(task.bins[0],task.bins[1],task.bins[2],task.bins[3]), uvec4(cv))) +
|
||||||
|
mix(uvec4(0), uvec4(16,32,64,128), lessThanEqual(uvec4(task.bins[4],task.bins[5],task.bins[6],task.bins[7]), uvec4(cv)));
|
||||||
|
uint act = a.x+a.y+a.z+a.w;
|
||||||
|
uint id = findLSB(act^(act>>1));
|
||||||
|
|
||||||
|
//uint point = mix(binB, binA, id<4)[id&3u];
|
||||||
|
uint point = task.bins[id];
|
||||||
|
|
||||||
|
return (point&0xFFFFu)+(mid-(point>>16));
|
||||||
|
*/
|
||||||
|
#pragma unroll
|
||||||
|
for (uint i = 0; i<7; i++) {
|
||||||
|
uint point = task.bins[i];
|
||||||
|
if (point<=cv&&cv<task.bins[i+1]) {
|
||||||
|
return (point&0xFFFFu)+(mid-(point>>16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
for (uint i = 0; i<7; i++) {
|
||||||
|
uint point = task.bins[i];
|
||||||
|
if (point <= ((mid<<16)|0xFFFFu) && ((mid<<16)|0xFFFFu)<task.bins[i+1]) {
|
||||||
|
binId = i;
|
||||||
|
return (point&0xFFFFu)+(mid-(point>>16));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
#import <voxy:lod/quad_format.glsl>
|
||||||
|
#import <voxy:lod/block_model.glsl>
|
||||||
|
|
||||||
|
layout(binding = 0, std140) uniform SceneUniform {
|
||||||
|
mat4 MVP;
|
||||||
|
ivec3 baseSectionPos;
|
||||||
|
uint frameId;
|
||||||
|
vec3 cameraSubPos;
|
||||||
|
uint pad_;
|
||||||
|
vec2 screenSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 4, std430) readonly restrict buffer QuadBuffer {
|
||||||
|
Quad quadData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 5, std430) readonly restrict buffer ModelBuffer {
|
||||||
|
BlockModel modelData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 6, std430) readonly restrict buffer ModelColourBuffer {
|
||||||
|
uint colourData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 1) uniform sampler2D lightSampler;
|
||||||
|
vec4 getLighting(uint index) {
|
||||||
|
int i2 = int(index);
|
||||||
|
return texture(lightSampler, clamp((vec2((i2>>4)&0xF, i2&0xF))/16, vec2(8.0f/255), vec2(248.0f/255)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//===============
|
||||||
|
|
||||||
|
|
||||||
|
vec3 swizzelDataAxis(uint axis, vec3 data) {
|
||||||
|
return mix(mix(data.zxy,data.xzy,bvec3(axis==0)),data,bvec3(axis==1));
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 getFaceSize(uint faceData) {
|
||||||
|
float EPSILON = 0.00005f;
|
||||||
|
|
||||||
|
vec4 faceOffsetsSizes = extractFaceSizes(faceData);
|
||||||
|
|
||||||
|
//Expand the quads by a very small amount (because of the subtraction after this also becomes an implicit add)
|
||||||
|
faceOffsetsSizes.xz -= vec2(EPSILON);
|
||||||
|
|
||||||
|
//Make the end relative to the start
|
||||||
|
faceOffsetsSizes.yw -= faceOffsetsSizes.xz;
|
||||||
|
|
||||||
|
return faceOffsetsSizes;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 faceNormal(uint face) {
|
||||||
|
//TODO: optimize this garbage
|
||||||
|
return vec3(uint((face>>1)==2), uint((face>>1)==0), uint((face>>1)==1)) * (float(int(face)&1)*2-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint packVec4(vec4 vec) {
|
||||||
|
uvec4 vec_=uvec4(vec*255)<<uvec4(24,16,8,0);
|
||||||
|
return vec_.x|vec_.y|vec_.z|vec_.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===============
|
||||||
|
vec3 cornerPos;
|
||||||
|
vec2 axisFaceSize;
|
||||||
|
uint face;
|
||||||
|
|
||||||
|
vec4 faceSize;
|
||||||
|
|
||||||
|
uint modelId;
|
||||||
|
BlockModel model;
|
||||||
|
uint faceData;
|
||||||
|
bool isTranslucent;
|
||||||
|
bool hasAO;
|
||||||
|
bool isShaded;
|
||||||
|
|
||||||
|
void setup(Quad quad) {
|
||||||
|
face = extractFace(quad);
|
||||||
|
modelId = extractStateId(quad);
|
||||||
|
model = modelData[modelId];
|
||||||
|
faceData = model.faceData[face];
|
||||||
|
isTranslucent = modelIsTranslucent(model);
|
||||||
|
hasAO = modelHasMipmaps(model);//TODO: replace with per face AO flag
|
||||||
|
isShaded = hasAO;//TODO: make this a per face flag
|
||||||
|
|
||||||
|
ivec2 quadSize = extractSize(quad);
|
||||||
|
|
||||||
|
faceSize = getFaceSize(faceData);
|
||||||
|
|
||||||
|
cornerPos = extractPos(quad);
|
||||||
|
float depthOffset = extractFaceIndentation(faceData);
|
||||||
|
cornerPos += swizzelDataAxis(face>>1, vec3(faceSize.xz, mix(depthOffset, 1-depthOffset, float(face&1u))));
|
||||||
|
cornerPos *= (1<<task.lodLvl);
|
||||||
|
cornerPos += task.cameraOffset;
|
||||||
|
|
||||||
|
axisFaceSize = (faceSize.yw + quadSize - 1);
|
||||||
|
|
||||||
|
//uv =
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
vec2 getUvCorner(uint corner) {
|
||||||
|
return faceSize.xz + axisFaceSize*vec2((corner>>1)&1u, corner&1u);;
|
||||||
|
}
|
||||||
|
|
||||||
|
uvec4 createQuadData(Quad quad) {
|
||||||
|
uint flags = faceHasAlphaCuttout(faceData);
|
||||||
|
|
||||||
|
ivec2 quadSize = extractSize(quad);
|
||||||
|
//We need to have a conditional override based on if the model size is < a full face + quadSize > 1
|
||||||
|
flags |= uint(any(greaterThan(quadSize, ivec2(1)))) & faceHasAlphaCuttoutOverride(faceData);
|
||||||
|
|
||||||
|
flags |= uint(!modelHasMipmaps(model))<<1;
|
||||||
|
|
||||||
|
//Compute lighting
|
||||||
|
vec4 tinting = getLighting(extractLightId(quad));
|
||||||
|
|
||||||
|
//Apply model colour tinting
|
||||||
|
uint tintColour = model.colourTint;
|
||||||
|
if (modelHasBiomeLUT(model)) {
|
||||||
|
tintColour = colourData[tintColour + extractBiomeId(quad)];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint conditionalTinting = 0;
|
||||||
|
if (tintColour != uint(-1)) {
|
||||||
|
flags |= 1u<<2;
|
||||||
|
conditionalTinting = tintColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint addin = 0;
|
||||||
|
if (!isTranslucent) {
|
||||||
|
tinting.w = 0.0;
|
||||||
|
//Encode the face, the lod level and
|
||||||
|
uint encodedData = 0;
|
||||||
|
encodedData |= face;
|
||||||
|
encodedData |= (task.lodLvl<<3);
|
||||||
|
encodedData |= uint(hasAO)<<6;
|
||||||
|
addin = encodedData;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Apply face tint
|
||||||
|
if (isShaded) {
|
||||||
|
//TODO: make branchless, infact apply ahead of time to the texture itself in ModelManager since that is
|
||||||
|
// per face
|
||||||
|
if ((face>>1) == 1) {//NORTH, SOUTH
|
||||||
|
tinting.xyz *= 0.8f;
|
||||||
|
} else if ((face>>1) == 2) {//EAST, WEST
|
||||||
|
tinting.xyz *= 0.6f;
|
||||||
|
} else if (face == 0) {//DOWN
|
||||||
|
tinting.xyz *= 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
uvec4 interData;
|
||||||
|
|
||||||
|
interData.x = (modelId<<16) | flags | (uint(quadSize.x-1)<<8) | (uint(quadSize.y-1)<<12);
|
||||||
|
|
||||||
|
interData.y = packVec4(tinting);
|
||||||
|
interData.z = conditionalTinting;
|
||||||
|
interData.w = addin|(face<<8);
|
||||||
|
|
||||||
|
return interData;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 emitVertexPos(int corner) {
|
||||||
|
vec3 pointPos = swizzelDataAxis(face>>1,vec3(axisFaceSize*mix(vec2(0),vec2(1<<task.lodLvl),bvec2((corner>>1)&1, corner&1)),0))+cornerPos;
|
||||||
|
return MVP*vec4(pointPos, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bvec2 whatRender(vec4 p1, vec4 p2, vec4 p0, vec4 p3) {
|
||||||
|
vec2 ssmin = ((p1.xy/p1.w)+1)*screenSize;
|
||||||
|
vec2 ssmax = ssmin;
|
||||||
|
|
||||||
|
vec2 point = ((p2.xy/p2.w)+1)*screenSize;
|
||||||
|
ssmin = min(ssmin, point);
|
||||||
|
ssmax = max(ssmax, point);
|
||||||
|
|
||||||
|
point = ((p0.xy/p0.w)+1)*screenSize;
|
||||||
|
vec2 t0min = min(ssmin, point);
|
||||||
|
vec2 t0max = max(ssmax, point);
|
||||||
|
|
||||||
|
point = ((p3.xy/p3.w)+1)*screenSize;
|
||||||
|
vec2 t1min = min(ssmin, point);
|
||||||
|
vec2 t1max = max(ssmax, point);
|
||||||
|
|
||||||
|
//Possibly cull the triangles if they dont cover the center of a pixel on the screen (degen)
|
||||||
|
float degenBias = 0.01f;
|
||||||
|
bool t0draw = all(notEqual(round(t0min-degenBias),round(t0max+degenBias)));
|
||||||
|
bool t1draw = all(notEqual(round(t1min-degenBias),round(t1max+degenBias)));
|
||||||
|
return bvec2(t0draw, t1draw);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
layout(binding = STATISTICS_BUFFER_BINDING, std430) restrict buffer statisticsBuffer {
|
||||||
|
uint visibleSectionCounts[5];
|
||||||
|
uint quadCounts[5];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
/*
|
||||||
|
if (task.quadCount == 0) {
|
||||||
|
SetMeshOutputsEXT(0,0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SetMeshOutputsEXT(0,0);
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
atomicAdd(quadCounts[task.quadCount%5], 1);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
*/
|
||||||
|
|
||||||
|
uint qid = uint(-1);
|
||||||
|
Quad quad;
|
||||||
|
if (gl_GlobalInvocationID.x<task.quadCount) {
|
||||||
|
qid = getQuadId() + task.baseQuad;
|
||||||
|
quad = quadData[qid];
|
||||||
|
setup(quad);
|
||||||
|
bool render = dot(faceNormal(face), cornerPos-cameraSubPos) <= 0;
|
||||||
|
qid = render?qid:uint(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
subgroupBarrier();
|
||||||
|
uint triId_ = subgroupExclusiveAdd(qid!=uint(-1)?2:0);
|
||||||
|
uint qc = subgroupMax(triId_+(qid!=uint(-1)?2:0));
|
||||||
|
SetMeshOutputsEXT(qc*2, qc);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
if (subgroupElect()) {
|
||||||
|
atomicAdd(quadCounts[task.lodLvl], qc);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (qid != uint(-1)) {
|
||||||
|
uint vertId_ = triId_*2;//hack cause we are emitting full quads (2 tris) just to test
|
||||||
|
uint triId = triId_;
|
||||||
|
uint vertId = vertId_;
|
||||||
|
|
||||||
|
vec4 p1 = emitVertexPos(1);
|
||||||
|
vec4 p2 = emitVertexPos(2);
|
||||||
|
vec4 p0 = emitVertexPos(0);
|
||||||
|
vec4 p3 = emitVertexPos(3);
|
||||||
|
|
||||||
|
uvec4 data = createQuadData(quad);
|
||||||
|
|
||||||
|
//Emit common
|
||||||
|
gl_MeshVerticesEXT[vertId++].gl_Position = p1;
|
||||||
|
gl_MeshVerticesEXT[vertId++].gl_Position = p2;
|
||||||
|
|
||||||
|
|
||||||
|
//Prim 1
|
||||||
|
gl_PrimitiveTriangleIndicesEXT[triId] = uvec3(vertId_+0, vertId_+1, vertId);
|
||||||
|
gl_MeshVerticesEXT[vertId++].gl_Position = p0;
|
||||||
|
primOut[triId++].data = data;
|
||||||
|
|
||||||
|
//Prim 2
|
||||||
|
gl_PrimitiveTriangleIndicesEXT[triId] = uvec3(vertId_+0, vertId, vertId_+1);
|
||||||
|
gl_MeshVerticesEXT[vertId++].gl_Position = p3;
|
||||||
|
primOut[triId++].data = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
126
src/main/resources/assets/voxy/shaders/lod/meshext/task.glsl
Normal file
126
src/main/resources/assets/voxy/shaders/lod/meshext/task.glsl
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
#version 460 core
|
||||||
|
|
||||||
|
#extension GL_EXT_mesh_shader : require
|
||||||
|
|
||||||
|
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
|
||||||
|
|
||||||
|
#import <voxy:lod/section.glsl>
|
||||||
|
|
||||||
|
layout(binding = 0, std140) uniform SceneUniform {
|
||||||
|
mat4 MVP;
|
||||||
|
ivec3 baseSectionPos;
|
||||||
|
uint frameId;
|
||||||
|
vec3 cameraSubPos;
|
||||||
|
uint pad_;
|
||||||
|
vec2 screenSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 1, std430) restrict readonly buffer IndirectSectionLookupBuffer {
|
||||||
|
uint sectionCount;
|
||||||
|
uint indirectLookup[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 2, std430) restrict readonly buffer SectionBuffer {
|
||||||
|
SectionMeta sectionData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
layout(binding = 3, std430) restrict readonly buffer VisibilityBuffer {
|
||||||
|
uint visibilityData[];
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
layout(binding = STATISTICS_BUFFER_BINDING, std430) restrict buffer statisticsBuffer {
|
||||||
|
uint visibleSectionCounts[5];
|
||||||
|
uint quadCounts[5];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Task {
|
||||||
|
//Tightly packed, prefix sum + offset
|
||||||
|
//uvec4 binA;
|
||||||
|
//uvec4 binB;
|
||||||
|
uint bins[8];
|
||||||
|
vec3 cameraOffset;
|
||||||
|
uint lodLvl;
|
||||||
|
|
||||||
|
uint baseQuad;
|
||||||
|
uint quadCount;
|
||||||
|
uint padddd[64];
|
||||||
|
};
|
||||||
|
taskPayloadSharedEXT Task task;
|
||||||
|
|
||||||
|
#define BIN(br, cnt) if (br) { task.bins[i++] = (sum<<16)|off; sum += cnt; } off += cnt;
|
||||||
|
//#define BIN(br, cnt) if (br) { batch[i++] = (sum<<16)|off; sum += cnt; } off += cnt;
|
||||||
|
|
||||||
|
bvec3 and(bvec3 a, bvec3 b) {
|
||||||
|
return bvec3(a.x&&b.x, a.y&&b.y, a.z&&b.z);
|
||||||
|
}
|
||||||
|
uint fillBins(uvec4 counts, ivec3 relative) {//Returns quad count
|
||||||
|
#pragma unroll
|
||||||
|
for (uint i = 0; i < 8; i++) task.bins[i] = uint(-1);
|
||||||
|
|
||||||
|
uvec3 cA = counts.yzw&0xFFFFu;
|
||||||
|
uvec3 cB = counts.yzw>>16;
|
||||||
|
|
||||||
|
bvec3 a = and(notEqual(cA, uvec3(0)), lessThanEqual(ivec3(0), relative.yzx));
|
||||||
|
bvec3 b = and(notEqual(cB, uvec3(0)), lessThanEqual(relative.yzx, ivec3(0)));
|
||||||
|
|
||||||
|
uint dsc = counts.x>>16;//double sided quads
|
||||||
|
uint sum = 0;
|
||||||
|
uint off = counts.x&0xFFFFu;//translucent quads
|
||||||
|
uint i = 0;
|
||||||
|
|
||||||
|
//TODO: might need to move this into shared memory or somethign? so that compiler can reason about it (or make the bin an array in here and mesh)
|
||||||
|
//uint batch[8] = {uint(-1), uint(-1), uint(-1), uint(-1), uint(-1),uint(-1),uint(-1),uint(-1)};
|
||||||
|
|
||||||
|
BIN(dsc!=0, dsc);//Double sided quads
|
||||||
|
|
||||||
|
//TODO: compute prefix sums and then jsut batch set into the array (this is an optimization)
|
||||||
|
|
||||||
|
BIN(a.x, cA.x);//Down
|
||||||
|
BIN(b.x, cB.x);//Up
|
||||||
|
BIN(a.y, cA.y);//North
|
||||||
|
BIN(b.y, cB.y);//South
|
||||||
|
BIN(a.z, cA.z);//West
|
||||||
|
BIN(b.z, cB.z);//East
|
||||||
|
|
||||||
|
//task.binA = uvec4(batch[0], batch[1], batch[2], batch[3]);
|
||||||
|
//task.binB = uvec4(batch[4], batch[5], batch[6], batch[7]);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
uint secId = indirectLookup[gl_WorkGroupID.x];
|
||||||
|
uint vis = visibilityData[secId];
|
||||||
|
|
||||||
|
bool shouldRender = (vis&0x7fffffffu) == frameId-1;//-1 since we are technically in the next frame for the primary rasterization
|
||||||
|
bool renderTemporally = (vis&0x80000000u)==0;
|
||||||
|
|
||||||
|
uint qc = 0;
|
||||||
|
if (shouldRender) {
|
||||||
|
SectionMeta section = sectionData[secId];
|
||||||
|
|
||||||
|
uint detail = extractDetail(section);
|
||||||
|
ivec3 ipos = extractPosition(section);
|
||||||
|
|
||||||
|
ivec3 relative = ipos-(baseSectionPos>>detail);
|
||||||
|
|
||||||
|
#ifdef HAS_STATISTICS
|
||||||
|
atomicAdd(visibleSectionCounts[detail], 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//TODO: here enqueue the id here for both translucent and temporal (if relevant) (* note technically dont need for temporal as can just check :tm: if we are in temporal render mode)
|
||||||
|
|
||||||
|
task.baseQuad = extractQuadStart(section);
|
||||||
|
qc = fillBins(section.b, relative);
|
||||||
|
task.quadCount = qc;
|
||||||
|
|
||||||
|
task.cameraOffset = vec3(((ipos<<detail) - baseSectionPos)<<5);
|
||||||
|
task.lodLvl = detail;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//It appears to be valid to read from taskPayloadSharedEXT
|
||||||
|
EmitMeshTasksEXT((qc+(MESH_SIZE-1))/MESH_SIZE, 1, 1);
|
||||||
|
}
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
"iris.MixinProgramSet",
|
"iris.MixinProgramSet",
|
||||||
"iris.MixinShaderPackSourceNames",
|
"iris.MixinShaderPackSourceNames",
|
||||||
"iris.MixinStandardMacros",
|
"iris.MixinStandardMacros",
|
||||||
"minecraft.MixinClientWorld",
|
|
||||||
"minecraft.MixinClientChunkManager",
|
"minecraft.MixinClientChunkManager",
|
||||||
"minecraft.MixinClientCommonNetworkHandler",
|
"minecraft.MixinClientCommonNetworkHandler",
|
||||||
"minecraft.MixinClientLoginNetworkHandler",
|
"minecraft.MixinClientLoginNetworkHandler",
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
"minecraft": ["1.21.8","1.21.7","1.21.6"],
|
"minecraft": ["1.21.8","1.21.7","1.21.6"],
|
||||||
"fabricloader": ">=0.14.22",
|
"fabricloader": ">=0.14.22",
|
||||||
"fabric-api": ">=0.91.1",
|
"fabric-api": ">=0.91.1",
|
||||||
"sodium": "=0.7.*"
|
"sodium": ">=0.6.13"
|
||||||
},
|
},
|
||||||
"accessWidener": "voxy.accesswidener"
|
"accessWidener": "voxy.accesswidener"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user