diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 11179cb0..c8cbdaa1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true env: - RUNTIME_VERSION: 21 + RUNTIME_VERSION: 25 jobs: build: diff --git a/build.gradle.kts b/build.gradle.kts index adce75d9..103715ea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -87,17 +87,11 @@ subprojects { dependencies { "minecraft"(libs.minecraft) - "mappings"(loom.layered { - officialMojangMappings { - nameSyntheticMembers = false - } - parchment(variantOf(libs.parchment) { artifactType("zip") }) - }) "vineflowerDecompilerClasspath"(libs.vineflower) } - configurations.named("modLocalRuntime") { - shouldResolveConsistentlyWith(configurations.getByName("modImplementation")) + configurations.named("localRuntime") { + shouldResolveConsistentlyWith(configurations.getByName("implementation")) } } diff --git a/gradle.properties b/gradle.properties index c57a9d41..37e79f60 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,7 @@ cfProjectId=402098 # Project configuration -targetJavaVersion=21 +targetJavaVersion=25 enabledPlatforms=fabric,neoforge # Gradle diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties index 63e5bbdf..5a334ba9 100644 --- a/gradle/gradle-daemon-jvm.properties +++ b/gradle/gradle-daemon-jvm.properties @@ -1,2 +1,2 @@ #This file is generated by updateDaemonJvm -toolchainVersion=21 +toolchainVersion=25 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 409054e0..25c6591f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,45 +3,48 @@ format = { version = "1.1" } [versions] indra = "3.1.3" -minecraft = "1.21.11" -fabricLoader = "0.18.3" -fabricApi = "0.140.0+1.21.11" -modmenu = "16.0.0-rc.1" -viafabricplus = "3.3.0" -vineflower = "1.10.1" -cuiProtocol = "4.0.2" +minecraft = "26.1.1" +fabricLoader = "0.18.6" +fabricApi = "0.145.4+26.1.1" +modmenu = "18.0.0-alpha.8" +viafabricplus = "4.5.1" +vineflower = "1.11.2" +cuiProtocol = "4.0.3" [libraries] minecraft = { module = "com.mojang:minecraft", version.ref = "minecraft" } -parchment = { module = "org.parchmentmc.data:parchment-1.21.10", version = "2025.10.12" } fabric-loader = { module = "net.fabricmc:fabric-loader", version.ref = "fabricLoader" } fabric-api = { module = "net.fabricmc.fabric-api:fabric-api", version.ref = "fabricApi" } fabric-api-bom = { module = "net.fabricmc.fabric-api:fabric-api-bom", version.ref = "fabricApi" } fabric-api-networking = { module = "net.fabricmc.fabric-api:fabric-networking-api-v1" } modmenu = { module = "com.terraformersmc:modmenu", version.ref = "modmenu" } neoforge = { module = "net.neoforged:neoforge", version = "21.10.9-beta"} -viafabricplus-api = { module = "de.florianmichael:ViaFabricPlus", version.ref = "viafabricplus" } +viafabricplus-api = { module = "com.viaversion:viafabricplus", version.ref = "viafabricplus" } viaversion = { module = "com.viaversion:viaversion-common", version = "5.0.5" } vineflower = { module = "org.vineflower:vineflower", version.ref = "vineflower" } worldedit = { module = "com.sk89q.worldedit:worldedit-fabric-mc1.21", version = "7.3.14" } [plugins] -architecturyPlugin = { id = "architectury-plugin", version = "3.4.160" } +architecturyPlugin = { id = "architectury-plugin", version = "3.5.163" } curseForgeGradle = { id = "net.darkhax.curseforgegradle", version = "1.1.26" } indra-git = { id = "net.kyori.indra.git", version.ref = "indra" } indra-spotlessLicenser = { id = "net.kyori.indra.licenser.spotless", version.ref = "indra" } jvmConflictResolution = { id = "org.gradlex.jvm-dependency-conflict-resolution", version = "2.1.2" } -loom = { id = "dev.architectury.loom", version = "1.13.463" } +loom = { id = "dev.architectury.loom-no-remap", version = "1.14.471" } minotaur = { id = "com.modrinth.minotaur", version = "2.8.7" } publishGithubRelease = { id = "ca.stellardrift.publish-github-release", version = "0.1.0" } shadow = { id = "com.gradleup.shadow", version = "8.3.5" } spotless = { id = "com.diffplug.spotless", version = "6.25.0" } versions = { id = "com.github.ben-manes.versions", version = "0.51.0" } +[libraries.cuiProtocol-common] +module = "org.enginehub.worldeditcui-protocol:worldeditcui-protocol-common-mc26.1" +version.ref = "cuiProtocol" + [libraries.cuiProtocol-fabric] -module = "org.enginehub.worldeditcui-protocol:worldeditcui-protocol-fabric-mc1.21.11" +module = "org.enginehub.worldeditcui-protocol:worldeditcui-protocol-fabric-mc26.1" version.ref = "cuiProtocol" [libraries.cuiProtocol-neoforge] -module = "org.enginehub.worldeditcui-protocol:worldeditcui-protocol-neoforge-mc1.21.11" +module = "org.enginehub.worldeditcui-protocol:worldeditcui-protocol-neoforge-mc26.1" version.ref = "cuiProtocol" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b95..61285a65 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a793..c61a118f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f3b75f3b..adff685a 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -205,15 +203,14 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9b42019c..e509b2dd 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/worldeditcui-fabric/build.gradle.kts b/worldeditcui-fabric/build.gradle.kts index f3dfaa0c..84c5833b 100644 --- a/worldeditcui-fabric/build.gradle.kts +++ b/worldeditcui-fabric/build.gradle.kts @@ -37,14 +37,19 @@ loom { val fabricApi by configurations.creating dependencies { - "include"(libs.cuiProtocol.fabric) - "modImplementation"(libs.cuiProtocol.fabric) - modImplementation(libs.fabric.loader) - modImplementation(libs.modmenu) - modCompileOnly(libs.viafabricplus.api) { + "include"(libs.cuiProtocol.fabric) { + attributes { + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling::class, Bundling.SHADOWED)) + } + } + "implementation"(libs.cuiProtocol.common) + "implementation"(libs.cuiProtocol.fabric) + implementation(libs.fabric.loader) + implementation(libs.modmenu) + compileOnly(libs.viafabricplus.api) { isTransitive = false } - modCompileOnly(libs.viaversion) + compileOnly(libs.viaversion) // [1] declare fabric-api dependency... fabricApi(libs.fabric.api) @@ -91,7 +96,7 @@ dependencies { fabricApiDependencies.values.forEach { "include"(it) - "modImplementation"(it) + "implementation"(it) } // for development @@ -227,7 +232,7 @@ tasks { apiToken = cfApiToken.get() - with(upload(cfProjectId.get(), remapJar)) { + with(upload(cfProjectId.get(), jar)) { displayName = project.version releaseType = Constants.RELEASE_TYPE_RELEASE changelog = changelogContents.getOrElse("") @@ -255,7 +260,7 @@ modrinth { token = modrinthToken projectId = "worldedit-cui" syncBodyFrom = providers.provider { file("README.md").readText(Charsets.UTF_8) } - uploadFile.set(tasks.remapJar) + uploadFile.set(tasks.jar) gameVersions.add(libs.versions.minecraft.get()) changelog = changelogContents dependencies { @@ -277,5 +282,5 @@ githubRelease { repository = "EngineHub/WorldEditCUI" releaseName = "WorldEditCUI v$version" releaseBody = changelogContents - artifacts.from(tasks.remapJar) + artifacts.from(tasks.jar) } diff --git a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/config/CUIConfiguration.java b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/config/CUIConfiguration.java index 37607366..5733129f 100644 --- a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/config/CUIConfiguration.java +++ b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/config/CUIConfiguration.java @@ -80,6 +80,7 @@ public void write( private boolean debugMode = false; private boolean promiscuous = false; + private boolean compatibilityRenderer = false; private boolean clearAllOnKey = false; private Colour cuboidGridColor = ConfiguredColour.CUBOIDGRID.getDefault(); @@ -135,6 +136,10 @@ public void setPromiscuous(boolean promiscuous) { this.promiscuous = promiscuous; } + public boolean isExperimentalRenderer() { + return this.compatibilityRenderer; + } + public boolean isClearAllOnKey() { return this.clearAllOnKey; } @@ -167,6 +172,7 @@ public static CUIConfiguration create() { configArray.put("debugMode", config.debugMode); configArray.put("promiscuous", config.promiscuous); + configArray.put("compatibilityRenderer", config.compatibilityRenderer); configArray.put("clearAllOnKey", config.clearAllOnKey); configArray.put("cuboidGridColor", config.cuboidGridColor); @@ -202,6 +208,7 @@ public Map getConfigArray() { public void configChanged() { debugMode = (Boolean) configArray.get("debugMode"); promiscuous = (Boolean) configArray.get("promiscuous"); + compatibilityRenderer = (Boolean) configArray.get("compatibilityRenderer"); clearAllOnKey = (Boolean) configArray.get("clearAllOnKey"); cuboidGridColor = (Colour) configArray.get("cuboidGridColor"); @@ -223,7 +230,7 @@ public void configChanged() { public Object getDefaultValue(String text) { return switch (text) { - case "debugMode", "promiscuous", "clearAllOnKey" -> false; + case "debugMode", "promiscuous", "compatibilityRenderer", "clearAllOnKey" -> false; case "cuboidGridColor" -> ConfiguredColour.CUBOIDGRID.getDefault(); case "cuboidEdgeColor" -> ConfiguredColour.CUBOIDBOX.getDefault(); case "cuboidFirstPointColor" -> ConfiguredColour.CUBOIDPOINT1.getDefault(); @@ -265,6 +272,7 @@ public Object getDefaultValue(String text) { return switch (text) { case "debugMode" -> "worldeditcui.options.debugMode"; case "promiscuous" -> "worldeditcui.options.compat.spammy"; + case "compatibilityRenderer" -> "worldeditcui.options.compat.renderer"; case "clearAllOnKey" -> "worldeditcui.options.extra.clearall"; case "cuboidGridColor" -> "worldeditcui.color.cuboidgrid"; case "cuboidEdgeColor" -> "worldeditcui.color.cuboidedge"; diff --git a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/event/listeners/CUIListenerWorldRender.java b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/event/listeners/CUIListenerWorldRender.java index 7beb3a01..371214ed 100644 --- a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/event/listeners/CUIListenerWorldRender.java +++ b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/event/listeners/CUIListenerWorldRender.java @@ -10,16 +10,14 @@ package org.enginehub.worldeditcui.event.listeners; import com.mojang.blaze3d.buffers.GpuBufferSlice; -import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.opengl.GlStateManager; -import com.mojang.blaze3d.platform.DestFactor; -import com.mojang.blaze3d.platform.SourceFactor; import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.fog.FogRenderer; import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.ProfilerFiller; import org.enginehub.worldeditcui.WorldEditCUI; +import org.enginehub.worldeditcui.config.CUIConfiguration; import org.enginehub.worldeditcui.render.BufferBuilderRenderSink; import org.enginehub.worldeditcui.render.LineStyle; import org.enginehub.worldeditcui.render.PipelineProvider; @@ -28,7 +26,9 @@ import org.joml.Matrix4fStack; import org.lwjgl.opengl.GL32; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * Listener for WorldRenderEvent @@ -42,38 +42,56 @@ public class CUIListenerWorldRender private final WorldEditCUI controller; private final Minecraft minecraft; + private final CUIConfiguration configuration; private final CUIRenderContext ctx = new CUIRenderContext(); private final List pipelines; - private int currentPipelineIdx; + private final Set disabledPipelines = new HashSet<>(); + private boolean experimentalRendererSelected; + private PipelineProvider activePipeline; private RenderSink sink; - public CUIListenerWorldRender(final WorldEditCUI controller, final Minecraft minecraft, final List pipelines) + public CUIListenerWorldRender(final WorldEditCUI controller, final Minecraft minecraft, final CUIConfiguration configuration, final List pipelines) { this.controller = controller; this.minecraft = minecraft; + this.configuration = configuration; this.pipelines = List.copyOf(pipelines); + this.experimentalRendererSelected = configuration.isExperimentalRenderer(); } private RenderSink providePipeline() { - if (this.sink != null) - { + final boolean useExperimentalRenderer = this.configuration.isExperimentalRenderer(); + final PipelineProvider preferred = preferredPipeline(); + if (useExperimentalRenderer != this.experimentalRendererSelected) { + this.experimentalRendererSelected = useExperimentalRenderer; + this.disabledPipelines.clear(); + this.activePipeline = null; + this.sink = null; + } else if (this.sink != null && this.activePipeline != null + && (this.activePipeline == preferred || !preferred.available() || this.disabledPipelines.contains(preferred.id()))) { return this.sink; + } else if (this.sink != null && this.activePipeline != null && this.disabledPipelines.contains(this.activePipeline.id())) { + this.activePipeline = null; + this.sink = null; } - for (int i = this.currentPipelineIdx; i < this.pipelines.size(); i++) + for (final PipelineProvider pipeline : orderedPipelines()) { - final PipelineProvider pipeline = this.pipelines.get(i); + if (this.disabledPipelines.contains(pipeline.id())) { + continue; + } if (pipeline.available()) { try { final RenderSink sink = pipeline.provide(); - this.currentPipelineIdx = i; + this.activePipeline = pipeline; return this.sink = sink; } catch (final Exception ex) { + this.disabledPipelines.add(pipeline.id()); this.controller.getDebugger().info("Failed to render with pipeline " + pipeline.id() + ", which declared itself as available... trying next"); } } @@ -83,16 +101,45 @@ private RenderSink providePipeline() } private void invalidatePipeline() { - if (this.currentPipelineIdx < this.pipelines.size() - 1) { - this.currentPipelineIdx++; - this.sink = null; + if (this.activePipeline != null) { + this.disabledPipelines.add(this.activePipeline.id()); + } + this.activePipeline = null; + this.sink = null; + } + + private PipelineProvider preferredPipeline() { + return pipelineById(this.configuration.isExperimentalRenderer() ? "vanilla" : "legacy-vanilla"); + } + + private List orderedPipelines() { + if (this.configuration.isExperimentalRenderer()) { + return List.of( + pipelineById("vanilla"), + pipelineById("legacy-vanilla"), + pipelineById("optifine") + ); + } + return List.of( + pipelineById("legacy-vanilla"), + pipelineById("optifine"), + pipelineById("vanilla") + ); + } + + private PipelineProvider pipelineById(final String id) { + for (final PipelineProvider pipeline : this.pipelines) { + if (pipeline.id().equals(id)) { + return pipeline; + } } + throw new IllegalStateException("Missing pipeline provider " + id); } public void onRender(final float partialTicks) { try { final RenderSink sink = this.providePipeline(); - if (!this.pipelines.get(this.currentPipelineIdx).shouldRender()) + if (this.activePipeline != null && !this.activePipeline.shouldRender()) { // allow ignoring eg. shadow pass return; @@ -103,18 +150,15 @@ public void onRender(final float partialTicks) { final GpuBufferSlice fogStart = RenderSystem.getShaderFog(); RenderSystem.setShaderFog(this.minecraft.gameRenderer.fogRenderer.getBuffer(FogRenderer.FogMode.NONE)); final Matrix4fStack poseStack = RenderSystem.getModelViewStack(); + final boolean legacySink = sink instanceof BufferBuilderRenderSink; poseStack.pushMatrix(); - GlStateManager._disableCull(); - GlStateManager._enableBlend(); - // RenderSystem.disableTexture(); - GlStateManager._enableDepthTest(); - GlStateManager._blendFuncSeparate( - GlConst.toGl(SourceFactor.SRC_ALPHA), - GlConst.toGl(DestFactor.ONE_MINUS_SRC_ALPHA), - GlConst.toGl(SourceFactor.SRC_ALPHA), - GlConst.toGl(DestFactor.ONE_MINUS_SRC_ALPHA)); - GlStateManager._depthMask(true); - BufferBuilderRenderSink.LineWidth.set(LineStyle.DEFAULT_WIDTH); + if (legacySink) { + GlStateManager._enableBlend(); + // RenderSystem.disableTexture(); + GlStateManager._enableDepthTest(); + GlStateManager._depthMask(true); + BufferBuilderRenderSink.LineWidth.set(LineStyle.DEFAULT_WIDTH); + } try { this.controller.renderSelections(this.ctx); @@ -124,10 +168,11 @@ public void onRender(final float partialTicks) { this.invalidatePipeline(); } - GlStateManager._depthFunc(GL32.GL_LEQUAL); - // RenderSystem.enableTexture(); - GlStateManager._disableBlend(); - GlStateManager._enableCull(); + if (legacySink) { + GlStateManager._depthFunc(GL32.GL_LEQUAL); + // RenderSystem.enableTexture(); + GlStateManager._disableBlend(); + } poseStack.popMatrix(); RenderSystem.setShaderFog(fogStart); profiler.pop(); diff --git a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/fabric/FabricModWorldEditCUI.java b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/fabric/FabricModWorldEditCUI.java index 9fb58f48..eb49e856 100644 --- a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/fabric/FabricModWorldEditCUI.java +++ b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/fabric/FabricModWorldEditCUI.java @@ -13,10 +13,11 @@ import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; -import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +import net.fabricmc.fabric.api.client.keymapping.v1.KeyMappingHelper; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; -import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderContext; -import net.fabricmc.fabric.api.client.rendering.v1.world.WorldRenderEvents; +import net.fabricmc.fabric.api.client.rendering.v1.level.LevelExtractionContext; +import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderEvents; import net.fabricmc.fabric.api.networking.v1.PacketSender; import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; @@ -28,6 +29,7 @@ import org.enginehub.worldeditcui.config.CUIConfiguration; import org.enginehub.worldeditcui.event.listeners.CUIListenerChannel; import org.enginehub.worldeditcui.event.listeners.CUIListenerWorldRender; +import org.enginehub.worldeditcui.render.LegacyVanillaPipelineProvider; import org.enginehub.worldeditcui.protocol.CUIPacket; import org.enginehub.worldeditcui.protocol.CUIPacketHandler; import org.enginehub.worldeditcui.render.OptifinePipelineProvider; @@ -58,6 +60,7 @@ public final class FabricModWorldEditCUI implements ModInitializer { private static final List RENDER_PIPELINES = List.of( new OptifinePipelineProvider(), + new LegacyVanillaPipelineProvider(), new VanillaPipelineProvider() ); @@ -81,7 +84,7 @@ public final class FabricModWorldEditCUI implements ModInitializer { * @return new, registered keybinding in the mod category */ private static KeyMapping key(final String name, final int code) { - return KeyBindingHelper.registerKeyBinding( + return KeyMappingHelper.registerKeyMapping( new KeyMapping("key." + MOD_ID + '.' + name, code, KEYBIND_CATEGORY_WECUI)); } @@ -98,15 +101,15 @@ public void onInitialize() { ClientLifecycleEvents.CLIENT_STARTED.register(this::onGameInitDone); CUINetworking.subscribeToCuiPacket(this::onPluginMessage); ClientPlayConnectionEvents.JOIN.register(this::onJoinGame); - WorldRenderEvents.END_EXTRACTION.register(ctx -> { + LevelRenderEvents.END_EXTRACTION.register(ctx -> { // MC now handles this separately to the actual render, due to it occurring across threads. // We need to store this for later use during actual render. - lastPartialTicks = ctx.tickCounter().getRealtimeDeltaTicks(); + this.onEndExtraction(ctx); }); - WorldRenderEvents.END_MAIN.register(ctx -> { + LevelRenderEvents.END_MAIN.register(ctx -> { try { RenderSystem.getModelViewStack().pushMatrix(); - RenderSystem.getModelViewStack().mul(ctx.matrices().last().pose()); + RenderSystem.getModelViewStack().mul(ctx.poseStack().last().pose()); // RenderSystem.applyModelViewMatrix(); this.onPostRenderEntities(ctx); } finally { @@ -175,7 +178,7 @@ private void onPluginMessage(final CUIPacket payload, final CUIPacketHandler.Pac public void onGameInitDone(final Minecraft client) { this.controller = new WorldEditCUI(); this.controller.initialise(client); - this.worldRenderListener = new CUIListenerWorldRender(this.controller, client, RENDER_PIPELINES); + this.worldRenderListener = new CUIListenerWorldRender(this.controller, client, this.controller.getConfiguration(), RENDER_PIPELINES); this.channelListener = new CUIListenerChannel(this.controller); } @@ -185,7 +188,11 @@ public void onJoinGame(final ClientPacketListener handler, final PacketSender se this.helo(handler); } - public void onPostRenderEntities(final WorldRenderContext ctx) { + private void onEndExtraction(final LevelExtractionContext ctx) { + this.lastPartialTicks = ctx.deltaTracker().getRealtimeDeltaTicks(); + } + + public void onPostRenderEntities(final LevelRenderContext ctx) { if (this.visible) { this.worldRenderListener.onRender(lastPartialTicks); } diff --git a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/gui/CUIConfigList.java b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/gui/CUIConfigList.java index d611e003..391c77a4 100644 --- a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/gui/CUIConfigList.java +++ b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/gui/CUIConfigList.java @@ -13,7 +13,7 @@ import com.mojang.logging.LogUtils; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.components.ContainerObjectSelectionList; import net.minecraft.client.gui.components.CycleButton; @@ -104,17 +104,18 @@ protected void updateFromConfig() { } @Override - public void renderContent(GuiGraphics gfx, int mouseX, int mouseY, boolean isMouseOver, float partialTick) { - super.renderContent(gfx, mouseX, mouseY, isMouseOver, partialTick); + public void extractContent(GuiGraphicsExtractor gfx, int mouseX, int mouseY, boolean isMouseOver, float partialTick) { + super.extractContent(gfx, mouseX, mouseY, isMouseOver, partialTick); this.toggleBotton.setX(getRowLeft() + 105); this.toggleBotton.setY(getY()); - this.toggleBotton.render(gfx, mouseX, mouseY, partialTick); + this.toggleBotton.extractRenderState(gfx, mouseX, mouseY, partialTick); } } public class ColorConfigEntry extends ConfigEntry { private final EditBox textField; + private String currentInput; public ColorConfigEntry(String tag) { super(tag); @@ -122,8 +123,14 @@ public ColorConfigEntry(String tag) { Colour cValue = (Colour)configuration.getConfigArray().get(tag); textField = new EditBox(CUIConfigList.this.minecraft.font, 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, Component.literal(cValue.hexString())); textField.setMaxLength(9); // # + 8 hex chars - textField.setValue(cValue.hexString()); + this.currentInput = cValue.hexString(); + textField.setValue(this.currentInput); textField.setResponder(updated -> { + if (!isAcceptableColorInput(updated)) { + textField.setValue(this.currentInput); + return; + } + this.currentInput = updated; Colour tested = Colour.parseRgbaOrNull(updated); if (tested != null) { configuration.changeValue(tag, tested); @@ -139,21 +146,22 @@ public ColorConfigEntry(String tag) { .result() .orElseGet(() -> FormattedCharSequence.forward(string, invalidFormat)); }); - textField.setFilter(value -> { - // filter for #AARRGGBB - if (!value.isEmpty() && value.charAt(0) != '#') { // does not start with hex - return false; - } + } - for (int i = 1; i < value.length(); i++) { // any characters that are not valid in a hex string - final char c = value.charAt(i); - if ((c < '0' || c > '9') && (c < 'A' || c > 'F') && (c < 'a' || c > 'f')) { - return false; - } + private boolean isAcceptableColorInput(final String value) { + // filter for #AARRGGBB + if (!value.isEmpty() && value.charAt(0) != '#') { // does not start with hex + return false; + } + + for (int i = 1; i < value.length(); i++) { // any characters that are not valid in a hex string + final char c = value.charAt(i); + if ((c < '0' || c > '9') && (c < 'A' || c > 'F') && (c < 'a' || c > 'f')) { + return false; } + } - return true; - }); + return true; } @Override @@ -168,15 +176,16 @@ public ColorConfigEntry(String tag) { @Override protected void updateFromConfig() { - this.textField.setValue(((Colour)configuration.getConfigArray().get(tag)).hexString()); + this.currentInput = ((Colour)configuration.getConfigArray().get(tag)).hexString(); + this.textField.setValue(this.currentInput); } @Override - public void renderContent(GuiGraphics gfx, int mouseX, int mouseY, boolean isMouseOver, float partialTick) { - super.renderContent(gfx, mouseX, mouseY, isMouseOver, partialTick); + public void extractContent(GuiGraphicsExtractor gfx, int mouseX, int mouseY, boolean isMouseOver, float partialTick) { + super.extractContent(gfx, mouseX, mouseY, isMouseOver, partialTick); this.textField.setX(getRowLeft() + 105); this.textField.setY(getY()); - this.textField.render(gfx, mouseX, mouseY, partialTick); + this.textField.extractRenderState(gfx, mouseX, mouseY, partialTick); } } @@ -203,7 +212,7 @@ public ConfigEntry(String tag) { } @Override - public void renderContent(GuiGraphics gfx, int mouseX, int mouseY, boolean hovered, float partialTick) { + public void extractContent(GuiGraphicsExtractor gfx, int mouseX, int mouseY, boolean hovered, float partialTick) { // new API handles entry position internally int left = this.getX(); int top = this.getY(); // or getRowTop() @@ -212,11 +221,11 @@ public void renderContent(GuiGraphics gfx, int mouseX, int mouseY, boolean hover this.textField.setX(textLeft); this.textField.setY(top); - this.textField.render(gfx, mouseX, mouseY, partialTick); + this.textField.extractRenderState(gfx, mouseX, mouseY, partialTick); this.resetButton.setX(left + 190); this.resetButton.setY(top); - this.resetButton.render(gfx, mouseX, mouseY, partialTick); + this.resetButton.extractRenderState(gfx, mouseX, mouseY, partialTick); } protected abstract void updateFromConfig(); diff --git a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/gui/CUIConfigPanel.java b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/gui/CUIConfigPanel.java index 11a41879..3715198c 100644 --- a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/gui/CUIConfigPanel.java +++ b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/gui/CUIConfigPanel.java @@ -9,8 +9,7 @@ */ package org.enginehub.worldeditcui.gui; -import net.minecraft.client.gui.GuiGraphics; -import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.GuiGraphicsExtractor; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.CommonComponents; @@ -27,7 +26,6 @@ public class CUIConfigPanel extends Screen { private final Screen parent; final CUIConfiguration configuration; - private AbstractWidget done; private CUIConfigList configList; private final Component screenTitle; @@ -42,23 +40,19 @@ public CUIConfigPanel(Screen parent, CUIConfiguration configuration) { protected void init() { super.init(); - done = this.addWidget(Button.builder(CommonComponents.GUI_DONE, (button) -> { + this.addRenderableWidget(Button.builder(CommonComponents.GUI_DONE, (button) -> { configuration.configChanged(); assert minecraft != null; this.minecraft.setScreen(parent); }).bounds((this.width - BUTTON_DONE_WIDTH) / 2, this.height - (BUTTON_HEIGHT + 7), BUTTON_DONE_WIDTH, BUTTON_HEIGHT).build()); this.configList = CUIConfigList.create(this, this.minecraft); - this.addWidget(this.configList); + this.addRenderableWidget(this.configList); } @Override - public void render(GuiGraphics gfx, int mouseX, int mouseY, float delta) { - super.render(gfx, mouseX, mouseY, delta); - - this.configList.render(gfx, mouseX, mouseY, delta); - gfx.drawCenteredString(this.font, screenTitle, this.width / 2, 8, 0xFFFFFF); - - this.done.render(gfx, mouseX, mouseY, delta); + public void extractRenderState(GuiGraphicsExtractor gfx, int mouseX, int mouseY, float delta) { + super.extractRenderState(gfx, mouseX, mouseY, delta); + gfx.centeredText(this.font, screenTitle, this.width / 2, 8, 0xFFFFFF); } } diff --git a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/BatchedRenderSink.java b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/BatchedRenderSink.java new file mode 100644 index 00000000..11630412 --- /dev/null +++ b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/BatchedRenderSink.java @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2011-2024 WorldEditCUI team and contributors + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.enginehub.worldeditcui.render; + +import com.mojang.blaze3d.vertex.BufferBuilder; +import com.mojang.blaze3d.vertex.MeshData; +import com.mojang.blaze3d.vertex.Tesselator; +import com.mojang.blaze3d.vertex.VertexFormat; +import org.jetbrains.annotations.Nullable; +import org.joml.Vector3f; + +public final class BatchedRenderSink implements RenderSink { + private enum Primitive { + LINE_LOOP, + LINES, + QUADS + } + + private final TypeFactory types; + private @Nullable VariantSet currentVariants; + private @Nullable RenderTarget activeTarget; + private @Nullable Primitive activePrimitive; + private @Nullable BufferBuilder builder; + private boolean active; + private float r = -1f; + private float g; + private float b; + private float a; + private float currentLineWidth = LineStyle.DEFAULT_WIDTH; + private float loopX; + private float loopY; + private float loopZ; + private float loopFirstX; + private float loopFirstY; + private float loopFirstZ; + private boolean canLoop; + + public BatchedRenderSink(final TypeFactory types) { + this.types = types; + } + + @Override + public RenderSink color(final float r, final float g, final float b, final float alpha) { + this.r = r; + this.g = g; + this.b = b; + this.a = alpha; + return this; + } + + @Override + public boolean apply(final LineStyle line, final RenderStyle.RenderType type) { + if (!line.renderType.matches(type)) { + return false; + } + this.currentLineWidth = line.lineWidth; + this.currentVariants = this.types.forStyle(line.renderType); + return true; + } + + @Override + public RenderSink vertex(final double x, final double y, final double z) { + if (this.r == -1f) { + throw new IllegalStateException("No colour has been set!"); + } + if (!this.active || this.activeTarget == null || this.builder == null) { + throw new IllegalStateException("Tried to draw when not active"); + } + + if (this.activePrimitive == Primitive.LINE_LOOP) { + if (this.canLoop) { + final Vector3f normal = this.activeTarget.hasNormals() ? this.computeNormal(this.loopX, this.loopY, this.loopZ, x, y, z) : null; + this.addVertex(this.loopX, this.loopY, this.loopZ, normal); + this.addVertex(x, y, z, normal); + } else { + this.loopFirstX = (float) x; + this.loopFirstY = (float) y; + this.loopFirstZ = (float) z; + } + this.loopX = (float) x; + this.loopY = (float) y; + this.loopZ = (float) z; + this.canLoop = true; + } else if (this.activePrimitive == Primitive.LINES) { + if (this.canLoop) { + final Vector3f normal = this.activeTarget.hasNormals() ? this.computeNormal(this.loopX, this.loopY, this.loopZ, x, y, z) : null; + this.addVertex(this.loopX, this.loopY, this.loopZ, normal); + this.addVertex(x, y, z, normal); + this.canLoop = false; + } else { + this.loopX = (float) x; + this.loopY = (float) y; + this.loopZ = (float) z; + this.canLoop = true; + } + } else { + this.addVertex(x, y, z, null); + } + + return this; + } + + private void addVertex(final double x, final double y, final double z, @Nullable final Vector3f normal) { + if (this.builder == null) { + throw new IllegalStateException("No active builder"); + } + this.builder.addVertex((float) x, (float) y, (float) z) + .setColor(this.r, this.g, this.b, this.a) + .setLineWidth(this.currentLineWidth); + if (normal != null) { + this.builder.setNormal(normal.x(), normal.y(), normal.z()); + } + } + + private Vector3f computeNormal(final double x0, final double y0, final double z0, final double x1, final double y1, final double z1) { + final double dX = (x1 - x0); + final double dY = (y1 - y0); + final double dZ = (z1 - z0); + final double length = Math.sqrt(dX * dX + dY * dY + dZ * dZ); + return new Vector3f((float) (dX / length), (float) (dY / length), (float) (dZ / length)); + } + + @Override + public RenderSink beginLineLoop() { + this.transitionState(currentVariants().lineLoop(), Primitive.LINE_LOOP); + return this; + } + + @Override + public RenderSink endLineLoop() { + this.end(currentVariants().lineLoop(), Primitive.LINE_LOOP); + if (this.canLoop) { + this.canLoop = false; + final Vector3f normal = activeTargetOrThrow().hasNormals() + ? this.computeNormal(this.loopX, this.loopY, this.loopZ, this.loopFirstX, this.loopFirstY, this.loopFirstZ) + : null; + this.addVertex(this.loopX, this.loopY, this.loopZ, normal); + this.addVertex(this.loopFirstX, this.loopFirstY, this.loopFirstZ, normal); + } + return this; + } + + @Override + public RenderSink beginLines() { + this.transitionState(currentVariants().lines(), Primitive.LINES); + return this; + } + + @Override + public RenderSink endLines() { + this.end(currentVariants().lines(), Primitive.LINES); + this.canLoop = false; + return this; + } + + @Override + public RenderSink beginQuads() { + this.transitionState(currentVariants().quads(), Primitive.QUADS); + return this; + } + + @Override + public RenderSink endQuads() { + this.end(currentVariants().quads(), Primitive.QUADS); + return this; + } + + @Override + public void flush() { + if (this.builder == null || this.activeTarget == null) { + return; + } + if (this.active) { + throw new IllegalStateException("Tried to flush while still active"); + } + final MeshData mesh = this.builder.buildOrThrow(); + this.activeTarget.draw(mesh); + this.builder = null; + this.activeTarget = null; + this.activePrimitive = null; + } + + private void end(final RenderTarget target, final Primitive primitive) { + if (!this.active) { + throw new IllegalStateException("Could not exit " + target + ", was not active"); + } + if (this.activeTarget != target || this.activePrimitive != primitive) { + throw new IllegalStateException("Expected to end state " + primitive + " but was in " + this.activePrimitive); + } + this.active = false; + } + + private void transitionState(final RenderTarget target, final Primitive primitive) { + if (this.active) { + throw new IllegalStateException("Tried to enter new state before previous operation had been completed"); + } + if (this.activeTarget != null && this.activeTarget != target) { + this.flush(); + } + if (this.activeTarget == null) { + this.builder = Tesselator.getInstance().begin(target.mode(), target.format()); + this.activeTarget = target; + } + this.activePrimitive = primitive; + this.active = true; + } + + private VariantSet currentVariants() { + if (this.currentVariants == null) { + throw new IllegalStateException("No render style has been applied"); + } + return this.currentVariants; + } + + private RenderTarget activeTargetOrThrow() { + if (this.activeTarget == null) { + throw new IllegalStateException("No active render target"); + } + return this.activeTarget; + } + + @FunctionalInterface + public interface MeshDrawer { + void draw(MeshData mesh); + } + + public static final class RenderTarget { + private final VertexFormat.Mode mode; + private final VertexFormat format; + private final boolean hasNormals; + private final MeshDrawer drawer; + + public RenderTarget(final VertexFormat.Mode mode, final VertexFormat format, final MeshDrawer drawer) { + this.mode = mode; + this.format = format; + this.hasNormals = format.getElementAttributeNames().contains("Normal"); + this.drawer = drawer; + } + + VertexFormat.Mode mode() { + return this.mode; + } + + VertexFormat format() { + return this.format; + } + + boolean hasNormals() { + return this.hasNormals; + } + + void draw(final MeshData mesh) { + this.drawer.draw(mesh); + } + } + + public static final class VariantSet { + private final RenderTarget quads; + private final RenderTarget lines; + private final RenderTarget lineLoop; + + public VariantSet(final RenderTarget quads, final RenderTarget lines, final RenderTarget lineLoop) { + this.quads = quads; + this.lines = lines; + this.lineLoop = lineLoop; + } + + RenderTarget quads() { + return this.quads; + } + + RenderTarget lines() { + return this.lines; + } + + RenderTarget lineLoop() { + return this.lineLoop; + } + } + + public interface TypeFactory { + VariantSet forStyle(RenderStyle.RenderType renderType); + } +} diff --git a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/BufferBuilderRenderSink.java b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/BufferBuilderRenderSink.java index fd76842b..f809f128 100644 --- a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/BufferBuilderRenderSink.java +++ b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/BufferBuilderRenderSink.java @@ -9,12 +9,14 @@ */ package org.enginehub.worldeditcui.render; +import com.mojang.blaze3d.opengl.GlConst; import com.mojang.blaze3d.opengl.GlStateManager; import com.mojang.blaze3d.vertex.BufferBuilder; import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.blaze3d.vertex.VertexFormat; import org.jetbrains.annotations.Nullable; import org.joml.Vector3f; +import com.mojang.blaze3d.platform.CompareOp; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL32; @@ -38,7 +40,7 @@ public class BufferBuilderRenderSink implements RenderSink { // line state private float lastLineWidth = -1; - private int lastDepthFunc = -1; + private @Nullable CompareOp lastDepthTest; public BufferBuilderRenderSink(final TypeFactory types) { this(types, () -> {}, () -> {}); @@ -79,14 +81,14 @@ public RenderSink color(final float r, final float g, final float b, final float public boolean apply(final LineStyle line, final RenderStyle.RenderType type) { if (line.renderType.matches(type)) { - if (line.lineWidth != this.lastLineWidth || line.renderType.depthFunc() != this.lastDepthFunc) { + if (line.lineWidth != this.lastLineWidth || line.renderType.depthTest() != this.lastDepthTest) { this.flush(); if (this.active && this.activeRenderType != null) { this.canFlush = true; this.builder = Tesselator.getInstance().begin(this.activeRenderType.mode, this.activeRenderType.format); } LineWidth.set(this.lastLineWidth = line.lineWidth); - GlStateManager._depthFunc(this.lastDepthFunc = line.renderType.depthFunc()); + GlStateManager._depthFunc(GlConst.toGl(this.lastDepthTest = line.renderType.depthTest())); } return true; } diff --git a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/LegacyVanillaPipelineProvider.java b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/LegacyVanillaPipelineProvider.java new file mode 100644 index 00000000..1712a4cf --- /dev/null +++ b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/LegacyVanillaPipelineProvider.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2011-2024 WorldEditCUI team and contributors + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.enginehub.worldeditcui.render; + +import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.ColorTargetState; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.vertex.DefaultVertexFormat; +import com.mojang.blaze3d.vertex.VertexFormat; +import net.minecraft.client.renderer.RenderPipelines; +import net.minecraft.client.renderer.rendertype.RenderSetup; +import net.minecraft.client.renderer.rendertype.RenderType; +import net.minecraft.client.renderer.rendertype.RenderTypes; + +public final class LegacyVanillaPipelineProvider implements PipelineProvider { + + public static class DefaultTypeFactory implements BufferBuilderRenderSink.TypeFactory { + public static final DefaultTypeFactory INSTANCE = new DefaultTypeFactory(); + private static final RenderSetup QUADS_SETUP = RenderSetup.builder( + RenderPipeline.builder(RenderPipeline.builder(RenderPipelines.MATRICES_PROJECTION_SNIPPET) + .withVertexShader("core/position_color") + .withFragmentShader("core/position_color") + .withColorTargetState(new ColorTargetState(BlendFunction.TRANSLUCENT)) + .withVertexFormat(DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS) + .buildSnippet()) + .withLocation("pipeline/wecui_legacy_quads") + .withCull(false) + .build() + ).bufferSize(1536).sortOnUpload().createRenderSetup(); + private static final BufferBuilderRenderSink.RenderType QUADS = new BufferBuilderRenderSink.RenderType( + VertexFormat.Mode.QUADS, + DefaultVertexFormat.POSITION_COLOR, + RenderType.create("wecui_legacy_quads", QUADS_SETUP) + ); + private static final BufferBuilderRenderSink.RenderType LINES = new BufferBuilderRenderSink.RenderType( + VertexFormat.Mode.LINES, + DefaultVertexFormat.POSITION_COLOR_NORMAL_LINE_WIDTH, + RenderTypes.LINES + ); + private static final BufferBuilderRenderSink.RenderType LINES_LOOP = new BufferBuilderRenderSink.RenderType( + VertexFormat.Mode.LINES, + DefaultVertexFormat.POSITION_COLOR_NORMAL_LINE_WIDTH, + RenderTypes.LINES + ); + + private DefaultTypeFactory() {} + + @Override + public BufferBuilderRenderSink.RenderType quads() { + return QUADS; + } + + @Override + public BufferBuilderRenderSink.RenderType lines() { + return LINES; + } + + @Override + public BufferBuilderRenderSink.RenderType linesLoop() { + return LINES_LOOP; + } + } + + @Override + public String id() { + return "legacy-vanilla"; + } + + @Override + public boolean available() { + return true; + } + + @Override + public RenderSink provide() { + return new BufferBuilderRenderSink(DefaultTypeFactory.INSTANCE); + } +} diff --git a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/RenderStyle.java b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/RenderStyle.java index 0bb4c3a7..6629ee33 100644 --- a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/RenderStyle.java +++ b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/RenderStyle.java @@ -9,8 +9,8 @@ */ package org.enginehub.worldeditcui.render; +import com.mojang.blaze3d.platform.CompareOp; import org.enginehub.worldeditcui.config.Colour; -import org.lwjgl.opengl.GL32; /** * Render style adapter, can be one of the built-in {@link ConfiguredColour}s @@ -28,28 +28,28 @@ public enum RenderType /** * Render type to draw lines regardless of depth */ - ANY(GL32.GL_ALWAYS), + ANY(CompareOp.ALWAYS_PASS), /** * Render type for "hidden" lines (under world geometry) */ - HIDDEN(GL32.GL_GEQUAL), + HIDDEN(CompareOp.GREATER_THAN_OR_EQUAL), /** * Render type for visible lines (over world geometry) */ - VISIBLE(GL32.GL_LESS); + VISIBLE(CompareOp.LESS_THAN); - final int depthFunc; + final CompareOp depthTest; - private RenderType(int depthFunc) + private RenderType(final CompareOp depthTest) { - this.depthFunc = depthFunc; + this.depthTest = depthTest; } - public int depthFunc() + public CompareOp depthTest() { - return this.depthFunc; + return this.depthTest; } public boolean matches(RenderType other) @@ -67,4 +67,4 @@ public boolean matches(RenderType other) public abstract Colour getColour(); public abstract LineStyle[] getLines(); -} \ No newline at end of file +} diff --git a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/VanillaPipelineProvider.java b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/VanillaPipelineProvider.java index 2326c289..e1cabeba 100644 --- a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/VanillaPipelineProvider.java +++ b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/VanillaPipelineProvider.java @@ -10,48 +10,89 @@ package org.enginehub.worldeditcui.render; import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.ColorTargetState; +import com.mojang.blaze3d.pipeline.DepthStencilState; import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.CompareOp; import com.mojang.blaze3d.vertex.DefaultVertexFormat; import com.mojang.blaze3d.vertex.VertexFormat; import net.minecraft.client.renderer.RenderPipelines; import net.minecraft.client.renderer.rendertype.RenderSetup; import net.minecraft.client.renderer.rendertype.RenderType; -import net.minecraft.client.renderer.rendertype.RenderTypes; + +import java.util.EnumMap; +import java.util.Locale; +import java.util.Map; public final class VanillaPipelineProvider implements PipelineProvider { - public static class DefaultTypeFactory implements BufferBuilderRenderSink.TypeFactory { + public static class DefaultTypeFactory implements BatchedRenderSink.TypeFactory { public static final DefaultTypeFactory INSTANCE = new DefaultTypeFactory(); - private static final RenderSetup QUADS_SETUP = RenderSetup.builder( - RenderPipeline.builder(RenderPipeline.builder(RenderPipelines.MATRICES_PROJECTION_SNIPPET) - .withVertexShader("core/position_color") - .withFragmentShader("core/position_color") - .withBlend(BlendFunction.TRANSLUCENT) - .withVertexFormat(DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS) - .buildSnippet()) - .withLocation("pipeline/wecui_quads").withCull(false).build() - ).bufferSize(1536).sortOnUpload().createRenderSetup(); - private static final BufferBuilderRenderSink.RenderType QUADS = new BufferBuilderRenderSink.RenderType(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR, - RenderType.create("quads", QUADS_SETUP) - ); - private static final BufferBuilderRenderSink.RenderType LINES = new BufferBuilderRenderSink.RenderType(VertexFormat.Mode.LINES, DefaultVertexFormat.POSITION_COLOR_NORMAL_LINE_WIDTH, RenderTypes.LINES); - private static final BufferBuilderRenderSink.RenderType LINES_LOOP = new BufferBuilderRenderSink.RenderType(VertexFormat.Mode.LINES, DefaultVertexFormat.POSITION_COLOR_NORMAL_LINE_WIDTH, RenderTypes.LINES); + private static final RenderPipeline.Snippet QUADS_SNIPPET = RenderPipeline.builder(RenderPipelines.MATRICES_PROJECTION_SNIPPET) + .withVertexShader("core/position_color") + .withFragmentShader("core/position_color") + .withColorTargetState(new ColorTargetState(BlendFunction.TRANSLUCENT)) + .withVertexFormat(DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.QUADS) + .withCull(false) + .withDepthStencilState(DepthStencilState.DEFAULT) + .buildSnippet(); + private static final RenderPipeline.Snippet LINES_SNIPPET = RenderPipeline.builder(RenderPipelines.MATRICES_FOG_SNIPPET, RenderPipelines.GLOBALS_SNIPPET) + .withVertexShader("core/rendertype_lines") + .withFragmentShader("core/rendertype_lines") + .withColorTargetState(new ColorTargetState(BlendFunction.TRANSLUCENT)) + .withCull(false) + .withVertexFormat(DefaultVertexFormat.POSITION_COLOR_NORMAL_LINE_WIDTH, VertexFormat.Mode.LINES) + .withDepthStencilState(DepthStencilState.DEFAULT) + .buildSnippet(); + private static final Map VARIANTS = createVariants(); private DefaultTypeFactory() {} @Override - public BufferBuilderRenderSink.RenderType quads() { - return QUADS; + public BatchedRenderSink.VariantSet forStyle(final RenderStyle.RenderType renderType) { + final BatchedRenderSink.VariantSet variants = VARIANTS.get(renderType); + if (variants == null) { + throw new IllegalArgumentException("Unsupported render type " + renderType); + } + return variants; } - @Override - public BufferBuilderRenderSink.RenderType lines() { - return LINES; + private static Map createVariants() { + final EnumMap variants = new EnumMap<>(RenderStyle.RenderType.class); + for (final RenderStyle.RenderType renderType : RenderStyle.RenderType.values()) { + final String idSuffix = renderType.name().toLowerCase(Locale.ROOT); + final CompareOp depthTest = renderType.depthTest(); + final BatchedRenderSink.RenderTarget lines = createLinesTarget(idSuffix, depthTest); + variants.put(renderType, new BatchedRenderSink.VariantSet( + createQuadsTarget(idSuffix, depthTest), + lines, + lines + )); + } + return Map.copyOf(variants); } - @Override - public BufferBuilderRenderSink.RenderType linesLoop() { - return LINES_LOOP; + private static BatchedRenderSink.RenderTarget createQuadsTarget(final String idSuffix, final CompareOp depthTest) { + final RenderPipeline pipeline = RenderPipeline.builder(QUADS_SNIPPET) + .withLocation("pipeline/wecui_quads_" + idSuffix) + .withDepthStencilState(new DepthStencilState(depthTest, true)) + .build(); + final RenderSetup setup = RenderSetup.builder(pipeline) + .bufferSize(1536) + .sortOnUpload() + .createRenderSetup(); + final RenderType renderType = RenderType.create("wecui_quads_" + idSuffix, setup); + return new BatchedRenderSink.RenderTarget(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR, renderType::draw); + } + + private static BatchedRenderSink.RenderTarget createLinesTarget(final String idSuffix, final CompareOp depthTest) { + final RenderPipeline pipeline = RenderPipeline.builder(LINES_SNIPPET) + .withLocation("pipeline/wecui_lines_" + idSuffix) + .withDepthStencilState(new DepthStencilState(depthTest, true)) + .build(); + final RenderSetup setup = RenderSetup.builder(pipeline).createRenderSetup(); + final RenderType renderType = RenderType.create("wecui_lines_" + idSuffix, setup); + return new BatchedRenderSink.RenderTarget(VertexFormat.Mode.LINES, DefaultVertexFormat.POSITION_COLOR_NORMAL_LINE_WIDTH, renderType::draw); } } @@ -67,6 +108,6 @@ public boolean available() { @Override public RenderSink provide() { - return new BufferBuilderRenderSink(DefaultTypeFactory.INSTANCE); + return new BatchedRenderSink(DefaultTypeFactory.INSTANCE); } } diff --git a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/shapes/Render3DGrid.java b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/shapes/Render3DGrid.java index 81b7e0c9..9a72b65f 100644 --- a/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/shapes/Render3DGrid.java +++ b/worldeditcui-fabric/src/main/java/org/enginehub/worldeditcui/render/shapes/Render3DGrid.java @@ -9,8 +9,6 @@ */ package org.enginehub.worldeditcui.render.shapes; -import com.mojang.blaze3d.opengl.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.util.Mth; import org.enginehub.worldeditcui.event.listeners.CUIRenderContext; import org.enginehub.worldeditcui.render.LineStyle; @@ -88,8 +86,6 @@ public void render(CUIRenderContext ctx) if (this.spacing != 1.0) { - GlStateManager._disableCull(); - double[] vertices = { x1, y1, z1, x2, y1, z1, x2, y1, z2, x1, y1, z2, // bottom x1, y2, z1, x2, y2, z1, x2, y2, z2, x1, y2, z2, // top @@ -112,9 +108,6 @@ public void render(CUIRenderContext ctx) ctx.endQuads(); } } - - ctx.flush(); // todo: only needed because of disable/enable cull - GlStateManager._enableCull(); } if (this.spacing < Render3DGrid.MIN_SPACING) diff --git a/worldeditcui-fabric/src/main/resources/assets/worldeditcui/lang/en_us.json b/worldeditcui-fabric/src/main/resources/assets/worldeditcui/lang/en_us.json index 8cb54396..b31e3223 100644 --- a/worldeditcui-fabric/src/main/resources/assets/worldeditcui/lang/en_us.json +++ b/worldeditcui-fabric/src/main/resources/assets/worldeditcui/lang/en_us.json @@ -35,6 +35,8 @@ "worldeditcui.options.compat.title": "Compatibility Options", "worldeditcui.options.compat.spammy": "Promiscuous Mode", "worldeditcui.options.compat.spammy.tooltip": "Enable this option to send /we cui all the time", + "worldeditcui.options.compat.renderer": "Experimental Renderer", + "worldeditcui.options.compat.renderer.tooltip": "Use the new batched vanilla renderer instead of the default legacy renderer", "worldeditcui.options.color.title": "Display Colors", "worldeditcui.options.extra.title": "Advanced Options", "worldeditcui.options.extra.clearall": "Clear displayed regions", diff --git a/worldeditcui-fabric/src/main/resources/fabric.mod.json b/worldeditcui-fabric/src/main/resources/fabric.mod.json index 579d7190..ee814efd 100644 --- a/worldeditcui-fabric/src/main/resources/fabric.mod.json +++ b/worldeditcui-fabric/src/main/resources/fabric.mod.json @@ -36,14 +36,14 @@ "depends": { "worldeditcui_protocol": "*", "fabric-api-base": "*", - "fabric-key-binding-api-v1": "^1.0.0", + "fabric-key-mapping-api-v1": "^2.0.0", "fabric-networking-api-v1": "*", "fabric-rendering-v1": ">=1.5.0", - "fabric-lifecycle-events-v1": "^2.0.0", + "fabric-lifecycle-events-v1": "^4.0.0", "fabric-screen-api-v1": ">=1.0.9", "fabric-resource-loader-v0": ">=0.4.17", "fabric-transitive-access-wideners-v1": "*", - "minecraft": ">=1.21.9" + "minecraft": ">=26.1" }, "suggests": { "worldedit": ">=7.3.17" diff --git a/worldeditcui-fabric/src/main/resources/worldeditcui.accesswidener b/worldeditcui-fabric/src/main/resources/worldeditcui.accesswidener index 875ab35c..8f2b12ff 100644 --- a/worldeditcui-fabric/src/main/resources/worldeditcui.accesswidener +++ b/worldeditcui-fabric/src/main/resources/worldeditcui.accesswidener @@ -1,4 +1,4 @@ -accessWidener v2 named +accessWidener v2 official # fogRenderer accessible field net/minecraft/client/renderer/GameRenderer fogRenderer Lnet/minecraft/client/renderer/fog/FogRenderer;