diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..2577895
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,15 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+polar: # Replace with a single Polar username
+buy_me_a_coffee: radiance.mod
+thanks_dev: # Replace with a single thanks.dev username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.gitignore b/.gitignore
index e13478d..6adbb6c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,4 +123,5 @@ core.dll
core.lib
libnvidia-ngx-dlss*
include/
-shaders/
\ No newline at end of file
+shaders/
+libxess*
\ No newline at end of file
diff --git a/README-CN.md b/README-CN.md
index c5060bd..9d9ad46 100644
--- a/README-CN.md
+++ b/README-CN.md
@@ -9,17 +9,13 @@ Radiance是一个Minecraft Mod,旨在将原版的OpenGL渲染器完全替换
[演示视频 (B站)](https://www.bilibili.com/video/BV1NevXBCEPg/)
-
+
-# (重要) 关于PBR材质包
+# 关于PBR材质包
-目前,Radiance需要一点小的文件结构修改,才能使用PBR材质包。这可能会在接下来的版本里面被简化掉。
+从0.1.4开始,不需要任何预处理了,直接加载!
-1. 解压材质包
-2. 进入解压出来的材质包,将`assets/minecraft/textures/**/*_s.png`移动到`assets/minecraft/textures/specular/**/*_s.png`,以及`assets/minecraft/textures/**/*_n.png`移动到`assets/minecraft/textures/normal/**/*_n.png`。这背后的原因是Radiance会给specular和normal贴图单独创建内置的贴图纹理,这个移动让原版不自己加载这些贴图,从而省下巨量内存。
-3. 将处理好的文件夹压缩。
-
-一个样例`python`脚本可以用作这个处理过程的[参考](https://gist.github.com/Ljiong201108/e5a2fe8f8ac63f3cc70bf5557a948377)。
+尽管有内置的发光贴图,依旧推荐使用一个PBR材质包来获得更好的体验。
# 安装指南
@@ -84,15 +80,30 @@ git clone https://github.com/Minecraft-Radiance/MCVR.git
最后,用`./gradlew build`构建。
-# Todo列表
+# 画面 / 图形设置怎么打开
-- [ ] 移植到更多版本和mod加载器(WIP,最高优先级)
+Radiance 目前的游戏内设置入口在 Minecraft 的视频设置里:
+
+1. 打开 **选项**
+2. 打开 **视频设置**
+3. 找到 **Pipeline** 分类
+4. 点击 **Radiance Settings**
+
+进入后就是 Radiance 的统一设置界面,可以调整画面、光照、云、水体、DLSS/HDR、材质和后处理。
-- [ ] 帧生成
+# 设置文档
-- [ ] XESS支持
+当前所有模块设置的中文参考文档见:
-- [ ] HDR
+- [`docs/SETTINGS_REFERENCE.zh-CN.md`](docs/SETTINGS_REFERENCE.zh-CN.md)
+
+# Todo列表
+
+- [ ] 移植到更多版本和mod加载器(WIP,最高优先级)
+- [x] XESS支持
+- [ ] 实验性帧生成(NVIDIA + Windows + Vulkan,进行中)
+- [x] HDR(实验性 scRGB 输出)
+- [ ] 第一人称手的影子会显示在手持物品上
以及更多...
@@ -104,6 +115,8 @@ git clone https://github.com/Minecraft-Radiance/MCVR.git
这个项目也使用了FSR3。更多信息请访问[这个页面](https://gpuopen.com/fidelityfx-super-resolution-3/)。
+这个项目也使用了XeSS SR。更多信息请访问[这个页面](https://www.intel.com/content/www/us/en/developer/articles/technical/xess-sr-developer-guide.html)。
+
特别感谢所有这个项目使用的开源库的制作者,包括[NRD](https://github.com/NVIDIA-RTX/NRD)、[GLFW](https://github.com/glfw/glfw)、[GLM](https://github.com/icaven/glm)、[STB Image](https://github.com/nothings/stb)和[VMA](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator)。
如果有应该致谢但是没有被提及的,请通知本项目作者。我们会把致谢添加在需要添加的位置。
diff --git a/README.md b/README.md
index 994a39e..f0e991d 100644
--- a/README.md
+++ b/README.md
@@ -9,17 +9,13 @@ Due to the variety of C++ usage in the modern industrial rendering pipeline, a s
[Showcase Video (Youtube)](https://www.youtube.com/watch?v=jGIQffPM1Wg)
-
+
-# (Important) PBR Texture Packs
+# PBR Texture Packs
-Currently, to enable PBR texture packs, small adaption is necessary to allow miltiplexing. This maybe simpilfied in further versions.
+Starting from 0.1.4, no pre-processing is necessary. Load directly!
-1. Unzip the texture pack
-2. Go into the unziped folder, move `assets/minecraft/textures/**/*_s.png` to `assets/minecraft/textures/specular/**/*_s.png` and `assets/minecraft/textures/**/*_n.png` to `assets/minecraft/textures/normal/**/*_n.png`. The fact behind is that Radiance will create seperate texture atlases for specular and normal textures. This disables vanilla minecraft to read them (4x memory save).
-3. Zip the folder
-
-A sample `python` script can be found [here](https://gist.github.com/Ljiong201108/e5a2fe8f8ac63f3cc70bf5557a948377).
+Although there are already internal emission textures, it is still recommended to use a PBR texture pack for better experience.
# Installation Guide
@@ -87,15 +83,29 @@ Use `cmake` to build it and install it. Please refer to [this](https://github.co
Finally, build with `./gradlew build`.
-# TODO List
+# Opening the graphics/settings screen
-- [ ] port to more versions and mod loaders (WIP, first priority)
+Radiance's in-game settings are currently opened from Minecraft's video settings:
+
+1. Open **Options**
+2. Open **Video Settings**
+3. Find the **Pipeline** category
+4. Click **Radiance Settings**
+
+This opens the unified Radiance settings screen for graphics, lighting, clouds, water, DLSS/HDR, materials, and post-processing.
-- [ ] Frame Generation
+# Settings Reference
-- [ ] XESS support
+A Chinese settings reference covering all current module options is available at:
-- [ ] HDR
+- [`docs/SETTINGS_REFERENCE.zh-CN.md`](docs/SETTINGS_REFERENCE.zh-CN.md)
+
+# TODO List
+
+- [ ] port to more versions and mod loaders (WIP, first priority)
+- [x] XESS support
+- [ ] Experimental Frame Generation (NVIDIA + Windows + Vulkan, WIP)
+- [x] HDR (Experimental scRGB output)
And more...
@@ -107,6 +117,8 @@ This project uses Nvidia's DLSS (Deep Learning Super Sampling) technology. Pleas
This project uses FSR3. Please refer to [this page](https://gpuopen.com/fidelityfx-super-resolution-3/) for more information.
+This project uses XeSS SR. Please refer to [this page](https://www.intel.com/content/www/us/en/developer/articles/technical/xess-sr-developer-guide.html) for more information.
+
Special thanks to all contributors of open-source libraries used in this project, including [NRD](https://github.com/NVIDIA-RTX/NRD), [GLFW](https://github.com/glfw/glfw), [GLM](https://github.com/icaven/glm), [STB Image](https://github.com/nothings/stb) and [VMA](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator). If any are not credited and should be, please inform the author and credit will be applied where required.
# Disclaimer
diff --git a/build.gradle b/build.gradle
index 2b11d66..ac96d36 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,6 +10,91 @@ base {
archivesName = project.archives_base_name
}
+def radianceRuntimeDirPath = providers.gradleProperty("radianceRuntimeDir")
+ .orElse(providers.environmentVariable("RADIANCE_RUNTIME_DIR"))
+ .orNull
+def radianceRuntimeDir = radianceRuntimeDirPath ? file(radianceRuntimeDirPath) : null
+def builtWindowsCoreDll = file("external/MCVR/build/src/core/Release/core.dll")
+def radianceWindowsJarPath = providers.gradleProperty("radianceWindowsJar")
+ .orElse(providers.environmentVariable("RADIANCE_WINDOWS_JAR"))
+ .orNull
+def radianceWindowsJar = radianceWindowsJarPath ? file(radianceWindowsJarPath) : null
+def extractedRadianceWindowsRuntimeDir = layout.buildDirectory.dir("generated/radianceRuntime/windows")
+def syncedBuiltWindowsRuntimeDir = layout.buildDirectory.dir("generated/radianceRuntime/localBuild")
+def bundledXessRuntimeDir = file("external/MCVR/extern/xess/bin")
+def radianceAllowIncompleteRuntimePackaging = providers
+ .gradleProperty("radianceAllowIncompleteRuntimePackaging")
+ .map { it.toBoolean() }
+ .orElse(false)
+def radianceRequestedTasks = gradle.startParameter.taskNames.collect { it.toLowerCase() }
+def radiancePackagingTaskRequested = radianceRequestedTasks.any { taskName ->
+ taskName == "build"
+ || taskName == "assemble"
+ || taskName.contains("jar")
+ || taskName.contains("publish")
+ || taskName.contains("remap")
+}
+
+def radianceWindowsJarExplicitlyConfigured =
+ gradle.startParameter.projectProperties.containsKey("radianceWindowsJar")
+ || System.getenv("RADIANCE_WINDOWS_JAR") != null
+
+tasks.register("extractRadianceWindowsRuntime") {
+ onlyIf {
+ radianceWindowsJar != null && radianceWindowsJar.exists()
+ }
+ if (radianceWindowsJar != null) {
+ inputs.file(radianceWindowsJar)
+ }
+ outputs.dir(extractedRadianceWindowsRuntimeDir)
+
+ doLast {
+ if (radianceWindowsJar == null || !radianceWindowsJar.exists()) {
+ throw new GradleException("Radiance Windows runtime jar not found: ${radianceWindowsJarPath}")
+ }
+
+ def outputDir = extractedRadianceWindowsRuntimeDir.get().asFile
+ delete(outputDir)
+ outputDir.mkdirs()
+
+ copy {
+ from(zipTree(radianceWindowsJar))
+ include "core.dll"
+ include "nvngx_*.dll"
+ include "libxess*.dll"
+ include "sl.*.dll"
+ include "NvLowLatencyVk.dll"
+ into outputDir
+ }
+
+ def extractedCoreDll = new File(outputDir, "core.dll")
+ if (!extractedCoreDll.exists()) {
+ throw new GradleException("core.dll was not found in ${radianceWindowsJar}")
+ }
+
+ logger.lifecycle("Extracted Radiance Windows runtime from ${radianceWindowsJar} -> ${outputDir}")
+ }
+}
+
+tasks.register("syncBuiltWindowsRuntime") {
+ onlyIf {
+ builtWindowsCoreDll.exists()
+ }
+ inputs.file(builtWindowsCoreDll)
+ outputs.dir(syncedBuiltWindowsRuntimeDir)
+
+ doLast {
+ def destinationDir = syncedBuiltWindowsRuntimeDir.get().asFile
+ delete(destinationDir)
+ destinationDir.mkdirs()
+ copy {
+ from(builtWindowsCoreDll)
+ into(destinationDir)
+ rename { "core.dll" }
+ }
+ }
+}
+
compileJava {
options.compilerArgs += ['-h', "${projectDir}/src/main/native/include"]
outputs.upToDateWhen { false }
@@ -20,7 +105,7 @@ loom {
}
tasks.named("runClient", JavaExec) {
- args "--width", "1920", "--height", "1080"
+ args "--width", "1920", "--height", "1080"
}
repositories {
@@ -49,8 +134,8 @@ dependencies {
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
// https://mvnrepository.com/artifact/org.yaml/snakeyaml
- implementation "org.yaml:snakeyaml:2.5"
- include "org.yaml:snakeyaml:2.5"
+ implementation "org.yaml:snakeyaml:2.6"
+ include "org.yaml:snakeyaml:2.6"
}
processResources {
@@ -58,6 +143,88 @@ processResources {
inputs.property "minecraft_version", project.minecraft_version
inputs.property "loader_version", project.loader_version
filteringCharset "UTF-8"
+ duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+
+ def runtimeDirectorySource = null
+ def supplementalWindowsRuntimeSource = null
+ def mainResourcesDir = file("src/main/resources")
+ if (radianceRuntimeDir != null) {
+ if (radianceRuntimeDir.exists()) {
+ runtimeDirectorySource = radianceRuntimeDir
+ if (!new File(radianceRuntimeDir, "core.dll").exists() && builtWindowsCoreDll.exists()) {
+ dependsOn(tasks.named("syncBuiltWindowsRuntime"))
+ supplementalWindowsRuntimeSource = syncedBuiltWindowsRuntimeDir
+ }
+ } else {
+ logger.warn("Configured radiance runtime directory does not exist: ${radianceRuntimeDir}")
+ }
+ } else if (radianceWindowsJar != null && radianceWindowsJar.exists()) {
+ dependsOn(tasks.named("extractRadianceWindowsRuntime"))
+ runtimeDirectorySource = extractedRadianceWindowsRuntimeDir
+ } else if (builtWindowsCoreDll.exists()) {
+ dependsOn(tasks.named("syncBuiltWindowsRuntime"))
+ supplementalWindowsRuntimeSource = syncedBuiltWindowsRuntimeDir
+ } else if (radianceWindowsJarExplicitlyConfigured && radianceWindowsJar != null
+ && !radianceWindowsJar.exists()) {
+ logger.warn("Configured Radiance Windows jar does not exist: ${radianceWindowsJar}")
+ }
+
+ boolean packagedWindowsCoreAvailable =
+ new File(mainResourcesDir, "core.dll").exists() || builtWindowsCoreDll.exists()
+ boolean packagedLinuxCoreAvailable = new File(mainResourcesDir, "libcore.so").exists()
+
+ if (radianceRuntimeDir != null && radianceRuntimeDir.exists()) {
+ packagedWindowsCoreAvailable |= new File(radianceRuntimeDir, "core.dll").exists()
+ packagedLinuxCoreAvailable |= new File(radianceRuntimeDir, "libcore.so").exists()
+ } else if (radianceWindowsJar != null && radianceWindowsJar.exists()) {
+ packagedWindowsCoreAvailable = true
+ }
+
+ if (!packagedWindowsCoreAvailable || !packagedLinuxCoreAvailable) {
+ def missingRuntimeTargets = []
+ if (!packagedWindowsCoreAvailable) {
+ missingRuntimeTargets << "Windows (core.dll)"
+ }
+ if (!packagedLinuxCoreAvailable) {
+ missingRuntimeTargets << "Linux (libcore.so)"
+ }
+
+ def incompleteRuntimeMessage = (
+ "Radiance runtime packaging is incomplete: missing "
+ + missingRuntimeTargets.join(", ")
+ + ". Provide the missing runtime via src/main/resources or -PradianceRuntimeDir=..., "
+ + "or pass -PradianceAllowIncompleteRuntimePackaging=true to package a platform-limited artifact intentionally."
+ )
+ if (radiancePackagingTaskRequested && !radianceAllowIncompleteRuntimePackaging.get()) {
+ throw new GradleException(incompleteRuntimeMessage)
+ }
+ logger.warn(incompleteRuntimeMessage)
+ }
+
+ if (runtimeDirectorySource != null) {
+ from(runtimeDirectorySource) {
+ include "core.dll"
+ include "libcore.so"
+ include "nvngx_*.dll"
+ include "libxess*.dll"
+ include "libxell.dll"
+ include "sl.*.dll"
+ include "NvLowLatencyVk.dll"
+ }
+ }
+
+ if (supplementalWindowsRuntimeSource != null) {
+ from(supplementalWindowsRuntimeSource) {
+ include "core.dll"
+ }
+ }
+
+ if (bundledXessRuntimeDir.exists()) {
+ from(bundledXessRuntimeDir) {
+ include "libxess*.dll"
+ include "libxell.dll"
+ }
+ }
filesMatching("fabric.mod.json") {
expand "version": project.version,
diff --git a/docs/SETTINGS_REFERENCE.zh-CN.md b/docs/SETTINGS_REFERENCE.zh-CN.md
new file mode 100644
index 0000000..eb5b61d
--- /dev/null
+++ b/docs/SETTINGS_REFERENCE.zh-CN.md
@@ -0,0 +1,207 @@
+# Radiance 设置参考(中文)
+
+> 本文档覆盖 `src/main/resources/modules/*.yaml` 中当前暴露的全部模块设置。
+> 默认值以仓库当前版本为准;如果后续 YAML 变更,请同步更新本文档。
+
+## 设置入口
+
+游戏内打开路径:
+
+- **选项**
+- **视频设置**
+- **Pipeline**
+- **Radiance Settings**
+
+进入后即可打开 Radiance 的统一画面设置界面。
+
+## 阅读方式
+
+- **默认值**:模块 YAML 里的当前默认值。
+- **作用**:这个设置主要控制什么。
+- **调整建议**:什么时候该调高 / 调低,或者推荐保持默认。
+
+---
+
+## 视频设置:画质等级
+
+> 位置:**视频设置 -> Pipeline -> 画质等级**
+
+`画质等级` 会联动调整**整条渲染管线**,而不是只改一两个数字。
+当前会覆盖这些模块 / 参数:
+
+- **Ray Tracing**:反射次数、远景几何、远景材质、地形 meshing、merge span、GI 模式、反射材质分级、更新间隔、PBR 采样、SHaRC、基础辐亮度、直接/间接光
+- **NRD**:历史长度、预模糊半径、最大模糊半径、命中距离重建、anti-firefly
+- **Upscaler**:FSR/XeSS/DLSS 质量档,FSR 锐化
+- **Tone Mapping**:曲线、自动曝光、测光模式、饱和度、白点
+
+### 档位说明
+
+| 档位 | 定位 | 说明 |
+|---|---|---|
+| `performance` | 优先帧率 | 远景简化更激进,但保留 **2 次** 反射,避免环境完全发黑。 |
+| `balanced` | 默认推荐 | 当前默认档;兼顾画质、稳定性和帧数。 |
+| `quality` | 优先画质 | 远景保真、反射与 GI 更完整,但 GPU / CPU 压力明显更高。 |
+
+---
+
+## DLSS 模块
+
+| 设置 | 默认值 | 作用 | 调整建议 |
+|---|---:|---|---|
+| `mode` | `balanced` | 控制 DLSS 质量档位。 | 帧率不够用 `performance`;画质优先 `quality`;原生抗锯齿可用 `dlaa`。 |
+
+---
+
+## FSR Upscaler 模块
+
+| 设置 | 默认值 | 作用 | 调整建议 |
+|---|---:|---|---|
+| `enable` | `true` | 是否启用 FSR3 Upscaler。 | 出问题先关掉排查。 |
+| `quality_mode` | `balanced` | 控制内部渲染分辨率与最终输出分辨率的比例。 | 低端机器可降到 `balanced / performance`。 |
+| `sharpness` | `0.7` | 锐化强度。 | 过高会有振铃/边缘发硬;过低会发糊。 |
+
+---
+
+## NRD 模块
+
+> NRD 主要是降噪稳定性、拖影控制、历史长度、预模糊半径一类参数。
+> 如果你不知道怎么调,**优先保持默认**。
+
+| 设置 | 默认值 | 作用 | 调整建议 |
+|---|---:|---|---|
+| `antilag_luminance_sigma_scale` | `4.0` | 亮度变化触发 anti-lag 的阈值尺度。 | 亮度闪烁明显时可略调低;误触发太多可调高。 |
+| `antilag_luminance_sensitivity` | `3.0` | 亮度变化敏感度。 | 过高更容易重置历史,过低更容易拖影。 |
+| `responsive_accumulation_roughness_threshold` | `0.0` | 粗糙度到达该阈值后更偏向响应式累积。 | 反射拖影明显时可略提高。 |
+| `responsive_accumulation_min_accumulated_frame_num` | `3` | 响应式累积启动前需要的最少历史帧数。 | 动态场景多时可略减。 |
+| `max_accumulated_frame_num` | `60` | 最大历史累积帧数。 | 调高更稳但更容易拖;调低更灵敏但更噪。 |
+| `max_fast_accumulated_frame_num` | `3` | 快速路径历史上限。 | 过高容易残影。 |
+| `max_stabilized_frame_num` | `63` | 稳定阶段历史长度上限。 | 静态画面噪点多时可调高。 |
+| `history_fix_frame_num` | `3` | 历史修正使用的帧数。 | 一般保持默认。 |
+| `history_fix_base_pixel_stride` | `14` | 历史修正基础像素步长。 | 越大越便宜,但修正更粗。 |
+| `history_fix_alternate_pixel_stride` | `14` | 历史修正交替步长。 | 通常与上项一起调。 |
+| `fast_history_clamping_sigma_scale` | `1.5` | 快速历史夹取强度。 | 拖影重可调低,闪烁多可调高。 |
+| `diffuse_prepass_blur_radius` | `30.0` | 漫反射预模糊半径。 | 噪声大可调高;细节损失大可调低。 |
+| `specular_prepass_blur_radius` | `50.0` | 镜面预模糊半径。 | 反射噪点多可调高;反射发糊可调低。 |
+| `min_hit_distance_weight` | `0.1` | 命中距离参与权重的最小值。 | 一般保持默认。 |
+| `min_blur_radius` | `1.0` | 最小模糊半径。 | 想保细节可略降。 |
+| `max_blur_radius` | `100.0` | 最大模糊半径。 | 想压噪可提高;不想糊可降低。 |
+| `lobe_angle_fraction` | `0.15` | 用于镜面 lobe 相似度判定。 | 主要影响反射稳定性。 |
+| `roughness_fraction` | `0.15` | 粗糙度相似性权重。 | 反射面变化快时可略调。 |
+| `plane_distance_sensitivity` | `0.02` | 平面距离差异敏感度。 | 边缘漏光/串面时可调高。 |
+| `specular_probability_thresholds_for_mv_modification_min` | `0.5` | 修改运动矢量时的镜面概率下限。 | 一般成对与 max 一起调。 |
+| `specular_probability_thresholds_for_mv_modification_max` | `0.9` | 修改运动矢量时的镜面概率上限。 | 一般保持默认。 |
+| `firefly_suppressor_min_relative_scale` | `2.0` | Firefly 抑制阈值。 | 亮点/火花多可调低。 |
+| `min_material_for_diffuse` | `4.0` | 进入漫反射处理的最小材质阈值。 | 高级调参,通常不动。 |
+| `min_material_for_specular` | `4.0` | 进入镜面处理的最小材质阈值。 | 高级调参,通常不动。 |
+| `checkerboard_mode` | `off` | 棋盘式输入模式。 | 如果输入不是棋盘渲染,保持 `off`。 |
+| `enable_anti_firefly` | `true` | 是否启用 Firefly 抑制。 | 出现亮点闪烁时不要关。 |
+| `hit_distance_reconstruction_mode` | `5x5` | 命中距离重建卷积核。 | 画面更稳但更糊:`5x5`;更锐利但可能更噪:`3x3 / off`。 |
+
+---
+
+## Post Render 模块
+
+| 设置 | 默认值 | 作用 | 调整建议 |
+|---|---:|---|---|
+| `star_count` | `3000` | 星空粒子数量。 | 太多会增加后处理开销。 |
+| `star_min_size` | `0.5` | 星星最小尺寸。 | 调太低会不明显。 |
+| `star_max_size` | `0.7` | 星星最大尺寸。 | 调太高会显得假。 |
+| `star_radius` | `400.0` | 星空分布半径。 | 影响视觉空间感,性能影响较小。 |
+
+---
+
+## Ray Tracing 模块
+
+> 这是当前最核心的一组设置。
+> 如果目标是“**尽量稳**”,推荐从默认值开始,只调下面几项:
+>
+> - `far_field_start_distance_chunks`
+> - `far_field_material_mode`
+> - `terrain_update_interval_frames`
+> - `entity_update_interval_frames`
+> - `num_ray_bounces`
+> - `pbr_sampling_mode`
+
+| 设置 | 默认值 | 作用 | 调整建议 |
+|---|---:|---|---|
+| `shader_pack_path` | `""` | 自定义光追 shader pack 路径;为空时使用内置包。 | 只有在自定义 shader pack 时填写。 |
+| `world_representation_mode` | `triangle_blas` | 世界主要表示方式:传统三角形 BLAS 或 Chunk AABB。 | 目前稳定优先用 `triangle_blas`。 |
+| `chunk_traversal_mode` | `triangle_hit` | Chunk 内求交/遍历模式。 | 目前稳定优先用 `triangle_hit`。 |
+| `chunk_data_layout` | `triangle_geometry` | Chunk 数据组织形式。 | 当前 native 主路径优先用 `triangle_geometry`。 |
+| `chunk_macrocell_size` | `disabled` | 宏块加速尺寸。 | 实验性,非 Route-B 调试时建议关闭。 |
+| `terrain_meshing_mode` | `legacy_quads` | 地形面片预处理模式。 | 当前默认回退到稳定的 `legacy_quads`;实验性优化可手动试 `coplanar_merge / greedy_meshing`。 |
+| `greedy_merge_max_span` | `16` | Greedy/共面合并的最大跨度。 | 只有启用合并模式时才明显生效。 |
+| `blas_inclusion_mode` | `opaque_and_shadow` | 哪些几何进入 BLAS。 | 稳定和性能都较均衡,推荐保持默认。 |
+| `glass_path_mode` | `special_path` | 玻璃走 BLAS、特殊路径或排除。 | 玻璃表现异常时可切回 `blas`。 |
+| `foliage_path_mode` | `blas` | 树叶/植物等 cutout 几何走哪条路径。 | 当前默认回退到 `blas`,优先保证稳定和完整显示。 |
+| `decoration_path_mode` | `blas` | 装饰物几何走哪条路径。 | 当前默认回退到 `blas`,优先保证稳定。 |
+| `far_field_geometry_mode` | `simplified_shell` | 远景几何简化方式。 | 远处块很多时默认就很值;若想保真可改 `exact_chunks`。 |
+| `far_field_start_distance_chunks` | `24` | 从多少个 chunk 之外开始按“远景”处理。 | 性能不够就减小;远景细节太差就增大。 |
+| `far_field_material_mode` | `flat_surface` | 远景材质是完整 PBR 还是扁平平面材质。 | 追求性能建议 `flat_surface`;追求一致性用 `full_pbr`。 |
+| `reflection_ray_material_mode` | `water_glass_metal` | 哪些材质值得打反射光线。 | 低成本推荐默认;全材质反射更贵。 |
+| `diffuse_gi_mode` | `low_cost_hybrid` | 漫反射 GI 的预算模型。 | 默认更适合游戏运行;画质极限再用 `full_ray_tracing`。 |
+| `separate_entity_terrain_accel_structures` | `true` | 实体和地形是否分离更新/加速结构。 | 推荐开启。 |
+| `terrain_update_interval_frames` | `8` | 地形重建最小帧间隔。 | 卡 CPU 可调高;更新不及时可调低。 |
+| `entity_update_interval_frames` | `1` | 实体重放缓存的刷新帧间隔。 | 卡顿时可适当调高,但会引入位置滞后。 |
+| `block_entity_update_interval_frames` | `1` | 方块实体重放缓存刷新间隔。 | 动态方块实体异常时不要调太高。 |
+| `particle_update_interval_frames` | `1` | 粒子重放缓存刷新间隔。 | 性能优先可调高。 |
+| `num_ray_bounces` | `2` | 光线反弹次数。 | 性能不够时优先降到 `2`;极限压性能才建议 `1`。 |
+| `use_jitter` | `true` | 是否启用抖动采样。 | 关闭更稳定但更容易锯齿/噪声固定。 |
+| `pbr_sampling_mode` | `bilinear` | PBR 贴图采样模式。 | `nearest` 更快更锐;`bilinear` 更平滑。 |
+| `transparent_split_mode` | `deterministic` | 透明路径分裂策略。 | 排查不稳定行为时优先 `deterministic`。 |
+| `direct_light_strength` | `1.0` | 直接光贡献强度。 | 过亮就降,过暗就升。 |
+| `indirect_light_strength` | `16.0` | 间接光贡献强度。 | 场景偏灰/过曝时优先调整这里。 |
+| `basic_radiance` | `5.0` | 基础辐亮度底噪。 | 太低画面死黑;太高会发灰。 |
+| `atmosphere_planet_radius` | `6360.0` | 大气模型的行星半径。 | 通常不改。 |
+| `atmosphere_top_radius` | `6460.0` | 大气顶高度。 | 通常不改。 |
+| `rayleigh_scale_height` | `8.0` | 瑞利散射尺度高度。 | 影响天空过渡和远景蓝雾。 |
+| `mie_scale_height` | `1.2` | 米氏散射尺度高度。 | 影响雾感、太阳附近泛白。 |
+| `rayleigh_scattering_coefficient` | `0.000005802,0.000013558,0.0000331` | 瑞利散射系数 RGB。 | 高级调参,不熟悉建议别动。 |
+| `mie_anisotropy` | `0.8` | 米氏散射各向异性。 | 越高越偏向前向散射。 |
+| `mie_scattering_coefficient` | `0.000021,0.000021,0.000021` | 米氏散射系数 RGB。 | 高级调参。 |
+| `minimum_view_cosine` | `0.02` | 天体/大气相关的最小视角余弦。 | 主要影响天体大小/可见范围。 |
+| `sun_radiance` | `8.0,8.0,8.0` | 太阳辐亮度。 | 太阳太刺眼时降低。 |
+| `moon_radiance` | `0.24,0.3,0.6` | 月亮辐亮度。 | 夜景太暗/太蓝可微调。 |
+| `use_sharc` | `true` | 是否启用 SHaRC。 | 新 shader pack 或异常时可关掉排查。 |
+| `sharc_debug_mode` | `off` | SHaRC 调试输出模式。 | 只有调试时才开。 |
+
+---
+
+## Tone Mapping 模块
+
+| 设置 | 默认值 | 作用 | 调整建议 |
+|---|---:|---|---|
+| `method` | `pbr_neutral` | 色调映射曲线。 | 默认最中性;想电影感可试 `aces / uncharted2`。 |
+| `middle_grey` | `0.18` | 自动曝光的中灰目标。 | 画面整体偏亮/偏暗时可微调。 |
+| `exposure_up_speed` | `8.0` | 变亮时曝光调整速度。 | 越高适应越快。 |
+| `exposure_down_speed` | `8.0` | 变暗时曝光调整速度。 | 越高适应越快。 |
+| `log2_luminance_min` | `-12.0` | 自动曝光统计下限。 | 极暗场景可略降。 |
+| `log2_luminance_max` | `4.0` | 自动曝光统计上限。 | 极亮场景可略升。 |
+| `low_percent` | `0.005` | 直方图低百分位截断。 | 可降低暗部异常值影响。 |
+| `high_percent` | `0.99` | 直方图高百分位截断。 | 可降低高光异常值影响。 |
+| `min_exposure` | `0.01` | 自动曝光最小值。 | 防止过暗。 |
+| `max_exposure` | `2.0` | 自动曝光最大值。 | 防止过曝。 |
+| `enable_auto_exposure` | `true` | 是否启用自动曝光。 | 想固定亮度时关闭。 |
+| `exposure_metering_mode` | `center` | 曝光测光模式。 | 主体在中间时用 `center`;整体场景用 `global`。 |
+| `center_metering_percent` | `20.0` | 中心测光区域占比。 | 越小越聚焦中央。 |
+| `manual_exposure` | `1.0` | 关闭自动曝光时的手动曝光值。 | 仅在 auto exposure 关闭时生效。 |
+| `exposure_bias` | `0.0` | 曝光偏移。 | 比大改曲线更适合做整体微调。 |
+| `white_point` | `32.0` | 某些曲线的白点。 | 高光压缩不舒服时可调。 |
+| `saturation` | `1.0` | 饱和度。 | 想更淡/更艳可调。 |
+| `clamp_output` | `true` | 是否钳制输出范围。 | 正常建议开启。 |
+
+---
+
+## XeSS 模块
+
+| 设置 | 默认值 | 作用 | 调整建议 |
+|---|---:|---|---|
+| `enable` | `true` | 是否启用 XeSS。 | 平台不兼容时关闭。 |
+| `quality_mode` | `balanced` | XeSS 质量档位。 | 低端机可降到 `balanced / performance`。 |
+| `pre_exposure` | `1.0` | XeSS 使用的预曝光值。 | 只有在曝光链不稳定时才需要改。 |
+
+---
+
+## Temporal Accumulation 模块
+
+当前版本 **没有暴露可调设置**。
diff --git a/gradle.properties b/gradle.properties
index 3e46926..e0cf47c 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
-# Done to increase the memory available to gradle.
-org.gradle.jvmargs=-Xmx1G
+# Keep the Gradle daemon modest so constrained Windows sessions can still start it.
+org.gradle.jvmargs=-Xmx512m -Dfile.encoding=UTF-8
# Fabric Properties
# check these on https://modmuss50.me/fabric.html
minecraft_version=1.21.4
@@ -7,7 +7,7 @@ yarn_mappings=1.21.4+build.8
loader_version=0.18.3
loom_version=1.14-SNAPSHOT
# Mod Properties
-mod_version=0.1.3-alpha
+mod_version=0.1.6-beta
maven_group=com.radiance
archives_base_name=Radiance
# Dependencies
diff --git a/scripts/inspect_radiance_runtime.ps1 b/scripts/inspect_radiance_runtime.ps1
new file mode 100644
index 0000000..1622334
--- /dev/null
+++ b/scripts/inspect_radiance_runtime.ps1
@@ -0,0 +1,166 @@
+param(
+ [string]$JarPath = "C:\Users\Felix\Downloads\Radiance-0.1.3-alpha-fabric-1.21.4-windows.jar",
+ [string]$CoreDllPath
+)
+
+Set-StrictMode -Version Latest
+$ErrorActionPreference = "Stop"
+
+Add-Type -AssemblyName System.IO.Compression.FileSystem
+
+function Get-AsciiStrings {
+ param(
+ [byte[]]$Bytes,
+ [int]$MinLength = 4
+ )
+
+ $results = New-Object System.Collections.Generic.List[string]
+ $builder = New-Object System.Text.StringBuilder
+
+ foreach ($byteValue in $Bytes) {
+ if (($byteValue -ge 32 -and $byteValue -le 126) -or $byteValue -eq 9) {
+ [void]$builder.Append([char]$byteValue)
+ } else {
+ if ($builder.Length -ge $MinLength) {
+ $results.Add($builder.ToString())
+ }
+ [void]$builder.Clear()
+ }
+ }
+
+ if ($builder.Length -ge $MinLength) {
+ $results.Add($builder.ToString())
+ }
+
+ return $results
+}
+
+function Get-ZipEntryBytes {
+ param(
+ [System.IO.Compression.ZipArchive]$Archive,
+ [string]$EntryName
+ )
+
+ $entry = $Archive.Entries | Where-Object { $_.FullName -eq $EntryName } | Select-Object -First 1
+ if ($null -eq $entry) {
+ return $null
+ }
+
+ $stream = $entry.Open()
+ try {
+ $memoryStream = New-Object System.IO.MemoryStream
+ $stream.CopyTo($memoryStream)
+ return $memoryStream.ToArray()
+ } finally {
+ $stream.Dispose()
+ }
+}
+
+if (-not $CoreDllPath -and -not (Test-Path $JarPath)) {
+ throw "Jar not found: $JarPath"
+}
+
+$coreDllBytes = $null
+$shaderEntryMap = [ordered]@{}
+$jar = $null
+
+if ($CoreDllPath) {
+ if (-not (Test-Path $CoreDllPath)) {
+ throw "core.dll path not found: $CoreDllPath"
+ }
+ $coreDllBytes = [System.IO.File]::ReadAllBytes((Resolve-Path $CoreDllPath))
+}
+
+if (Test-Path $JarPath) {
+ $resolvedJarPath = (Resolve-Path $JarPath)
+ $jar = [System.IO.Compression.ZipFile]::OpenRead($resolvedJarPath)
+ try {
+ if ($null -eq $coreDllBytes) {
+ $coreDllBytes = Get-ZipEntryBytes -Archive $jar -EntryName "core.dll"
+ }
+
+ foreach ($entry in $jar.Entries) {
+ if ($entry.FullName -like "shaders/world/ray_tracing/*.spv") {
+ $shaderEntryMap[$entry.FullName] = Get-ZipEntryBytes -Archive $jar -EntryName $entry.FullName
+ }
+ }
+ } finally {
+ $jar.Dispose()
+ }
+}
+
+if ($null -eq $coreDllBytes) {
+ throw "Unable to locate core.dll bytes from jar or explicit path."
+}
+
+$optionPatterns = [ordered]@{
+ "world_representation_mode" = @("world_representation_mode", "chunk_aabb")
+ "chunk_traversal_mode" = @("chunk_traversal_mode", "voxel_dda", "brick", "macrocell")
+ "chunk_data_layout" = @("chunk_data_layout", "occupancy_bitmask", "occupancy_palette", "face_mask")
+ "chunk_macrocell_size" = @("chunk_macrocell_size", "macrocell")
+ "diffuse_gi_mode" = @("diffuse_gi_mode", "radiance_cache", "low_cost_hybrid", "probe", "sharc")
+ "num_ray_bounces" = @("num_ray_bounces", "numRayBounces", "rayBounces")
+ "use_jitter" = @("use_jitter", "cameraJitter", "unjitteredPixelCenter")
+}
+
+$coreStrings = Get-AsciiStrings -Bytes $coreDllBytes -MinLength 4
+$coreSha256 = [System.BitConverter]::ToString(
+ [System.Security.Cryptography.SHA256]::HashData($coreDllBytes)
+).Replace("-", "").ToLowerInvariant()
+
+Write-Host "=== core.dll ==="
+Write-Host "size : $($coreDllBytes.Length)"
+Write-Host "sha256 : $coreSha256"
+Write-Host ""
+
+Write-Host "=== core.dll option evidence ==="
+foreach ($kvp in $optionPatterns.GetEnumerator()) {
+ $hits = New-Object System.Collections.Generic.List[string]
+ foreach ($needle in $kvp.Value) {
+ foreach ($candidate in $coreStrings) {
+ if ($candidate -like "*$needle*" -and -not $hits.Contains($candidate)) {
+ $hits.Add($candidate)
+ }
+ }
+ }
+
+ if ($hits.Count -eq 0) {
+ Write-Host ("- {0}: no direct core.dll string evidence" -f $kvp.Key)
+ } else {
+ Write-Host ("- {0}:" -f $kvp.Key)
+ foreach ($hit in $hits | Select-Object -First 6) {
+ Write-Host (" {0}" -f $hit)
+ }
+ }
+}
+
+Write-Host ""
+Write-Host "=== shader option evidence (world/ray_tracing) ==="
+foreach ($kvp in $optionPatterns.GetEnumerator()) {
+ $shaderHits = New-Object System.Collections.Generic.List[string]
+ foreach ($shaderEntry in $shaderEntryMap.GetEnumerator()) {
+ $shaderStrings = Get-AsciiStrings -Bytes $shaderEntry.Value -MinLength 4
+ $matched = $false
+ foreach ($needle in $kvp.Value) {
+ if ($matched) {
+ break
+ }
+ foreach ($candidate in $shaderStrings) {
+ if ($candidate -like "*$needle*") {
+ $shaderHits.Add("$($shaderEntry.Key) :: $candidate")
+ $matched = $true
+ break
+ }
+ }
+ }
+ }
+
+ if ($shaderHits.Count -eq 0) {
+ Write-Host ("- {0}: no ray-tracing shader string evidence" -f $kvp.Key)
+ } else {
+ Write-Host ("- {0}:" -f $kvp.Key)
+ foreach ($hit in $shaderHits | Select-Object -First 8) {
+ Write-Host (" {0}" -f $hit)
+ }
+ }
+}
diff --git a/src/main/java/com/radiance/client/RadianceClient.java b/src/main/java/com/radiance/client/RadianceClient.java
index b4ed513..ea011df 100644
--- a/src/main/java/com/radiance/client/RadianceClient.java
+++ b/src/main/java/com/radiance/client/RadianceClient.java
@@ -1,6 +1,8 @@
package com.radiance.client;
import com.mojang.logging.LogUtils;
+import com.radiance.client.util.LightSourceRegistry;
+import com.radiance.client.util.MaterialToolkit;
import com.radiance.client.option.Options;
import com.radiance.client.pipeline.Pipeline;
import com.radiance.client.proxy.vulkan.RendererProxy;
@@ -44,34 +46,51 @@ public void onInitializeClient() {
// core lib
String osName = System.getProperty("os.name");
if (osName.toLowerCase().contains("windows")) {
- Path libTargetPath = radianceDir.resolve("core.lib");
- Path libResourcePath = Path.of("core.lib");
- copyFileFromResource(libTargetPath, libResourcePath);
-
Path dllTargetPath = radianceDir.resolve("core.dll");
Path dllResourcePath = Path.of("core.dll");
copyFileFromResource(dllTargetPath, dllResourcePath);
-
- System.load(dllTargetPath.toAbsolutePath().toString());
-
- Path dlssTargetPath = radianceDir.resolve("nvngx_dlss.dll");
- Path dlssDTargetPath = radianceDir.resolve("nvngx_dlssd.dll");
-
- if (!Files.exists(dlssTargetPath) || !Files.exists(dlssDTargetPath)) {
- throw new RuntimeException("DLSS runtime libraries not found!");
+ Path ngxDlssPath = radianceDir.resolve("nvngx_dlss.dll");
+ Path ngxDlssdPath = radianceDir.resolve("nvngx_dlssd.dll");
+ Path ngxDlssgPath = radianceDir.resolve("nvngx_dlssg.dll");
+ Path xessPath = radianceDir.resolve("libxess.dll");
+ Path xessDx11Path = radianceDir.resolve("libxess_dx11.dll");
+ Path xessFgPath = radianceDir.resolve("libxess_fg.dll");
+ copyOptionalFileFromResource(ngxDlssPath, Path.of("nvngx_dlss.dll"));
+ copyOptionalFileFromResource(ngxDlssdPath, Path.of("nvngx_dlssd.dll"));
+ copyOptionalFileFromResource(ngxDlssgPath, Path.of("nvngx_dlssg.dll"));
+ copyOptionalFileFromResource(xessPath, Path.of("libxess.dll"));
+ // currently not used, can be used later for fg
+ copyOptionalFileFromResource(xessDx11Path, Path.of("libxess_dx11.dll"));
+ copyOptionalFileFromResource(xessFgPath, Path.of("libxess_fg.dll"));
+ copyOptionalFileFromResource(radianceDir.resolve("sl.interposer.dll"),
+ Path.of("sl.interposer.dll"));
+ copyOptionalFileFromResource(radianceDir.resolve("sl.common.dll"),
+ Path.of("sl.common.dll"));
+ copyOptionalFileFromResource(radianceDir.resolve("sl.reflex.dll"),
+ Path.of("sl.reflex.dll"));
+ copyOptionalFileFromResource(radianceDir.resolve("sl.pcl.dll"),
+ Path.of("sl.pcl.dll"));
+ copyOptionalFileFromResource(radianceDir.resolve("NvLowLatencyVk.dll"),
+ Path.of("NvLowLatencyVk.dll"));
+
+ loadOptionalLibrary(xessPath);
+
+ try {
+ System.load(dllTargetPath.toAbsolutePath().toString());
+ } catch (UnsatisfiedLinkError e) {
+ throw new RuntimeException("Failed to load core library from: " + dllTargetPath
+ + ". This may indicate missing dependencies or an incompatible platform.", e);
}
} else if (osName.toLowerCase().contains("linux")) {
Path soTargetPath = radianceDir.resolve("libcore.so");
Path soResourcePath = Path.of("libcore.so");
copyFileFromResource(soTargetPath, soResourcePath);
- System.load(soTargetPath.toAbsolutePath().toString());
-
- Path dlssTargetPath = radianceDir.resolve("libnvidia-ngx-dlss.so.310.5.3");
- Path dlssDTargetPath = radianceDir.resolve("libnvidia-ngx-dlssd.so.310.5.3");
-
- if (!Files.exists(dlssTargetPath) || !Files.exists(dlssDTargetPath)) {
- throw new RuntimeException("DLSS runtime libraries not found!");
+ try {
+ System.load(soTargetPath.toAbsolutePath().toString());
+ } catch (UnsatisfiedLinkError e) {
+ throw new RuntimeException("Failed to load core library from: " + soTargetPath
+ + ". This may indicate missing dependencies or an incompatible platform.", e);
}
} else {
throw new RuntimeException("The OS " + osName + " is not supported");
@@ -80,15 +99,17 @@ public void onInitializeClient() {
// shaders
Path shaderTargetPath = radianceDir.resolve("shaders");
Path shaderResourcePath = Path.of("shaders");
- copyFolderFromResource(shaderTargetPath, shaderResourcePath);
+ copyFolderFromResource(shaderTargetPath, shaderResourcePath, true);
// modules
Path moduleTargetPath = radianceDir.resolve("modules");
Path moduleResourcePath = Path.of("modules");
- copyFolderFromResource(moduleTargetPath, moduleResourcePath);
+ copyFolderFromResource(moduleTargetPath, moduleResourcePath, true);
RendererProxy.initFolderPath(radianceDir.toAbsolutePath().toString());
Pipeline.initFolderPath(radianceDir);
+ MaterialToolkit.init(radianceDir);
+ LightSourceRegistry.init(radianceDir);
Options.readOptions();
@@ -98,7 +119,13 @@ public void onInitializeClient() {
public void copyFileFromResource(Path targetPath, Path resourcePath) {
try (InputStream is = getClass().getResourceAsStream(toResourcePath(resourcePath))) {
if (is == null) {
- throw new IOException("Cannot find target path: " + resourcePath);
+ if (Files.exists(targetPath)) {
+ LOGGER.warn("Resource {} not found in jar, using existing file {}", resourcePath,
+ targetPath.toAbsolutePath());
+ return;
+ }
+ throw new IOException("Required runtime file is missing from both jar and disk. Resource: "
+ + resourcePath + ", expected existing file: " + targetPath.toAbsolutePath());
}
Files.createDirectories(targetPath.getParent());
@@ -108,6 +135,25 @@ public void copyFileFromResource(Path targetPath, Path resourcePath) {
}
}
+ public void copyOptionalFileFromResource(Path targetPath, Path resourcePath) {
+ try (InputStream is = getClass().getResourceAsStream(toResourcePath(resourcePath))) {
+ if (is == null) {
+ return;
+ }
+
+ Files.createDirectories(targetPath.getParent());
+ Files.copy(is, targetPath, StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void loadOptionalLibrary(Path path) {
+ if (Files.exists(path)) {
+ System.load(path.toAbsolutePath().toString());
+ }
+ }
+
public String toResourcePath(Path path) {
String joined = StreamSupport.stream(path.spliterator(), false).map(Object::toString)
.collect(Collectors.joining("/"));
@@ -115,11 +161,21 @@ public String toResourcePath(Path path) {
}
public void copyFolderFromResource(Path targetPath, Path resourcePath) {
+ copyFolderFromResource(targetPath, resourcePath, false);
+ }
+
+ public void copyFolderFromResource(Path targetPath, Path resourcePath, boolean preserveExisting) {
String resourcePathStr = toResourcePath(resourcePath);
URL url = getClass().getResource(resourcePathStr);
if (url == null) {
- throw new RuntimeException("Resource folder not found: " + resourcePathStr);
+ if (Files.isDirectory(targetPath)) {
+ LOGGER.warn("Resource folder {} not found in jar, using existing directory {}",
+ resourcePathStr, targetPath.toAbsolutePath());
+ return;
+ }
+ throw new RuntimeException("Required runtime folder is missing from both jar and disk. Resource: "
+ + resourcePathStr + ", expected existing directory: " + targetPath.toAbsolutePath());
}
try {
@@ -141,30 +197,35 @@ public void copyFolderFromResource(Path targetPath, Path resourcePath) {
}
Path root = fs.getPath(resourcePathStr);
- walkAndCopy(root, targetPath, resourcePath);
+ walkAndCopy(root, targetPath, resourcePath, preserveExisting);
} finally {
if (created) {
try {
fs.close();
- } catch (IOException ignored) {
+ } catch (IOException e) {
+ LOGGER.warn("Failed to close JAR filesystem", e);
}
}
}
} else {
Path root = Paths.get(uri);
- walkAndCopy(root, targetPath, resourcePath);
+ walkAndCopy(root, targetPath, resourcePath, preserveExisting);
}
} catch (URISyntaxException | IOException e) {
throw new RuntimeException("Failed to copy resource folder", e);
}
}
- private void walkAndCopy(Path walkRoot, Path targetRoot, Path baseResourcePath)
+ private void walkAndCopy(Path walkRoot, Path targetRoot, Path baseResourcePath,
+ boolean preserveExisting)
throws IOException {
try (Stream stream = Files.walk(walkRoot)) {
stream.filter(Files::isRegularFile).forEach(source -> {
String relativePathStr = walkRoot.relativize(source).toString();
Path targetFile = targetRoot.resolve(relativePathStr);
+ if (preserveExisting && Files.exists(targetFile)) {
+ return;
+ }
Path childResourcePath = baseResourcePath.resolve(relativePathStr);
copyFileFromResource(targetFile, childResourcePath);
});
diff --git a/src/main/java/com/radiance/client/constant/VulkanConstants.java b/src/main/java/com/radiance/client/constant/VulkanConstants.java
index 7d19c82..fff2377 100644
--- a/src/main/java/com/radiance/client/constant/VulkanConstants.java
+++ b/src/main/java/com/radiance/client/constant/VulkanConstants.java
@@ -53,20 +53,30 @@ public String getName() {
return name;
}
+ public VkFormat toUnorm() {
+ return switch (this) {
+ case VK_FORMAT_R8_SRGB -> VK_FORMAT_R8_UNORM;
+ case VK_FORMAT_R8G8_SRGB -> VK_FORMAT_R8G8_UNORM;
+ case VK_FORMAT_R8G8B8_SRGB -> VK_FORMAT_R8G8B8_UNORM;
+ case VK_FORMAT_R8G8B8A8_SRGB -> VK_FORMAT_R8G8B8A8_UNORM;
+ default -> this;
+ };
+ }
+
public NativeImage.InternalFormat getNativeImageInternalFormat() {
return switch (this) {
- case VK_FORMAT_R8_UNORM -> NativeImage.InternalFormat.RED;
- case VK_FORMAT_R8G8_UNORM -> NativeImage.InternalFormat.RG;
- case VK_FORMAT_R8G8B8_UNORM -> NativeImage.InternalFormat.RGB;
- case VK_FORMAT_R8G8B8A8_UNORM -> NativeImage.InternalFormat.RGBA;
+ case VK_FORMAT_R8_UNORM, VK_FORMAT_R8_SRGB -> NativeImage.InternalFormat.RED;
+ case VK_FORMAT_R8G8_UNORM, VK_FORMAT_R8G8_SRGB -> NativeImage.InternalFormat.RG;
+ case VK_FORMAT_R8G8B8_UNORM, VK_FORMAT_R8G8B8_SRGB -> NativeImage.InternalFormat.RGB;
+ case VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB -> NativeImage.InternalFormat.RGBA;
default -> throw new IllegalStateException("Unexpected value: " + this.value);
};
}
public NativeImage.Format getNativeImageFormat() {
return switch (this) {
- case VK_FORMAT_R8G8B8_UNORM -> NativeImage.Format.RGB;
- case VK_FORMAT_R8G8B8A8_UNORM -> NativeImage.Format.RGBA;
+ case VK_FORMAT_R8G8B8_UNORM, VK_FORMAT_R8G8B8_SRGB -> NativeImage.Format.RGB;
+ case VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_R8G8B8A8_SRGB -> NativeImage.Format.RGBA;
default -> throw new IllegalStateException("Unexpected value: " + this.value);
};
}
diff --git a/src/main/java/com/radiance/client/gui/AttributeWidgetUtil.java b/src/main/java/com/radiance/client/gui/AttributeWidgetUtil.java
new file mode 100644
index 0000000..d64d8b0
--- /dev/null
+++ b/src/main/java/com/radiance/client/gui/AttributeWidgetUtil.java
@@ -0,0 +1,518 @@
+package com.radiance.client.gui;
+
+import com.radiance.client.pipeline.config.AttributeConfig;
+
+import java.util.List;
+import java.util.Locale;
+
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.widget.ButtonWidget;
+import net.minecraft.client.gui.widget.ClickableWidget;
+import net.minecraft.client.gui.widget.SliderWidget;
+import net.minecraft.client.gui.widget.TextFieldWidget;
+import net.minecraft.text.Text;
+import net.minecraft.util.math.MathHelper;
+
+final class AttributeWidgetUtil {
+
+ private AttributeWidgetUtil() {
+ }
+
+ static boolean shouldValidateBorder(String type) {
+ return type.equals("int") || type.equals("float") || type.equals("string") || type.equals(
+ "vec3");
+ }
+
+ static boolean isStrictInt(String s) {
+ if (s == null || s.isEmpty()) {
+ return false;
+ }
+ try {
+ Integer.parseInt(s);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ static boolean isStrictFloat(String s) {
+ if (s == null || s.isEmpty()) {
+ return false;
+ }
+ try {
+ Float.parseFloat(s);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ static void drawBorder(DrawContext ctx, int x, int y, int w, int h, int color) {
+ ctx.fill(x, y, x + w, y + 1, color);
+ ctx.fill(x, y + h - 1, x + w, y + h, color);
+ ctx.fill(x, y, x + 1, y + h, color);
+ ctx.fill(x + w - 1, y, x + w, y + h, color);
+ }
+
+ static void layoutWidgets(List widgets, int x, int y, int singleWidth,
+ int tripleWidth, int gap) {
+ if (widgets.size() == 1) {
+ ClickableWidget w = widgets.get(0);
+ w.setX(x);
+ w.setY(y);
+ if (w.getWidth() <= 0) {
+ w.setWidth(singleWidth);
+ }
+ return;
+ }
+
+ if (widgets.size() == 3 && widgets.stream().allMatch(
+ widget -> widget instanceof TextFieldWidget)) {
+ for (int i = 0; i < 3; i++) {
+ ClickableWidget cw = widgets.get(i);
+ cw.setX(x + i * (tripleWidth + gap));
+ cw.setY(y);
+ cw.setWidth(tripleWidth);
+ }
+ return;
+ }
+
+ int cursorX = x;
+ for (ClickableWidget widget : widgets) {
+ int preferredWidth = widget.getWidth() > 0 ? widget.getWidth() : singleWidth;
+ widget.setX(cursorX);
+ widget.setY(y);
+ widget.setWidth(preferredWidth);
+ cursorX += preferredWidth + gap;
+ }
+ }
+
+ static int totalWidgetWidth(List widgets, int singleWidth, int tripleWidth, int gap) {
+ if (widgets.size() == 3 && widgets.stream().allMatch(
+ widget -> widget instanceof TextFieldWidget)) {
+ return (tripleWidth * 3) + (gap * 2);
+ }
+ if (widgets.size() == 1) {
+ ClickableWidget widget = widgets.get(0);
+ return widget.getWidth() > 0 ? widget.getWidth() : singleWidth;
+ }
+
+ int total = 0;
+ for (int i = 0; i < widgets.size(); i++) {
+ ClickableWidget widget = widgets.get(i);
+ total += widget.getWidth() > 0 ? widget.getWidth() : singleWidth;
+ if (i + 1 < widgets.size()) {
+ total += gap;
+ }
+ }
+ return total;
+ }
+
+ static List buildWidgets(AttributeConfig cfg, Screen owner, TextRenderer textRenderer,
+ int width,
+ int vec3ComponentWidth) {
+ String type = cfg.type == null ? "" : cfg.type.toLowerCase(Locale.ROOT);
+
+ if (type.startsWith("enum:")) {
+ return List.of(buildEnumWidget(cfg, cfg.type.substring(5), width));
+ }
+
+ if (type.startsWith("int_range:")) {
+ return buildIntRange(cfg, owner, cfg.type.substring(10), width);
+ }
+
+ if (type.startsWith("float_range:")) {
+ return buildFloatRange(cfg, owner, cfg.type.substring(12), width);
+ }
+
+ return switch (type) {
+ case "bool" -> List.of(buildBoolWidget(cfg, width));
+ case "int" -> List.of(buildIntWidget(cfg, textRenderer, width));
+ case "float" -> List.of(buildFloatWidget(cfg, textRenderer, width));
+ case "string" -> List.of(buildStringWidget(cfg, textRenderer, width));
+ case "vec3" -> buildVec3Widget(cfg, textRenderer, vec3ComponentWidth);
+ default -> List.of(buildStringWidget(cfg, textRenderer, width));
+ };
+ }
+
+ private static ClickableWidget buildBoolWidget(AttributeConfig cfg, int width) {
+ boolean b = "render_pipeline.true".equalsIgnoreCase(cfg.value);
+ return ButtonWidget.builder(
+ Text.translatable(b ? "render_pipeline.true" : "render_pipeline.false"), btn -> {
+ boolean nv = !"render_pipeline.true".equalsIgnoreCase(cfg.value);
+ setValue(cfg, nv ? "render_pipeline.true" : "render_pipeline.false");
+ btn.setMessage(Text.translatable(cfg.value));
+ }).dimensions(0, 0, width, 20).build();
+ }
+
+ private static ClickableWidget buildEnumWidget(AttributeConfig cfg, String raw, int width) {
+ String[] values = raw.isEmpty() ? new String[]{""} : raw.split("-");
+ int idx = 0;
+ if (cfg.value != null) {
+ for (int i = 0; i < values.length; i++) {
+ if (values[i].equals(cfg.value)) {
+ idx = i;
+ break;
+ }
+ }
+ } else {
+ cfg.value = values[0];
+ }
+
+ int[] index = new int[]{idx};
+ return ButtonWidget.builder(Text.translatable(values[index[0]]), btn -> {
+ index[0] = (index[0] + 1) % values.length;
+ setValue(cfg, values[index[0]]);
+ btn.setMessage(Text.translatable(cfg.value));
+ }).dimensions(0, 0, width, 20).build();
+ }
+
+ private static ClickableWidget buildIntWidget(AttributeConfig cfg, TextRenderer textRenderer,
+ int width) {
+ TextFieldWidget tf = new TextFieldWidget(textRenderer, 0, 0, width, 20, Text.empty());
+ tf.setMaxLength(64);
+ tf.setText(cfg.value == null ? "" : cfg.value);
+ tf.setTextPredicate(s -> s.isEmpty() || s.equals("-") || s.matches("-?\\d+"));
+ tf.setChangedListener(text -> {
+ if (isStrictInt(text)) {
+ setValue(cfg, text);
+ }
+ });
+ return tf;
+ }
+
+ private static ClickableWidget buildFloatWidget(AttributeConfig cfg, TextRenderer textRenderer,
+ int width) {
+ TextFieldWidget tf = new TextFieldWidget(textRenderer, 0, 0, width, 20, Text.empty());
+ tf.setMaxLength(64);
+ tf.setText(cfg.value == null ? "" : cfg.value);
+ tf.setTextPredicate(
+ s -> s.isEmpty() || s.equals("-") || s.equals(".") || s.equals("-.") || s.matches(
+ "-?\\d+")
+ || s.matches("-?\\d+\\.") || s.matches("-?\\d*\\.\\d+"));
+ tf.setChangedListener(text -> {
+ if (isStrictFloat(text)) {
+ setValue(cfg, text);
+ }
+ });
+ return tf;
+ }
+
+ private static ClickableWidget buildStringWidget(AttributeConfig cfg, TextRenderer textRenderer,
+ int width) {
+ TextFieldWidget tf = new TextFieldWidget(textRenderer, 0, 0, width, 20, Text.empty());
+ tf.setMaxLength(128);
+ tf.setText(cfg.value == null ? "" : cfg.value);
+ tf.setChangedListener(text -> setValue(cfg, text));
+ return tf;
+ }
+
+ private static List buildVec3Widget(AttributeConfig cfg,
+ TextRenderer textRenderer,
+ int componentWidth) {
+ if (cfg.value == null || cfg.value.isEmpty()) {
+ cfg.value = "0,0,0";
+ }
+
+ float[] v = parseVec3(cfg.value);
+ TextFieldWidget x = vecField(textRenderer, v[0], componentWidth);
+ TextFieldWidget y = vecField(textRenderer, v[1], componentWidth);
+ TextFieldWidget z = vecField(textRenderer, v[2], componentWidth);
+
+ Runnable syncIfValid = () -> {
+ String sx = x.getText();
+ String sy = y.getText();
+ String sz = z.getText();
+
+ if (isStrictFloat(sx) && isStrictFloat(sy) && isStrictFloat(sz)) {
+ setValue(cfg, sx + "," + sy + "," + sz);
+ }
+ };
+
+ x.setChangedListener(s -> syncIfValid.run());
+ y.setChangedListener(s -> syncIfValid.run());
+ z.setChangedListener(s -> syncIfValid.run());
+
+ syncIfValid.run();
+ return List.of(x, y, z);
+ }
+
+ private static TextFieldWidget vecField(TextRenderer textRenderer, float v, int width) {
+ TextFieldWidget tf = new TextFieldWidget(textRenderer, 0, 0, width, 20, Text.empty());
+ tf.setMaxLength(32);
+ tf.setText(trimFloat(v));
+ tf.setTextPredicate(
+ s -> s.isEmpty() || s.equals("-") || s.equals(".") || s.equals("-.") || s.matches(
+ "-?\\d+")
+ || s.matches("-?\\d+\\.") || s.matches("-?\\d*\\.\\d+"));
+ return tf;
+ }
+
+ private static List buildIntRange(AttributeConfig cfg, Screen owner, String raw,
+ int width) {
+ Range r = parseRange(raw);
+ int start = (int) r.start;
+ int end = (int) r.end;
+ if (start > end) {
+ int t = start;
+ start = end;
+ end = t;
+ }
+ final int minInt = start;
+ final int maxInt = end;
+
+ int cur = minInt;
+ if (isInt(cfg.value)) {
+ cur = Integer.parseInt(cfg.value);
+ } else {
+ setValue(cfg, String.valueOf(minInt));
+ }
+ cur = MathHelper.clamp(cur, minInt, maxInt);
+
+ int sliderWidth = Math.max(80, width - 24);
+ IntRangeSlider slider = new IntRangeSlider(0, 0, sliderWidth, 20, minInt, maxInt, cur, cfg,
+ cfg instanceof BoundAttributeConfig bound ? bound.defaultValue()
+ : Integer.toString(minInt));
+ slider.updateMessage();
+ ButtonWidget editButton = ButtonWidget.builder(Text.literal("#"), button -> {
+ if (owner == null) {
+ return;
+ }
+ MinecraftClient.getInstance().setScreen(new NumericSliderInputScreen(owner, cfg.name,
+ true, minInt, maxInt, cfg.value, cfg instanceof BoundAttributeConfig bound
+ ? bound.defaultValue()
+ : Integer.toString(minInt), value -> {
+ setValue(cfg, value);
+ slider.syncFromValueString(value);
+ }));
+ }).dimensions(0, 0, 20, 20).build();
+ return List.of(slider, editButton);
+ }
+
+ private static List buildFloatRange(AttributeConfig cfg, Screen owner,
+ String raw, int width) {
+ Range r = parseRange(raw);
+ float start = (float) r.start;
+ float end = (float) r.end;
+ if (start > end) {
+ float t = start;
+ start = end;
+ end = t;
+ }
+ final float minFloat = start;
+ final float maxFloat = end;
+
+ float cur = minFloat;
+ if (isFloat(cfg.value)) {
+ cur = Float.parseFloat(cfg.value);
+ } else {
+ setValue(cfg, formatTwoDecimals(minFloat));
+ }
+ cur = MathHelper.clamp(cur, minFloat, maxFloat);
+
+ int sliderWidth = Math.max(80, width - 24);
+ FloatRangeSlider slider = new FloatRangeSlider(0, 0, sliderWidth, 20, minFloat, maxFloat,
+ cur, cfg, cfg instanceof BoundAttributeConfig bound ? bound.defaultValue()
+ : formatTwoDecimals(minFloat));
+ slider.updateMessage();
+ ButtonWidget editButton = ButtonWidget.builder(Text.literal("#"), button -> {
+ if (owner == null) {
+ return;
+ }
+ MinecraftClient.getInstance().setScreen(new NumericSliderInputScreen(owner, cfg.name,
+ false, minFloat, maxFloat, cfg.value, cfg instanceof BoundAttributeConfig bound
+ ? bound.defaultValue()
+ : formatTwoDecimals(minFloat), value -> {
+ setValue(cfg, value);
+ slider.syncFromValueString(value);
+ }));
+ }).dimensions(0, 0, 20, 20).build();
+ return List.of(slider, editButton);
+ }
+
+ private static void setValue(AttributeConfig cfg, String value) {
+ if (cfg instanceof BoundAttributeConfig bound) {
+ bound.apply(value);
+ return;
+ }
+ cfg.value = value;
+ }
+
+ private static boolean isInt(String s) {
+ try {
+ Integer.parseInt(s);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ private static boolean isFloat(String s) {
+ try {
+ Float.parseFloat(s);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ private static float[] parseVec3(String s) {
+ if (s == null || s.isEmpty()) {
+ return new float[]{0, 0, 0};
+ }
+
+ String[] p = s.split("[,\\s]+");
+ float x = p.length > 0 && isFloat(p[0]) ? Float.parseFloat(p[0]) : 0;
+ float y = p.length > 1 && isFloat(p[1]) ? Float.parseFloat(p[1]) : 0;
+ float z = p.length > 2 && isFloat(p[2]) ? Float.parseFloat(p[2]) : 0;
+ return new float[]{x, y, z};
+ }
+
+ private static String formatTwoDecimals(float v) {
+ return String.format(Locale.ROOT, "%.2f", v);
+ }
+
+ private static String trimFloat(float v) {
+ String s = Float.toString(v);
+ if (s.endsWith(".0")) {
+ return s.substring(0, s.length() - 2);
+ }
+ return s;
+ }
+
+ private static Range parseRange(String raw) {
+ int dash = raw.lastIndexOf('-');
+ if (dash <= 0) {
+ return new Range(0, 1);
+ }
+ String a = raw.substring(0, dash);
+ String b = raw.substring(dash + 1);
+ double start = 0;
+ double end = 1;
+ try {
+ start = Double.parseDouble(a);
+ end = Double.parseDouble(b);
+ } catch (Exception ignored) {
+ }
+ return new Range(start, end);
+ }
+
+ private record Range(double start, double end) {
+
+ }
+
+ private static class IntRangeSlider extends SliderWidget {
+
+ private final int start;
+ private final int end;
+ private final AttributeConfig cfg;
+ private final String defaultValue;
+
+ public IntRangeSlider(int x, int y, int width, int height, int start, int end, int cur,
+ AttributeConfig cfg, String defaultValue) {
+ super(x, y, width, height, Text.empty(),
+ (cur - (double) start) / (double) (end - start));
+ this.start = start;
+ this.end = end;
+ this.cfg = cfg;
+ this.defaultValue = defaultValue;
+ this.value = (cur - (double) start) / (double) (end - start);
+ }
+
+ private int current() {
+ if (end == start) {
+ return start;
+ }
+ return start + (int) Math.round(this.value * (end - start));
+ }
+
+ @Override
+ protected void updateMessage() {
+ setMessage(Text.translatable(Integer.toString(current())));
+ }
+
+ @Override
+ protected void applyValue() {
+ int v = MathHelper.clamp(current(), start, end);
+ setValue(cfg, Integer.toString(v));
+ }
+
+ public void syncFromValueString(String valueString) {
+ if (!isInt(valueString)) {
+ return;
+ }
+ int value = MathHelper.clamp(Integer.parseInt(valueString), start, end);
+ this.value = (value - (double) start) / (double) (end - start);
+ updateMessage();
+ }
+
+ @Override
+ public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ if (button == 1 && this.isMouseOver(mouseX, mouseY)) {
+ syncFromValueString(defaultValue);
+ setValue(cfg, Integer.toString(current()));
+ return true;
+ }
+ return super.mouseClicked(mouseX, mouseY, button);
+ }
+ }
+
+ private static class FloatRangeSlider extends SliderWidget {
+
+ private final float start;
+ private final float end;
+ private final AttributeConfig cfg;
+ private final String defaultValue;
+
+ public FloatRangeSlider(int x, int y, int width, int height, float start, float end,
+ float cur,
+ AttributeConfig cfg, String defaultValue) {
+ super(x, y, width, height, Text.empty(), (cur - start) / (double) (end - start));
+ this.start = start;
+ this.end = end;
+ this.cfg = cfg;
+ this.defaultValue = defaultValue;
+ this.value = (cur - start) / (double) (end - start);
+ }
+
+ private float current() {
+ if (end == start) {
+ return start;
+ }
+ return (float) (start + this.value * (end - start));
+ }
+
+ @Override
+ protected void updateMessage() {
+ setMessage(Text.translatable(formatTwoDecimals(current())));
+ }
+
+ @Override
+ protected void applyValue() {
+ float v = MathHelper.clamp(current(), start, end);
+ setValue(cfg, formatTwoDecimals(v));
+ }
+
+ public void syncFromValueString(String valueString) {
+ if (!isFloat(valueString)) {
+ return;
+ }
+ float value = MathHelper.clamp(Float.parseFloat(valueString), start, end);
+ this.value = (value - start) / (double) (end - start);
+ updateMessage();
+ }
+
+ @Override
+ public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ if (button == 1 && this.isMouseOver(mouseX, mouseY)) {
+ syncFromValueString(defaultValue);
+ setValue(cfg, formatTwoDecimals(current()));
+ return true;
+ }
+ return super.mouseClicked(mouseX, mouseY, button);
+ }
+ }
+}
diff --git a/src/main/java/com/radiance/client/gui/ModuleAttributeScreen.java b/src/main/java/com/radiance/client/gui/ModuleAttributeScreen.java
index adf9eb8..7722e90 100644
--- a/src/main/java/com/radiance/client/gui/ModuleAttributeScreen.java
+++ b/src/main/java/com/radiance/client/gui/ModuleAttributeScreen.java
@@ -2,29 +2,37 @@
import com.radiance.client.pipeline.Module;
import com.radiance.client.pipeline.config.AttributeConfig;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
+
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
import net.minecraft.client.gui.widget.ClickableWidget;
-import net.minecraft.client.gui.widget.SliderWidget;
import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.text.Text;
-import net.minecraft.util.math.MathHelper;
public class ModuleAttributeScreen extends Screen {
private static final int OK_BORDER = 0xFF34D058;
private static final int BAD_BORDER = 0xFFE5534B;
+ private static final int ROW_LEFT = 20;
+ private static final int ROW_RIGHT = 20;
+ private static final int WIDGET_WIDTH = 160;
+ private static final int VEC3_COMPONENT_WIDTH = 52;
+ private static final int VEC3_GAP = 2;
private static final int HEADER_HEIGHT = 32;
private static final String MODULE_ATTRIBUTE_SCREEN_NO_ATTRIBUTES = "module_attribute_screen.no_attributes";
+
private final Screen parent;
private final Module module;
+
private final List rows = new ArrayList<>();
+
private int scrollY = 0;
public ModuleAttributeScreen(Screen parent, Module module) {
@@ -48,7 +56,8 @@ protected void init() {
}
for (AttributeConfig cfg : list) {
- List ws = buildWidgets(cfg);
+ List ws = AttributeWidgetUtil.buildWidgets(cfg, this, textRenderer,
+ WIDGET_WIDTH, VEC3_COMPONENT_WIDTH);
for (ClickableWidget w : ws) {
addDrawableChild(w);
}
@@ -61,16 +70,22 @@ public void close() {
MinecraftClient.getInstance().setScreen(parent);
}
+ @Override
+ public void renderBackground(DrawContext context, int mouseX, int mouseY, float delta) {
+ context.fill(0, 0, this.width, this.height, RadianceTheme.panelBg);
+ }
+
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
super.render(context, mouseX, mouseY, delta);
- context.drawTextWithShadow(textRenderer, Text.translatable(module.name), 10,
- HEADER_HEIGHT + 8, 0xFFEAEAEA);
+ RadianceTheme.drawOutlinedText(context, textRenderer, Text.translatable(module.name), 10,
+ HEADER_HEIGHT + 8, RadianceTheme.textPrimary);
if (rows.isEmpty()) {
- context.drawTextWithShadow(textRenderer, "module_attribute_screen.no_attributes", 10,
- 60, 0xFFB0B0B0);
+ RadianceTheme.drawOutlinedText(context, textRenderer,
+ Text.translatable(MODULE_ATTRIBUTE_SCREEN_NO_ATTRIBUTES), 10, 60,
+ RadianceTheme.textSecondary);
return;
}
@@ -81,20 +96,16 @@ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
Row row = rows.get(i);
int y = baseY + i * rowH;
- context.drawTextWithShadow(textRenderer, Text.translatable(row.cfg.name), 20, y + 6,
- 0xFFD0D0D0);
-
boolean visible = y >= (HEADER_HEIGHT + 18) && y <= (this.height - 24);
if (visible) {
- context.drawTextWithShadow(textRenderer, Text.translatable(row.cfg.name), 20, y + 6,
- 0xFFD0D0D0);
+ RadianceTheme.drawOutlinedText(context, textRenderer,
+ Text.translatable(row.cfg.name), ROW_LEFT, y + 6, RadianceTheme.textPrimary);
}
layoutRowWidgets(row, y);
String type = row.cfg.type == null ? "" : row.cfg.type.toLowerCase(Locale.ROOT);
-
- boolean doBorder = shouldValidateBorder(type);
+ boolean doBorder = AttributeWidgetUtil.shouldValidateBorder(type);
for (ClickableWidget w : row.widgets) {
w.visible = visible;
@@ -108,91 +119,35 @@ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
if (type.equals("vec3")) {
if (w instanceof TextFieldWidget tf) {
- ok = isStrictFloat(tf.getText());
+ ok = AttributeWidgetUtil.isStrictFloat(tf.getText());
}
} else if (type.equals("int")) {
if (w instanceof TextFieldWidget tf) {
- ok = isStrictInt(tf.getText());
+ ok = AttributeWidgetUtil.isStrictInt(tf.getText());
}
} else if (type.equals("float")) {
if (w instanceof TextFieldWidget tf) {
- ok = isStrictFloat(tf.getText());
+ ok = AttributeWidgetUtil.isStrictFloat(tf.getText());
}
- } else { // string and other types
- ok = true;
}
- int bx = w.getX() - 1;
- int by = w.getY() - 1;
- int bw = w.getWidth() + 2;
- int bh = w.getHeight() + 2;
- drawBorder(context, bx, by, bw, bh, ok ? OK_BORDER : BAD_BORDER);
+ if (w instanceof TextFieldWidget tf) {
+ int c = ok ? OK_BORDER : BAD_BORDER;
+ AttributeWidgetUtil.drawBorder(context, tf.getX(), tf.getY(), tf.getWidth(), tf.getHeight(), c);
+ }
}
}
}
- private boolean shouldValidateBorder(String type) {
- return type.equals("int") || type.equals("float") || type.equals("string") || type.equals(
- "vec3");
- }
-
- private boolean isStrictInt(String s) {
- if (s == null || s.isEmpty()) {
- return false;
- }
- try {
- Integer.parseInt(s);
- return true;
- } catch (Exception e) {
- return false;
- }
- }
-
- private boolean isStrictFloat(String s) {
- if (s == null || s.isEmpty()) {
- return false;
- }
- try {
- Float.parseFloat(s);
- return true;
- } catch (Exception e) {
- return false;
- }
- }
-
private void layoutRowWidgets(Row row, int y) {
- int x = 200;
-
- if (row.widgets.size() == 1) {
- ClickableWidget w = row.widgets.get(0);
- w.setX(x);
- w.setY(y);
- w.setWidth(160);
- return;
- }
-
- if (row.widgets.size() == 3) {
- int w = 52;
- int gap = 2;
- for (int i = 0; i < 3; i++) {
- ClickableWidget cw = row.widgets.get(i);
- cw.setX(x + i * (w + gap));
- cw.setY(y);
- cw.setWidth(w);
- }
- }
- }
-
- private void drawBorder(DrawContext ctx, int x, int y, int w, int h, int color) {
- ctx.fill(x, y, x + w, y + 1, color);
- ctx.fill(x, y + h - 1, x + w, y + h, color);
- ctx.fill(x, y, x + 1, y + h, color);
- ctx.fill(x + w - 1, y, x + w, y + h, color);
+ int widgetWidth = AttributeWidgetUtil.totalWidgetWidth(row.widgets, WIDGET_WIDTH, VEC3_COMPONENT_WIDTH,
+ VEC3_GAP);
+ int x = this.width - ROW_RIGHT - widgetWidth;
+ AttributeWidgetUtil.layoutWidgets(row.widgets, x, y, WIDGET_WIDTH, VEC3_COMPONENT_WIDTH, VEC3_GAP);
}
@Override
- public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount,
- double verticalAmount) {
+ public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount, double verticalAmount) {
int rowH = 22;
int contentH = 60 + rows.size() * rowH + 20;
int minScroll = Math.min(0, this.height - contentH);
@@ -207,355 +162,14 @@ public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmou
return true;
}
- private List buildWidgets(AttributeConfig cfg) {
- String type = cfg.type == null ? "" : cfg.type.toLowerCase(Locale.ROOT);
-
- if (type.startsWith("enum:")) {
- return List.of(buildEnumWidget(cfg, cfg.type.substring(5)));
- }
-
- if (type.startsWith("int_range:")) {
- return List.of(buildIntRange(cfg, cfg.type.substring(10)));
- }
-
- if (type.startsWith("float_range:")) {
- return List.of(buildFloatRange(cfg, cfg.type.substring(12)));
- }
-
- return switch (type) {
- case "bool" -> List.of(buildBoolWidget(cfg));
- case "int" -> List.of(buildIntWidget(cfg));
- case "float" -> List.of(buildFloatWidget(cfg));
- case "string" -> List.of(buildStringWidget(cfg));
- case "vec3" -> buildVec3Widget(cfg);
- default -> List.of(buildStringWidget(cfg));
- };
- }
-
- private ClickableWidget buildBoolWidget(AttributeConfig cfg) {
- boolean b = "true".equalsIgnoreCase(cfg.value);
- return ButtonWidget.builder(Text.translatable(b ? "true" : "false"), btn -> {
- boolean nv = !"true".equalsIgnoreCase(cfg.value);
- cfg.value = nv ? "true" : "false";
- btn.setMessage(Text.translatable(cfg.value));
- }).dimensions(200, 0, 160, 20).build();
- }
-
- private ClickableWidget buildEnumWidget(AttributeConfig cfg, String raw) {
- String[] values = raw.isEmpty() ? new String[]{""} : raw.split("-");
- int idx = 0;
- if (cfg.value != null) {
- for (int i = 0; i < values.length; i++) {
- if (values[i].equals(cfg.value)) {
- idx = i;
- break;
- }
- }
- } else {
- cfg.value = values[0];
- }
-
- int[] index = new int[]{idx};
- return ButtonWidget.builder(Text.translatable(values[index[0]]), btn -> {
- index[0] = (index[0] + 1) % values.length;
- cfg.value = values[index[0]];
- btn.setMessage(Text.translatable(cfg.value));
- }).dimensions(200, 0, 160, 20).build();
- }
-
- private ClickableWidget buildIntWidget(AttributeConfig cfg) {
- TextFieldWidget tf = new TextFieldWidget(textRenderer, 200, 0, 160, 20, Text.empty());
- tf.setMaxLength(64);
- tf.setText(cfg.value == null ? "" : cfg.value);
- tf.setTextPredicate(s -> s.isEmpty() || s.equals("-") || s.matches("-?\\d+"));
- tf.setChangedListener(text -> {
- if (isStrictInt(text)) {
- cfg.value = text;
- }
- });
- return tf;
- }
-
- private ClickableWidget buildFloatWidget(AttributeConfig cfg) {
- TextFieldWidget tf = new TextFieldWidget(textRenderer, 200, 0, 160, 20, Text.empty());
- tf.setMaxLength(64);
- tf.setText(cfg.value == null ? "" : cfg.value);
- tf.setTextPredicate(
- s -> s.isEmpty() || s.equals("-") || s.equals(".") || s.equals("-.") || s.matches(
- "-?\\d+") || s.matches("-?\\d+\\.") || s.matches("-?\\d*\\.\\d+"));
- tf.setChangedListener(text -> {
- if (isStrictFloat(text)) {
- cfg.value = text;
- }
- });
- return tf;
- }
-
- private ClickableWidget buildStringWidget(AttributeConfig cfg) {
- TextFieldWidget tf = new TextFieldWidget(textRenderer, 200, 0, 160, 20, Text.empty());
- tf.setMaxLength(128);
- tf.setText(cfg.value == null ? "" : cfg.value);
- tf.setChangedListener(text -> cfg.value = text);
- return tf;
- }
-
- private List buildVec3Widget(AttributeConfig cfg) {
- if (cfg.value == null || cfg.value.isEmpty()) {
- cfg.value = "0,0,0";
- }
-
- float[] v = parseVec3(cfg.value);
- TextFieldWidget x = vecField(v[0]);
- TextFieldWidget y = vecField(v[1]);
- TextFieldWidget z = vecField(v[2]);
-
- Runnable syncIfValid = () -> {
- String sx = x.getText();
- String sy = y.getText();
- String sz = z.getText();
-
- if (isStrictFloat(sx) && isStrictFloat(sy) && isStrictFloat(sz)) {
- cfg.value = sx + "," + sy + "," + sz;
- }
- };
-
- x.setChangedListener(s -> syncIfValid.run());
- y.setChangedListener(s -> syncIfValid.run());
- z.setChangedListener(s -> syncIfValid.run());
-
- syncIfValid.run();
- return List.of(x, y, z);
- }
-
- private TextFieldWidget vecField(float v) {
- TextFieldWidget tf = new TextFieldWidget(textRenderer, 0, 0, 52, 20, Text.empty());
- tf.setMaxLength(32);
- tf.setText(trimFloat(v));
- tf.setTextPredicate(
- s -> s.isEmpty() || s.equals("-") || s.equals(".") || s.equals("-.") || s.matches(
- "-?\\d+") || s.matches("-?\\d+\\.") || s.matches("-?\\d*\\.\\d+"));
- return tf;
- }
-
- private ClickableWidget buildIntRange(AttributeConfig cfg, String raw) {
- Range r = parseRange(raw);
- int start = (int) r.start;
- int end = (int) r.end;
- if (start > end) {
- int t = start;
- start = end;
- end = t;
- }
-
- int cur = start;
- if (isInt(cfg.value)) {
- cur = Integer.parseInt(cfg.value);
- } else {
- cfg.value = String.valueOf(start);
- }
- cur = MathHelper.clamp(cur, start, end);
-
- IntRangeSlider slider = new IntRangeSlider(200, 0, 160, 20, start, end, cur, cfg);
- slider.updateMessage();
- return slider;
- }
-
- private ClickableWidget buildFloatRange(AttributeConfig cfg, String raw) {
- Range r = parseRange(raw);
- float start = (float) r.start;
- float end = (float) r.end;
- if (start > end) {
- float t = start;
- start = end;
- end = t;
- }
-
- float cur = start;
- if (isFloat(cfg.value)) {
- cur = Float.parseFloat(cfg.value);
- } else {
- cfg.value = trimFloat(start);
- }
- cur = MathHelper.clamp(cur, start, end);
-
- FloatRangeSlider slider = new FloatRangeSlider(200, 0, 160, 20, start, end, cur, cfg);
- slider.updateMessage();
- return slider;
- }
-
- private boolean isValueValid(AttributeConfig cfg, List widgets) {
- String type = cfg.type == null ? "" : cfg.type.toLowerCase(Locale.ROOT);
-
- if (type.startsWith("enum:") || type.startsWith("int_range:") || type.startsWith(
- "float_range:") || type.equals("bool") || type.equals("string")) {
- return true;
- }
-
- if (type.equals("int")) {
- return cfg.value == null || cfg.value.isEmpty() || isInt(cfg.value);
- }
-
- if (type.equals("float")) {
- return cfg.value == null || cfg.value.isEmpty() || isFloat(cfg.value);
- }
-
- if (type.equals("vec3")) {
- if (widgets.size() != 3) {
- return false;
- }
- for (ClickableWidget w : widgets) {
- if (w instanceof TextFieldWidget tf) {
- String s = tf.getText();
- if (!(s.isEmpty() || isFloat(s))) {
- return false;
- }
- } else {
- return false;
- }
- }
- return true;
- }
-
- return true;
- }
-
- private boolean isInt(String s) {
- try {
- Integer.parseInt(s);
- return true;
- } catch (Exception e) {
- return false;
- }
- }
-
- private boolean isFloat(String s) {
- try {
- Float.parseFloat(s);
- return true;
- } catch (Exception e) {
- return false;
- }
- }
-
- private float[] parseVec3(String s) {
- if (s == null || s.isEmpty()) {
- return new float[]{0, 0, 0};
- }
-
- String[] p = s.split("[,\\s]+");
- float x = p.length > 0 && isFloat(p[0]) ? Float.parseFloat(p[0]) : 0;
- float y = p.length > 1 && isFloat(p[1]) ? Float.parseFloat(p[1]) : 0;
- float z = p.length > 2 && isFloat(p[2]) ? Float.parseFloat(p[2]) : 0;
- return new float[]{x, y, z};
- }
-
- private String trimFloat(float v) {
- String s = Float.toString(v);
- if (s.endsWith(".0")) {
- return s.substring(0, s.length() - 2);
- }
- return s;
- }
+ private static class Row {
- private Range parseRange(String raw) {
- int dash = raw.lastIndexOf('-');
- if (dash <= 0) {
- return new Range(0, 1);
- }
- String a = raw.substring(0, dash);
- String b = raw.substring(dash + 1);
- double start = 0;
- double end = 1;
- try {
- start = Double.parseDouble(a);
- end = Double.parseDouble(b);
- } catch (Exception ignored) {
- }
- return new Range(start, end);
- }
-
- private record Range(double start, double end) {
-
- }
-
- private record Row(AttributeConfig cfg, List widgets) {
-
- }
-
- private static class IntRangeSlider extends SliderWidget {
-
- private final int start;
- private final int end;
private final AttributeConfig cfg;
+ private final List widgets;
- public IntRangeSlider(int x, int y, int width, int height, int start, int end, int cur,
- AttributeConfig cfg) {
- super(x, y, width, height, Text.empty(),
- (cur - (double) start) / (double) (end - start));
- this.start = start;
- this.end = end;
+ private Row(AttributeConfig cfg, List widgets) {
this.cfg = cfg;
- this.value = (cur - (double) start) / (double) (end - start);
- }
-
- private int current() {
- if (end == start) {
- return start;
- }
- return start + (int) Math.round(this.value * (end - start));
- }
-
- @Override
- protected void updateMessage() {
- setMessage(Text.translatable(Integer.toString(current())));
- }
-
- @Override
- protected void applyValue() {
- int v = MathHelper.clamp(current(), start, end);
- cfg.value = Integer.toString(v);
- }
- }
-
- private static class FloatRangeSlider extends SliderWidget {
-
- private final float start;
- private final float end;
- private final AttributeConfig cfg;
-
- public FloatRangeSlider(int x, int y, int width, int height, float start, float end,
- float cur, AttributeConfig cfg) {
- super(x, y, width, height, Text.empty(), (cur - start) / (double) (end - start));
- this.start = start;
- this.end = end;
- this.cfg = cfg;
- this.value = (cur - start) / (double) (end - start);
- }
-
- private float current() {
- if (end == start) {
- return start;
- }
- return (float) (start + this.value * (end - start));
- }
-
- @Override
- protected void updateMessage() {
- setMessage(Text.translatable(trim(current())));
- }
-
- @Override
- protected void applyValue() {
- float v = MathHelper.clamp(current(), start, end);
- cfg.value = trim(v);
- }
-
- private String trim(float v) {
- String s = Float.toString(v);
- if (s.endsWith(".0")) {
- return s.substring(0, s.length() - 2);
- }
- return s;
+ this.widgets = widgets;
}
}
}
diff --git a/src/main/java/com/radiance/client/gui/RadianceTheme.java b/src/main/java/com/radiance/client/gui/RadianceTheme.java
new file mode 100644
index 0000000..59e693e
--- /dev/null
+++ b/src/main/java/com/radiance/client/gui/RadianceTheme.java
@@ -0,0 +1,204 @@
+package com.radiance.client.gui;
+
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.widget.ClickableWidget;
+import net.minecraft.text.Text;
+
+public final class RadianceTheme {
+
+ private RadianceTheme() {
+ }
+
+ private static final int BASE_PANEL = 0x0A0A0A;
+ private static final int BASE_WIDGET = 0x1A1A2A;
+ private static final int BASE_HOVER = 0x2A2A4A;
+ private static final int BASE_ACTIVE = 0x3A3A5A;
+ private static final int BASE_DROPDOWN = 0x101018;
+ private static final int BASE_HEADER = 0x0A0A0A;
+ private static final int BASE_BORDER = 0x808080;
+ private static final int BASE_BORDER_FOCUS = 0xC0C0E0;
+ private static final int BASE_TEXT_PRIMARY = 0xE0E0E0;
+ private static final int BASE_TEXT_SECONDARY = 0x909090;
+ private static final int BASE_TEXT_ACCENT = 0x8888CC;
+
+ public static final int TEXT_ERROR = 0xFFFF5555;
+ public static final int TEXT_SUCCESS = 0xFF55FF55;
+ public static final int TEXT_LINK = 0xFF55FFFF;
+ public static final int TEXT_PATH = 0xFFFFAA00;
+ public static final int SELECTED_BAR = 0xFF6060FF;
+ public static final int GPU_TAG = 0xFF707090;
+
+ public static int panelBg;
+ public static int widgetBg;
+ public static int widgetBgHover;
+ public static int widgetBgActive;
+ public static int dropdownBg;
+ public static int headerBg;
+ public static int borderDefault;
+ public static int borderFocused;
+ public static int textPrimary;
+ public static int textSecondary;
+ public static int textAccent;
+
+ private static float globalAlpha = 0.55f;
+ private static float effectiveAlpha = 0.55f;
+ private static final java.util.Map screenAlphaOverrides = new java.util.HashMap<>();
+
+ public static ClickableWidget activeSlider = null;
+ private static long fadeStartMs = 0;
+ private static boolean fadingOut = false;
+ public static final long FADE_OUT_MS = 100;
+ public static final long FADE_IN_MS = 150;
+
+ public static boolean peekActive = false;
+ private static boolean adaptiveDimmingEnabled = false;
+ private static float sceneBrightness = 0.5f;
+
+ static {
+ recompute();
+ }
+
+ public static void setGlobalAlpha(float alpha) {
+ globalAlpha = Math.max(0f, Math.min(1f, alpha));
+ recompute();
+ }
+
+ public static void setScreenAlpha(String screenName, float alpha) {
+ if (alpha < 0) {
+ screenAlphaOverrides.remove(screenName);
+ } else {
+ screenAlphaOverrides.put(screenName, Math.max(0f, Math.min(1f, alpha)));
+ }
+ }
+
+ public static void setAdaptiveDimmingEnabled(boolean enabled) {
+ adaptiveDimmingEnabled = enabled;
+ recompute();
+ }
+
+ public static void setSceneBrightness(float brightness) {
+ sceneBrightness = Math.max(0f, Math.min(1f, brightness));
+ if (adaptiveDimmingEnabled) {
+ recompute();
+ }
+ }
+
+ public static void recompute() {
+ effectiveAlpha = globalAlpha;
+ if (adaptiveDimmingEnabled) {
+ float adjustment = (sceneBrightness - 0.5f) * 0.2f;
+ effectiveAlpha = Math.max(0f, Math.min(1f, globalAlpha + adjustment));
+ }
+
+ panelBg = withAlpha(BASE_PANEL, effectiveAlpha * 0.7f);
+ widgetBg = withAlpha(BASE_WIDGET, effectiveAlpha);
+ widgetBgHover = withAlpha(BASE_HOVER, effectiveAlpha);
+ widgetBgActive = withAlpha(BASE_ACTIVE, effectiveAlpha);
+ dropdownBg = withAlpha(BASE_DROPDOWN, Math.min(1f, effectiveAlpha + 0.15f));
+ headerBg = withAlpha(BASE_HEADER, effectiveAlpha * 0.5f);
+ borderDefault = withAlpha(BASE_BORDER, effectiveAlpha * 0.6f);
+ borderFocused = withAlpha(BASE_BORDER_FOCUS, effectiveAlpha);
+ textPrimary = withAlpha(BASE_TEXT_PRIMARY, 1.0f);
+ textSecondary = withAlpha(BASE_TEXT_SECONDARY, 0.8f);
+ textAccent = withAlpha(BASE_TEXT_ACCENT, 1.0f);
+ }
+
+ public static int currentPanelBg(Screen screen) {
+ if (screen == null) {
+ return panelBg;
+ }
+ Float override = screenAlphaOverrides.get(screen.getClass().getSimpleName());
+ if (override == null) {
+ return panelBg;
+ }
+ return withAlpha(BASE_PANEL, override * 0.7f);
+ }
+
+ public static void beginSliderFocus(ClickableWidget slider) {
+ activeSlider = slider;
+ fadeStartMs = System.currentTimeMillis();
+ fadingOut = true;
+ }
+
+ public static void endSliderFocus() {
+ activeSlider = null;
+ fadeStartMs = System.currentTimeMillis();
+ fadingOut = false;
+ }
+
+ public static float inactiveFadeFactor() {
+ if (peekActive) {
+ return 0f;
+ }
+ if (activeSlider == null && fadeStartMs == 0) {
+ return 1f;
+ }
+
+ long elapsed = System.currentTimeMillis() - fadeStartMs;
+ if (fadingOut || activeSlider != null) {
+ return Math.max(0f, 1f - (elapsed / (float) FADE_OUT_MS));
+ }
+ float factor = Math.min(1f, elapsed / (float) FADE_IN_MS);
+ if (factor >= 1f) {
+ fadeStartMs = 0;
+ }
+ return factor;
+ }
+
+ public static int withAlpha(int rgb, float alpha) {
+ int a = Math.max(0, Math.min(255, (int) (alpha * 255)));
+ return (a << 24) | (rgb & 0x00FFFFFF);
+ }
+
+ public static int scaleAlpha(int argb, float multiplier) {
+ int a = (argb >>> 24) & 0xFF;
+ a = Math.max(0, Math.min(255, (int) (a * multiplier)));
+ return (a << 24) | (argb & 0x00FFFFFF);
+ }
+
+ public static void drawOutlinedText(DrawContext ctx, TextRenderer renderer, Text text, int x,
+ int y, int color) {
+ drawOutlinedText(ctx, renderer, text, x, y, color, 1f);
+ }
+
+ public static void drawOutlinedText(DrawContext ctx, TextRenderer renderer, Text text, int x,
+ int y, int color, float alphaMult) {
+ int outlineColor = withAlpha(0x000000, 0.4f * alphaMult);
+ int mainColor = scaleAlpha(color, alphaMult);
+
+ ctx.drawText(renderer, text, x - 1, y, outlineColor, false);
+ ctx.drawText(renderer, text, x + 1, y, outlineColor, false);
+ ctx.drawText(renderer, text, x, y - 1, outlineColor, false);
+ ctx.drawText(renderer, text, x, y + 1, outlineColor, false);
+ ctx.drawText(renderer, text, x, y, mainColor, false);
+ }
+
+ public static void drawCategoryHeader(DrawContext ctx, TextRenderer renderer, Text text, int x,
+ int y, int width, int entryHeight) {
+ drawCategoryHeader(ctx, renderer, text, x, y, width, entryHeight, 1f);
+ }
+
+ public static void drawCategoryHeader(DrawContext ctx, TextRenderer renderer, Text text, int x,
+ int y, int width, int entryHeight, float alphaMult) {
+ if (alphaMult <= 0f) {
+ return;
+ }
+
+ int lineY = y + entryHeight / 2;
+ int textW = renderer.getWidth(text);
+ int textX = x + (width - textW) / 2;
+ int textY = y + entryHeight - 9 - 1;
+ int lineColor = withAlpha(BASE_TEXT_ACCENT, 0.3f * alphaMult);
+
+ if (textX > x + 4) {
+ ctx.fill(x, lineY, textX - 4, lineY + 1, lineColor);
+ }
+ if (textX + textW + 4 < x + width) {
+ ctx.fill(textX + textW + 4, lineY, x + width, lineY + 1, lineColor);
+ }
+ drawOutlinedText(ctx, renderer, text, textX, textY,
+ textAccent & 0x00FFFFFF | 0xFF000000, alphaMult);
+ }
+}
diff --git a/src/main/java/com/radiance/client/gui/RenderPipelineScreen.java b/src/main/java/com/radiance/client/gui/RenderPipelineScreen.java
index 381b65d..7f5d1a1 100644
--- a/src/main/java/com/radiance/client/gui/RenderPipelineScreen.java
+++ b/src/main/java/com/radiance/client/gui/RenderPipelineScreen.java
@@ -4,17 +4,24 @@
import com.radiance.client.pipeline.Module;
import com.radiance.client.pipeline.ModuleEntry;
import com.radiance.client.pipeline.Pipeline;
+import com.radiance.client.pipeline.Presets;
+import com.radiance.client.pipeline.config.AttributeConfig;
import com.radiance.client.pipeline.config.ImageConfig;
import com.radiance.mixin_related.extensions.vulkan_render_integration.IDrawContextExt;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.Drawable;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.widget.ButtonWidget;
+import net.minecraft.client.gui.widget.ClickableWidget;
+import net.minecraft.client.gui.widget.TextFieldWidget;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
@@ -29,7 +36,7 @@ public class RenderPipelineScreen extends Screen {
"R16G16B16A16_SFLOAT", 0xFFFFB84E);
private static final int HEADER_HEIGHT = 32;
- private static final float GLOBAL_SCALE = 0.6f;
+ private static final float GLOBAL_SCALE = 0.75f;
private static final String RENDER_PIPELINE_SCREEN_BACK = "render_pipeline_screen.back";
private static final String RENDER_PIPELINE_SCREEN_SAVE_AND_BUIld = "render_pipeline_screen.save_and_build";
private static final String RENDER_PIPELINE_SCREEN_RELOAD = "render_pipeline_screen.reload";
@@ -44,11 +51,45 @@ public class RenderPipelineScreen extends Screen {
private ImageConfig localFinalOutput = null;
private ImageConfig pendingPort = null;
private boolean isPendingOutput = false;
+ private boolean isPanning = false;
+
+ private Mode mode = Mode.PIPELINE;
+ private final List presets = new ArrayList<>();
+ private PresetEntry activePreset = null;
+ private PresetSelector activePresetSelector = null;
+ private final List presetBlocks = new ArrayList<>();
+ private final List presetWidgets = new ArrayList<>();
+ private int presetScrollY = 0;
+
+ private ButtonWidget modeToggleBtn;
+ private ButtonWidget secondaryBtn;
+
+ private static final String RENDER_PIPELINE_PRESET_NAME = "render_pipeline.preset.name";
+ private static final String RENDER_PIPELINE_MODE_NAME = "render_pipeline.mode.name";
public RenderPipelineScreen(Screen parent) {
super(Text.literal("Render Pipeline"));
this.parent = parent;
+
+ registerDefaultPresets();
+
+ Pipeline.PipelineMode pipelineMode = Pipeline.getPipelineMode();
+ this.mode = pipelineMode == Pipeline.PipelineMode.PRESET ? Mode.PRESET : Mode.PIPELINE;
+
+ String presetName = Pipeline.processPresetName(Pipeline.getActivePreset());
+ if (presetName != null) {
+ for (PresetEntry e : presets) {
+ if (e != null && e.name() != null && e.name().equals(presetName)) {
+ this.activePreset = e;
+ break;
+ }
+ }
+ }
+
+ if (this.mode == Mode.PRESET && this.activePreset == null && !this.presets.isEmpty()) {
+ this.activePreset = this.presets.getFirst();
+ }
}
private int getFormatColor(String format) {
@@ -61,31 +102,108 @@ private int getFormatColor(String format) {
@Override
protected void init() {
- refreshPipeline();
+ rebuildUI();
+ }
+
+ private void rebuildUI() {
+ clearChildren();
+ activeSelector = null;
+ activePresetSelector = null;
+ presetWidgets.clear();
+
+ if (mode == Mode.PRESET && activePreset == null && !presets.isEmpty()) {
+ activePreset = presets.getFirst();
+ }
+
+ if (mode == Mode.PIPELINE) {
+ refreshPipeline();
+ }
+
+ int backX = 10;
+ int backW = 60;
+ int toggleX = backX + backW + 5;
+ int toggleW = 110;
+ int secondaryX = toggleX + toggleW + 5;
+ int secondaryW = 150;
addDrawableChild(
ButtonWidget.builder(Text.translatable(RENDER_PIPELINE_SCREEN_BACK), button -> close())
- .dimensions(10, 6, 60, 20).build());
+ .dimensions(backX, 6, backW, 20).build());
+
+ modeToggleBtn = addDrawableChild(ButtonWidget.builder(
+ Text.translatable(RENDER_PIPELINE_MODE_NAME)
+ .append(Text.literal(": ").append(Text.translatable(mode.key))), button -> {
+ if (mode == Mode.PIPELINE) {
+ if (activePreset == null && !presets.isEmpty()) {
+ activePreset = presets.getFirst();
+ }
+ Pipeline.switchToPresetMode(
+ activePreset != null ? activePreset.name() : "Default");
+ mode = Mode.PRESET;
+ } else {
+ Pipeline.switchToPipelineMode();
+ mode = Mode.PIPELINE;
+ }
+ rebuildUI();
+ }).dimensions(toggleX, 6, toggleW, 20).build());
+
+ if (mode == Mode.PIPELINE) {
+ secondaryBtn = addDrawableChild(
+ ButtonWidget.builder(Text.translatable(RENDER_PIPELINE_SCREEN_ADD_MODULE),
+ button -> {
+ Map entries = Pipeline.INSTANCE.getModuleEntries();
+ if (entries != null && !entries.isEmpty()) {
+ activeSelector = new ModuleSelector(secondaryX, HEADER_HEIGHT + 4,
+ entries);
+ }
+ }).dimensions(secondaryX, 6, secondaryW, 20).build());
+ } else {
+ Text activePresetText = activePreset != null
+ ? Text.translatable(activePreset.name())
+ : Text.literal("N/A"); // to make sure
+ secondaryBtn = addDrawableChild(
+ ButtonWidget.builder(Text.translatable(RENDER_PIPELINE_PRESET_NAME)
+ .append(Text.literal(": "))
+ .append(activePresetText), button -> {
+ if (!presets.isEmpty()) {
+ activePresetSelector = new PresetSelector(secondaryX, HEADER_HEIGHT + 4,
+ presets);
+ }
+ }).dimensions(secondaryX, 6, secondaryW, 20).build());
+ }
- addDrawableChild(
+ ButtonWidget saveBtn = addDrawableChild(
ButtonWidget.builder(Text.translatable(RENDER_PIPELINE_SCREEN_SAVE_AND_BUIld),
button -> {
- syncToPipeline();
- }).dimensions(80, 6, 100, 20).build());
+ if (mode == Mode.PIPELINE) {
+ syncToPipeline();
+ } else {
+ syncPresetToPipeline();
+ }
+ }).dimensions(secondaryX + secondaryW + 5, 6, 100, 20).build());
- addDrawableChild(
+ ButtonWidget reloadBtn = addDrawableChild(
ButtonWidget.builder(Text.translatable(RENDER_PIPELINE_SCREEN_RELOAD), button -> {
- refreshPipeline();
- }).dimensions(190, 6, 100, 20).build());
+ if (mode == Mode.PIPELINE) {
+ refreshPipeline();
+ } else {
+ applyActivePreset();
+ }
+ }).dimensions(secondaryX + secondaryW + 110, 6, 100, 20).build());
- addDrawableChild(
- ButtonWidget.builder(Text.translatable(RENDER_PIPELINE_SCREEN_ADD_MODULE), button -> {
- Map entries = Pipeline.INSTANCE.getModuleEntries();
- if (entries != null && !entries.isEmpty()) {
- activeSelector = new ModuleSelector(300, HEADER_HEIGHT + 4, entries);
+ saveBtn.active = true;
+ reloadBtn.active = true;
+ secondaryBtn.visible = true;
+ secondaryBtn.active = true;
- }
- }).dimensions(300, 6, 100, 20).build());
+ if (mode == Mode.PRESET) {
+ presetScrollY = 0;
+ if (activePreset == null && !presets.isEmpty()) {
+ activePreset = presets.get(0);
+ }
+ Pipeline.switchToPresetMode(activePreset != null ? activePreset.name() : "Default");
+ applyActivePreset();
+ }
}
public void refreshPipeline() {
@@ -124,6 +242,11 @@ public void syncToPipeline() {
@Override
public void close() {
+ if (mode == Mode.PIPELINE) {
+ syncToPipeline();
+ } else {
+ syncPresetToPipeline();
+ }
MinecraftClient.getInstance().setScreen(parent);
}
@@ -133,7 +256,7 @@ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
node.updateWidth(textRenderer);
}
- this.renderBackground(context, mouseX, mouseY, delta);
+ context.fill(0, 0, this.width, this.height, RadianceTheme.panelBg);
context.getMatrices().push();
context.getMatrices().scale(GLOBAL_SCALE, GLOBAL_SCALE, 1f);
@@ -145,20 +268,132 @@ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
drawable.render(context, scaledMouseX, scaledMouseY, delta);
}
- context.drawTextWithShadow(textRenderer,
- Text.translatable(RENDER_PIPELINE_SCREEN_BACK_HINT), 10, HEADER_HEIGHT + 8, 0xFFEAEAEA);
+ RadianceTheme.drawOutlinedText(context, textRenderer,
+ Text.translatable(RENDER_PIPELINE_SCREEN_BACK_HINT), 10, HEADER_HEIGHT + 8,
+ RadianceTheme.textSecondary);
- for (ModuleNode node : nodes) {
- drawModuleNode(context, node);
+ if (mode == Mode.PIPELINE) {
+ for (ModuleNode node : nodes) {
+ drawModuleNode(context, node);
+ }
+
+ drawConnections(context);
+
+ if (activeSelector != null) {
+ context.getMatrices().push();
+ context.getMatrices().translate(0, 0, 200);
+ activeSelector.render(context, scaledMouseX, scaledMouseY);
+ context.getMatrices().pop();
+ }
+ } else {
+ renderPresetMode(context);
+ if (activePresetSelector != null) {
+ context.getMatrices().push();
+ context.getMatrices().translate(0, 0, 200);
+ activePresetSelector.render(context, scaledMouseX, scaledMouseY);
+ context.getMatrices().pop();
+ }
}
- drawConnections(context);
+ context.getMatrices().pop();
+ }
- if (activeSelector != null) {
- activeSelector.render(context, scaledMouseX, scaledMouseY);
+ private int scaledW() {
+ return (int) (this.width / GLOBAL_SCALE);
+ }
+
+ private int scaledH() {
+ return (int) (this.height / GLOBAL_SCALE);
+ }
+
+ private void renderPresetMode(DrawContext ctx) {
+ int sw = scaledW();
+ int sh = scaledH();
+ int x0 = 10;
+ int y0 = HEADER_HEIGHT + 28;
+ int contentW = sw - 20;
+
+ int y = y0 + presetScrollY;
+ int titleGap = 12;
+ int afterTitle = 8;
+ int rowH = 22;
+
+ for (PresetModuleBlock block : presetBlocks) {
+ if (block.rows.isEmpty()) {
+ continue;
+ }
+ int titleY = y;
+ int tw = textRenderer.getWidth(Text.translatable(block.module.name));
+ int tx = x0 + (contentW - tw) / 2;
+
+ boolean titleVisible = titleY >= (HEADER_HEIGHT + 18) && titleY <= (sh - 24);
+ if (titleVisible) {
+ ctx.drawTextWithShadow(textRenderer, Text.translatable(block.module.name), tx,
+ titleY, 0xFFEAEAEA);
+ }
+
+ y += titleGap + afterTitle;
+
+ for (int i = 0; i < block.rows.size(); i++) {
+ PresetRow row = block.rows.get(i);
+ int ry = y + i * rowH;
+ boolean visible = ry >= (HEADER_HEIGHT + 18) && ry <= (sh - 24);
+
+ if (visible) {
+ ctx.drawTextWithShadow(textRenderer, Text.translatable(row.cfg.name), x0 + 10,
+ ry + 6, 0xFFD0D0D0);
+ }
+
+ layoutPresetRowWidgets(row, x0 + contentW - 10, ry);
+
+ String type = row.cfg.type == null ? "" : row.cfg.type.toLowerCase(Locale.ROOT);
+ boolean doBorder = AttributeWidgetUtil.shouldValidateBorder(type);
+
+ for (ClickableWidget w : row.widgets) {
+ w.visible = visible;
+ w.active = visible;
+
+ if (!doBorder) {
+ continue;
+ }
+
+ boolean ok = true;
+ if (type.equals("vec3")) {
+ if (w instanceof TextFieldWidget tf) {
+ ok = AttributeWidgetUtil.isStrictFloat(tf.getText());
+ }
+ } else if (type.equals("int")) {
+ if (w instanceof TextFieldWidget tf) {
+ ok = AttributeWidgetUtil.isStrictInt(tf.getText());
+ }
+ } else if (type.equals("float")) {
+ if (w instanceof TextFieldWidget tf) {
+ ok = AttributeWidgetUtil.isStrictFloat(tf.getText());
+ }
+ }
+
+ int bx = w.getX() - 1;
+ int by = w.getY() - 1;
+ int bw = w.getWidth() + 2;
+ int bh = w.getHeight() + 2;
+ AttributeWidgetUtil.drawBorder(ctx, bx, by, bw, bh,
+ ok ? 0xFF34D058 : 0xFFE5534B);
+ }
+ }
+
+ y += block.rows.size() * rowH + 18;
}
}
+ private void layoutPresetRowWidgets(PresetRow row, int rightEdge, int y) {
+ int singleWidth = 200;
+ int tripleWidth = 64;
+ int gap = 4;
+ int widgetWidth = AttributeWidgetUtil.totalWidgetWidth(row.widgets, singleWidth, tripleWidth, gap);
+ int x = rightEdge - widgetWidth;
+ AttributeWidgetUtil.layoutWidgets(row.widgets, x, y, singleWidth, tripleWidth, gap);
+ }
+
private PortPos getPortPosition(ImageConfig config, boolean isOutput) {
for (ModuleNode node : nodes) {
if (node.module == config.owner) {
@@ -198,7 +433,7 @@ private void drawBezier(DrawContext ctx, int x1, int y1, int x2, int y2, int col
float cx = b0 * x1 + b1 * (x1 + ctrlOffset) + b2 * (x2 - ctrlOffset) + b3 * x2;
float cy = b0 * y1 + b1 * y1 + b2 * y2 + b3 * y2;
- ((IDrawContextExt) (Object) ctx).neoVoxelRT$drawOrientedQuad(RenderLayer.getGui(),
+ ((IDrawContextExt) (Object) ctx).radiance$drawOrientedQuad(RenderLayer.getGui(),
prevX, prevY, cx, cy, thickness, color);
prevX = cx;
@@ -222,12 +457,12 @@ private void drawModuleNode(DrawContext context, ModuleNode moduleNode) {
int w = moduleNode.width;
int h = moduleNode.height();
- context.fill(x, y, x + w, y + h, 0xFF20242C);
+ context.fill(x, y, x + w, y + h, RadianceTheme.widgetBg);
- context.fill(x, y, x + w, y + moduleNode.headerH, 0xFF2B3240);
+ context.fill(x, y, x + w, y + moduleNode.headerH, RadianceTheme.headerBg);
- context.drawTextWithShadow(textRenderer, Text.translatable(moduleNode.module.name), x + 6,
- y + 5, 0xFFEAEAEA);
+ RadianceTheme.drawOutlinedText(context, textRenderer,
+ Text.translatable(moduleNode.module.name), x + 6, y + 5, RadianceTheme.textPrimary);
int btnSize = 12;
int deleteX = x + w - btnSize - 4;
@@ -237,7 +472,8 @@ private void drawModuleNode(DrawContext context, ModuleNode moduleNode) {
context.drawTexture(RenderLayer::getGuiTextured, GEAR_TEX, gearX, btnY, 0, 0, btnSize,
btnSize, btnSize, btnSize);
- context.drawTextWithShadow(textRenderer, "×", deleteX + 3, btnY + 2, 0xFFFF5A5A);
+ context.drawTextWithShadow(textRenderer, "×", deleteX + 3, btnY + 2,
+ RadianceTheme.TEXT_ERROR);
for (int i = 0; i < moduleNode.rows(); i++) {
int ry = y + moduleNode.headerH + moduleNode.pad + i * moduleNode.rowH + 7;
@@ -251,7 +487,8 @@ private void drawModuleNode(DrawContext context, ModuleNode moduleNode) {
boolean isConnected = moduleConnections.stream().anyMatch(l -> l.dst == in);
drawPortDot(context, dotX, dotY, color, isConnected, false);
- context.drawTextWithShadow(textRenderer, in.name, x + 18, ry + 2, 0xFFD0D0D0);
+ context.drawTextWithShadow(textRenderer, in.name, x + 18, ry + 2,
+ RadianceTheme.textPrimary);
}
if (i < moduleNode.module.outputImageConfigs.size()) {
@@ -265,7 +502,7 @@ private void drawModuleNode(DrawContext context, ModuleNode moduleNode) {
int nameWidth = textRenderer.getWidth(out.name);
context.drawTextWithShadow(textRenderer, out.name, (dotX - 8) - nameWidth, ry + 2,
- 0xFFD0D0D0);
+ RadianceTheme.textPrimary);
}
}
}
@@ -276,7 +513,7 @@ private void drawPortDot(DrawContext ctx, int cx, int cy, int color, boolean fil
ctx.fill(cx - 3, cy - 3, cx + 4, cy + 4, 0xFF000000);
ctx.fill(cx - 2, cy - 2, cx + 3, cy + 3, color);
if (!filled) {
- ctx.fill(cx - 1, cy - 1, cx + 2, cy + 2, 0xFF20242C);
+ ctx.fill(cx - 1, cy - 1, cx + 2, cy + 2, RadianceTheme.widgetBg);
}
}
@@ -399,10 +636,27 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) {
mouseX /= GLOBAL_SCALE;
mouseY /= GLOBAL_SCALE;
+ lastMouseX = mouseX;
+ lastMouseY = mouseY;
+
+ isPanning = false;
+ draggedNode = null;
+
if (mouseY < HEADER_HEIGHT) {
return super.mouseClicked(mouseX, mouseY, button);
}
+ if (mode == Mode.PRESET) {
+ if (activePresetSelector != null) {
+ if (activePresetSelector.onClick(mouseX, mouseY)) {
+ activePresetSelector = null;
+ return true;
+ }
+ activePresetSelector = null;
+ }
+ return super.mouseClicked(mouseX, mouseY, button);
+ }
+
for (ModuleNode node : nodes) {
if (button == 0 && isDeleteClicked(node, mouseX, mouseY)) {
deleteNode(node);
@@ -453,7 +707,6 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) {
activeSelector = null;
}
- draggedNode = null;
for (int i = nodes.size() - 1; i >= 0; i--) {
ModuleNode node = nodes.get(i);
if (mouseX >= node.module.x && mouseX <= node.module.x + node.width && mouseY >= (
@@ -461,29 +714,70 @@ public boolean mouseClicked(double mouseX, double mouseY, int button) {
+ node.height())) {
if (button == 0) {
draggedNode = node;
+ } else if (button == 1) {
+ isPanning = true;
}
- break;
+ return true;
}
}
- lastMouseX = mouseX;
- lastMouseY = mouseY;
+ if (button == 1 || button == 0) {
+ isPanning = true;
+ return true;
+ }
+
return super.mouseClicked(mouseX, mouseY, button);
}
+ @Override
+ public boolean mouseScrolled(double mouseX, double mouseY, double horizontalAmount,
+ double verticalAmount) {
+ if (mode != Mode.PRESET) {
+ return super.mouseScrolled(mouseX, mouseY, horizontalAmount, verticalAmount);
+ }
+
+ int sh = scaledH();
+ int y0 = HEADER_HEIGHT + 28;
+ int rowH = 22;
+ int contentH = y0 + presetBlocks.stream().filter(b -> !b.rows.isEmpty())
+ .mapToInt(b -> 12 + 8 + (b.rows.size() * rowH) + 18).sum();
+ int minScroll = Math.min(0, sh - contentH - 10);
+
+ presetScrollY += (int) (verticalAmount * 12);
+ if (presetScrollY > 0) {
+ presetScrollY = 0;
+ }
+ if (presetScrollY < minScroll) {
+ presetScrollY = minScroll;
+ }
+ return true;
+ }
+
@Override
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX,
double deltaY) {
mouseX /= GLOBAL_SCALE;
mouseY /= GLOBAL_SCALE;
+ if (mode == Mode.PRESET) {
+ return super.mouseDragged(mouseX, mouseY, button, deltaX / GLOBAL_SCALE,
+ deltaY / GLOBAL_SCALE);
+ }
+
+ if (super.mouseDragged(mouseX, mouseY, button, deltaX / GLOBAL_SCALE,
+ deltaY / GLOBAL_SCALE)) {
+ lastMouseX = mouseX;
+ lastMouseY = mouseY;
+ return true;
+ }
+
double dx = mouseX - lastMouseX;
double dy = mouseY - lastMouseY;
if (button == 0 && draggedNode != null) {
draggedNode.module.x += dx;
draggedNode.module.y += dy;
- } else if (button == 1 || (button == 0 && draggedNode == null)) {
+ } else if (isPanning && (button == 1 || button == 0)) {
for (ModuleNode node : nodes) {
node.module.x += dx;
node.module.y += dy;
@@ -492,9 +786,27 @@ public boolean mouseDragged(double mouseX, double mouseY, int button, double del
lastMouseX = mouseX;
lastMouseY = mouseY;
- return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
+ return true;
+ }
+
+
+ @Override
+ public boolean mouseReleased(double mouseX, double mouseY, int button) {
+ mouseX /= GLOBAL_SCALE;
+ mouseY /= GLOBAL_SCALE;
+
+ lastMouseX = mouseX;
+ lastMouseY = mouseY;
+
+ boolean handled = super.mouseReleased(mouseX, mouseY, button);
+
+ draggedNode = null;
+ isPanning = false;
+
+ return handled;
}
+
private record ModuleConnection(ImageConfig src, ImageConfig dst) {
}
@@ -512,22 +824,30 @@ private class ModuleSelector {
public ModuleSelector(int x, int y, Map entries) {
this.x = x;
this.y = y;
- this.options = new ArrayList<>(entries.values());
+ this.options = new ArrayList<>();
+ for (ModuleEntry entry : entries.values()) {
+ if (entry == null || entry.name == null) {
+ continue;
+ }
+ if (Pipeline.isModuleAvailable(entry.name)) {
+ this.options.add(entry);
+ }
+ }
this.width = 120;
}
public void render(DrawContext ctx, int mouseX, int mouseY) {
int currentY = y;
ctx.fill(x - 1, y - 1, x + width + 1, y + (options.size() * itemHeight) + 1,
- 0xFFFFFFFF);
+ RadianceTheme.borderDefault);
for (ModuleEntry entry : options) {
boolean hovered = mouseX >= x && mouseX <= x + width && mouseY >= currentY
&& mouseY <= currentY + itemHeight;
ctx.fill(x, currentY, x + width, currentY + itemHeight,
- hovered ? 0xFF444444 : 0xFF222222);
+ hovered ? RadianceTheme.widgetBgHover : RadianceTheme.dropdownBg);
ctx.drawTextWithShadow(textRenderer, Text.translatable(entry.name), x + 5,
- currentY + 5, 0xFFE0E0E0);
+ currentY + 5, RadianceTheme.textPrimary);
currentY += itemHeight;
}
}
@@ -540,6 +860,9 @@ public boolean onClick(double mouseX, double mouseY) {
int index = (int) ((mouseY - y) / itemHeight);
if (index >= 0 && index < options.size()) {
ModuleEntry selected = options.get(index);
+ if (!Pipeline.isModuleAvailable(selected.name)) {
+ return true;
+ }
Module module = selected.loadModule();
module.x = 100;
module.y = 100;
@@ -549,4 +872,145 @@ public boolean onClick(double mouseX, double mouseY) {
return false;
}
}
+
+ private enum Mode {
+ PIPELINE("render_pipeline.mode.pipeline"), PRESET("render_pipeline.mode.preset");
+
+ public final String key;
+
+ Mode(String key) {
+ this.key = key;
+ }
+ }
+
+ private record PresetEntry(String name) {
+
+ }
+
+ private void registerDefaultPresets() {
+ this.presets.clear();
+ if (Pipeline.isPresetAvailable(Presets.RT_DLSSRR.key)) {
+ this.presets.add(new PresetEntry(Presets.RT_DLSSRR.key));
+ }
+ if (Pipeline.isPresetAvailable(Presets.RT_NRD.key)) {
+ this.presets.add(new PresetEntry(Presets.RT_NRD.key));
+ }
+ if (Pipeline.isPresetAvailable(Presets.RT_NRD_FSR.key)) {
+ this.presets.add(new PresetEntry(Presets.RT_NRD_FSR.key));
+ }
+ if (Pipeline.isPresetAvailable(Presets.RT_NRD_XESS.key)) {
+ this.presets.add(new PresetEntry(Presets.RT_NRD_XESS.key));
+ }
+ }
+
+ private void applyActivePreset() {
+ presetBlocks.clear();
+ for (ClickableWidget w : presetWidgets) {
+ this.children().remove(w);
+ this.drawables.remove(w);
+ }
+ presetWidgets.clear();
+
+ if (activePreset == null) {
+ return;
+ }
+
+ Pipeline.switchToPresetMode(activePreset.name());
+
+ List modules = new ArrayList<>(Pipeline.INSTANCE.getModules());
+
+ for (Module m : modules) {
+ PresetModuleBlock block = new PresetModuleBlock(m);
+ presetBlocks.add(block);
+ if (m.attributeConfigs == null || m.attributeConfigs.isEmpty()) {
+ continue;
+ }
+ for (AttributeConfig cfg : m.attributeConfigs) {
+ List ws = buildPresetWidgets(cfg);
+ for (ClickableWidget w : ws) {
+ presetWidgets.add(addDrawableChild(w));
+ }
+ block.rows.add(new PresetRow(cfg, ws));
+ }
+ }
+
+ if (secondaryBtn != null && mode == Mode.PRESET) {
+ Text activePresetText = activePreset != null
+ ? Text.translatable(activePreset.name())
+ : Text.literal("N/A");
+ secondaryBtn.setMessage(Text.translatable(RENDER_PIPELINE_PRESET_NAME)
+ .append(Text.literal(": "))
+ .append(activePresetText));
+ }
+ }
+
+ private void syncPresetToPipeline() {
+ Pipeline.savePipeline();
+ Pipeline.build();
+ refreshPipeline();
+ }
+
+ private List buildPresetWidgets(AttributeConfig cfg) {
+ return AttributeWidgetUtil.buildWidgets(cfg, this, textRenderer, 200, 64);
+ }
+
+ private class PresetSelector {
+
+ private final int x, y, width;
+ private final List options;
+ private final int itemHeight = 18;
+
+ public PresetSelector(int x, int y, List presets) {
+ this.x = x;
+ this.y = y;
+ this.options = new ArrayList<>(presets);
+ this.width = 140;
+ }
+
+ public void render(DrawContext ctx, int mouseX, int mouseY) {
+ int currentY = y;
+ ctx.fill(x - 1, y - 1, x + width + 1, y + (options.size() * itemHeight) + 1,
+ 0xFFFFFFFF);
+
+ for (PresetEntry entry : options) {
+ boolean hovered = mouseX >= x && mouseX <= x + width && mouseY >= currentY
+ && mouseY <= currentY + itemHeight;
+ ctx.fill(x, currentY, x + width, currentY + itemHeight,
+ hovered ? 0xFF444444 : 0xFF222222);
+ ctx.drawTextWithShadow(textRenderer, Text.translatable(entry.name()), x + 5,
+ currentY + 5, 0xFFE0E0E0);
+ currentY += itemHeight;
+ }
+ }
+
+ public boolean onClick(double mouseX, double mouseY) {
+ if (mouseX < x || mouseX > x + width || mouseY < y || mouseY > y + (options.size()
+ * itemHeight)) {
+ return false;
+ }
+ int index = (int) ((mouseY - y) / itemHeight);
+ if (index >= 0 && index < options.size()) {
+ activePreset = options.get(index);
+ presetScrollY = 0;
+ Pipeline.switchToPresetMode(activePreset != null ? activePreset.name() : "Default");
+ applyActivePreset();
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private static class PresetModuleBlock {
+
+ private final Module module;
+ private final List rows = new ArrayList<>();
+
+ private PresetModuleBlock(Module module) {
+ this.module = module;
+ }
+ }
+
+ private record PresetRow(AttributeConfig cfg, List widgets) {
+
+ }
}
diff --git a/src/main/java/com/radiance/client/option/EnvironmentRenderStyles.java b/src/main/java/com/radiance/client/option/EnvironmentRenderStyles.java
new file mode 100644
index 0000000..490a3c5
--- /dev/null
+++ b/src/main/java/com/radiance/client/option/EnvironmentRenderStyles.java
@@ -0,0 +1,94 @@
+package com.radiance.client.option;
+
+import com.radiance.client.pipeline.Pipeline;
+import java.util.Objects;
+
+public final class EnvironmentRenderStyles {
+
+ public static final String RAY_TRACING_MODULE = "render_pipeline.module.ray_tracing.name";
+ public static final String WATER_SURFACE_MODE_ATTRIBUTE =
+ "render_pipeline.module.ray_tracing.attribute.water_surface_mode";
+ public static final String CLOUD_VOLUME_MODE_ATTRIBUTE =
+ "render_pipeline.module.ray_tracing.attribute.cloud_volume_mode";
+ public static final float WATER_SURFACE_SENTINEL = 31.25f;
+
+ private EnvironmentRenderStyles() {
+ }
+
+ public static WaterSurfaceMode waterSurfaceMode() {
+ return WaterSurfaceMode.fromKey(Pipeline.getModuleAttributeValue(RAY_TRACING_MODULE,
+ WATER_SURFACE_MODE_ATTRIBUTE, WaterSurfaceMode.SPARKLING.key()));
+ }
+
+ public static CloudVolumeMode cloudVolumeMode() {
+ return CloudVolumeMode.fromKey(Pipeline.getModuleAttributeValue(RAY_TRACING_MODULE,
+ CLOUD_VOLUME_MODE_ATTRIBUTE, CloudVolumeMode.EFFICIENT_VOLUME.key()));
+ }
+
+ public enum WaterSurfaceMode {
+ NATIVE_LIKE("render_pipeline.module.ray_tracing.attribute.water_surface_mode.native_like",
+ 0.0f),
+ SPARKLING("render_pipeline.module.ray_tracing.attribute.water_surface_mode.sparkling",
+ 1.0f);
+
+ private final String key;
+ private final float shaderId;
+
+ WaterSurfaceMode(String key, float shaderId) {
+ this.key = key;
+ this.shaderId = shaderId;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public float shaderId() {
+ return shaderId;
+ }
+
+ public static WaterSurfaceMode fromKey(String value) {
+ for (WaterSurfaceMode mode : values()) {
+ if (Objects.equals(mode.key, value)) {
+ return mode;
+ }
+ }
+ return SPARKLING;
+ }
+ }
+
+ public enum CloudVolumeMode {
+ NATIVE("render_pipeline.module.ray_tracing.attribute.cloud_volume_mode.native", 0.0f),
+ EFFICIENT_VOLUME(
+ "render_pipeline.module.ray_tracing.attribute.cloud_volume_mode.efficient_volume",
+ 1.0f),
+ REALISTIC_VOLUME(
+ "render_pipeline.module.ray_tracing.attribute.cloud_volume_mode.realistic_volume",
+ 2.0f);
+
+ private final String key;
+ private final float shaderId;
+
+ CloudVolumeMode(String key, float shaderId) {
+ this.key = key;
+ this.shaderId = shaderId;
+ }
+
+ public String key() {
+ return key;
+ }
+
+ public float shaderId() {
+ return shaderId;
+ }
+
+ public static CloudVolumeMode fromKey(String value) {
+ for (CloudVolumeMode mode : values()) {
+ if (Objects.equals(mode.key, value)) {
+ return mode;
+ }
+ }
+ return EFFICIENT_VOLUME;
+ }
+ }
+}
diff --git a/src/main/java/com/radiance/client/option/Options.java b/src/main/java/com/radiance/client/option/Options.java
index ee3f99f..eb2ef90 100644
--- a/src/main/java/com/radiance/client/option/Options.java
+++ b/src/main/java/com/radiance/client/option/Options.java
@@ -1,11 +1,16 @@
package com.radiance.client.option;
import com.radiance.client.RadianceClient;
+import com.radiance.client.pipeline.Pipeline;
+import com.radiance.client.pipeline.Presets;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
import java.util.Properties;
public class Options {
@@ -31,12 +36,20 @@ public class Options {
public static final String DLSS_MODE_DLAA = "options.video.dlss_mode.dlaa";
public static final String DLSS_MODE_KEY = "options.video.dlss_mode";
+ public static final String QUALITY_LEVEL_KEY = "options.video.quality_level";
public static final String UPSCALER_TYPE_KEY = "options.video.upscaler_type";
public static final String UPSCALER_QUALITY_KEY = "options.video.upscaler_quality";
public static final String DENOISER_MODE_KEY = "options.video.denoiser_mode";
+ public static final String HDR_OUTPUT_KEY = "options.video.hdr_output";
+ public static final String DLSS_FRAME_GENERATION_KEY = "options.video.dlss_frame_generation";
public static final String RAY_BOUNCES_KEY = "options.video.ray_bounces";
public static final String CHUNK_BUILDING_BATCH_SIZE_KEY = "options.video.chunk_building_batch_size";
public static final String CHUNK_BUILDING_TOTAL_BATCHES_KEY = "options.video.chunk_building_total_batches";
+ public static final String OUTPUT_SCALE_2X_KEY = "options.video.output_scale_2x";
+ public static final String SIMPLIFIED_INDIRECT_KEY = "options.video.simplified_indirect";
+ public static final String REFLEX_ENABLED_KEY = "options.video.reflex_enabled";
+ public static final String REFLEX_BOOST_KEY = "options.video.reflex_boost";
+ public static final String VRR_MODE_KEY = "options.video.vrr_mode";
public static final String PIPELINE_SETUP_KEY = "options.video.pipeline_setup";
public static final String UPSCALER_TYPE_NATIVE = "options.video.upscaler_type.native";
@@ -46,22 +59,202 @@ public class Options {
public static final String UPSCALER_QUALITY_QUALITY = "options.video.upscaler_quality.quality";
public static final String UPSCALER_QUALITY_BALANCED = "options.video.upscaler_quality.balanced";
public static final String UPSCALER_QUALITY_PERFORMANCE = "options.video.upscaler_quality.performance";
+ public static final String QUALITY_LEVEL_FLUENT = "options.video.quality_level.fluent";
+ public static final String QUALITY_LEVEL_PERFORMANCE = "options.video.quality_level.performance";
+ public static final String QUALITY_LEVEL_BALANCED = "options.video.quality_level.balanced";
+ public static final String QUALITY_LEVEL_QUALITY = "options.video.quality_level.quality";
+ public static final String QUALITY_LEVEL_ULTRA = "options.video.quality_level.ultra";
+ public static final String QUALITY_LEVEL_EXTREME = "options.video.quality_level.extreme";
public static final String DENOISER_MODE_DLSS = "options.video.denoiser_mode.dlss";
public static final String DENOISER_MODE_SVGF = "options.video.denoiser_mode.svgf";
public static final String DENOISER_MODE_NRD = "options.video.denoiser_mode.nrd";
public static final String DENOISER_MODE_TEMPORAL = "options.video.denoiser_mode.temporal";
+ public static final String HDR_OUTPUT = "options.video.hdr_output";
+
+ private static final String RAY_TRACING_MODULE = "render_pipeline.module.ray_tracing.name";
+ private static final String DLSS_MODULE = "render_pipeline.module.dlss.name";
+ private static final String FSR3_MODULE = "render_pipeline.module.fsr_upscaler.name";
+ private static final String XESS_MODULE = "render_pipeline.module.xess_sr.name";
+ private static final String RT_TERRAIN_MESHING_MODE =
+ "render_pipeline.module.ray_tracing.attribute.terrain_meshing_mode";
+ private static final String RT_GREEDY_MERGE_MAX_SPAN =
+ "render_pipeline.module.ray_tracing.attribute.greedy_merge_max_span";
+ private static final String RT_FAR_FIELD_GEOMETRY_MODE =
+ "render_pipeline.module.ray_tracing.attribute.far_field_geometry_mode";
+ private static final String RT_FAR_FIELD_START_DISTANCE_CHUNKS =
+ "render_pipeline.module.ray_tracing.attribute.far_field_start_distance_chunks";
+ private static final String RT_FAR_FIELD_MATERIAL_MODE =
+ "render_pipeline.module.ray_tracing.attribute.far_field_material_mode";
+ private static final String RT_GLASS_PATH_MODE =
+ "render_pipeline.module.ray_tracing.attribute.glass_path_mode";
+ private static final String RT_FOLIAGE_PATH_MODE =
+ "render_pipeline.module.ray_tracing.attribute.foliage_path_mode";
+ private static final String RT_DECORATION_PATH_MODE =
+ "render_pipeline.module.ray_tracing.attribute.decoration_path_mode";
+ private static final String RT_BLAS_INCLUSION_MODE =
+ "render_pipeline.module.ray_tracing.attribute.blas_inclusion_mode";
+ private static final String RT_REFLECTION_RAY_MATERIAL_MODE =
+ "render_pipeline.module.ray_tracing.attribute.reflection_ray_material_mode";
+ private static final String RT_DIFFUSE_GI_MODE =
+ "render_pipeline.module.ray_tracing.attribute.diffuse_gi_mode";
+ private static final String RT_CLOUD_VOLUME_MODE =
+ "render_pipeline.module.ray_tracing.attribute.cloud_volume_mode";
+ private static final String RT_TRANSPARENT_SPLIT_MODE =
+ "render_pipeline.module.ray_tracing.attribute.transparent_split_mode";
+ private static final String RT_USE_JITTER =
+ "render_pipeline.module.ray_tracing.attribute.use_jitter";
+ private static final String RT_SEPARATE_ENTITY_TERRAIN_ACCEL_STRUCTURES =
+ "render_pipeline.module.ray_tracing.attribute.separate_entity_terrain_accel_structures";
+ private static final String RT_TERRAIN_UPDATE_INTERVAL_FRAMES =
+ "render_pipeline.module.ray_tracing.attribute.terrain_update_interval_frames";
+ private static final String RT_ENTITY_UPDATE_INTERVAL_FRAMES =
+ "render_pipeline.module.ray_tracing.attribute.entity_update_interval_frames";
+ private static final String RT_BLOCK_ENTITY_UPDATE_INTERVAL_FRAMES =
+ "render_pipeline.module.ray_tracing.attribute.block_entity_update_interval_frames";
+ private static final String RT_PARTICLE_UPDATE_INTERVAL_FRAMES =
+ "render_pipeline.module.ray_tracing.attribute.particle_update_interval_frames";
+ private static final String RT_PARTICLE_CRIT_GLOW =
+ "render_pipeline.module.ray_tracing.attribute.particle_crit_glow";
+ private static final String RT_PARTICLE_DEATH_SMOKE_GLOW =
+ "render_pipeline.module.ray_tracing.attribute.particle_death_smoke_glow";
+ private static final String RT_PARTICLE_CRIT_GLOW_STRENGTH =
+ "render_pipeline.module.ray_tracing.attribute.particle_crit_glow_strength";
+ private static final String RT_PARTICLE_DEATH_SMOKE_GLOW_STRENGTH =
+ "render_pipeline.module.ray_tracing.attribute.particle_death_smoke_glow_strength";
+ private static final String RT_NUM_RAY_BOUNCES =
+ "render_pipeline.module.ray_tracing.attribute.num_ray_bounces";
+ private static final String RT_PBR_SAMPLING_MODE =
+ "render_pipeline.module.ray_tracing.attribute.pbr_sampling_mode";
+ private static final String RT_USE_SHARC =
+ "render_pipeline.module.ray_tracing.attribute.use_sharc";
+ private static final String RT_BASIC_RADIANCE =
+ "render_pipeline.module.ray_tracing.attribute.basic_radiance";
+ private static final String RT_DIRECT_LIGHT_STRENGTH =
+ "render_pipeline.module.ray_tracing.attribute.direct_light_strength";
+ private static final String RT_INDIRECT_LIGHT_STRENGTH =
+ "render_pipeline.module.ray_tracing.attribute.indirect_light_strength";
+ private static final String FSR3_QUALITY_MODE =
+ "render_pipeline.module.fsr_upscaler.attribute.quality_mode";
+ private static final String FSR3_SHARPNESS =
+ "render_pipeline.module.fsr_upscaler.attribute.sharpness";
+ private static final String XESS_QUALITY_MODE =
+ "render_pipeline.module.xess_sr.attribute.quality_mode";
+ private static final String XESS_PRE_EXPOSURE =
+ "render_pipeline.module.xess_sr.attribute.pre_exposure";
+ private static final String DLSS_MODE_ATTRIBUTE =
+ "render_pipeline.module.dlss.attribute.mode";
+ private static final String NRD_MODULE = "render_pipeline.module.nrd.name";
+ private static final String NRD_ANTILAG_LUMINANCE_SIGMA_SCALE =
+ "render_pipeline.module.nrd.attribute.antilag_luminance_sigma_scale";
+ private static final String NRD_ANTILAG_LUMINANCE_SENSITIVITY =
+ "render_pipeline.module.nrd.attribute.antilag_luminance_sensitivity";
+ private static final String NRD_RESPONSIVE_ACCUMULATION_ROUGHNESS_THRESHOLD =
+ "render_pipeline.module.nrd.attribute.responsive_accumulation_roughness_threshold";
+ private static final String NRD_RESPONSIVE_ACCUMULATION_MIN_ACCUMULATED_FRAME_NUM =
+ "render_pipeline.module.nrd.attribute.responsive_accumulation_min_accumulated_frame_num";
+ private static final String NRD_MAX_ACCUMULATED_FRAME_NUM =
+ "render_pipeline.module.nrd.attribute.max_accumulated_frame_num";
+ private static final String NRD_MAX_FAST_ACCUMULATED_FRAME_NUM =
+ "render_pipeline.module.nrd.attribute.max_fast_accumulated_frame_num";
+ private static final String NRD_MAX_STABILIZED_FRAME_NUM =
+ "render_pipeline.module.nrd.attribute.max_stabilized_frame_num";
+ private static final String NRD_HISTORY_FIX_FRAME_NUM =
+ "render_pipeline.module.nrd.attribute.history_fix_frame_num";
+ private static final String NRD_HISTORY_FIX_BASE_PIXEL_STRIDE =
+ "render_pipeline.module.nrd.attribute.history_fix_base_pixel_stride";
+ private static final String NRD_HISTORY_FIX_ALTERNATE_PIXEL_STRIDE =
+ "render_pipeline.module.nrd.attribute.history_fix_alternate_pixel_stride";
+ private static final String NRD_FAST_HISTORY_CLAMPING_SIGMA_SCALE =
+ "render_pipeline.module.nrd.attribute.fast_history_clamping_sigma_scale";
+ private static final String NRD_DIFFUSE_PREPASS_BLUR_RADIUS =
+ "render_pipeline.module.nrd.attribute.diffuse_prepass_blur_radius";
+ private static final String NRD_SPECULAR_PREPASS_BLUR_RADIUS =
+ "render_pipeline.module.nrd.attribute.specular_prepass_blur_radius";
+ private static final String NRD_MIN_HIT_DISTANCE_WEIGHT =
+ "render_pipeline.module.nrd.attribute.min_hit_distance_weight";
+ private static final String NRD_MIN_BLUR_RADIUS =
+ "render_pipeline.module.nrd.attribute.min_blur_radius";
+ private static final String NRD_MAX_BLUR_RADIUS =
+ "render_pipeline.module.nrd.attribute.max_blur_radius";
+ private static final String NRD_LOBE_ANGLE_FRACTION =
+ "render_pipeline.module.nrd.attribute.lobe_angle_fraction";
+ private static final String NRD_ROUGHNESS_FRACTION =
+ "render_pipeline.module.nrd.attribute.roughness_fraction";
+ private static final String NRD_PLANE_DISTANCE_SENSITIVITY =
+ "render_pipeline.module.nrd.attribute.plane_distance_sensitivity";
+ private static final String NRD_SPECULAR_PROBABILITY_THRESHOLD_MIN =
+ "render_pipeline.module.nrd.attribute.specular_probability_thresholds_for_mv_modification_min";
+ private static final String NRD_SPECULAR_PROBABILITY_THRESHOLD_MAX =
+ "render_pipeline.module.nrd.attribute.specular_probability_thresholds_for_mv_modification_max";
+ private static final String NRD_FIREFLY_SUPPRESSOR_MIN_RELATIVE_SCALE =
+ "render_pipeline.module.nrd.attribute.firefly_suppressor_min_relative_scale";
+ private static final String NRD_MIN_MATERIAL_FOR_DIFFUSE =
+ "render_pipeline.module.nrd.attribute.min_material_for_diffuse";
+ private static final String NRD_MIN_MATERIAL_FOR_SPECULAR =
+ "render_pipeline.module.nrd.attribute.min_material_for_specular";
+ private static final String NRD_HIT_DISTANCE_RECONSTRUCTION_MODE =
+ "render_pipeline.module.nrd.attribute.hit_distance_reconstruction_mode";
+ private static final String NRD_ENABLE_ANTI_FIREFLY =
+ "render_pipeline.module.nrd.attribute.enable_anti_firefly";
+ private static final String TONE_MAPPING_MODULE = "render_pipeline.module.tone_mapping.name";
+ private static final String TM_METHOD =
+ "render_pipeline.module.tone_mapping.attribute.method";
+ private static final String TM_MIDDLE_GREY =
+ "render_pipeline.module.tone_mapping.attribute.middle_grey";
+ private static final String TM_EXPOSURE_UP_SPEED =
+ "render_pipeline.module.tone_mapping.attribute.exposure_up_speed";
+ private static final String TM_EXPOSURE_DOWN_SPEED =
+ "render_pipeline.module.tone_mapping.attribute.exposure_down_speed";
+ private static final String TM_LOG2_LUMINANCE_MIN =
+ "render_pipeline.module.tone_mapping.attribute.log2_luminance_min";
+ private static final String TM_LOG2_LUMINANCE_MAX =
+ "render_pipeline.module.tone_mapping.attribute.log2_luminance_max";
+ private static final String TM_LOW_PERCENT =
+ "render_pipeline.module.tone_mapping.attribute.low_percent";
+ private static final String TM_HIGH_PERCENT =
+ "render_pipeline.module.tone_mapping.attribute.high_percent";
+ private static final String TM_MIN_EXPOSURE =
+ "render_pipeline.module.tone_mapping.attribute.min_exposure";
+ private static final String TM_MAX_EXPOSURE =
+ "render_pipeline.module.tone_mapping.attribute.max_exposure";
+ private static final String TM_ENABLE_AUTO_EXPOSURE =
+ "render_pipeline.module.tone_mapping.attribute.enable_auto_exposure";
+ private static final String TM_EXPOSURE_METERING_MODE =
+ "render_pipeline.module.tone_mapping.attribute.exposure_metering_mode";
+ private static final String TM_CENTER_METERING_PERCENT =
+ "render_pipeline.module.tone_mapping.attribute.center_metering_percent";
+ private static final String TM_MANUAL_EXPOSURE =
+ "render_pipeline.module.tone_mapping.attribute.manual_exposure";
+ private static final String TM_EXPOSURE_BIAS =
+ "render_pipeline.module.tone_mapping.attribute.exposure_bias";
+ private static final String TM_SATURATION =
+ "render_pipeline.module.tone_mapping.attribute.saturation";
+ private static final String TM_WHITE_POINT =
+ "render_pipeline.module.tone_mapping.attribute.white_point";
+ private static final String TM_CLAMP_OUTPUT =
+ "render_pipeline.module.tone_mapping.attribute.clamp_output";
public static int maxFps = 260;
public static int inactivityFpsLimit = 260;
public static boolean vsync = true;
+ public static int qualityLevel = QualityLevel.BALANCED.getId();
public static int dlssMode = 1;
public static int upscalerType = 1;
public static int upscalerQuality = 1;
public static int denoiserMode = 1;
+ public static boolean hdrOutput = false;
+ public static boolean dlssFrameGeneration = false;
+ public static boolean outputScale2x = false;
+ public static boolean simplifiedIndirect = false;
+ public static boolean reflexEnabled = false;
+ public static boolean reflexBoost = false;
+ public static boolean vrrMode = false;
public static int rayBounces = 4;
- public static int chunkBuildingBatchSize = 2;
- public static int chunkBuildingTotalBatches = 4;
+ public static int chunkBuildingBatchSize = 14;
+ public static int chunkBuildingTotalBatches = 16;
+ private static final List invalidOptionKeys = new ArrayList<>();
public static void readOptions() {
+ clearInvalidOptionWarnings();
Path path = RadianceClient.radianceDir.resolve(OPTION_PROPERTIES);
if (!Files.exists(path)) {
// System.out.println("Generating default options...");
@@ -73,18 +266,37 @@ public static void readOptions() {
try (InputStream in = Files.newInputStream(path)) {
props.load(in);
- setMaxFps(Integer.parseInt(props.getProperty("maxFps", String.valueOf(maxFps))), false);
- setInactivityFpsLimit(Integer.parseInt(
- props.getProperty("inactivityFpsLimit", String.valueOf(inactivityFpsLimit))),
- false);
- setVsync(Boolean.parseBoolean(props.getProperty("vsync", String.valueOf(vsync))),
- false);
- setChunkBuildingBatchSize(Integer.parseInt(props.getProperty("chunkBuildingBatchSize",
- String.valueOf(chunkBuildingBatchSize))),
- false);
+ setMaxFps(parseIntProperty(props, "maxFps", maxFps), false);
+ setInactivityFpsLimit(parseIntProperty(props, "inactivityFpsLimit",
+ inactivityFpsLimit), false);
+ setVsync(parseBooleanProperty(props, "vsync", vsync), false);
+ setHdrOutput(parseBooleanProperty(props, "hdrOutput", hdrOutput), false);
+ setDlssFrameGeneration(parseBooleanProperty(props, "dlssFrameGeneration",
+ dlssFrameGeneration), false);
+ setOutputScale2x(parseBooleanProperty(props, "outputScale2x", outputScale2x), false);
+ setSimplifiedIndirect(parseBooleanProperty(props, "simplifiedIndirect",
+ simplifiedIndirect), false);
+ setReflexEnabled(parseBooleanProperty(props, "reflexEnabled", reflexEnabled), false);
+ setReflexBoost(parseBooleanProperty(props, "reflexBoost", reflexBoost), false);
+ setVrrMode(parseBooleanProperty(props, "vrrMode", vrrMode), false);
+ qualityLevel = parseIntProperty(props, "qualityLevel", qualityLevel);
+ dlssMode = clamp(parseIntProperty(props, "dlssMode", dlssMode), 0, 3);
+ upscalerType = clamp(parseIntProperty(props, "upscalerType", upscalerType), 0, 1);
+ upscalerQuality = clamp(parseIntProperty(props, "upscalerQuality", upscalerQuality), 0,
+ 3);
+ denoiserMode = clamp(parseIntProperty(props, "denoiserMode", denoiserMode), 0, 3);
+ setRayBounces(parseIntProperty(props, "rayBounces", rayBounces), false);
+ setChunkBuildingBatchSize(parseIntProperty(props, "chunkBuildingBatchSize",
+ chunkBuildingBatchSize), false);
setChunkBuildingTotalBatches(
- Integer.parseInt(props.getProperty("chunkBuildingTotalBatches",
- String.valueOf(chunkBuildingTotalBatches))), false);
+ parseIntProperty(props, "chunkBuildingTotalBatches", chunkBuildingTotalBatches),
+ false);
+
+ if (hasInvalidOptionValues()) {
+ RadianceClient.LOGGER.warn(
+ "Invalid values were found in {} for keys {}. Defaults were applied and the file will be rewritten.",
+ path, invalidOptionKeys);
+ }
overwriteConfig();
// System.out.println("Successfully read options: " + path);
@@ -93,12 +305,63 @@ public static void readOptions() {
}
}
+ public static boolean hasInvalidOptionValues() {
+ return !invalidOptionKeys.isEmpty();
+ }
+
+ public static List getInvalidOptionKeys() {
+ return List.copyOf(invalidOptionKeys);
+ }
+
+ private static void clearInvalidOptionWarnings() {
+ invalidOptionKeys.clear();
+ }
+
+ private static void recordInvalidOption(String key, String rawValue) {
+ if (!invalidOptionKeys.contains(key)) {
+ invalidOptionKeys.add(key);
+ }
+ RadianceClient.LOGGER.warn(
+ "Invalid options.properties value for '{}': '{}'. Falling back to the default value.",
+ key, rawValue);
+ }
+
+ private static int parseIntProperty(Properties props, String key, int fallback) {
+ String rawValue = props.getProperty(key);
+ if (rawValue == null || rawValue.isBlank()) {
+ return fallback;
+ }
+ try {
+ return Integer.parseInt(rawValue.trim());
+ } catch (NumberFormatException e) {
+ recordInvalidOption(key, rawValue);
+ return fallback;
+ }
+ }
+
+ private static boolean parseBooleanProperty(Properties props, String key, boolean fallback) {
+ String rawValue = props.getProperty(key);
+ if (rawValue == null || rawValue.isBlank()) {
+ return fallback;
+ }
+ if ("true".equalsIgnoreCase(rawValue.trim())) {
+ return true;
+ }
+ if ("false".equalsIgnoreCase(rawValue.trim())) {
+ return false;
+ }
+ recordInvalidOption(key, rawValue);
+ return fallback;
+ }
+
public static void overwriteConfig() {
Path path = RadianceClient.radianceDir.resolve(OPTION_PROPERTIES);
Properties props = new Properties();
props.setProperty("maxFps", String.valueOf(maxFps));
props.setProperty("inactivityFpsLimit", String.valueOf(inactivityFpsLimit));
props.setProperty("vsync", String.valueOf(vsync));
+ props.setProperty("hdrOutput", String.valueOf(hdrOutput));
+ props.setProperty("qualityLevel", String.valueOf(qualityLevel));
props.setProperty("dlssMode", String.valueOf(dlssMode));
props.setProperty("upscalerType", String.valueOf(upscalerType));
props.setProperty("upscalerQuality", String.valueOf(upscalerQuality));
@@ -106,6 +369,12 @@ public static void overwriteConfig() {
props.setProperty("rayBounces", String.valueOf(rayBounces));
props.setProperty("chunkBuildingBatchSize", String.valueOf(chunkBuildingBatchSize));
props.setProperty("chunkBuildingTotalBatches", String.valueOf(chunkBuildingTotalBatches));
+ props.setProperty("dlssFrameGeneration", String.valueOf(dlssFrameGeneration));
+ props.setProperty("outputScale2x", String.valueOf(outputScale2x));
+ props.setProperty("simplifiedIndirect", String.valueOf(simplifiedIndirect));
+ props.setProperty("reflexEnabled", String.valueOf(reflexEnabled));
+ props.setProperty("reflexBoost", String.valueOf(reflexBoost));
+ props.setProperty("vrrMode", String.valueOf(vrrMode));
try {
Files.createDirectories(path.getParent());
@@ -172,4 +441,871 @@ public static void setChunkBuildingTotalBatches(int chunkBuildingTotalBatches, b
overwriteConfig();
}
}
+
+ public native static void nativeSetHdrOutput(boolean hdrOutput, boolean write);
+
+ public static void setHdrOutput(boolean hdrOutput, boolean write) {
+ Options.hdrOutput = hdrOutput;
+ nativeSetHdrOutput(hdrOutput, write);
+ if (write) {
+ overwriteConfig();
+ }
+ }
+
+ public native static void nativeSetDlssFrameGeneration(boolean dlssFrameGeneration, boolean write);
+ public native static boolean nativeHasDlssFrameGenerationAvailable();
+ public native static void nativeSetOutputScale2x(boolean enabled, boolean write);
+ public native static void nativeSetSimplifiedIndirect(boolean enabled, boolean write);
+ public native static void nativeSetReflexEnabled(boolean enabled, boolean write);
+ public native static void nativeSetReflexBoost(boolean enabled, boolean write);
+ public native static boolean nativeIsReflexSupported();
+ public native static void nativeSetVrrMode(boolean enabled, boolean write);
+ public native static int nativeGetDisplayRefreshRate();
+
+ public static void setDlssFrameGeneration(boolean dlssFrameGeneration, boolean write) {
+ if (write && dlssFrameGeneration && !nativeHasDlssFrameGenerationAvailable()) {
+ RadianceClient.LOGGER.warn(
+ "DLSS Frame Generation was requested, but the current system/runtime does not expose it. Keeping the option disabled.");
+ dlssFrameGeneration = false;
+ }
+ Options.dlssFrameGeneration = dlssFrameGeneration;
+ nativeSetDlssFrameGeneration(dlssFrameGeneration, write);
+ if (write) {
+ overwriteConfig();
+ }
+ }
+
+ public static void setOutputScale2x(boolean enabled, boolean write) {
+ outputScale2x = enabled;
+ nativeSetOutputScale2x(enabled, write);
+ if (write) {
+ overwriteConfig();
+ }
+ }
+
+ public static void setSimplifiedIndirect(boolean enabled, boolean write) {
+ simplifiedIndirect = enabled;
+ nativeSetSimplifiedIndirect(enabled, write);
+ if (write) {
+ overwriteConfig();
+ }
+ }
+
+ public static boolean isReflexSupported() {
+ try {
+ return nativeIsReflexSupported();
+ } catch (UnsatisfiedLinkError e) {
+ return false;
+ }
+ }
+
+ public static void setReflexEnabled(boolean enabled, boolean write) {
+ if (write && enabled && !isReflexSupported()) {
+ RadianceClient.LOGGER.warn(
+ "NVIDIA Reflex was requested, but the current runtime does not expose Streamline Reflex support. Keeping the option disabled.");
+ enabled = false;
+ }
+ reflexEnabled = enabled;
+ nativeSetReflexEnabled(enabled, write);
+ if (write) {
+ overwriteConfig();
+ }
+ }
+
+ public static void setReflexBoost(boolean enabled, boolean write) {
+ reflexBoost = enabled;
+ nativeSetReflexBoost(enabled, write);
+ if (write) {
+ overwriteConfig();
+ }
+ }
+
+ public static void setVrrMode(boolean enabled, boolean write) {
+ vrrMode = enabled;
+ nativeSetVrrMode(enabled, write);
+ if (write) {
+ overwriteConfig();
+ }
+ }
+
+ public static int getDisplayRefreshRate() {
+ try {
+ return nativeGetDisplayRefreshRate();
+ } catch (UnsatisfiedLinkError e) {
+ return 0;
+ }
+ }
+
+ public native static void nativeSetRayBounces(int rayBounces, boolean write);
+
+ public static void setRayBounces(int rayBounces, boolean write) {
+ Options.rayBounces = rayBounces;
+ nativeSetRayBounces(rayBounces, write);
+ if (write) {
+ overwriteConfig();
+ }
+ }
+
+ public static void setDlssMode(int dlssMode, boolean write) {
+ Options.dlssMode = Math.max(0, Math.min(3, dlssMode));
+ String mappedMode = switch (Options.dlssMode) {
+ case 0 -> "render_pipeline.module.dlss.attribute.mode.performance";
+ case 1 -> "render_pipeline.module.dlss.attribute.mode.balanced";
+ case 2 -> "render_pipeline.module.dlss.attribute.mode.quality";
+ default -> "render_pipeline.module.dlss.attribute.mode.dlaa";
+ };
+ setAttr(DLSS_MODULE, DLSS_MODE_ATTRIBUTE, mappedMode);
+ if (write) {
+ overwriteConfig();
+ }
+ }
+
+ public static void applyQualityProfile(boolean rebuildPipeline) {
+ applyQualityProfile(QualityLevel.fromId(qualityLevel), rebuildPipeline, false);
+ }
+
+ public static void setQualityLevel(QualityLevel qualityLevel, boolean write) {
+ applyQualityProfile(qualityLevel, true, write);
+ }
+
+ private static void applyQualityProfile(QualityLevel level, boolean rebuildPipeline,
+ boolean writeOptions) {
+ QualityLevel resolvedLevel = level == null ? QualityLevel.BALANCED : level;
+ Options.qualityLevel = resolvedLevel.getId();
+
+ boolean pipelineChanged = false;
+ if (Pipeline.INSTANCE.getModuleEntries() != null) {
+ boolean shouldSwitchPreset = writeOptions || Pipeline.INSTANCE.getModules().isEmpty();
+ if (shouldSwitchPreset) {
+ pipelineChanged |= preparePresetForQuality(resolvedLevel);
+ }
+ if (!Pipeline.INSTANCE.getModules().isEmpty()) {
+ pipelineChanged |= applyPipelineQualityProfile(resolvedLevel);
+ }
+ }
+
+ if (writeOptions) {
+ overwriteConfig();
+ }
+ if (pipelineChanged && rebuildPipeline) {
+ Pipeline.savePipeline();
+ Pipeline.build();
+ }
+ }
+
+ private static boolean applyPipelineQualityProfile(QualityLevel level) {
+ return switch (level) {
+ case FLUENT -> applyFluentQualityProfile();
+ case PERFORMANCE -> applyPerformanceQualityProfile();
+ case BALANCED -> applyBalancedQualityProfile();
+ case HIGH -> applyHighQualityProfile();
+ case ULTRA -> applyUltraQualityProfile();
+ case EXTREME -> applyExtremeQualityProfile();
+ };
+ }
+
+ private static boolean preparePresetForQuality(QualityLevel level) {
+ String presetName = selectPresetForQuality(level);
+ if (presetName == null) {
+ return false;
+ }
+
+ String activePreset = Pipeline.processPresetName(Pipeline.getActivePreset());
+ if (Pipeline.getPipelineMode() == Pipeline.PipelineMode.PRESET
+ && Objects.equals(activePreset, presetName)
+ && !Pipeline.INSTANCE.getModules().isEmpty()) {
+ return false;
+ }
+
+ Pipeline.preparePresetMode(presetName);
+ return true;
+ }
+
+ private static String selectPresetForQuality(QualityLevel level) {
+ return switch (level) {
+ case HIGH, ULTRA, EXTREME -> firstAvailablePreset(
+ Presets.RT_DLSSRR.key,
+ Presets.RT_NRD_XESS.key,
+ Presets.RT_NRD_FSR.key,
+ Presets.RT_NRD.key);
+ default -> firstAvailablePreset(
+ Presets.RT_NRD_FSR.key,
+ Presets.RT_NRD_XESS.key,
+ Presets.RT_NRD.key,
+ Presets.RT_DLSSRR.key);
+ };
+ }
+
+ public static String getPreferredPresetForCurrentQuality() {
+ return selectPresetForQuality(QualityLevel.fromId(qualityLevel));
+ }
+
+ public static boolean shouldUseDlssPresetForCurrentQuality() {
+ return Objects.equals(getPreferredPresetForCurrentQuality(), Presets.RT_DLSSRR.key);
+ }
+
+ private static String firstAvailablePreset(String... presetNames) {
+ for (String presetName : presetNames) {
+ if (Pipeline.isPresetAvailable(presetName)) {
+ return presetName;
+ }
+ }
+ return null;
+ }
+
+ private static boolean applyFluentQualityProfile() {
+ setRayBounces(2, false);
+ setChunkBuildingBatchSize(18, false);
+ setChunkBuildingTotalBatches(20, false);
+ dlssMode = 1;
+ upscalerQuality = 3;
+ denoiserMode = 2;
+
+ boolean changed = false;
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_MESHING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.terrain_meshing_mode.coplanar_merge");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GREEDY_MERGE_MAX_SPAN, "24");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_GEOMETRY_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_geometry_mode.simplified_shell");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_START_DISTANCE_CHUNKS, "32");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_material_mode.flat_surface");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GLASS_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FOLIAGE_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DECORATION_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLAS_INCLUSION_MODE,
+ "render_pipeline.module.ray_tracing.attribute.blas_inclusion_mode.opaque_only");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_REFLECTION_RAY_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.reflection_ray_material_mode.water_glass_metal");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIFFUSE_GI_MODE,
+ "render_pipeline.module.ray_tracing.attribute.diffuse_gi_mode.low_cost_hybrid");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_CLOUD_VOLUME_MODE,
+ "render_pipeline.module.ray_tracing.attribute.cloud_volume_mode.native");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_UPDATE_INTERVAL_FRAMES, "4");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_ENTITY_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLOCK_ENTITY_UPDATE_INTERVAL_FRAMES, "2");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_UPDATE_INTERVAL_FRAMES, "3");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TRANSPARENT_SPLIT_MODE,
+ "render_pipeline.module.ray_tracing.attribute.transparent_split_mode.deterministic");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW_STRENGTH, "0.42");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW_STRENGTH, "0.20");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_NUM_RAY_BOUNCES, "2");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_JITTER, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PBR_SAMPLING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.pbr_sampling.bilinear");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_SHARC, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_SEPARATE_ENTITY_TERRAIN_ACCEL_STRUCTURES,
+ "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BASIC_RADIANCE, "5.8");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIRECT_LIGHT_STRENGTH, "1.0");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_INDIRECT_LIGHT_STRENGTH, "16.5");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SIGMA_SCALE, "4.6");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SENSITIVITY, "3.6");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_ROUGHNESS_THRESHOLD, "0.16");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_MIN_ACCUMULATED_FRAME_NUM, "1");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_ACCUMULATED_FRAME_NUM, "24");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_FAST_ACCUMULATED_FRAME_NUM, "1");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_STABILIZED_FRAME_NUM, "28");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_FRAME_NUM, "1");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_BASE_PIXEL_STRIDE, "10");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_ALTERNATE_PIXEL_STRIDE, "12");
+ changed |= setAttr(NRD_MODULE, NRD_FAST_HISTORY_CLAMPING_SIGMA_SCALE, "1.35");
+ changed |= setAttr(NRD_MODULE, NRD_DIFFUSE_PREPASS_BLUR_RADIUS, "18.0");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PREPASS_BLUR_RADIUS, "26.0");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_HIT_DISTANCE_WEIGHT, "0.12");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_BLUR_RADIUS, "1.2");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_BLUR_RADIUS, "48.0");
+ changed |= setAttr(NRD_MODULE, NRD_LOBE_ANGLE_FRACTION, "0.19");
+ changed |= setAttr(NRD_MODULE, NRD_ROUGHNESS_FRACTION, "0.18");
+ changed |= setAttr(NRD_MODULE, NRD_PLANE_DISTANCE_SENSITIVITY, "0.040");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MIN, "0.44");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MAX, "0.82");
+ changed |= setAttr(NRD_MODULE, NRD_FIREFLY_SUPPRESSOR_MIN_RELATIVE_SCALE, "2.4");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_DIFFUSE, "5.0");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_SPECULAR, "5.0");
+ changed |= setAttr(NRD_MODULE, NRD_HIT_DISTANCE_RECONSTRUCTION_MODE, "5x5");
+ changed |= setAttr(NRD_MODULE, NRD_ENABLE_ANTI_FIREFLY, "render_pipeline.true");
+ changed |= setAttr(FSR3_MODULE, FSR3_QUALITY_MODE,
+ "render_pipeline.module.fsr_upscaler.attribute.quality_mode.performance");
+ changed |= setAttr(FSR3_MODULE, FSR3_SHARPNESS, "0.60");
+ changed |= setAttr(XESS_MODULE, XESS_QUALITY_MODE,
+ "render_pipeline.module.xess_sr.attribute.quality_mode.performance");
+ changed |= setAttr(XESS_MODULE, XESS_PRE_EXPOSURE, "1.00");
+ changed |= setAttr(DLSS_MODULE, DLSS_MODE_ATTRIBUTE,
+ "render_pipeline.module.dlss.attribute.mode.performance");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_METHOD,
+ "render_pipeline.module.tone_mapping.attribute.method.pbr_neutral");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIDDLE_GREY, "0.20");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_UP_SPEED, "9.5");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_DOWN_SPEED, "8.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MIN, "-11.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MAX, "5.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOW_PERCENT, "0.010");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_HIGH_PERCENT, "0.985");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIN_EXPOSURE, "0.03");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MAX_EXPOSURE, "1.7");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_ENABLE_AUTO_EXPOSURE, "render_pipeline.true");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_METERING_MODE,
+ "render_pipeline.module.tone_mapping.attribute.exposure_metering_mode.global");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CENTER_METERING_PERCENT, "26.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MANUAL_EXPOSURE, "1.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_BIAS, "-0.10");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_SATURATION, "0.97");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_WHITE_POINT, "24.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CLAMP_OUTPUT, "render_pipeline.true");
+ return changed;
+ }
+
+ private static boolean applyPerformanceQualityProfile() {
+ setRayBounces(3, false);
+ setChunkBuildingBatchSize(18, false);
+ setChunkBuildingTotalBatches(20, false);
+ dlssMode = 0;
+ upscalerQuality = 2;
+ denoiserMode = 2;
+
+ boolean changed = false;
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_MESHING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.terrain_meshing_mode.coplanar_merge");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GREEDY_MERGE_MAX_SPAN, "28");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_GEOMETRY_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_geometry_mode.simplified_shell");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_START_DISTANCE_CHUNKS, "40");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_material_mode.flat_surface");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GLASS_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FOLIAGE_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DECORATION_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLAS_INCLUSION_MODE,
+ "render_pipeline.module.ray_tracing.attribute.blas_inclusion_mode.opaque_and_shadow");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_REFLECTION_RAY_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.reflection_ray_material_mode.water_glass_metal");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIFFUSE_GI_MODE,
+ "render_pipeline.module.ray_tracing.attribute.diffuse_gi_mode.low_cost_hybrid");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_CLOUD_VOLUME_MODE,
+ "render_pipeline.module.ray_tracing.attribute.cloud_volume_mode.efficient_volume");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_UPDATE_INTERVAL_FRAMES, "3");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_ENTITY_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLOCK_ENTITY_UPDATE_INTERVAL_FRAMES, "2");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_UPDATE_INTERVAL_FRAMES, "2");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TRANSPARENT_SPLIT_MODE,
+ "render_pipeline.module.ray_tracing.attribute.transparent_split_mode.deterministic");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW_STRENGTH, "0.50");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW_STRENGTH, "0.26");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_NUM_RAY_BOUNCES, "3");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_JITTER, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PBR_SAMPLING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.pbr_sampling.bilinear");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_SHARC, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_SEPARATE_ENTITY_TERRAIN_ACCEL_STRUCTURES,
+ "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BASIC_RADIANCE, "5.55");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIRECT_LIGHT_STRENGTH, "1.0");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_INDIRECT_LIGHT_STRENGTH, "17.0");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SIGMA_SCALE, "4.2");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SENSITIVITY, "3.3");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_ROUGHNESS_THRESHOLD, "0.12");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_MIN_ACCUMULATED_FRAME_NUM, "1");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_ACCUMULATED_FRAME_NUM, "32");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_FAST_ACCUMULATED_FRAME_NUM, "1");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_STABILIZED_FRAME_NUM, "36");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_FRAME_NUM, "1");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_BASE_PIXEL_STRIDE, "12");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_ALTERNATE_PIXEL_STRIDE, "12");
+ changed |= setAttr(NRD_MODULE, NRD_FAST_HISTORY_CLAMPING_SIGMA_SCALE, "1.40");
+ changed |= setAttr(NRD_MODULE, NRD_DIFFUSE_PREPASS_BLUR_RADIUS, "20.0");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PREPASS_BLUR_RADIUS, "30.0");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_HIT_DISTANCE_WEIGHT, "0.11");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_BLUR_RADIUS, "1.1");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_BLUR_RADIUS, "60.0");
+ changed |= setAttr(NRD_MODULE, NRD_LOBE_ANGLE_FRACTION, "0.18");
+ changed |= setAttr(NRD_MODULE, NRD_ROUGHNESS_FRACTION, "0.17");
+ changed |= setAttr(NRD_MODULE, NRD_PLANE_DISTANCE_SENSITIVITY, "0.034");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MIN, "0.46");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MAX, "0.84");
+ changed |= setAttr(NRD_MODULE, NRD_FIREFLY_SUPPRESSOR_MIN_RELATIVE_SCALE, "2.3");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_DIFFUSE, "4.5");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_SPECULAR, "4.5");
+ changed |= setAttr(NRD_MODULE, NRD_HIT_DISTANCE_RECONSTRUCTION_MODE, "5x5");
+ changed |= setAttr(NRD_MODULE, NRD_ENABLE_ANTI_FIREFLY, "render_pipeline.true");
+ changed |= setAttr(FSR3_MODULE, FSR3_QUALITY_MODE,
+ "render_pipeline.module.fsr_upscaler.attribute.quality_mode.balanced");
+ changed |= setAttr(FSR3_MODULE, FSR3_SHARPNESS, "0.66");
+ changed |= setAttr(XESS_MODULE, XESS_QUALITY_MODE,
+ "render_pipeline.module.xess_sr.attribute.quality_mode.balanced");
+ changed |= setAttr(XESS_MODULE, XESS_PRE_EXPOSURE, "1.00");
+ changed |= setAttr(DLSS_MODULE, DLSS_MODE_ATTRIBUTE,
+ "render_pipeline.module.dlss.attribute.mode.performance");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_METHOD,
+ "render_pipeline.module.tone_mapping.attribute.method.pbr_neutral");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIDDLE_GREY, "0.19");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_UP_SPEED, "9.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_DOWN_SPEED, "8.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MIN, "-11.5");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MAX, "5.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOW_PERCENT, "0.008");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_HIGH_PERCENT, "0.988");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIN_EXPOSURE, "0.025");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MAX_EXPOSURE, "1.9");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_ENABLE_AUTO_EXPOSURE, "render_pipeline.true");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_METERING_MODE,
+ "render_pipeline.module.tone_mapping.attribute.exposure_metering_mode.global");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CENTER_METERING_PERCENT, "24.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MANUAL_EXPOSURE, "1.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_BIAS, "-0.05");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_SATURATION, "0.99");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_WHITE_POINT, "26.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CLAMP_OUTPUT, "render_pipeline.true");
+ return changed;
+ }
+
+ private static boolean applyBalancedQualityProfile() {
+ setRayBounces(4, false);
+ setChunkBuildingBatchSize(16, false);
+ setChunkBuildingTotalBatches(18, false);
+ dlssMode = 1;
+ upscalerQuality = 1;
+ denoiserMode = 2;
+
+ boolean changed = false;
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_MESHING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.terrain_meshing_mode.coplanar_merge");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GREEDY_MERGE_MAX_SPAN, "30");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_GEOMETRY_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_geometry_mode.exact_chunks");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_START_DISTANCE_CHUNKS, "48");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_material_mode.full_pbr");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GLASS_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FOLIAGE_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DECORATION_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLAS_INCLUSION_MODE,
+ "render_pipeline.module.ray_tracing.attribute.blas_inclusion_mode.opaque_and_shadow");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_REFLECTION_RAY_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.reflection_ray_material_mode.water_glass_metal");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIFFUSE_GI_MODE,
+ "render_pipeline.module.ray_tracing.attribute.diffuse_gi_mode.low_cost_hybrid");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_CLOUD_VOLUME_MODE,
+ "render_pipeline.module.ray_tracing.attribute.cloud_volume_mode.efficient_volume");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_ENTITY_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLOCK_ENTITY_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TRANSPARENT_SPLIT_MODE,
+ "render_pipeline.module.ray_tracing.attribute.transparent_split_mode.deterministic");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW_STRENGTH, "0.60");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW_STRENGTH, "0.34");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_NUM_RAY_BOUNCES, "4");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_JITTER, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PBR_SAMPLING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.pbr_sampling.bilinear");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_SHARC, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_SEPARATE_ENTITY_TERRAIN_ACCEL_STRUCTURES,
+ "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BASIC_RADIANCE, "5.2");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIRECT_LIGHT_STRENGTH, "1.0");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_INDIRECT_LIGHT_STRENGTH, "17.6");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SIGMA_SCALE, "3.9");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SENSITIVITY, "3.1");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_ROUGHNESS_THRESHOLD, "0.06");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_MIN_ACCUMULATED_FRAME_NUM, "2");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_ACCUMULATED_FRAME_NUM, "64");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_FAST_ACCUMULATED_FRAME_NUM, "4");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_STABILIZED_FRAME_NUM, "68");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_FRAME_NUM, "2");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_BASE_PIXEL_STRIDE, "14");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_ALTERNATE_PIXEL_STRIDE, "14");
+ changed |= setAttr(NRD_MODULE, NRD_FAST_HISTORY_CLAMPING_SIGMA_SCALE, "1.7");
+ changed |= setAttr(NRD_MODULE, NRD_DIFFUSE_PREPASS_BLUR_RADIUS, "26.0");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PREPASS_BLUR_RADIUS, "42.0");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_HIT_DISTANCE_WEIGHT, "0.10");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_BLUR_RADIUS, "1.0");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_BLUR_RADIUS, "92.0");
+ changed |= setAttr(NRD_MODULE, NRD_LOBE_ANGLE_FRACTION, "0.17");
+ changed |= setAttr(NRD_MODULE, NRD_ROUGHNESS_FRACTION, "0.16");
+ changed |= setAttr(NRD_MODULE, NRD_PLANE_DISTANCE_SENSITIVITY, "0.023");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MIN, "0.48");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MAX, "0.86");
+ changed |= setAttr(NRD_MODULE, NRD_FIREFLY_SUPPRESSOR_MIN_RELATIVE_SCALE, "2.2");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_DIFFUSE, "4.0");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_SPECULAR, "4.0");
+ changed |= setAttr(NRD_MODULE, NRD_HIT_DISTANCE_RECONSTRUCTION_MODE, "5x5");
+ changed |= setAttr(NRD_MODULE, NRD_ENABLE_ANTI_FIREFLY, "render_pipeline.true");
+ changed |= setAttr(FSR3_MODULE, FSR3_QUALITY_MODE,
+ "render_pipeline.module.fsr_upscaler.attribute.quality_mode.quality");
+ changed |= setAttr(FSR3_MODULE, FSR3_SHARPNESS, "0.74");
+ changed |= setAttr(XESS_MODULE, XESS_QUALITY_MODE,
+ "render_pipeline.module.xess_sr.attribute.quality_mode.quality");
+ changed |= setAttr(XESS_MODULE, XESS_PRE_EXPOSURE, "1.02");
+ changed |= setAttr(DLSS_MODULE, DLSS_MODE_ATTRIBUTE,
+ "render_pipeline.module.dlss.attribute.mode.balanced");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_METHOD,
+ "render_pipeline.module.tone_mapping.attribute.method.pbr_neutral");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIDDLE_GREY, "0.18");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_UP_SPEED, "8.5");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_DOWN_SPEED, "7.5");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MIN, "-12.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MAX, "4.5");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOW_PERCENT, "0.006");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_HIGH_PERCENT, "0.990");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIN_EXPOSURE, "0.02");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MAX_EXPOSURE, "2.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_ENABLE_AUTO_EXPOSURE, "render_pipeline.true");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_METERING_MODE,
+ "render_pipeline.module.tone_mapping.attribute.exposure_metering_mode.center");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CENTER_METERING_PERCENT, "22.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MANUAL_EXPOSURE, "1.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_BIAS, "0.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_SATURATION, "1.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_WHITE_POINT, "29.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CLAMP_OUTPUT, "render_pipeline.true");
+ return changed;
+ }
+
+ private static boolean applyHighQualityProfile() {
+ setRayBounces(6, false);
+ setChunkBuildingBatchSize(14, false);
+ setChunkBuildingTotalBatches(16, false);
+ dlssMode = 2;
+ upscalerQuality = 0;
+ denoiserMode = 2;
+
+ boolean changed = false;
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_MESHING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.terrain_meshing_mode.coplanar_merge");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GREEDY_MERGE_MAX_SPAN, "34");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_GEOMETRY_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_geometry_mode.exact_chunks");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_START_DISTANCE_CHUNKS, "64");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_material_mode.full_pbr");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GLASS_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FOLIAGE_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DECORATION_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLAS_INCLUSION_MODE,
+ "render_pipeline.module.ray_tracing.attribute.blas_inclusion_mode.opaque_and_shadow");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_REFLECTION_RAY_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.reflection_ray_material_mode.all_materials");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIFFUSE_GI_MODE,
+ "render_pipeline.module.ray_tracing.attribute.diffuse_gi_mode.full_ray_tracing");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_CLOUD_VOLUME_MODE,
+ "render_pipeline.module.ray_tracing.attribute.cloud_volume_mode.realistic_volume");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_UPDATE_INTERVAL_FRAMES, "2");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_ENTITY_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLOCK_ENTITY_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TRANSPARENT_SPLIT_MODE,
+ "render_pipeline.module.ray_tracing.attribute.transparent_split_mode.stochastic");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW_STRENGTH, "0.72");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW_STRENGTH, "0.44");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_NUM_RAY_BOUNCES, "6");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_JITTER, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PBR_SAMPLING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.pbr_sampling.bilinear");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_SHARC, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_SEPARATE_ENTITY_TERRAIN_ACCEL_STRUCTURES,
+ "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BASIC_RADIANCE, "4.95");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIRECT_LIGHT_STRENGTH, "1.0");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_INDIRECT_LIGHT_STRENGTH, "18.6");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SIGMA_SCALE, "3.6");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SENSITIVITY, "2.8");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_ROUGHNESS_THRESHOLD, "0.04");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_MIN_ACCUMULATED_FRAME_NUM, "3");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_ACCUMULATED_FRAME_NUM, "78");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_FAST_ACCUMULATED_FRAME_NUM, "5");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_STABILIZED_FRAME_NUM, "82");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_FRAME_NUM, "3");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_BASE_PIXEL_STRIDE, "12");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_ALTERNATE_PIXEL_STRIDE, "12");
+ changed |= setAttr(NRD_MODULE, NRD_FAST_HISTORY_CLAMPING_SIGMA_SCALE, "1.6");
+ changed |= setAttr(NRD_MODULE, NRD_DIFFUSE_PREPASS_BLUR_RADIUS, "18.0");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PREPASS_BLUR_RADIUS, "30.0");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_HIT_DISTANCE_WEIGHT, "0.09");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_BLUR_RADIUS, "0.9");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_BLUR_RADIUS, "72.0");
+ changed |= setAttr(NRD_MODULE, NRD_LOBE_ANGLE_FRACTION, "0.16");
+ changed |= setAttr(NRD_MODULE, NRD_ROUGHNESS_FRACTION, "0.15");
+ changed |= setAttr(NRD_MODULE, NRD_PLANE_DISTANCE_SENSITIVITY, "0.020");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MIN, "0.50");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MAX, "0.88");
+ changed |= setAttr(NRD_MODULE, NRD_FIREFLY_SUPPRESSOR_MIN_RELATIVE_SCALE, "2.0");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_DIFFUSE, "3.5");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_SPECULAR, "3.5");
+ changed |= setAttr(NRD_MODULE, NRD_HIT_DISTANCE_RECONSTRUCTION_MODE, "5x5");
+ changed |= setAttr(NRD_MODULE, NRD_ENABLE_ANTI_FIREFLY, "render_pipeline.true");
+ changed |= setAttr(FSR3_MODULE, FSR3_QUALITY_MODE,
+ "render_pipeline.module.fsr_upscaler.attribute.quality_mode.native");
+ changed |= setAttr(FSR3_MODULE, FSR3_SHARPNESS, "0.80");
+ changed |= setAttr(XESS_MODULE, XESS_QUALITY_MODE,
+ "render_pipeline.module.xess_sr.attribute.quality_mode.ultra_quality");
+ changed |= setAttr(XESS_MODULE, XESS_PRE_EXPOSURE, "1.03");
+ changed |= setAttr(DLSS_MODULE, DLSS_MODE_ATTRIBUTE,
+ "render_pipeline.module.dlss.attribute.mode.quality");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_METHOD,
+ "render_pipeline.module.tone_mapping.attribute.method.pbr_neutral");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIDDLE_GREY, "0.18");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_UP_SPEED, "7.5");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_DOWN_SPEED, "6.8");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MIN, "-12.5");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MAX, "4.2");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOW_PERCENT, "0.004");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_HIGH_PERCENT, "0.992");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIN_EXPOSURE, "0.015");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MAX_EXPOSURE, "2.2");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_ENABLE_AUTO_EXPOSURE, "render_pipeline.true");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_METERING_MODE,
+ "render_pipeline.module.tone_mapping.attribute.exposure_metering_mode.center");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CENTER_METERING_PERCENT, "20.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MANUAL_EXPOSURE, "1.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_BIAS, "0.04");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_SATURATION, "1.02");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_WHITE_POINT, "32.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CLAMP_OUTPUT, "render_pipeline.true");
+ return changed;
+ }
+
+ private static boolean applyUltraQualityProfile() {
+ setRayBounces(8, false);
+ setChunkBuildingBatchSize(12, false);
+ setChunkBuildingTotalBatches(14, false);
+ dlssMode = 2;
+ upscalerQuality = 0;
+ denoiserMode = 2;
+
+ boolean changed = false;
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_MESHING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.terrain_meshing_mode.coplanar_merge");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GREEDY_MERGE_MAX_SPAN, "38");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_GEOMETRY_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_geometry_mode.exact_chunks");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_START_DISTANCE_CHUNKS, "72");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_material_mode.full_pbr");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GLASS_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FOLIAGE_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DECORATION_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLAS_INCLUSION_MODE,
+ "render_pipeline.module.ray_tracing.attribute.blas_inclusion_mode.all_geometry");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_REFLECTION_RAY_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.reflection_ray_material_mode.all_materials");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIFFUSE_GI_MODE,
+ "render_pipeline.module.ray_tracing.attribute.diffuse_gi_mode.full_ray_tracing");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_CLOUD_VOLUME_MODE,
+ "render_pipeline.module.ray_tracing.attribute.cloud_volume_mode.realistic_volume");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_ENTITY_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLOCK_ENTITY_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TRANSPARENT_SPLIT_MODE,
+ "render_pipeline.module.ray_tracing.attribute.transparent_split_mode.stochastic");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW_STRENGTH, "0.82");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW_STRENGTH, "0.54");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_NUM_RAY_BOUNCES, "8");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_JITTER, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PBR_SAMPLING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.pbr_sampling.bilinear");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_SHARC, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_SEPARATE_ENTITY_TERRAIN_ACCEL_STRUCTURES,
+ "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BASIC_RADIANCE, "4.8");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIRECT_LIGHT_STRENGTH, "1.0");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_INDIRECT_LIGHT_STRENGTH, "19.5");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SIGMA_SCALE, "3.3");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SENSITIVITY, "2.6");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_ROUGHNESS_THRESHOLD, "0.03");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_MIN_ACCUMULATED_FRAME_NUM, "3");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_ACCUMULATED_FRAME_NUM, "90");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_FAST_ACCUMULATED_FRAME_NUM, "6");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_STABILIZED_FRAME_NUM, "94");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_FRAME_NUM, "3");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_BASE_PIXEL_STRIDE, "10");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_ALTERNATE_PIXEL_STRIDE, "10");
+ changed |= setAttr(NRD_MODULE, NRD_FAST_HISTORY_CLAMPING_SIGMA_SCALE, "1.5");
+ changed |= setAttr(NRD_MODULE, NRD_DIFFUSE_PREPASS_BLUR_RADIUS, "14.0");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PREPASS_BLUR_RADIUS, "24.0");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_HIT_DISTANCE_WEIGHT, "0.08");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_BLUR_RADIUS, "0.8");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_BLUR_RADIUS, "60.0");
+ changed |= setAttr(NRD_MODULE, NRD_LOBE_ANGLE_FRACTION, "0.15");
+ changed |= setAttr(NRD_MODULE, NRD_ROUGHNESS_FRACTION, "0.14");
+ changed |= setAttr(NRD_MODULE, NRD_PLANE_DISTANCE_SENSITIVITY, "0.018");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MIN, "0.52");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MAX, "0.90");
+ changed |= setAttr(NRD_MODULE, NRD_FIREFLY_SUPPRESSOR_MIN_RELATIVE_SCALE, "1.9");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_DIFFUSE, "3.0");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_SPECULAR, "3.0");
+ changed |= setAttr(NRD_MODULE, NRD_HIT_DISTANCE_RECONSTRUCTION_MODE, "5x5");
+ changed |= setAttr(NRD_MODULE, NRD_ENABLE_ANTI_FIREFLY, "render_pipeline.true");
+ changed |= setAttr(FSR3_MODULE, FSR3_QUALITY_MODE,
+ "render_pipeline.module.fsr_upscaler.attribute.quality_mode.native");
+ changed |= setAttr(FSR3_MODULE, FSR3_SHARPNESS, "0.84");
+ changed |= setAttr(XESS_MODULE, XESS_QUALITY_MODE,
+ "render_pipeline.module.xess_sr.attribute.quality_mode.ultra_quality_plus");
+ changed |= setAttr(XESS_MODULE, XESS_PRE_EXPOSURE, "1.04");
+ changed |= setAttr(DLSS_MODULE, DLSS_MODE_ATTRIBUTE,
+ "render_pipeline.module.dlss.attribute.mode.quality");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_METHOD,
+ "render_pipeline.module.tone_mapping.attribute.method.pbr_neutral");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIDDLE_GREY, "0.18");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_UP_SPEED, "6.8");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_DOWN_SPEED, "6.2");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MIN, "-13.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MAX, "4.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOW_PERCENT, "0.003");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_HIGH_PERCENT, "0.994");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIN_EXPOSURE, "0.012");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MAX_EXPOSURE, "2.4");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_ENABLE_AUTO_EXPOSURE, "render_pipeline.true");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_METERING_MODE,
+ "render_pipeline.module.tone_mapping.attribute.exposure_metering_mode.center");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CENTER_METERING_PERCENT, "18.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MANUAL_EXPOSURE, "1.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_BIAS, "0.08");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_SATURATION, "1.03");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_WHITE_POINT, "34.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CLAMP_OUTPUT, "render_pipeline.true");
+ return changed;
+ }
+
+ private static boolean applyExtremeQualityProfile() {
+ setRayBounces(16, false);
+ setChunkBuildingBatchSize(10, false);
+ setChunkBuildingTotalBatches(12, false);
+ dlssMode = 3;
+ upscalerQuality = 0;
+ denoiserMode = 2;
+
+ boolean changed = false;
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_MESHING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.terrain_meshing_mode.coplanar_merge");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GREEDY_MERGE_MAX_SPAN, "42");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_GEOMETRY_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_geometry_mode.exact_chunks");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_START_DISTANCE_CHUNKS, "96");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FAR_FIELD_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.far_field_material_mode.full_pbr");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_GLASS_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_FOLIAGE_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DECORATION_PATH_MODE,
+ "render_pipeline.module.ray_tracing.attribute.geometry_path_mode.blas");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLAS_INCLUSION_MODE,
+ "render_pipeline.module.ray_tracing.attribute.blas_inclusion_mode.all_geometry");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_REFLECTION_RAY_MATERIAL_MODE,
+ "render_pipeline.module.ray_tracing.attribute.reflection_ray_material_mode.all_materials");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIFFUSE_GI_MODE,
+ "render_pipeline.module.ray_tracing.attribute.diffuse_gi_mode.full_ray_tracing");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_CLOUD_VOLUME_MODE,
+ "render_pipeline.module.ray_tracing.attribute.cloud_volume_mode.realistic_volume");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TERRAIN_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_ENTITY_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BLOCK_ENTITY_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_UPDATE_INTERVAL_FRAMES, "1");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_TRANSPARENT_SPLIT_MODE,
+ "render_pipeline.module.ray_tracing.attribute.transparent_split_mode.stochastic");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_CRIT_GLOW_STRENGTH, "1.0");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PARTICLE_DEATH_SMOKE_GLOW_STRENGTH, "0.72");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_NUM_RAY_BOUNCES, "16");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_JITTER, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_PBR_SAMPLING_MODE,
+ "render_pipeline.module.ray_tracing.attribute.pbr_sampling.bilinear");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_USE_SHARC, "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_SEPARATE_ENTITY_TERRAIN_ACCEL_STRUCTURES,
+ "render_pipeline.true");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_BASIC_RADIANCE, "4.6");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_DIRECT_LIGHT_STRENGTH, "1.0");
+ changed |= setAttr(RAY_TRACING_MODULE, RT_INDIRECT_LIGHT_STRENGTH, "21.0");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SIGMA_SCALE, "3.0");
+ changed |= setAttr(NRD_MODULE, NRD_ANTILAG_LUMINANCE_SENSITIVITY, "2.3");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_ROUGHNESS_THRESHOLD, "0.02");
+ changed |= setAttr(NRD_MODULE, NRD_RESPONSIVE_ACCUMULATION_MIN_ACCUMULATED_FRAME_NUM, "3");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_ACCUMULATED_FRAME_NUM, "112");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_FAST_ACCUMULATED_FRAME_NUM, "8");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_STABILIZED_FRAME_NUM, "116");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_FRAME_NUM, "3");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_BASE_PIXEL_STRIDE, "8");
+ changed |= setAttr(NRD_MODULE, NRD_HISTORY_FIX_ALTERNATE_PIXEL_STRIDE, "8");
+ changed |= setAttr(NRD_MODULE, NRD_FAST_HISTORY_CLAMPING_SIGMA_SCALE, "1.4");
+ changed |= setAttr(NRD_MODULE, NRD_DIFFUSE_PREPASS_BLUR_RADIUS, "10.0");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PREPASS_BLUR_RADIUS, "18.0");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_HIT_DISTANCE_WEIGHT, "0.07");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_BLUR_RADIUS, "0.7");
+ changed |= setAttr(NRD_MODULE, NRD_MAX_BLUR_RADIUS, "44.0");
+ changed |= setAttr(NRD_MODULE, NRD_LOBE_ANGLE_FRACTION, "0.14");
+ changed |= setAttr(NRD_MODULE, NRD_ROUGHNESS_FRACTION, "0.13");
+ changed |= setAttr(NRD_MODULE, NRD_PLANE_DISTANCE_SENSITIVITY, "0.016");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MIN, "0.55");
+ changed |= setAttr(NRD_MODULE, NRD_SPECULAR_PROBABILITY_THRESHOLD_MAX, "0.92");
+ changed |= setAttr(NRD_MODULE, NRD_FIREFLY_SUPPRESSOR_MIN_RELATIVE_SCALE, "1.8");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_DIFFUSE, "2.5");
+ changed |= setAttr(NRD_MODULE, NRD_MIN_MATERIAL_FOR_SPECULAR, "2.5");
+ changed |= setAttr(NRD_MODULE, NRD_HIT_DISTANCE_RECONSTRUCTION_MODE, "5x5");
+ changed |= setAttr(NRD_MODULE, NRD_ENABLE_ANTI_FIREFLY, "render_pipeline.true");
+ changed |= setAttr(FSR3_MODULE, FSR3_QUALITY_MODE,
+ "render_pipeline.module.fsr_upscaler.attribute.quality_mode.native");
+ changed |= setAttr(FSR3_MODULE, FSR3_SHARPNESS, "0.88");
+ changed |= setAttr(XESS_MODULE, XESS_QUALITY_MODE,
+ "render_pipeline.module.xess_sr.attribute.quality_mode.native");
+ changed |= setAttr(XESS_MODULE, XESS_PRE_EXPOSURE, "1.05");
+ changed |= setAttr(DLSS_MODULE, DLSS_MODE_ATTRIBUTE,
+ "render_pipeline.module.dlss.attribute.mode.dlaa");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_METHOD,
+ "render_pipeline.module.tone_mapping.attribute.method.pbr_neutral");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIDDLE_GREY, "0.18");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_UP_SPEED, "6.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_DOWN_SPEED, "5.6");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MIN, "-13.5");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOG2_LUMINANCE_MAX, "4.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_LOW_PERCENT, "0.002");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_HIGH_PERCENT, "0.996");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MIN_EXPOSURE, "0.010");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MAX_EXPOSURE, "2.6");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_ENABLE_AUTO_EXPOSURE, "render_pipeline.true");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_METERING_MODE,
+ "render_pipeline.module.tone_mapping.attribute.exposure_metering_mode.center");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CENTER_METERING_PERCENT, "16.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_MANUAL_EXPOSURE, "1.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_EXPOSURE_BIAS, "0.12");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_SATURATION, "1.04");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_WHITE_POINT, "36.0");
+ changed |= setAttr(TONE_MAPPING_MODULE, TM_CLAMP_OUTPUT, "render_pipeline.true");
+ return changed;
+ }
+
+ private static boolean setAttr(String moduleName, String attributeName, String value) {
+ return Pipeline.setModuleAttributeValue(moduleName, attributeName, value);
+ }
+
+ private static int clamp(int value, int min, int max) {
+ return Math.max(min, Math.min(max, value));
+ }
}
diff --git a/src/main/java/com/radiance/client/option/QualityLevel.java b/src/main/java/com/radiance/client/option/QualityLevel.java
new file mode 100644
index 0000000..febab8a
--- /dev/null
+++ b/src/main/java/com/radiance/client/option/QualityLevel.java
@@ -0,0 +1,58 @@
+package com.radiance.client.option;
+
+import static com.radiance.client.option.Options.QUALITY_LEVEL_BALANCED;
+import static com.radiance.client.option.Options.QUALITY_LEVEL_EXTREME;
+import static com.radiance.client.option.Options.QUALITY_LEVEL_FLUENT;
+import static com.radiance.client.option.Options.QUALITY_LEVEL_PERFORMANCE;
+import static com.radiance.client.option.Options.QUALITY_LEVEL_QUALITY;
+import static com.radiance.client.option.Options.QUALITY_LEVEL_ULTRA;
+
+import com.mojang.serialization.Codec;
+import net.minecraft.util.StringIdentifiable;
+import net.minecraft.util.TranslatableOption;
+
+public enum QualityLevel implements TranslatableOption, StringIdentifiable {
+ FLUENT(5, "fluent", QUALITY_LEVEL_FLUENT),
+ PERFORMANCE(0, "performance", QUALITY_LEVEL_PERFORMANCE),
+ BALANCED(1, "balanced", QUALITY_LEVEL_BALANCED),
+ HIGH(2, "high", QUALITY_LEVEL_QUALITY),
+ ULTRA(3, "ultra", QUALITY_LEVEL_ULTRA),
+ EXTREME(4, "extreme", QUALITY_LEVEL_EXTREME);
+
+ public static final Codec Codec =
+ StringIdentifiable.createCodec(QualityLevel::values);
+
+ private final int id;
+ private final String name;
+ private final String translationKey;
+
+ QualityLevel(int id, String name, String translationKey) {
+ this.id = id;
+ this.name = name;
+ this.translationKey = translationKey;
+ }
+
+ public static QualityLevel fromId(int id) {
+ for (QualityLevel value : values()) {
+ if (value.id == id) {
+ return value;
+ }
+ }
+ return BALANCED;
+ }
+
+ @Override
+ public int getId() {
+ return id;
+ }
+
+ @Override
+ public String getTranslationKey() {
+ return translationKey;
+ }
+
+ @Override
+ public String asString() {
+ return name;
+ }
+}
diff --git a/src/main/java/com/radiance/client/pipeline/Module.java b/src/main/java/com/radiance/client/pipeline/Module.java
index ac5ecdf..21d4a8c 100644
--- a/src/main/java/com/radiance/client/pipeline/Module.java
+++ b/src/main/java/com/radiance/client/pipeline/Module.java
@@ -23,20 +23,42 @@ public String toString() {
}
public ImageConfig getInputImageConfig(String name) {
+ ImageConfig imageConfig = findInputImageConfig(name);
+ if (imageConfig != null) {
+ return imageConfig;
+ }
+ throw new RuntimeException("No such image config: " + name);
+ }
+
+ public ImageConfig findInputImageConfig(String name) {
+ if (inputImageConfigs == null || name == null) {
+ return null;
+ }
for (ImageConfig imageConfig : inputImageConfigs) {
if (imageConfig.name.equals(name)) {
return imageConfig;
}
}
- throw new RuntimeException("No such image config: " + name);
+ return null;
}
public ImageConfig getOutputImageConfig(String name) {
+ ImageConfig imageConfig = findOutputImageConfig(name);
+ if (imageConfig != null) {
+ return imageConfig;
+ }
+ throw new RuntimeException("No such image config: " + name);
+ }
+
+ public ImageConfig findOutputImageConfig(String name) {
+ if (outputImageConfigs == null || name == null) {
+ return null;
+ }
for (ImageConfig imageConfig : outputImageConfigs) {
if (imageConfig.name.equals(name)) {
return imageConfig;
}
}
- throw new RuntimeException("No such image config: " + name);
+ return null;
}
}
diff --git a/src/main/java/com/radiance/client/pipeline/ModuleEntry.java b/src/main/java/com/radiance/client/pipeline/ModuleEntry.java
index abe43e4..1c09d50 100644
--- a/src/main/java/com/radiance/client/pipeline/ModuleEntry.java
+++ b/src/main/java/com/radiance/client/pipeline/ModuleEntry.java
@@ -8,6 +8,8 @@
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
@@ -55,6 +57,7 @@ public static Map loadAllModuleEntries() throws Exception {
}
} else if ("jar".equals(protocol)) {
JarURLConnection jarConn = (JarURLConnection) url.openConnection();
+ jarConn.setUseCaches(false);
try (JarFile jarFile = jarConn.getJarFile()) {
Enumeration jarEntries = jarFile.entries();
@@ -96,8 +99,7 @@ public Module loadModule() {
LoaderOptions options = new LoaderOptions();
Yaml yaml = new Yaml(new Constructor(Module.class, options));
- try (InputStream inputStream = getClass().getClassLoader()
- .getResourceAsStream(this.resourcePath)) {
+ try (InputStream inputStream = openModuleStream()) {
if (inputStream == null) {
throw new RuntimeException("Module not found in resource: " + this.resourcePath);
}
@@ -114,6 +116,17 @@ public Module loadModule() {
}
}
+ private InputStream openModuleStream() throws IOException {
+ if (RadianceClient.radianceDir != null && resourcePath != null && !resourcePath.isBlank()) {
+ Path diskPath = RadianceClient.radianceDir.resolve(resourcePath);
+ if (Files.exists(diskPath)) {
+ return Files.newInputStream(diskPath);
+ }
+ }
+
+ return getClass().getClassLoader().getResourceAsStream(this.resourcePath);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/src/main/java/com/radiance/client/pipeline/Pipeline.java b/src/main/java/com/radiance/client/pipeline/Pipeline.java
index c9d1a7e..175c68d 100644
--- a/src/main/java/com/radiance/client/pipeline/Pipeline.java
+++ b/src/main/java/com/radiance/client/pipeline/Pipeline.java
@@ -2,8 +2,10 @@
import com.radiance.client.RadianceClient;
import com.radiance.client.constant.VulkanConstants;
+import com.radiance.client.option.Options;
import com.radiance.client.pipeline.config.AttributeConfig;
import com.radiance.client.pipeline.config.ImageConfig;
+
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@@ -14,9 +16,11 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+
import org.lwjgl.system.MemoryUtil;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.LoaderOptions;
@@ -27,11 +31,23 @@
public class Pipeline {
public static Pipeline INSTANCE = new Pipeline();
+ private static final String RAY_TRACING_MODULE_NAME = "render_pipeline.module.ray_tracing.name";
+ private static final String DLSS_MODULE_NAME = "render_pipeline.module.dlss.name";
+ private static final String NRD_MODULE_NAME = "render_pipeline.module.nrd.name";
+ private static final String TEMPORAL_ACCUMULATION_MODULE_NAME = "render_pipeline.module.temporal_accumulation.name";
+ private static final String FSR3_MODULE_NAME = "render_pipeline.module.fsr_upscaler.name";
+ private static final String XESS_MODULE_NAME = "render_pipeline.module.xess_sr.name";
+ private static final String TONE_MAPPING_MODULE_NAME = "render_pipeline.module.tone_mapping.name";
+ private static final String POST_RENDER_MODULE_NAME = "render_pipeline.module.post_render.name";
private static Path PIPELINE_CONFIG_PATH = null;
private final List modules = new ArrayList<>();
private final Map> moduleConnections = new HashMap<>();
private Map moduleEntries;
+ private PipelineMode mode = PipelineMode.PRESET;
+ private String activePresetName = null;
+ private static boolean buildRecoveryInProgress = false;
+
private Pipeline() {
}
@@ -58,7 +74,16 @@ public static void clear() {
}
public static Module addModule(String name) {
- Module module = INSTANCE.moduleEntries.get(name).loadModule();
+ if (!isModuleAvailable(name)) {
+ throw new RuntimeException("Module with name " + name + " is not available.");
+ }
+
+ ModuleEntry moduleEntry = INSTANCE.moduleEntries.get(name);
+ if (moduleEntry == null) {
+ throw new RuntimeException("Module with name " + name + " not found.");
+ }
+
+ Module module = moduleEntry.loadModule();
if (module == null) {
throw new RuntimeException("Module with name " + name + " not found.");
}
@@ -67,6 +92,95 @@ public static Module addModule(String name) {
return module;
}
+ public static boolean isModuleAvailable(String moduleName) {
+ if (moduleName == null || moduleName.isEmpty()) {
+ return false;
+ }
+ if (INSTANCE.moduleEntries == null || !INSTANCE.moduleEntries.containsKey(moduleName)) {
+ return false;
+ }
+ return isNativeModuleAvailable(moduleName);
+ }
+
+ private static boolean areModulesAvailable(String... moduleNames) {
+ if (moduleNames == null || moduleNames.length == 0) {
+ return false;
+ }
+ for (String moduleName : moduleNames) {
+ if (!isModuleAvailable(moduleName)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isPresetAvailable(String presetName) {
+ if (Objects.equals(presetName, Presets.RT_DLSSRR.key)) {
+ return areModulesAvailable(
+ RAY_TRACING_MODULE_NAME,
+ DLSS_MODULE_NAME,
+ TONE_MAPPING_MODULE_NAME,
+ POST_RENDER_MODULE_NAME);
+ }
+
+ if (Objects.equals(presetName, Presets.RT_NRD.key)) {
+ return areModulesAvailable(
+ RAY_TRACING_MODULE_NAME,
+ NRD_MODULE_NAME,
+ TEMPORAL_ACCUMULATION_MODULE_NAME,
+ TONE_MAPPING_MODULE_NAME,
+ POST_RENDER_MODULE_NAME);
+ }
+
+ if (Objects.equals(presetName, Presets.RT_NRD_FSR.key)) {
+ return areModulesAvailable(
+ RAY_TRACING_MODULE_NAME,
+ NRD_MODULE_NAME,
+ FSR3_MODULE_NAME,
+ TONE_MAPPING_MODULE_NAME,
+ POST_RENDER_MODULE_NAME);
+ }
+
+ if (Objects.equals(presetName, Presets.RT_NRD_XESS.key)) {
+ return areModulesAvailable(
+ RAY_TRACING_MODULE_NAME,
+ NRD_MODULE_NAME,
+ XESS_MODULE_NAME,
+ TONE_MAPPING_MODULE_NAME,
+ POST_RENDER_MODULE_NAME);
+ }
+
+ return false;
+ }
+
+ private static String getBestAvailablePresetName() {
+ if (isPresetAvailable(Presets.RT_NRD_FSR.key)) {
+ return Presets.RT_NRD_FSR.key;
+ }
+ if (isPresetAvailable(Presets.RT_NRD_XESS.key)) {
+ return Presets.RT_NRD_XESS.key;
+ }
+ if (isPresetAvailable(Presets.RT_NRD.key)) {
+ return Presets.RT_NRD.key;
+ }
+ if (isPresetAvailable(Presets.RT_DLSSRR.key)) {
+ return Presets.RT_DLSSRR.key;
+ }
+ return null;
+ }
+
+ private static void assembleBestAvailablePreset(String reason) {
+ String fallbackPresetName = getBestAvailablePresetName();
+ if (fallbackPresetName == null) {
+ throw new RuntimeException("No compatible preset is available.");
+ }
+
+ if (reason != null && !reason.isEmpty()) {
+ RadianceClient.LOGGER.warn(reason + " Fallback preset: " + fallbackPresetName);
+ }
+ assemblePresetByKeyInternal(fallbackPresetName);
+ }
+
public static Module addModule(Module module) {
INSTANCE.modules.add(module);
@@ -76,7 +190,7 @@ public static Module addModule(Module module) {
public static void connect(ImageConfig src, ImageConfig dst) {
if (!Objects.equals(src.format, dst.format)) {
throw new RuntimeException(
- "Connected format does not match: " + src.format + " != " + dst.format);
+ "Connected format does not match: " + src.format + " != " + dst.format);
}
if (!INSTANCE.moduleConnections.containsKey(src)) {
INSTANCE.moduleConnections.put(src, new ArrayList<>());
@@ -85,7 +199,8 @@ public static void connect(ImageConfig src, ImageConfig dst) {
}
public static void connectOutput(ImageConfig src) {
- if (!Objects.equals(src.format, "R8G8B8A8_UNORM")) {
+ if (!Objects.equals(src.format, "R8G8B8A8_UNORM")
+ && !Objects.equals(src.format, "R16G16B16A16_SFLOAT")) {
throw new RuntimeException("Invalid output format.");
}
src.finalOutput = true;
@@ -93,106 +208,129 @@ public static void connectOutput(ImageConfig src) {
public static void build() {
try {
- Map dstTosrcMap = new HashMap<>();
- for (Map.Entry> entry : INSTANCE.moduleConnections.entrySet()) {
- ImageConfig source = entry.getKey();
- for (ImageConfig dest : entry.getValue()) {
- if (dstTosrcMap.containsKey(dest)) {
- throw new RuntimeException(
+ buildOnce();
+ savePipeline();
+ } catch (Exception initialFailure) {
+ RadianceClient.LOGGER.error("Failed to build render pipeline", initialFailure);
+ if (buildRecoveryInProgress) {
+ throw new IllegalStateException("Failed to build render pipeline", initialFailure);
+ }
+
+ buildRecoveryInProgress = true;
+ try {
+ clear();
+ assembleBestAvailablePreset(
+ "Pipeline build failed (" + initialFailure.getMessage() + ").");
+ buildOnce();
+ savePipeline();
+ } catch (Exception recoveryFailure) {
+ RadianceClient.LOGGER.error("Fallback pipeline build also failed",
+ recoveryFailure);
+ IllegalStateException combinedFailure = new IllegalStateException(
+ "Fallback pipeline build also failed", recoveryFailure);
+ combinedFailure.addSuppressed(initialFailure);
+ throw combinedFailure;
+ } finally {
+ buildRecoveryInProgress = false;
+ }
+ }
+ }
+
+ private static void buildOnce() {
+ Map dstTosrcMap = new HashMap<>();
+ for (Map.Entry> entry : INSTANCE.moduleConnections.entrySet()) {
+ ImageConfig source = entry.getKey();
+ for (ImageConfig dest : entry.getValue()) {
+ if (dstTosrcMap.containsKey(dest)) {
+ throw new RuntimeException(
"Input config '" + dest.name + "' has multiple sources connected!");
- }
- dstTosrcMap.put(dest, source);
}
+ dstTosrcMap.put(dest, source);
}
+ }
- ImageConfig finalOutputConfig = null;
- Module finalModule = null;
+ ImageConfig finalOutputConfig = null;
+ Module finalModule = null;
- for (Module module : INSTANCE.modules) {
- for (ImageConfig conf : module.outputImageConfigs) {
- if (conf.finalOutput) {
- if (finalOutputConfig != null) {
- throw new RuntimeException(
+ for (Module module : INSTANCE.modules) {
+ for (ImageConfig conf : module.outputImageConfigs) {
+ if (conf.finalOutput) {
+ if (finalOutputConfig != null) {
+ throw new RuntimeException(
"Multiple final outputs detected! Only one allows.");
- }
- finalOutputConfig = conf;
- finalModule = module;
}
+ finalOutputConfig = conf;
+ finalModule = module;
}
}
+ }
- if (finalOutputConfig == null) {
- throw new RuntimeException("No final output configured.");
- }
+ if (finalOutputConfig == null) {
+ throw new RuntimeException("No final output configured.");
+ }
- // topological sort
- List sortedModules = new ArrayList<>();
- Set visited = new HashSet<>();
- Set visiting = new HashSet<>();
+ // topological sort
+ List sortedModules = new ArrayList<>();
+ Set visited = new HashSet<>();
+ Set visiting = new HashSet<>();
- topologicalSort(finalModule, dstTosrcMap, visited, visiting, sortedModules);
+ topologicalSort(finalModule, dstTosrcMap, visited, visiting, sortedModules);
- // integrity check
- for (Module m : sortedModules) {
- for (ImageConfig inputConf : m.inputImageConfigs) {
- if (!dstTosrcMap.containsKey(inputConf)) {
- throw new RuntimeException(
+ // integrity check
+ for (Module m : sortedModules) {
+ for (ImageConfig inputConf : m.inputImageConfigs) {
+ if (!dstTosrcMap.containsKey(inputConf)) {
+ throw new RuntimeException(
"Module '" + m.name + "' has unconnected input: " + inputConf.name);
- }
}
}
+ }
- // image list
- List imageFormatList = new ArrayList<>();
- Map configToImageIdMap = new HashMap<>();
+ // image list
+ List imageFormatList = new ArrayList<>();
+ Map configToImageIdMap = new HashMap<>();
- int finalFmtId = VulkanConstants.VkFormat.getVkFormatByName(finalOutputConfig.format);
- imageFormatList.add(finalFmtId);
- configToImageIdMap.put(finalOutputConfig, 0);
+ int finalFmtId = VulkanConstants.VkFormat.getVkFormatByName(finalOutputConfig.format);
+ imageFormatList.add(finalFmtId);
+ configToImageIdMap.put(finalOutputConfig, 0);
- for (Module module : sortedModules) {
- for (ImageConfig outConfig : module.outputImageConfigs) {
- int imgId;
- if (configToImageIdMap.containsKey(outConfig)) {
- imgId = configToImageIdMap.get(outConfig);
+ for (Module module : sortedModules) {
+ for (ImageConfig outConfig : module.outputImageConfigs) {
+ int imgId;
+ if (configToImageIdMap.containsKey(outConfig)) {
+ imgId = configToImageIdMap.get(outConfig);
- if (imgId != 0) {
- throw new RuntimeException();
- }
- } else {
- imgId = imageFormatList.size();
- imageFormatList.add(
- VulkanConstants.VkFormat.getVkFormatByName(outConfig.format));
- configToImageIdMap.put(outConfig, imgId);
+ if (imgId != 0) {
+ throw new RuntimeException();
}
+ } else {
+ imgId = imageFormatList.size();
+ imageFormatList.add(
+ VulkanConstants.VkFormat.getVkFormatByName(outConfig.format));
+ configToImageIdMap.put(outConfig, imgId);
+ }
- List connectedInputs = INSTANCE.moduleConnections.get(outConfig);
- if (connectedInputs != null && !connectedInputs.isEmpty()) {
- for (ImageConfig inputConf : connectedInputs) {
- configToImageIdMap.put(inputConf, imgId);
- }
+ List connectedInputs = INSTANCE.moduleConnections.get(outConfig);
+ if (connectedInputs != null && !connectedInputs.isEmpty()) {
+ for (ImageConfig inputConf : connectedInputs) {
+ configToImageIdMap.put(inputConf, imgId);
}
}
}
+ }
- List> moduleAttributes = new ArrayList<>();
- for (Module m : sortedModules) {
- moduleAttributes.add(
+ List> moduleAttributes = new ArrayList<>();
+ for (Module m : sortedModules) {
+ moduleAttributes.add(
m.attributeConfigs != null ? m.attributeConfigs : new ArrayList<>());
- }
-
- buildNative(sortedModules, imageFormatList, configToImageIdMap, moduleAttributes);
- } catch (Exception e) {
- RadianceClient.LOGGER.error(e.toString());
- Pipeline.loadPipeline();
- } finally {
- savePipeline();
}
+
+ buildNative(sortedModules, imageFormatList, configToImageIdMap, moduleAttributes);
}
private static void topologicalSort(Module current,
- Map inputToSourceMap, Set visited, Set visiting,
- List result) {
+ Map inputToSourceMap, Set visited, Set visiting,
+ List result) {
if (visiting.contains(current)) {
throw new RuntimeException("Cycle detected involving module: " + current.name);
}
@@ -217,7 +355,7 @@ private static void topologicalSort(Module current,
}
private static void buildNative(List modules, List formats,
- Map imgMap, List> moduleAttributes) {
+ Map imgMap, List> moduleAttributes) {
List allocatedBuffers = new ArrayList<>();
try {
@@ -266,11 +404,11 @@ private static void buildNative(List modules, List formats,
if (!attrs.isEmpty()) {
ByteBuffer attrKVPointers = allocAndTrack(allocatedBuffers,
- attrs.size() * 2 * 8);
+ attrs.size() * 2 * 8);
for (AttributeConfig attr : attrs) {
byte[] kBytes = attr.name.getBytes(StandardCharsets.UTF_8);
byte[] vBytes = (attr.value != null ? attr.value : "").getBytes(
- StandardCharsets.UTF_8);
+ StandardCharsets.UTF_8);
ByteBuffer kBuf = allocAndTrack(allocatedBuffers, kBytes.length + 1);
kBuf.put(kBytes).put((byte) 0).flip();
@@ -323,131 +461,682 @@ private static ByteBuffer allocAndTrack(List allocatedBuffers, int s
}
public static void assembleDefault() {
+ String defaultPresetName = processPresetName(Presets.RT_DLSSRR.key);
+ if (defaultPresetName == null) {
+ assembleBestAvailablePreset("Default preset is unavailable.");
+ return;
+ }
+ assemblePresetByKeyInternal(defaultPresetName);
+ }
+
+ public static void assembleDLSSRR() {
+ if (!isPresetAvailable(Presets.RT_DLSSRR.key)) {
+ assembleBestAvailablePreset("DLSS preset is unavailable.");
+ return;
+ }
+ assembleDLSSRRInternal();
+ }
+
+ private static void assembleDLSSRRInternal() {
clear();
- // default: RT + DLSS + ToneMapping + Post
- // if DLSS available, otherwise NRD
- boolean dlssAvailable = isNativeModuleAvailable("render_pipeline.module.dlss.name");
+ Module rayTracingModule = addModule(RAY_TRACING_MODULE_NAME);
- Module rayTracingModule = addModule("render_pipeline.module.ray_tracing.name");
+ Module dlssModule = addModule(DLSS_MODULE_NAME);
- Module toneMappingModule = addModule("render_pipeline.module.tone_mapping.name");
+ Module toneMappingModule = addModule(TONE_MAPPING_MODULE_NAME);
- Module postRenderModule = addModule("render_pipeline.module.post_render.name");
+ Module postRenderModule = addModule(POST_RENDER_MODULE_NAME);
- if (dlssAvailable) {
- Module dlssModule = addModule("render_pipeline.module.dlss.name");
+ rayTracingModule.x = 100;
+ rayTracingModule.y = 220;
+ dlssModule.x = 380;
+ dlssModule.y = 220;
+ toneMappingModule.x = 660;
+ toneMappingModule.y = 140;
+ postRenderModule.x = 660;
+ postRenderModule.y = 300;
- connect(rayTracingModule.getOutputImageConfig("radiance"),
+ INSTANCE.activePresetName = Presets.RT_DLSSRR.key;
+
+ connect(rayTracingModule.getOutputImageConfig("radiance"),
dlssModule.getInputImageConfig("radiance"));
- connect(rayTracingModule.getOutputImageConfig("diffuse_albedo_metallic"),
+ connect(rayTracingModule.getOutputImageConfig("diffuse_albedo_metallic"),
dlssModule.getInputImageConfig("diffuse_albedo_metallic"));
- connect(rayTracingModule.getOutputImageConfig("specular_albedo"),
+ connect(rayTracingModule.getOutputImageConfig("specular_albedo"),
dlssModule.getInputImageConfig("specular_albedo"));
- connect(rayTracingModule.getOutputImageConfig("normal_roughness"),
+ connect(rayTracingModule.getOutputImageConfig("normal_roughness"),
dlssModule.getInputImageConfig("normal_roughness"));
- connect(rayTracingModule.getOutputImageConfig("motion_vector"),
+ connect(rayTracingModule.getOutputImageConfig("motion_vector"),
dlssModule.getInputImageConfig("motion_vector"));
- connect(rayTracingModule.getOutputImageConfig("linear_depth"),
+ connect(rayTracingModule.getOutputImageConfig("linear_depth"),
dlssModule.getInputImageConfig("linear_depth"));
- connect(rayTracingModule.getOutputImageConfig("specular_hit_depth"),
+ connect(rayTracingModule.getOutputImageConfig("specular_hit_depth"),
dlssModule.getInputImageConfig("specular_hit_depth"));
- connect(rayTracingModule.getOutputImageConfig("first_hit_depth"),
+ connect(rayTracingModule.getOutputImageConfig("first_hit_depth"),
dlssModule.getInputImageConfig("first_hit_depth"));
- connect(dlssModule.getOutputImageConfig("processed"),
+ connect(dlssModule.getOutputImageConfig("processed"),
toneMappingModule.getInputImageConfig("denoised_radiance"));
- connect(dlssModule.getOutputImageConfig("upscaled_first_hit_depth"),
+ connect(dlssModule.getOutputImageConfig("upscaled_first_hit_depth"),
postRenderModule.getInputImageConfig("first_hit_depth"));
- } else {
- Module denoiserModule = addModule("render_pipeline.module.nrd.name");
- Module upscalerModule = addModule("render_pipeline.module.fsr3_upscaler.name");
- connect(rayTracingModule.getOutputImageConfig("first_hit_diffuse_indirect_light"),
+ connect(toneMappingModule.getOutputImageConfig("mapped_output"),
+ postRenderModule.getInputImageConfig("ldr_input"));
+
+ connectOutput(postRenderModule.getOutputImageConfig("post_rendered"));
+ }
+
+ public static void assembleNRDFSR() {
+ if (!isPresetAvailable(Presets.RT_NRD_FSR.key)) {
+ assembleBestAvailablePreset("NRD+FSR preset is unavailable.");
+ return;
+ }
+ assembleNRDFSRInternal();
+ }
+
+ public static void assembleNRDXESS() {
+ if (!isPresetAvailable(Presets.RT_NRD_XESS.key)) {
+ assembleBestAvailablePreset("NRD+XeSS preset is unavailable.");
+ return;
+ }
+ assembleNRDXESSInternal();
+ }
+
+ private static void assembleNRDFSRInternal() {
+ clear();
+
+ Module rayTracingModule = addModule(RAY_TRACING_MODULE_NAME);
+
+ Module denoiserModule = addModule(NRD_MODULE_NAME);
+
+ Module upscalerModule = addModule(FSR3_MODULE_NAME);
+
+ Module toneMappingModule = addModule(TONE_MAPPING_MODULE_NAME);
+
+ Module postRenderModule = addModule(POST_RENDER_MODULE_NAME);
+
+ rayTracingModule.x = 100;
+ rayTracingModule.y = 220;
+ denoiserModule.x = 380;
+ denoiserModule.y = 120;
+ upscalerModule.x = 660;
+ upscalerModule.y = 220;
+ toneMappingModule.x = 940;
+ toneMappingModule.y = 120;
+ postRenderModule.x = 940;
+ postRenderModule.y = 300;
+
+ INSTANCE.activePresetName = Presets.RT_NRD_FSR.key;
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_diffuse_indirect_light"),
denoiserModule.getInputImageConfig("diffuse_radiance"));
- connect(rayTracingModule.getOutputImageConfig("first_hit_specular"),
+ connect(rayTracingModule.getOutputImageConfig("first_hit_specular"),
denoiserModule.getInputImageConfig("specular_radiance"));
- connect(rayTracingModule.getOutputImageConfig("first_hit_diffuse_direct_light"),
+ connect(rayTracingModule.getOutputImageConfig("first_hit_diffuse_direct_light"),
denoiserModule.getInputImageConfig("direct_radiance"));
- connect(rayTracingModule.getOutputImageConfig("diffuse_albedo_metallic"),
+ connect(rayTracingModule.getOutputImageConfig("diffuse_albedo_metallic"),
denoiserModule.getInputImageConfig("diffuse_albedo"));
- connect(rayTracingModule.getOutputImageConfig("specular_albedo"),
+ connect(rayTracingModule.getOutputImageConfig("specular_albedo"),
denoiserModule.getInputImageConfig("specular_albedo"));
- connect(rayTracingModule.getOutputImageConfig("normal_roughness"),
+ connect(rayTracingModule.getOutputImageConfig("normal_roughness"),
denoiserModule.getInputImageConfig("normal_roughness"));
- connect(rayTracingModule.getOutputImageConfig("motion_vector"),
+ connect(rayTracingModule.getOutputImageConfig("motion_vector"),
denoiserModule.getInputImageConfig("motion_vector"));
- connect(rayTracingModule.getOutputImageConfig("linear_depth"),
+ connect(rayTracingModule.getOutputImageConfig("linear_depth"),
denoiserModule.getInputImageConfig("linear_depth"));
- connect(rayTracingModule.getOutputImageConfig("first_hit_depth"),
+ connect(rayTracingModule.getOutputImageConfig("first_hit_depth"),
denoiserModule.getInputImageConfig("diffuseHitDepthImage"));
- connect(rayTracingModule.getOutputImageConfig("specular_hit_depth"),
+ connect(rayTracingModule.getOutputImageConfig("specular_hit_depth"),
denoiserModule.getInputImageConfig("specularHitDepthImage"));
- connect(rayTracingModule.getOutputImageConfig("first_hit_clear"),
+ connect(rayTracingModule.getOutputImageConfig("first_hit_clear"),
denoiserModule.getInputImageConfig("first_hit_clear"));
- connect(rayTracingModule.getOutputImageConfig("first_hit_base_emission"),
+ connect(rayTracingModule.getOutputImageConfig("first_hit_base_emission"),
denoiserModule.getInputImageConfig("first_hit_base_emission"));
- connect(denoiserModule.getOutputImageConfig("denoised_radiance"),
+ connect(rayTracingModule.getOutputImageConfig("fog_image"),
+ denoiserModule.getInputImageConfig("fog_image"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_refraction"),
+ denoiserModule.getInputImageConfig("first_hit_refraction"));
+
+ connect(denoiserModule.getOutputImageConfig("denoised_radiance"),
upscalerModule.getInputImageConfig("color"));
- connect(rayTracingModule.getOutputImageConfig("linear_depth"),
+ connect(rayTracingModule.getOutputImageConfig("linear_depth"),
upscalerModule.getInputImageConfig("depth"));
- connect(rayTracingModule.getOutputImageConfig("first_hit_depth"),
+ connect(rayTracingModule.getOutputImageConfig("first_hit_depth"),
upscalerModule.getInputImageConfig("first_hit_depth"));
- connect(rayTracingModule.getOutputImageConfig("motion_vector"),
+ connect(rayTracingModule.getOutputImageConfig("motion_vector"),
upscalerModule.getInputImageConfig("motion_vector"));
- connect(upscalerModule.getOutputImageConfig("upscaled_radiance"),
+ connect(upscalerModule.getOutputImageConfig("upscaled_radiance"),
toneMappingModule.getInputImageConfig("denoised_radiance"));
- connect(upscalerModule.getOutputImageConfig("upscaled_first_hit_depth"),
+ connect(upscalerModule.getOutputImageConfig("upscaled_first_hit_depth"),
postRenderModule.getInputImageConfig("first_hit_depth"));
- // connect(denoiserModule.getOutputImageConfig("denoised_radiance"),
- // toneMappingModule.getInputImageConfig("denoised_radiance"));
+ connect(toneMappingModule.getOutputImageConfig("mapped_output"),
+ postRenderModule.getInputImageConfig("ldr_input"));
+
+ connectOutput(postRenderModule.getOutputImageConfig("post_rendered"));
+ }
- // connect(rayTracingModule.getOutputImageConfig("first_hit_depth"),
- // postRenderModule.getInputImageConfig("first_hit_depth"));
+ private static void assembleNRDXESSInternal() {
+ clear();
+
+ Module rayTracingModule = addModule(RAY_TRACING_MODULE_NAME);
+
+ Module denoiserModule = addModule(NRD_MODULE_NAME);
+
+ Module upscalerModule = addModule(XESS_MODULE_NAME);
+
+ Module toneMappingModule = addModule(TONE_MAPPING_MODULE_NAME);
+
+ Module postRenderModule = addModule(POST_RENDER_MODULE_NAME);
+
+ rayTracingModule.x = 100;
+ rayTracingModule.y = 220;
+ denoiserModule.x = 380;
+ denoiserModule.y = 120;
+ upscalerModule.x = 660;
+ upscalerModule.y = 220;
+ toneMappingModule.x = 940;
+ toneMappingModule.y = 120;
+ postRenderModule.x = 940;
+ postRenderModule.y = 300;
+
+ INSTANCE.activePresetName = Presets.RT_NRD_XESS.key;
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_diffuse_indirect_light"),
+ denoiserModule.getInputImageConfig("diffuse_radiance"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_specular"),
+ denoiserModule.getInputImageConfig("specular_radiance"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_diffuse_direct_light"),
+ denoiserModule.getInputImageConfig("direct_radiance"));
+
+ connect(rayTracingModule.getOutputImageConfig("diffuse_albedo_metallic"),
+ denoiserModule.getInputImageConfig("diffuse_albedo"));
+
+ connect(rayTracingModule.getOutputImageConfig("specular_albedo"),
+ denoiserModule.getInputImageConfig("specular_albedo"));
+
+ connect(rayTracingModule.getOutputImageConfig("normal_roughness"),
+ denoiserModule.getInputImageConfig("normal_roughness"));
+
+ connect(rayTracingModule.getOutputImageConfig("motion_vector"),
+ denoiserModule.getInputImageConfig("motion_vector"));
+
+ connect(rayTracingModule.getOutputImageConfig("linear_depth"),
+ denoiserModule.getInputImageConfig("linear_depth"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_depth"),
+ denoiserModule.getInputImageConfig("diffuseHitDepthImage"));
+
+ connect(rayTracingModule.getOutputImageConfig("specular_hit_depth"),
+ denoiserModule.getInputImageConfig("specularHitDepthImage"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_clear"),
+ denoiserModule.getInputImageConfig("first_hit_clear"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_base_emission"),
+ denoiserModule.getInputImageConfig("first_hit_base_emission"));
+
+ connect(rayTracingModule.getOutputImageConfig("fog_image"),
+ denoiserModule.getInputImageConfig("fog_image"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_refraction"),
+ denoiserModule.getInputImageConfig("first_hit_refraction"));
+
+ connect(denoiserModule.getOutputImageConfig("denoised_radiance"),
+ upscalerModule.getInputImageConfig("color"));
+
+ connect(rayTracingModule.getOutputImageConfig("linear_depth"),
+ upscalerModule.getInputImageConfig("depth"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_depth"),
+ upscalerModule.getInputImageConfig("first_hit_depth"));
+
+ connect(rayTracingModule.getOutputImageConfig("motion_vector"),
+ upscalerModule.getInputImageConfig("motion_vector"));
+
+ connect(upscalerModule.getOutputImageConfig("upscaled_radiance"),
+ toneMappingModule.getInputImageConfig("denoised_radiance"));
+ connect(upscalerModule.getOutputImageConfig("upscaled_first_hit_depth"),
+ postRenderModule.getInputImageConfig("first_hit_depth"));
+
+ connect(toneMappingModule.getOutputImageConfig("mapped_output"),
+ postRenderModule.getInputImageConfig("ldr_input"));
+
+ connectOutput(postRenderModule.getOutputImageConfig("post_rendered"));
+ }
+
+ public static void assembleNRD() {
+ if (!isPresetAvailable(Presets.RT_NRD.key)) {
+ assembleBestAvailablePreset("NRD preset is unavailable.");
+ return;
}
+ assembleNRDInternal();
+ }
+
+ private static void assembleNRDInternal() {
+ clear();
+
+ Module rayTracingModule = addModule(RAY_TRACING_MODULE_NAME);
+
+ Module denoiserModule = addModule(NRD_MODULE_NAME);
+
+ Module temporalAccumulationModule = addModule(TEMPORAL_ACCUMULATION_MODULE_NAME);
+
+ Module toneMappingModule = addModule(TONE_MAPPING_MODULE_NAME);
+
+ Module postRenderModule = addModule(POST_RENDER_MODULE_NAME);
+
+ rayTracingModule.x = 100;
+ rayTracingModule.y = 220;
+ denoiserModule.x = 380;
+ denoiserModule.y = 120;
+ temporalAccumulationModule.x = 660;
+ temporalAccumulationModule.y = 120;
+ toneMappingModule.x = 940;
+ toneMappingModule.y = 120;
+ postRenderModule.x = 940;
+ postRenderModule.y = 300;
+
+ INSTANCE.activePresetName = Presets.RT_NRD.key;
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_diffuse_indirect_light"),
+ denoiserModule.getInputImageConfig("diffuse_radiance"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_specular"),
+ denoiserModule.getInputImageConfig("specular_radiance"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_diffuse_direct_light"),
+ denoiserModule.getInputImageConfig("direct_radiance"));
+
+ connect(rayTracingModule.getOutputImageConfig("diffuse_albedo_metallic"),
+ denoiserModule.getInputImageConfig("diffuse_albedo"));
+
+ connect(rayTracingModule.getOutputImageConfig("specular_albedo"),
+ denoiserModule.getInputImageConfig("specular_albedo"));
+
+ connect(rayTracingModule.getOutputImageConfig("normal_roughness"),
+ denoiserModule.getInputImageConfig("normal_roughness"));
+
+ connect(rayTracingModule.getOutputImageConfig("motion_vector"),
+ denoiserModule.getInputImageConfig("motion_vector"));
+
+ connect(rayTracingModule.getOutputImageConfig("linear_depth"),
+ denoiserModule.getInputImageConfig("linear_depth"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_depth"),
+ denoiserModule.getInputImageConfig("diffuseHitDepthImage"));
+
+ connect(rayTracingModule.getOutputImageConfig("specular_hit_depth"),
+ denoiserModule.getInputImageConfig("specularHitDepthImage"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_clear"),
+ denoiserModule.getInputImageConfig("first_hit_clear"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_base_emission"),
+ denoiserModule.getInputImageConfig("first_hit_base_emission"));
+
+ connect(rayTracingModule.getOutputImageConfig("fog_image"),
+ denoiserModule.getInputImageConfig("fog_image"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_refraction"),
+ denoiserModule.getInputImageConfig("first_hit_refraction"));
+
+ connect(denoiserModule.getOutputImageConfig("denoised_radiance"),
+ temporalAccumulationModule.getInputImageConfig("color"));
+
+ connect(rayTracingModule.getOutputImageConfig("motion_vector"),
+ temporalAccumulationModule.getInputImageConfig("motion"));
+
+ connect(rayTracingModule.getOutputImageConfig("normal_roughness"),
+ temporalAccumulationModule.getInputImageConfig("normal_roughness"));
+
+ connect(temporalAccumulationModule.getOutputImageConfig("accumulated_radiance"),
+ toneMappingModule.getInputImageConfig("denoised_radiance"));
+
+ connect(rayTracingModule.getOutputImageConfig("first_hit_depth"),
+ postRenderModule.getInputImageConfig("first_hit_depth"));
connect(toneMappingModule.getOutputImageConfig("mapped_output"),
- postRenderModule.getInputImageConfig("ldr_input"));
+ postRenderModule.getInputImageConfig("ldr_input"));
connectOutput(postRenderModule.getOutputImageConfig("post_rendered"));
}
+ private static void assemblePresetByKeyInternal(String presetName) {
+ if (Objects.equals(presetName, Presets.RT_DLSSRR.key)) {
+ assembleDLSSRRInternal();
+ return;
+ }
+
+ if (Objects.equals(presetName, Presets.RT_NRD.key)) {
+ assembleNRDInternal();
+ return;
+ }
+
+ if (Objects.equals(presetName, Presets.RT_NRD_FSR.key)) {
+ assembleNRDFSRInternal();
+ return;
+ }
+
+ if (Objects.equals(presetName, Presets.RT_NRD_XESS.key)) {
+ assembleNRDXESSInternal();
+ return;
+ }
+
+ throw new RuntimeException("Unsupported preset: " + presetName);
+ }
+
public static native void buildNative(long params);
public static native void collectNativeModules();
+ public static native void recollectNativeModules();
+
public static native boolean isNativeModuleAvailable(String name);
+ public static Module getModuleByName(String name) {
+ if (name == null) {
+ return null;
+ }
+
+ for (Module module : INSTANCE.modules) {
+ if (Objects.equals(module.name, name)) {
+ return module;
+ }
+ }
+
+ return null;
+ }
+
+ public static String getModuleAttributeValue(String moduleName, String attributeName,
+ String fallback) {
+ Module module = getModuleByName(moduleName);
+ if (module == null || module.attributeConfigs == null || attributeName == null) {
+ return fallback;
+ }
+
+ for (AttributeConfig attributeConfig : module.attributeConfigs) {
+ if (!Objects.equals(attributeConfig.name, attributeName)) {
+ continue;
+ }
+
+ return attributeConfig.value != null ? attributeConfig.value : fallback;
+ }
+
+ return fallback;
+ }
+
+ public static String getDefaultModuleAttributeValue(String moduleName, String attributeName,
+ String fallback) {
+ if (INSTANCE.moduleEntries == null || moduleName == null || attributeName == null) {
+ return fallback;
+ }
+
+ ModuleEntry moduleEntry = INSTANCE.moduleEntries.get(moduleName);
+ if (moduleEntry == null) {
+ return fallback;
+ }
+
+ Module module = moduleEntry.loadModule();
+ if (module == null || module.attributeConfigs == null) {
+ return fallback;
+ }
+
+ for (AttributeConfig attributeConfig : module.attributeConfigs) {
+ if (!Objects.equals(attributeConfig.name, attributeName)) {
+ continue;
+ }
+ return attributeConfig.value != null ? attributeConfig.value : fallback;
+ }
+
+ return fallback;
+ }
+
+ public static int getModuleAttributeIntValue(String moduleName, String attributeName,
+ int fallback) {
+ String value = getModuleAttributeValue(moduleName, attributeName, null);
+ if (value == null) {
+ return fallback;
+ }
+
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException ignored) {
+ return fallback;
+ }
+ }
+
+ public static float getModuleAttributeFloatValue(String moduleName, String attributeName,
+ float fallback) {
+ String value = getModuleAttributeValue(moduleName, attributeName, null);
+ if (value == null) {
+ return fallback;
+ }
+
+ try {
+ return Float.parseFloat(value);
+ } catch (NumberFormatException ignored) {
+ return fallback;
+ }
+ }
+
+ public static boolean getModuleAttributeBooleanValue(String moduleName, String attributeName,
+ boolean fallback) {
+ String value = getModuleAttributeValue(moduleName, attributeName, null);
+ if (value == null) {
+ return fallback;
+ }
+
+ if (Objects.equals(value, "render_pipeline.true")) {
+ return true;
+ }
+ if (Objects.equals(value, "render_pipeline.false")) {
+ return false;
+ }
+
+ return Boolean.parseBoolean(value);
+ }
+
+ public static boolean setModuleAttributeValue(String moduleName, String attributeName,
+ String value) {
+ Module module = getModuleByName(moduleName);
+ if (module == null || module.attributeConfigs == null || attributeName == null) {
+ return false;
+ }
+
+ for (AttributeConfig attributeConfig : module.attributeConfigs) {
+ if (!Objects.equals(attributeConfig.name, attributeName)) {
+ continue;
+ }
+
+ String normalizedValue = value == null ? "" : value;
+ if (Objects.equals(attributeConfig.value, normalizedValue)) {
+ return false;
+ }
+
+ attributeConfig.value = normalizedValue;
+ return true;
+ }
+
+ return false;
+ }
+
+ public PipelineMode getMode() {
+ return mode;
+ }
+
+ public String getActivePresetName() {
+ return activePresetName;
+ }
+
+ public static PipelineMode getPipelineMode() {
+ return INSTANCE.mode;
+ }
+
+ public static String getActivePreset() {
+ return INSTANCE.activePresetName;
+ }
+
+ public static void switchToPipelineMode() {
+ if (INSTANCE.mode == PipelineMode.PIPELINE) {
+ return;
+ }
+
+ INSTANCE.mode = PipelineMode.PIPELINE;
+
+ savePipeline();
+
+ build();
+ }
+
+ public static void switchToPresetMode(String presetName) {
+ preparePresetMode(presetName);
+
+ savePipeline();
+ build();
+ }
+
+ public static void preparePresetMode(String presetName) {
+ List carryOverModules = capturePresetModules();
+
+ INSTANCE.mode = PipelineMode.PRESET;
+ String processedPresetName = processPresetName(presetName);
+
+ // should set preset name properly
+ assemblePreset(processedPresetName);
+
+ PipelineConfigStorage storage = loadConfigStorage();
+ if (storage != null && Objects.equals(storage.mode, PipelineMode.PRESET.name())
+ && Objects.equals(storage.presetName, INSTANCE.activePresetName)) {
+ applyPresetModuleOverrides(storage.presetModules);
+ }
+
+ applyPresetModuleOverrides(carryOverModules);
+ }
+
+ public static void assemblePreset(String presetName) {
+ String processedPresetName = processPresetName(presetName);
+ if (processedPresetName == null) {
+ assembleBestAvailablePreset("Requested preset is unavailable.");
+ return;
+ }
+
+ assemblePresetByKeyInternal(processedPresetName);
+ }
+
+ public static String processPresetName(String presetName) {
+ String requestedPresetName = presetName;
+ if (requestedPresetName == null || requestedPresetName.isEmpty()) {
+ requestedPresetName = Presets.RT_DLSSRR.key;
+ }
+
+ if (!Objects.equals(requestedPresetName, Presets.RT_DLSSRR.key)
+ && !Objects.equals(requestedPresetName, Presets.RT_NRD.key)
+ && !Objects.equals(requestedPresetName, Presets.RT_NRD_FSR.key)
+ && !Objects.equals(requestedPresetName, Presets.RT_NRD_XESS.key)) {
+ requestedPresetName = Presets.RT_DLSSRR.key;
+ }
+
+ if (isPresetAvailable(requestedPresetName)) {
+ return requestedPresetName;
+ }
+
+ return getBestAvailablePresetName();
+ }
+
+ private static void applyPresetModuleOverrides(List storedModules) {
+ if (storedModules == null || storedModules.isEmpty()) {
+ return;
+ }
+
+ for (PresetStoredModule storedModule : storedModules) {
+ if (storedModule == null || storedModule.entryName == null) {
+ continue;
+ }
+
+ for (Module module : INSTANCE.modules) {
+ if (!Objects.equals(module.name, storedModule.entryName)) {
+ continue;
+ }
+
+ applyStoredAttributes(module, storedModule.attributes);
+ }
+ }
+ }
+
+ private static void applyStoredAttributes(Module module, List storedAttributes) {
+ if (module == null || module.attributeConfigs == null || storedAttributes == null) {
+ return;
+ }
+
+ for (StoredAttribute storedAttribute : storedAttributes) {
+ if (storedAttribute == null || storedAttribute.name == null) {
+ continue;
+ }
+
+ for (int i = 0; i < module.attributeConfigs.size(); i++) {
+ var attributeConfig = module.attributeConfigs.get(i);
+
+ if (!Objects.equals(attributeConfig.name, storedAttribute.name)) {
+ continue;
+ }
+ if (storedAttribute.type != null && !Objects.equals(attributeConfig.type, storedAttribute.type)) {
+ continue;
+ }
+
+ attributeConfig.value = storedAttribute.value;
+ break;
+ }
+ }
+ }
+
public static void savePipeline() {
if (PIPELINE_CONFIG_PATH == null) {
return;
}
+ PipelineConfigStorage storage = new PipelineConfigStorage();
+ storage.mode = INSTANCE.mode.toString();
+ storage.presetName = INSTANCE.activePresetName;
+
+ if (INSTANCE.mode == PipelineMode.PIPELINE) {
+ storage.pipeline = capturePipelineStorage();
+ } else {
+ storage.presetModules = capturePresetModules();
+ }
+
+ writeConfigStorage(storage);
+ }
+
+ private static PipelineStorage capturePipelineStorage() {
PipelineStorage pipelineStorage = new PipelineStorage();
pipelineStorage.modules = new ArrayList<>();
pipelineStorage.moduleConnections = new ArrayList<>();
@@ -464,21 +1153,7 @@ public static void savePipeline() {
storedModule.entryName = module.name;
storedModule.x = module.x;
storedModule.y = module.y;
-
- storedModule.attributes = new ArrayList<>();
- if (module.attributeConfigs != null) {
- for (int attributeIndex = 0; attributeIndex < module.attributeConfigs.size();
- attributeIndex++) {
- var attributeConfig = module.attributeConfigs.get(attributeIndex);
-
- StoredAttribute storedAttribute = new StoredAttribute();
- storedAttribute.type = attributeConfig.type;
- storedAttribute.name = attributeConfig.name;
- storedAttribute.value = attributeConfig.value;
-
- storedModule.attributes.add(storedAttribute);
- }
- }
+ storedModule.attributes = captureAttributes(module);
pipelineStorage.modules.add(storedModule);
}
@@ -500,8 +1175,7 @@ public static void savePipeline() {
continue;
}
- for (int index = 0; index < dstImageConfigs.size(); index++) {
- ImageConfig dstImageConfig = dstImageConfigs.get(index);
+ for (ImageConfig dstImageConfig : dstImageConfigs) {
if (dstImageConfig == null || dstImageConfig.owner == null) {
continue;
}
@@ -522,10 +1196,10 @@ public static void savePipeline() {
}
storedConnections.sort(
- Comparator.comparing((StoredConnection connection) -> connection.srcModuleId)
- .thenComparing(connection -> connection.srcImageName)
- .thenComparing(connection -> connection.dstModuleId)
- .thenComparing(connection -> connection.dstImageName));
+ Comparator.comparing((StoredConnection connection) -> connection.srcModuleId)
+ .thenComparing(connection -> connection.srcImageName)
+ .thenComparing(connection -> connection.dstModuleId)
+ .thenComparing(connection -> connection.dstImageName));
pipelineStorage.moduleConnections.addAll(storedConnections);
@@ -548,13 +1222,47 @@ public static void savePipeline() {
pipelineStorage.finalOutputModuleId = finalOutputModuleId;
pipelineStorage.finalOutputImageName = finalOutputImageName;
+ return pipelineStorage;
+ }
+
+ private static List captureAttributes(Module module) {
+ List out = new ArrayList<>();
+ if (module == null || module.attributeConfigs == null) {
+ return out;
+ }
+
+ for (AttributeConfig attributeConfig : module.attributeConfigs) {
+ StoredAttribute storedAttribute = new StoredAttribute();
+ storedAttribute.type = attributeConfig.type;
+ storedAttribute.name = attributeConfig.name;
+ storedAttribute.value = attributeConfig.value;
+ out.add(storedAttribute);
+ }
+ return out;
+ }
+
+ private static List capturePresetModules() {
+ List list = new ArrayList<>();
+
+ for (Module module : INSTANCE.modules) {
+ PresetStoredModule storedModule = new PresetStoredModule();
+ storedModule.entryName = module.name;
+ storedModule.attributes = captureAttributes(module);
+ list.add(storedModule);
+ }
+
+ list.sort(Comparator.comparing(m -> m.entryName == null ? "" : m.entryName));
+ return list;
+ }
+
+ private static void writeConfigStorage(PipelineConfigStorage storage) {
DumperOptions dumperOptions = new DumperOptions();
dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
dumperOptions.setPrettyFlow(true);
dumperOptions.setIndent(2);
Yaml yaml = new Yaml(dumperOptions);
- String yamlText = yaml.dump(pipelineStorage);
+ String yamlText = yaml.dumpAsMap(storage);
try {
Files.createDirectories(PIPELINE_CONFIG_PATH.getParent());
@@ -564,72 +1272,135 @@ public static void savePipeline() {
}
}
- public static void loadPipeline() {
- clear();
-
- if (!Files.exists(PIPELINE_CONFIG_PATH)) {
- assembleDefault();
- savePipeline();
- return;
+ private static PipelineConfigStorage loadConfigStorage() {
+ if (PIPELINE_CONFIG_PATH == null || !Files.exists(PIPELINE_CONFIG_PATH)) {
+ return null;
}
- PipelineStorage pipelineStorage;
try {
String yamlText = Files.readString(PIPELINE_CONFIG_PATH, StandardCharsets.UTF_8);
LoaderOptions loaderOptions = new LoaderOptions();
- TagInspector tagInspector = tag -> tag.getClassName()
- .startsWith("com.radiance.client.pipeline");
+ TagInspector tagInspector = tag -> tag.getClassName().startsWith("com.radiance.client.pipeline");
loaderOptions.setTagInspector(tagInspector);
- Constructor constructor = new Constructor(PipelineStorage.class, loaderOptions);
+
+ Constructor constructor = new Constructor(PipelineConfigStorage.class, loaderOptions);
Yaml yaml = new Yaml(constructor);
- pipelineStorage = yaml.load(yamlText);
+ PipelineConfigStorage storage = yaml.load(yamlText);
+ if (storage != null) {
+ storage.migrateLegacyFields();
+ }
+ return storage;
} catch (Exception e) {
- RadianceClient.LOGGER.error("Error while loading pipeline.", e);
- assembleDefault();
- savePipeline();
- return;
+ RadianceClient.LOGGER.error("Error while loading pipeline config.", e);
+ return null;
}
+ }
- if (pipelineStorage == null || pipelineStorage.modules == null
- || pipelineStorage.modules.isEmpty()) {
- assembleDefault();
- savePipeline();
- RadianceClient.LOGGER.error("Pipeline is empty.");
- return;
+ private static boolean hasUnavailableStoredModules(PipelineStorage pipelineStorage) {
+ if (pipelineStorage == null || pipelineStorage.modules == null) {
+ return true;
}
- if (pipelineStorage.finalOutputModuleId == null
- || pipelineStorage.finalOutputImageName == null) {
- assembleDefault();
- savePipeline();
- RadianceClient.LOGGER.error("Pipeline has no final output.");
- return;
+ for (StoredModule storedModule : pipelineStorage.modules) {
+ if (storedModule == null || storedModule.entryName == null) {
+ continue;
+ }
+ if (!isModuleAvailable(storedModule.entryName)) {
+ return true;
+ }
}
- for (int index = 0; index < pipelineStorage.modules.size(); index++) {
- StoredModule storedModule = pipelineStorage.modules.get(index);
+ return false;
+ }
- if (storedModule == null || storedModule.id == null || storedModule.entryName == null) {
+ private static boolean hasStoredModule(PipelineStorage pipelineStorage, String moduleName) {
+ if (pipelineStorage == null || pipelineStorage.modules == null || moduleName == null) {
+ return false;
+ }
+
+ for (StoredModule storedModule : pipelineStorage.modules) {
+ if (storedModule == null || storedModule.entryName == null) {
continue;
}
+ if (Objects.equals(storedModule.entryName, moduleName)) {
+ return true;
+ }
+ }
- if (!isNativeModuleAvailable("render_pipeline.module.dlss.name")) {
- assembleDefault();
- savePipeline();
- RadianceClient.LOGGER.error("DLSS is not available. Use NRD!");
- return;
+ return false;
+ }
+
+ private static boolean hasStaleDlssState(PipelineStorage pipelineStorage) {
+ boolean savedPipelineHasDlss = hasStoredModule(pipelineStorage, DLSS_MODULE_NAME);
+ boolean currentDlssPresetActive = Options.shouldUseDlssPresetForCurrentQuality()
+ && isModuleAvailable(DLSS_MODULE_NAME);
+
+ if (savedPipelineHasDlss == currentDlssPresetActive) {
+ return false;
+ }
+
+ RadianceClient.LOGGER.warn(
+ "Stored pipeline DLSS state ({}) does not match the current quality preset expectation ({}). Rebuilding the default preset.",
+ savedPipelineHasDlss, currentDlssPresetActive);
+ return true;
+ }
+
+ private static boolean hasDisconnectedCurrentModuleInputs() {
+ Map dstToSrcMap = new HashMap<>();
+ for (Map.Entry> entry : INSTANCE.moduleConnections.entrySet()) {
+ ImageConfig src = entry.getKey();
+ for (ImageConfig dst : entry.getValue()) {
+ dstToSrcMap.put(dst, src);
+ }
+ }
+
+ for (Module module : INSTANCE.modules) {
+ for (ImageConfig inputConf : module.inputImageConfigs) {
+ if (!dstToSrcMap.containsKey(inputConf)) {
+ RadianceClient.LOGGER.warn(
+ "Stored pipeline has unconnected input '{}' on module '{}'. Rebuilding default pipeline.",
+ inputConf.name, module.name);
+ return true;
+ }
}
}
+ return false;
+ }
+
+ private static void applyPipelineStorage(PipelineStorage pipelineStorage) {
+ if (pipelineStorage == null) {
+ assembleDefault();
+ return;
+ }
+
+ if (hasUnavailableStoredModules(pipelineStorage)) {
+ RadianceClient.LOGGER.warn("Stored pipeline contains unavailable modules. Falling back to NRD+FSR.");
+ INSTANCE.mode = PipelineMode.PRESET;
+ assembleNRDFSR();
+ return;
+ }
+
+ if (hasStaleDlssState(pipelineStorage)) {
+ INSTANCE.mode = PipelineMode.PRESET;
+ assembleDefault();
+ return;
+ }
+
clear();
- Map idToModule = new HashMap<>();
+ if (pipelineStorage.modules == null || pipelineStorage.modules.isEmpty()
+ || pipelineStorage.finalOutputModuleId == null || pipelineStorage.finalOutputImageName == null) {
+ INSTANCE.mode = PipelineMode.PRESET;
+ assembleDefault();
+ return;
+ }
- for (int index = 0; index < pipelineStorage.modules.size(); index++) {
- StoredModule storedModule = pipelineStorage.modules.get(index);
+ Map idToModule = new HashMap<>();
+ for (StoredModule storedModule : pipelineStorage.modules) {
if (storedModule == null || storedModule.id == null || storedModule.entryName == null) {
continue;
}
@@ -638,32 +1409,7 @@ public static void loadPipeline() {
module.x = storedModule.x;
module.y = storedModule.y;
- if (storedModule.attributes != null && module.attributeConfigs != null) {
- for (int attributeIndex = 0; attributeIndex < storedModule.attributes.size();
- attributeIndex++) {
- StoredAttribute storedAttribute = storedModule.attributes.get(attributeIndex);
- if (storedAttribute == null || storedAttribute.name == null) {
- continue;
- }
-
- for (int moduleAttributeIndex = 0;
- moduleAttributeIndex < module.attributeConfigs.size();
- moduleAttributeIndex++) {
- var attributeConfig = module.attributeConfigs.get(moduleAttributeIndex);
-
- if (!Objects.equals(attributeConfig.name, storedAttribute.name)) {
- continue;
- }
- if (storedAttribute.type != null && !Objects.equals(attributeConfig.type,
- storedAttribute.type)) {
- continue;
- }
-
- attributeConfig.value = storedAttribute.value;
- break;
- }
- }
- }
+ applyStoredAttributes(module, storedModule.attributes);
idToModule.put(storedModule.id, module);
}
@@ -676,34 +1422,30 @@ public static void loadPipeline() {
Module finalModule = idToModule.get(pipelineStorage.finalOutputModuleId);
if (finalModule == null) {
+ INSTANCE.mode = PipelineMode.PRESET;
assembleDefault();
- savePipeline();
- RadianceClient.LOGGER.error("Final output module not found.");
return;
}
- ImageConfig finalImageConfig = finalModule.getOutputImageConfig(
+ ImageConfig finalImageConfig = finalModule.findOutputImageConfig(
pipelineStorage.finalOutputImageName);
if (finalImageConfig == null) {
+ INSTANCE.mode = PipelineMode.PRESET;
assembleDefault();
- savePipeline();
- RadianceClient.LOGGER.error("Final output image not found.");
return;
}
finalImageConfig.finalOutput = true;
if (pipelineStorage.moduleConnections != null) {
- for (int index = 0; index < pipelineStorage.moduleConnections.size(); index++) {
- StoredConnection storedConnection = pipelineStorage.moduleConnections.get(index);
+ for (StoredConnection storedConnection : pipelineStorage.moduleConnections) {
if (storedConnection == null) {
continue;
}
if (storedConnection.srcModuleId == null || storedConnection.dstModuleId == null) {
continue;
}
- if (storedConnection.srcImageName == null
- || storedConnection.dstImageName == null) {
+ if (storedConnection.srcImageName == null || storedConnection.dstImageName == null) {
continue;
}
@@ -714,16 +1456,65 @@ public static void loadPipeline() {
continue;
}
- ImageConfig srcImageConfig = srcModule.getOutputImageConfig(
+ ImageConfig srcImageConfig = srcModule.findOutputImageConfig(
storedConnection.srcImageName);
- ImageConfig dstImageConfig = dstModule.getInputImageConfig(
+ ImageConfig dstImageConfig = dstModule.findInputImageConfig(
storedConnection.dstImageName);
+ if (srcImageConfig == null || dstImageConfig == null) {
+ continue;
+ }
+
connect(srcImageConfig, dstImageConfig);
}
}
+ }
- savePipeline();
+ public static void loadPipeline() {
+ try {
+ recollectNativeModules();
+ } catch (UnsatisfiedLinkError ignored) {
+ collectNativeModules();
+ }
+
+ PipelineConfigStorage storage = loadConfigStorage();
+
+ if (storage == null) {
+ INSTANCE.mode = PipelineMode.PRESET;
+ assemblePreset(Options.getPreferredPresetForCurrentQuality());
+ Options.applyQualityProfile(false);
+ savePipeline();
+ build();
+ return;
+ }
+
+ PipelineMode loadedMode = PipelineMode.fromString(storage.mode);
+ INSTANCE.mode = loadedMode;
+ if (storage.presetName != null && !storage.presetName.isEmpty()) {
+ INSTANCE.activePresetName = processPresetName(storage.presetName);
+ }
+
+ if (loadedMode == PipelineMode.PRESET) {
+ assemblePreset(INSTANCE.activePresetName);
+ applyPresetModuleOverrides(storage.presetModules);
+ build();
+ return;
+ }
+
+ if (storage.pipeline != null) {
+ applyPipelineStorage(storage.pipeline);
+ if (hasDisconnectedCurrentModuleInputs()) {
+ assembleDefault();
+ INSTANCE.mode = PipelineMode.PRESET;
+ savePipeline();
+ }
+ build();
+ return;
+ }
+
+ // fallback
+ assembleDefault();
+ build();
}
public Map getModuleEntries() {
@@ -738,16 +1529,70 @@ public Map> getModuleConnections() {
return moduleConnections;
}
- public static class PipelineStorage {
+ public enum PipelineMode {
+ PIPELINE,
+ PRESET;
+
+ static PipelineMode fromString(String s) {
+ if (s == null) {
+ return PRESET;
+ }
+ try {
+ return PipelineMode.valueOf(s.toUpperCase(Locale.ROOT));
+ } catch (Exception ignored) {
+ return PRESET;
+ }
+ }
+ }
+
+ public static class PipelineConfigStorage {
+ // new format
+ public String mode;
+ public String presetName;
+ public PipelineStorage pipeline;
+ public List presetModules;
public List modules;
public List moduleConnections;
public String finalOutputModuleId;
public String finalOutputImageName;
+
+ void migrateLegacyFields() {
+ if (pipeline != null) {
+ return;
+ }
+ if (modules == null || modules.isEmpty()) {
+ return;
+ }
+
+ PipelineStorage ps = new PipelineStorage();
+ ps.modules = modules;
+ ps.moduleConnections = moduleConnections != null ? moduleConnections : new ArrayList<>();
+ ps.finalOutputModuleId = finalOutputModuleId;
+ ps.finalOutputImageName = finalOutputImageName;
+
+ pipeline = ps;
+
+ // default mode for legacy, pipeline
+ if (mode == null) {
+ mode = "PIPELINE";
+ }
+ }
}
- public static class StoredModule {
+ public static class PresetStoredModule {
+ public String entryName;
+ public List attributes;
+ }
+ public static class PipelineStorage {
+ public List modules;
+ public List moduleConnections;
+ public String finalOutputModuleId;
+ public String finalOutputImageName;
+ }
+
+ public static class StoredModule {
public String id;
public String entryName;
public double x;
@@ -756,14 +1601,12 @@ public static class StoredModule {
}
public static class StoredAttribute {
-
public String type;
public String name;
public String value;
}
public static class StoredConnection {
-
public String srcModuleId;
public String srcImageName;
public String dstModuleId;
diff --git a/src/main/java/com/radiance/client/pipeline/Presets.java b/src/main/java/com/radiance/client/pipeline/Presets.java
new file mode 100644
index 0000000..54fe3f6
--- /dev/null
+++ b/src/main/java/com/radiance/client/pipeline/Presets.java
@@ -0,0 +1,17 @@
+package com.radiance.client.pipeline;
+
+public enum Presets {
+ RT_DLSSRR("RT_DLSSRR", "render_pipeline.preset.rt_dlss"),
+ RT_NRD("RT_NRD", "render_pipeline.preset.rt_nrd"),
+ RT_NRD_FSR("RT_NRD_FSR", "render_pipeline.preset.rt_nrd_fsr"),
+ RT_NRD_XESS("RT_NRD_XESS", "render_pipeline.preset.rt_nrd_xess"),
+ ;
+
+ public final String name;
+ public final String key;
+
+ Presets(String name, String key) {
+ this.name = name;
+ this.key = key;
+ }
+}
diff --git a/src/main/java/com/radiance/client/proxy/vulkan/BufferProxy.java b/src/main/java/com/radiance/client/proxy/vulkan/BufferProxy.java
index 3481584..d6ec9a1 100644
--- a/src/main/java/com/radiance/client/proxy/vulkan/BufferProxy.java
+++ b/src/main/java/com/radiance/client/proxy/vulkan/BufferProxy.java
@@ -7,11 +7,14 @@
import static org.lwjgl.system.MemoryUtil.memSet;
import com.mojang.blaze3d.systems.RenderSystem;
+import com.radiance.client.RadianceClient;
import com.radiance.client.constant.Constants;
+import com.radiance.client.option.EnvironmentRenderStyles;
import com.radiance.client.texture.TextureTracker;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.BuiltBuffer;
import net.minecraft.client.render.Camera;
@@ -25,6 +28,8 @@
public class BufferProxy {
+ private static final AtomicBoolean warnedAboutTextureSlotOverflow = new AtomicBoolean(false);
+
public static native int allocateBuffer();
public static native void initializeBuffer(int id, int size, int usageFlags);
@@ -282,7 +287,7 @@ public static void updateSkyUniform(float baseColorR, float baseColorG, float ba
float horizontalColorR, float horizontalColorG, float horizontalColorB,
float horizontalColorA, Vector3f sunDirection, int skyType, boolean sunRisingOrSetting,
boolean skyDark, boolean hasBlindnessOrDarkness, int submersionType, int moonPhase,
- float rainGradient, int sunTextureID, int moonTextureID) {
+ float rainGradient, float cloudLayerHeight, int sunTextureID, int moonTextureID) {
try (MemoryStack stack = stackPush()) {
int size = 160;
ByteBuffer bb = stack.malloc(size);
@@ -327,9 +332,12 @@ public static void updateSkyUniform(float baseColorR, float baseColorG, float ba
bb.putFloat(baseAddr, rainGradient);
baseAddr += Float.BYTES;
+ bb.putFloat(baseAddr, EnvironmentRenderStyles.waterSurfaceMode().shaderId());
+ baseAddr += Float.BYTES;
+ bb.putFloat(baseAddr, EnvironmentRenderStyles.cloudVolumeMode().shaderId());
baseAddr += Float.BYTES;
+ bb.putFloat(baseAddr, cloudLayerHeight);
baseAddr += Float.BYTES;
- baseAddr += Float.BYTES; // padding
// AtmosphereParams
baseAddr += Float.BYTES * 4 * 3; // skip
@@ -350,7 +358,7 @@ public static void updateSkyUniform(float baseColorR, float baseColorG, float ba
public static void updateMapping() {
try (MemoryStack stack = stackPush()) {
- final int elementCount = 4096;
+ final int elementCount = TextureTracker.MAX_TEXTURE_SLOTS;
int size = elementCount * Integer.BYTES * 3;
ByteBuffer bb = stack.malloc(size);
long addr = memAddress(bb);
@@ -363,9 +371,7 @@ public static void updateMapping() {
if (sourceID >= 0 && sourceID < elementCount) {
intView.put(sourceID * 3, targetID);
} else {
- throw new RuntimeException(
- "Specular mapping sourceID " + sourceID + " out of index [0, " + (
- elementCount - 1) + "]");
+ warnTextureSlotOverflow(sourceID, elementCount);
}
}
@@ -375,9 +381,7 @@ public static void updateMapping() {
if (sourceID >= 0 && sourceID < elementCount) {
intView.put(sourceID * 3 + 1, targetID);
} else {
- throw new RuntimeException(
- "Normal mapping sourceID " + sourceID + " out of index [0, " + (elementCount
- - 1) + "]");
+ warnTextureSlotOverflow(sourceID, elementCount);
}
}
@@ -387,9 +391,7 @@ public static void updateMapping() {
if (sourceID >= 0 && sourceID < elementCount) {
intView.put(sourceID * 3 + 2, targetID);
} else {
- throw new RuntimeException(
- "Flag mapping sourceID " + sourceID + " out of index [0, " + (elementCount
- - 1) + "]");
+ warnTextureSlotOverflow(sourceID, elementCount);
}
}
@@ -397,6 +399,14 @@ public static void updateMapping() {
}
}
+ private static void warnTextureSlotOverflow(int sourceId, int elementCount) {
+ if (warnedAboutTextureSlotOverflow.compareAndSet(false, true)) {
+ RadianceClient.LOGGER.warn(
+ "Texture id {} exceeded the supported mapping range [0, {}]. Auxiliary mappings for this id will be skipped.",
+ sourceId, elementCount - 1);
+ }
+ }
+
public static native void updateLightMapUniform(long ptr);
public static void updateLightMapUniform(float ambientLightFactor, float skyFactor,
diff --git a/src/main/java/com/radiance/client/proxy/vulkan/RendererProxy.java b/src/main/java/com/radiance/client/proxy/vulkan/RendererProxy.java
index 8ef59d3..fd996fe 100644
--- a/src/main/java/com/radiance/client/proxy/vulkan/RendererProxy.java
+++ b/src/main/java/com/radiance/client/proxy/vulkan/RendererProxy.java
@@ -2,23 +2,32 @@
import com.mojang.blaze3d.systems.RenderSystem;
import com.radiance.client.constant.Constants;
+import com.radiance.client.RadianceClient;
+import com.radiance.client.util.HdrPngScreenshotWriter;
import com.radiance.mixin_related.extensions.vulkan_render_integration.INativeImageExt;
+import java.nio.ByteBuffer;
+import java.nio.file.Path;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.texture.NativeImage;
import net.minecraft.client.util.Window;
+import org.lwjgl.system.MemoryUtil;
public class RendererProxy {
private static int pipelineType = -1;
+ private static volatile boolean shuttingDown = false;
public static native void initFolderPath(String folderPath);
public static native void initRenderer(String[] glfwLibCandidates, long windowHandle);
+ public static native void beginShutdownNative();
+
public static void initRenderer(Window window) {
String mapped = System.mapLibraryName("glfw");
String[] candidates = {mapped, "libglfw.so.3", "libglfw.3.dylib", "glfw3.dll"};
+ shuttingDown = false;
RendererProxy.initRenderer(candidates, window.getHandle());
RenderSystem.apiDescription = "Vulkan 1.4";
}
@@ -32,17 +41,61 @@ public static void initRenderer(Window window) {
public static native void present();
public static void submitCommandAndPresent() {
- submitCommand();
- present();
+ synchronized (TextureProxy.class) {
+ if (shuttingDown) {
+ return;
+ }
+ submitCommand();
+ if (shuttingDown) {
+ return;
+ }
+ present();
+ }
+ }
+
+ public static void requestShutdown() {
+ if (shuttingDown) {
+ return;
+ }
+ shuttingDown = true;
+ synchronized (TextureProxy.class) {
+ beginShutdownNative();
+ }
+ }
+
+ public static boolean isShuttingDown() {
+ return shuttingDown;
+ }
+
+ public static void closeRenderer() {
+ shuttingDown = true;
+ synchronized (TextureProxy.class) {
+ close();
+ }
}
public static void bindOverlayPipeline(int type) {
pipelineType = type;
}
+ public static boolean hasOverlayPipeline() {
+ return pipelineType >= 0;
+ }
+
+ public static int getOverlayPipelineType() {
+ return pipelineType;
+ }
+
public static native void drawOverlay(int vertexId, int indexId, int pipelineType,
int indexCount, int indexType);
+ public static void drawOverlay(BufferProxy.VertexIndexBufferHandle handle, int indexCount,
+ int pipelineType,
+ VertexFormat.IndexType indexType) {
+ drawOverlay(handle.vertexId, handle.indexId, pipelineType, indexCount,
+ Constants.IndexTypes.getValue(indexType));
+ }
+
public static void drawOverlay(BufferProxy.VertexIndexBufferHandle handle, int indexCount,
VertexFormat.IndexType indexType) {
drawOverlay(handle.vertexId, handle.indexId, pipelineType, indexCount,
@@ -53,7 +106,7 @@ public static void drawOverlay(BufferProxy.VertexIndexBufferHandle handle, int i
public static native void postBlur();
- public static native void close();
+ private static native void close();
public static native void shouldRenderWorld(boolean renderWorld);
@@ -71,7 +124,21 @@ public static NativeImage takeScreenshotWithoutUI() {
mc.getWindow()
.getHeight();
NativeImage nativeImage = new NativeImage(width, height, false);
- ((INativeImageExt) (Object) nativeImage).neoVoxelRT$loadFromTextureImageWithoutUI(0, true);
+ ((INativeImageExt) (Object) nativeImage).radiance$loadFromTextureImageWithoutUI(0, true);
return nativeImage;
}
+
+ public static Path exportHdrScreenshot() {
+ MinecraftClient mc = MinecraftClient.getInstance();
+ int width = mc.getWindow().getWidth();
+ int height = mc.getWindow().getHeight();
+ ByteBuffer buffer = MemoryUtil.memAlloc(width * height * 8);
+ try {
+ takeScreenshot(false, width, height, 8, MemoryUtil.memAddress(buffer));
+ Path directory = RadianceClient.radianceDir.resolve("screenshots");
+ return HdrPngScreenshotWriter.write(directory, width, height, buffer);
+ } finally {
+ MemoryUtil.memFree(buffer);
+ }
+ }
}
diff --git a/src/main/java/com/radiance/client/proxy/vulkan/TextureProxy.java b/src/main/java/com/radiance/client/proxy/vulkan/TextureProxy.java
index 4c1c560..0285d91 100644
--- a/src/main/java/com/radiance/client/proxy/vulkan/TextureProxy.java
+++ b/src/main/java/com/radiance/client/proxy/vulkan/TextureProxy.java
@@ -7,6 +7,8 @@ public class TextureProxy {
public synchronized static native int generateTextureId();
+ public synchronized static native void releaseTextureId(int id);
+
public synchronized static native void prepareImage(int id, int mipLevels, int width,
int height, int format);
@@ -31,24 +33,26 @@ public synchronized static native void queueUpload(long srcPointer,
int height,
int level);
+ public synchronized static native void setTextureAlphaClass(int id, int alphaClass);
+
public static void prepareImage(NativeImage.InternalFormat internalFormat, int id,
int mipLevels, int width, int height) {
switch (internalFormat) {
case RGBA:
prepareImage(id, mipLevels, width, height,
- VulkanConstants.VkFormat.VK_FORMAT_R8G8B8A8_UNORM);
+ VulkanConstants.VkFormat.VK_FORMAT_R8G8B8A8_SRGB);
break;
case RGB:
prepareImage(id, mipLevels, width, height,
- VulkanConstants.VkFormat.VK_FORMAT_R8G8B8_UNORM);
+ VulkanConstants.VkFormat.VK_FORMAT_R8G8B8_SRGB);
break;
case RG:
prepareImage(id, mipLevels, width, height,
- VulkanConstants.VkFormat.VK_FORMAT_R8G8_UNORM);
+ VulkanConstants.VkFormat.VK_FORMAT_R8G8_SRGB);
break;
case RED:
prepareImage(id, mipLevels, width, height,
- VulkanConstants.VkFormat.VK_FORMAT_R8_UNORM);
+ VulkanConstants.VkFormat.VK_FORMAT_R8_SRGB);
break;
}
}
diff --git a/src/main/java/com/radiance/client/proxy/world/AtlasSafeGreedyMesher.java b/src/main/java/com/radiance/client/proxy/world/AtlasSafeGreedyMesher.java
new file mode 100644
index 0000000..61d31f1
--- /dev/null
+++ b/src/main/java/com/radiance/client/proxy/world/AtlasSafeGreedyMesher.java
@@ -0,0 +1,659 @@
+package com.radiance.client.proxy.world;
+
+import com.radiance.client.constant.Constants;
+import com.radiance.client.vertex.PBRVertexFormatElements;
+import com.radiance.client.vertex.PBRVertexFormats;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import net.minecraft.client.render.BuiltBuffer;
+import net.minecraft.client.render.VertexFormat;
+import net.minecraft.client.util.BufferAllocator;
+import org.lwjgl.system.MemoryUtil;
+
+final class AtlasSafeGreedyMesher {
+
+ private static final float EPSILON = 1.0e-4f;
+ private static final int STRIDE = PBRVertexFormats.PBR_TRIANGLE.getVertexSizeByte();
+ private static final int POS_OFFSET =
+ PBRVertexFormats.PBR_TRIANGLE.getOffsetsByElementId()[PBRVertexFormatElements.PBR_POS.id()];
+ private static final int UV_OFFSET = PBRVertexFormats.PBR_TRIANGLE.getOffsetsByElementId()[
+ PBRVertexFormatElements.PBR_TEXTURE_UV.id()];
+
+ private AtlasSafeGreedyMesher() {
+ }
+
+ static BuiltBuffer optimize(BuiltBuffer builtBuffer,
+ RayTracingTuning.TerrainMeshingMode terrainMeshingMode, int maxMergeSpan) {
+ BuiltBuffer.DrawParameters drawParameters = builtBuffer.getDrawParameters();
+ if (terrainMeshingMode == RayTracingTuning.TerrainMeshingMode.LEGACY_QUADS
+ || drawParameters.mode() != VertexFormat.DrawMode.QUADS
+ || Constants.VertexFormats.getValue(drawParameters.format())
+ != Constants.VertexFormats.PBR_TRIANGLE.getValue()
+ || drawParameters.vertexCount() < 8) {
+ return builtBuffer;
+ }
+
+ ByteBuffer byteBuffer = builtBuffer.getBuffer().slice().order(ByteOrder.nativeOrder());
+ int quadCount = drawParameters.vertexCount() / 4;
+ List quads = new ArrayList<>(quadCount);
+ for (int quadIndex = 0; quadIndex < quadCount; quadIndex++) {
+ quads.add(Quad.parse(byteBuffer, quadIndex * 4 * STRIDE));
+ }
+
+ List optimizedQuads = packGreedy(quads, terrainMeshingMode,
+ Math.max(1, maxMergeSpan));
+ if (optimizedQuads == quads) {
+ return builtBuffer;
+ }
+
+ int totalSize = optimizedQuads.size() * 4 * STRIDE;
+ BufferAllocator bufferAllocator = new BufferAllocator(totalSize);
+ long address = bufferAllocator.allocate(totalSize);
+ ByteBuffer output = MemoryUtil.memByteBuffer(address, totalSize);
+ for (Quad quad : optimizedQuads) {
+ quad.write(output);
+ }
+ output.flip();
+
+ BufferAllocator.CloseableBuffer closeableBuffer = bufferAllocator.getAllocated();
+ if (closeableBuffer == null) {
+ bufferAllocator.close();
+ throw new IllegalStateException("Failed to allocate merged chunk buffer");
+ }
+
+ return new BuiltBuffer(closeableBuffer, new BuiltBuffer.DrawParameters(
+ drawParameters.format(),
+ optimizedQuads.size() * 4,
+ drawParameters.mode().getIndexCount(optimizedQuads.size() * 4),
+ drawParameters.mode(),
+ VertexFormat.IndexType.smallestFor(optimizedQuads.size() * 4)));
+ }
+
+ private static List packGreedy(List quads,
+ RayTracingTuning.TerrainMeshingMode terrainMeshingMode, int maxMergeSpan) {
+ Map mergeGroups = new LinkedHashMap<>();
+ List