Merge branch 'imbris/wgpu-master-rebased' into 'master'

wgpu

See merge request veloren/veloren!1947
This commit is contained in:
Imbris 2021-06-03 19:58:48 +00:00
commit e7a766b310
124 changed files with 10775 additions and 6141 deletions

View File

@ -9,9 +9,11 @@ csv-import = "run --manifest-path common/Cargo.toml --features=bin_csv --bin csv
test-server = "run --bin veloren-server-cli --no-default-features -- -b" test-server = "run --bin veloren-server-cli --no-default-features -- -b"
tracy-server = "-Zunstable-options run --bin veloren-server-cli --no-default-features --features tracy,simd --profile no_overflow" tracy-server = "-Zunstable-options run --bin veloren-server-cli --no-default-features --features tracy,simd --profile no_overflow"
tracy-world-server = "-Zunstable-options run --bin veloren-server-cli --features tracy,simd --profile no_overflow -- -b" tracy-world-server = "-Zunstable-options run --bin veloren-server-cli --features tracy,simd --profile no_overflow -- -b"
test-voxygen = "run --bin veloren-voxygen --no-default-features --features gl,simd" test-voxygen = "run --bin veloren-voxygen --no-default-features --features simd"
tracy-voxygen = "-Zunstable-options run --bin veloren-voxygen --no-default-features --features tracy,gl,simd --profile no_overflow" tracy-voxygen = "-Zunstable-options run --bin veloren-voxygen --no-default-features --features tracy,simd --profile no_overflow"
server = "run --bin veloren-server-cli" server = "run --bin veloren-server-cli"
dbg-voxygen = "run --bin veloren-voxygen -Zunstable-options --profile debuginfo"
[env] [env]
RUSTC_FORCE_INCREMENTAL = "1" RUSTC_FORCE_INCREMENTAL = "1"

View File

@ -37,6 +37,7 @@ before_script:
- export VELOREN_ASSETS="$(pwd)/assets" - export VELOREN_ASSETS="$(pwd)/assets"
- echo "VELOREN_ASSETS=$VELOREN_ASSETS" - echo "VELOREN_ASSETS=$VELOREN_ASSETS"
- export RUSTFLAGS="-D warnings" - export RUSTFLAGS="-D warnings"
- export SHADERC_LIB_DIR=/shaderc/combined/
- rm -rf target || echo "it seems that sometimes OLD data is left over" - rm -rf target || echo "it seems that sometimes OLD data is left over"
# 8866215 is the user that is used to sync data to the collaboration repos # 8866215 is the user that is used to sync data to the collaboration repos

View File

@ -75,17 +75,25 @@ coverage:
.twindows: .twindows:
image: registry.gitlab.com/veloren/veloren-docker-ci/cache/release-windows:${CACHE_IMAGE_TAG} image: registry.gitlab.com/veloren/veloren-docker-ci/cache/release-windows:${CACHE_IMAGE_TAG}
script: script:
- update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix
- update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
- ln -s /dockercache/target target - ln -s /dockercache/target target
- rm -r target/release/incremental/veloren_* || echo "all good" # TMP FIX FOR 2021-03-22-nightly - rm -r target/release/incremental/veloren_* || echo "all good" # TMP FIX FOR 2021-03-22-nightly
- VELOREN_USERDATA_STRATEGY=executable cargo build --target=x86_64-pc-windows-gnu --release - VELOREN_USERDATA_STRATEGY=executable cargo build --target=x86_64-pc-windows-gnu --release
- cp -r target/x86_64-pc-windows-gnu/release/veloren-server-cli.exe $CI_PROJECT_DIR - cp -r target/x86_64-pc-windows-gnu/release/veloren-server-cli.exe $CI_PROJECT_DIR
- cp -r target/x86_64-pc-windows-gnu/release/veloren-voxygen.exe $CI_PROJECT_DIR - cp -r target/x86_64-pc-windows-gnu/release/veloren-voxygen.exe $CI_PROJECT_DIR
- cp /usr/lib/gcc/x86_64-w64-mingw32/7.3-posix/libgcc_s_seh-1.dll $CI_PROJECT_DIR
- cp /usr/lib/gcc/x86_64-w64-mingw32/7.3-posix/libstdc++-6.dll $CI_PROJECT_DIR
- cp /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll $CI_PROJECT_DIR
artifacts: artifacts:
paths: paths:
- veloren-server-cli.exe - veloren-server-cli.exe
- veloren-voxygen.exe - veloren-voxygen.exe
- assets/ - assets/
- LICENSE - LICENSE
- libgcc_s_seh-1.dll
- libstdc++-6.dll
- libwinpthread-1.dll
expire_in: 1 week expire_in: 1 week
.tmacos: .tmacos:

View File

@ -65,6 +65,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Renamed Animal Trainers to Beastmasters and gave them their own set of armor to wear - Renamed Animal Trainers to Beastmasters and gave them their own set of armor to wear
- ChargedRanged attacks (such as some bow attacks) use an FOV zoom effect to indicate charge. - ChargedRanged attacks (such as some bow attacks) use an FOV zoom effect to indicate charge.
- Add chest to each dungeon with unique loot - Add chest to each dungeon with unique loot
- Added a new option in the graphics menu to enable GPU timing (not always supported). The timing values can be viewed in the HUD debug info (F3) and will be saved as chrome trace files in the working directory when taking a screenshot.
- Added new Present Mode option in the graphics menu. Selecting Fifo (i.e. vsync) or Mailbox can be used to eliminate screen tearing.
### Changed ### Changed
@ -113,6 +115,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Water extinguishes entities on fire - Water extinguishes entities on fire
- Item pickups are shown in separate window and inventory-full shows above item - Item pickups are shown in separate window and inventory-full shows above item
- Reworked bow - Reworked bow
- Switched to the `wgpu` graphics library giving us support for vulkan, dx12, metal, and dx11 (support for opengl is lost for the moment). This improves the graphics performance for many users.
- Reworked sprite rendering to vastly reduce the CPU work. Large sprite view distances are now much more performant.
- Optimized rendering of quads (most of the graphics in the game) using an index buffer, decreasing the number of vertices that need to be processed by 33%.
- Moved the rest of screenshot work into the background. Screenshoting no longer induces large pauses.
### Removed ### Removed

784
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -98,11 +98,6 @@ incremental = true
inherits = 'release' inherits = 'release'
debug = 1 debug = 1
[patch.crates-io]
# macos CI fix isn't merged yet
winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" }
vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics2" }
[workspace.metadata.nix] [workspace.metadata.nix]
systems = ["x86_64-linux"] systems = ["x86_64-linux"]
@ -113,3 +108,36 @@ key = "veloren-nix.cachix.org-1:zokfKJqVsNV6kI/oJdLF6TYBdNPYGSb+diMVQPn/5Rc="
[workspace.metadata.nix.crateOverride.veloren-network] [workspace.metadata.nix.crateOverride.veloren-network]
buildInputs = ["openssl"] buildInputs = ["openssl"]
nativeBuildInputs = ["pkg-config"] nativeBuildInputs = ["pkg-config"]
[patch.crates-io]
# macos CI fix isn't released yet
winit = { git = "https://gitlab.com/veloren/winit.git", branch = "macos-test-spiffed" }
vek = { git = "https://gitlab.com/veloren/vek.git", branch = "fix_intrinsics2" }
# patch wgpu so we can use wgpu-profiler crate
wgpu = { git = "https://github.com/gfx-rs/wgpu-rs.git", rev = "7486bdad64bb5d17b709ecccb41e063469efff88" }
# # use the latest fixes in naga (remove when updates trickle down to wgpu-rs)
# naga = { git = "https://github.com/gfx-rs/naga.git", rev = "3a0f0144112ff621dd7f731bf455adf6cab19164" }
# # use the latest fixes in gfx (remove when updates trickle down to wgpu-rs)
# gfx-hal = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" }
# gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" }
# gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" }
# gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" }
# gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" }
# gfx-backend-dx11 = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" }
# gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx.git", rev = "e305dcca3557923a6a8810162d8dd09cb45a43a6" }
# # Uncomment this to use a local fork of wgpu (for testing purposes)
# [patch.'https://github.com/gfx-rs/wgpu']
# wgpu-core = { path = "../wgpu/wgpu-core" }
# wgpu-types = { path = "../wgpu/wgpu-types" }
# # Uncomment this to use a local fork of gfx-hal (for testing purposes)
# [patch."https://github.com/gfx-rs/gfx"]
# gfx-hal = { path = "../gfx/src/hal" }
# gfx-backend-empty = { path = "../gfx/src/backend/empty" }
# gfx-backend-vulkan = { path = "../gfx/src/backend/vulkan" }
# gfx-backend-gl = { path = "../gfx/src/backend/gl" }
# gfx-backend-dx12 = { path = "../gfx/src/backend/dx12" }
# gfx-backend-dx11 = { path = "../gfx/src/backend/dx11" }
# gfx-backend-metal = { path = "../gfx/src/backend/metal" }

View File

@ -10,6 +10,7 @@
"hud.settings.press_behavior.hold": "Hold", "hud.settings.press_behavior.hold": "Hold",
"hud.settings.help_window": "Help Window", "hud.settings.help_window": "Help Window",
"hud.settings.debug_info": "Debug Info", "hud.settings.debug_info": "Debug Info",
"hud.settings.show_hitboxes": "Show hitboxes",
"hud.settings.tips_on_startup": "Tips-On-Startup", "hud.settings.tips_on_startup": "Tips-On-Startup",
"hud.settings.ui_scale": "UI-Scale", "hud.settings.ui_scale": "UI-Scale",
"hud.settings.relative_scaling": "Relative Scaling", "hud.settings.relative_scaling": "Relative Scaling",
@ -57,6 +58,10 @@
"hud.settings.sprites_view_distance": "Sprites View Distance", "hud.settings.sprites_view_distance": "Sprites View Distance",
"hud.settings.figures_view_distance": "Entities View Distance", "hud.settings.figures_view_distance": "Entities View Distance",
"hud.settings.maximum_fps": "Maximum FPS", "hud.settings.maximum_fps": "Maximum FPS",
"hud.settings.present_mode": "Present Mode",
"hud.settings.present_mode.fifo": "Fifo",
"hud.settings.present_mode.mailbox": "Mailbox",
"hud.settings.present_mode.immediate": "Immediate",
"hud.settings.fov": "Field of View (deg)", "hud.settings.fov": "Field of View (deg)",
"hud.settings.gamma": "Gamma", "hud.settings.gamma": "Gamma",
"hud.settings.exposure": "Exposure", "hud.settings.exposure": "Exposure",
@ -76,6 +81,7 @@
"hud.settings.fullscreen_mode": "Fullscreen Mode", "hud.settings.fullscreen_mode": "Fullscreen Mode",
"hud.settings.fullscreen_mode.exclusive": "Exclusive", "hud.settings.fullscreen_mode.exclusive": "Exclusive",
"hud.settings.fullscreen_mode.borderless": "Borderless", "hud.settings.fullscreen_mode.borderless": "Borderless",
"hud.settings.gpu_profiler": "Enable GPU timing (not supported everywhere)",
"hud.settings.particles": "Particles", "hud.settings.particles": "Particles",
"hud.settings.lossy_terrain_compression": "Lossy terrain compression", "hud.settings.lossy_terrain_compression": "Lossy terrain compression",
"hud.settings.resolution": "Resolution", "hud.settings.resolution": "Resolution",

View File

@ -1,5 +1,3 @@
uniform sampler2D src_color;
const float FXAA_SCALE = 1.25; const float FXAA_SCALE = 1.25;
/** /**
@ -57,17 +55,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//optimized version for mobile, where dependent //optimized version for mobile, where dependent
//texture reads can be a bottleneck //texture reads can be a bottleneck
vec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 resolution, vec4 fxaa(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution,
vec2 v_rgbNW, vec2 v_rgbNE, vec2 v_rgbNW, vec2 v_rgbNE,
vec2 v_rgbSW, vec2 v_rgbSE, vec2 v_rgbSW, vec2 v_rgbSE,
vec2 v_rgbM) { vec2 v_rgbM) {
vec4 color; vec4 color;
mediump vec2 inverseVP = vec2(1.0 / resolution.x, 1.0 / resolution.y); mediump vec2 inverseVP = vec2(1.0 / resolution.x, 1.0 / resolution.y);
vec3 rgbNW = texture(tex, v_rgbNW).xyz; vec3 rgbNW = texture(sampler2D(tex, smplr), v_rgbNW).xyz;
vec3 rgbNE = texture(tex, v_rgbNE).xyz; vec3 rgbNE = texture(sampler2D(tex, smplr), v_rgbNE).xyz;
vec3 rgbSW = texture(tex, v_rgbSW).xyz; vec3 rgbSW = texture(sampler2D(tex, smplr), v_rgbSW).xyz;
vec3 rgbSE = texture(tex, v_rgbSE).xyz; vec3 rgbSE = texture(sampler2D(tex, smplr), v_rgbSE).xyz;
vec4 texColor = texture(tex, v_rgbM); vec4 texColor = texture(sampler2D(tex, smplr), v_rgbM);
vec3 rgbM = texColor.xyz; vec3 rgbM = texColor.xyz;
vec3 luma = vec3(0.299, 0.587, 0.114); vec3 luma = vec3(0.299, 0.587, 0.114);
float lumaNW = dot(rgbNW, luma); float lumaNW = dot(rgbNW, luma);
@ -91,11 +89,11 @@ vec4 fxaa(sampler2D tex, vec2 fragCoord, vec2 resolution,
dir * rcpDirMin)) * inverseVP; dir * rcpDirMin)) * inverseVP;
vec3 rgbA = 0.5 * ( vec3 rgbA = 0.5 * (
texture(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + texture(sampler2D(tex, smplr), fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz +
texture(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); texture(sampler2D(tex, smplr), fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz);
vec3 rgbB = rgbA * 0.5 + 0.25 * ( vec3 rgbB = rgbA * 0.5 + 0.25 * (
texture(tex, fragCoord * inverseVP + dir * -0.5).xyz + texture(sampler2D(tex, smplr), fragCoord * inverseVP + dir * -0.5).xyz +
texture(tex, fragCoord * inverseVP + dir * 0.5).xyz); texture(sampler2D(tex, smplr), fragCoord * inverseVP + dir * 0.5).xyz);
float lumaB = dot(rgbB, luma); float lumaB = dot(rgbB, luma);
if ((lumaB < lumaMin) || (lumaB > lumaMax)) if ((lumaB < lumaMin) || (lumaB > lumaMax))
@ -119,7 +117,7 @@ void texcoords(vec2 fragCoord, vec2 resolution,
} }
vec4 aa_apply(sampler2D tex, vec2 fragCoord, vec2 resolution) { vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) {
mediump vec2 v_rgbNW; mediump vec2 v_rgbNW;
mediump vec2 v_rgbNE; mediump vec2 v_rgbNE;
mediump vec2 v_rgbSW; mediump vec2 v_rgbSW;
@ -133,5 +131,5 @@ vec4 aa_apply(sampler2D tex, vec2 fragCoord, vec2 resolution) {
texcoords(scaled_fc, scaled_res, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); texcoords(scaled_fc, scaled_res, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM);
//compute FXAA //compute FXAA
return fxaa(tex, scaled_fc, scaled_res, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); return fxaa(tex, smplr, scaled_fc, scaled_res, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM);
} }

View File

@ -1,24 +1,22 @@
uniform sampler2DMS src_color; vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) {
vec4 aa_apply(sampler2DMS tex, vec2 fragCoord, vec2 resolution) {
ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y); ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y);
vec4 sample1 = texelFetch(tex, texel_coord, 0); vec4 sample1 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 0);
vec4 sample2 = texelFetch(tex, texel_coord, 1); vec4 sample2 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 1);
vec4 sample3 = texelFetch(tex, texel_coord, 2); vec4 sample3 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 2);
vec4 sample4 = texelFetch(tex, texel_coord, 3); vec4 sample4 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 3);
vec4 sample5 = texelFetch(tex, texel_coord, 4); vec4 sample5 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 4);
vec4 sample6 = texelFetch(tex, texel_coord, 5); vec4 sample6 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 5);
vec4 sample7 = texelFetch(tex, texel_coord, 6); vec4 sample7 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 6);
vec4 sample8 = texelFetch(tex, texel_coord, 7); vec4 sample8 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 7);
vec4 sample9 = texelFetch(tex, texel_coord, 8); vec4 sample9 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 8);
vec4 sample10 = texelFetch(tex, texel_coord, 9); vec4 sample10 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 9);
vec4 sample11 = texelFetch(tex, texel_coord, 10); vec4 sample11 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 10);
vec4 sample12 = texelFetch(tex, texel_coord, 11); vec4 sample12 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 11);
vec4 sample13 = texelFetch(tex, texel_coord, 12); vec4 sample13 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 12);
vec4 sample14 = texelFetch(tex, texel_coord, 13); vec4 sample14 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 13);
vec4 sample15 = texelFetch(tex, texel_coord, 14); vec4 sample15 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 14);
vec4 sample16 = texelFetch(tex, texel_coord, 15); vec4 sample16 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 15);
// Average Samples // Average Samples
vec4 msaa_color = ( vec4 msaa_color = (

View File

@ -1,12 +1,10 @@
uniform sampler2DMS src_color; vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) {
vec4 aa_apply(sampler2DMS tex, vec2 fragCoord, vec2 resolution) {
ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y); ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y);
vec4 sample1 = texelFetch(tex, texel_coord, 0); vec4 sample1 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 0);
vec4 sample2 = texelFetch(tex, texel_coord, 1); vec4 sample2 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 1);
vec4 sample3 = texelFetch(tex, texel_coord, 2); vec4 sample3 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 2);
vec4 sample4 = texelFetch(tex, texel_coord, 3); vec4 sample4 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 3);
// Average Samples // Average Samples
vec4 msaa_color = (sample1 + sample2 + sample3 + sample4) / 4.0; vec4 msaa_color = (sample1 + sample2 + sample3 + sample4) / 4.0;

View File

@ -1,16 +1,14 @@
uniform sampler2DMS src_color; vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) {
vec4 aa_apply(sampler2DMS tex, vec2 fragCoord, vec2 resolution) {
ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y); ivec2 texel_coord = ivec2(fragCoord.x, fragCoord.y);
vec4 sample1 = texelFetch(tex, texel_coord, 0); vec4 sample1 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 0);
vec4 sample2 = texelFetch(tex, texel_coord, 1); vec4 sample2 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 1);
vec4 sample3 = texelFetch(tex, texel_coord, 2); vec4 sample3 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 2);
vec4 sample4 = texelFetch(tex, texel_coord, 3); vec4 sample4 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 3);
vec4 sample5 = texelFetch(tex, texel_coord, 4); vec4 sample5 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 4);
vec4 sample6 = texelFetch(tex, texel_coord, 5); vec4 sample6 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 5);
vec4 sample7 = texelFetch(tex, texel_coord, 6); vec4 sample7 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 6);
vec4 sample8 = texelFetch(tex, texel_coord, 7); vec4 sample8 = texelFetch(sampler2DMS(tex, smplr), texel_coord, 7);
// Average Samples // Average Samples
vec4 msaa_color = (sample1 + sample2 + sample3 + sample4 + sample5 + sample6 + sample7 + sample8) / 8.0; vec4 msaa_color = (sample1 + sample2 + sample3 + sample4 + sample5 + sample6 + sample7 + sample8) / 8.0;

View File

@ -1,5 +1,3 @@
uniform sampler2D src_color; vec4 aa_apply(texture2D tex, sampler smplr, vec2 fragCoord, vec2 resolution) {
return texture(sampler2D(tex, smplr), fragCoord / resolution);
vec4 aa_apply(sampler2D tex, vec2 fragCoord, vec2 resolution) {
return texture(src_color, fragCoord / resolution);
} }

View File

@ -0,0 +1,16 @@
#version 420 core
layout(set = 0, binding = 0)
uniform texture2D t_src_color;
layout(set = 0, binding = 1)
uniform sampler s_src_color;
layout(location = 0) in vec2 uv;
layout(location = 0) out vec4 tgt_color;
void main() {
vec4 color = texture(sampler2D(t_src_color, s_src_color), uv);
tgt_color = vec4(color.rgb, 1);
}

View File

@ -0,0 +1,15 @@
#version 420 core
layout(location = 0) out vec2 uv;
void main() {
// Generate fullscreen triangle
vec2 v_pos = vec2(
float(gl_VertexIndex / 2) * 4.0 - 1.0,
float(gl_VertexIndex % 2) * 4.0 - 1.0
);
uv = (v_pos * vec2(1.0, -1.0) + 1.0) * 0.5;
gl_Position = vec4(v_pos, 0.0, 1.0);
}

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -22,46 +22,45 @@
#include <srgb.glsl> #include <srgb.glsl>
#include <cloud.glsl> #include <cloud.glsl>
uniform sampler2D src_depth; layout(set = 1, binding = 0)
uniform texture2D t_src_color;
layout(set = 1, binding = 1)
uniform sampler s_src_color;
in vec2 f_pos; layout(set = 1, binding = 2)
uniform texture2D t_src_depth;
layout(set = 1, binding = 3)
uniform sampler s_src_depth;
layout (std140) layout(location = 0) in vec2 uv;
layout (std140, set = 1, binding = 4)
uniform u_locals { uniform u_locals {
mat4 proj_mat_inv; mat4 proj_mat_inv;
mat4 view_mat_inv; mat4 view_mat_inv;
}; };
out vec4 tgt_color; layout(location = 0) out vec4 tgt_color;
float depth_at(vec2 uv) {
float buf_depth = texture(src_depth, uv).x;
vec4 clip_space = vec4(uv * 2.0 - 1.0, buf_depth, 1.0);
vec4 view_space = proj_mat_inv * clip_space;
view_space /= view_space.w;
return -view_space.z;
}
vec3 wpos_at(vec2 uv) { vec3 wpos_at(vec2 uv) {
float buf_depth = texture(src_depth, uv).x * 2.0 - 1.0; float buf_depth = texture(sampler2D(t_src_depth, s_src_depth), uv).x;
mat4 inv = view_mat_inv * proj_mat_inv;//inverse(all_mat); mat4 inv = view_mat_inv * proj_mat_inv;//inverse(all_mat);
vec4 clip_space = vec4(uv * 2.0 - 1.0, buf_depth, 1.0); vec4 clip_space = vec4((uv * 2.0 - 1.0) * vec2(1, -1), buf_depth, 1.0);
vec4 view_space = inv * clip_space; vec4 view_space = inv * clip_space;
view_space /= view_space.w; view_space /= view_space.w;
if (buf_depth == 1.0) { if (buf_depth == 0.0) {
vec3 direction = normalize(view_space.xyz); vec3 direction = normalize(view_space.xyz);
return direction.xyz * 100000.0 + cam_pos.xyz; return direction.xyz * 524288.0625 + cam_pos.xyz;
} else { } else {
return view_space.xyz; return view_space.xyz;
} }
} }
void main() { void main() {
vec2 uv = (f_pos + 1.0) * 0.5; vec4 color = texture(sampler2D(t_src_color, s_src_color), uv);
vec4 color = texture(src_color, uv); // Apply clouds
// Apply clouds to `aa_color`
#if (CLOUD_MODE != CLOUD_MODE_NONE) #if (CLOUD_MODE != CLOUD_MODE_NONE)
vec3 wpos = wpos_at(uv); vec3 wpos = wpos_at(uv);
float dist = distance(wpos, cam_pos.xyz); float dist = distance(wpos, cam_pos.xyz);

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -18,12 +18,17 @@
#include <globals.glsl> #include <globals.glsl>
in vec2 v_pos; layout(location = 0) out vec2 uv;
out vec2 f_pos;
void main() { void main() {
f_pos = v_pos; // Generate fullscreen triangle
vec2 v_pos = vec2(
float(gl_VertexIndex / 2) * 4.0 - 1.0,
float(gl_VertexIndex % 2) * 4.0 - 1.0
);
gl_Position = vec4(v_pos, -1.0, 1.0); // Flip y and transform into 0.0 to 1.0 range
uv = (v_pos * vec2(1.0, -1.0) + 1.0) * 0.5;
gl_Position = vec4(v_pos, 0.0, 1.0);
} }

View File

@ -0,0 +1,19 @@
#version 420 core
#include <globals.glsl>
layout (location = 0)
in vec4 f_color;
layout (std140, set = 1, binding = 0)
uniform u_locals {
vec4 w_pos;
vec4 w_color;
};
layout (location = 0)
out vec4 tgt_color;
void main() {
tgt_color = f_color;
}

View File

@ -0,0 +1,20 @@
#version 420 core
#include <globals.glsl>
layout (location = 0)
in vec3 v_pos;
layout (std140, set = 1, binding = 0)
uniform u_locals {
vec4 w_pos;
vec4 w_color;
};
layout (location = 0)
out vec4 f_color;
void main() {
f_color = w_color;
gl_Position = all_mat * vec4((v_pos + w_pos.xyz) - focus_off.xyz, 1);
}

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#define FIGURE_SHADER #define FIGURE_SHADER
@ -17,14 +17,17 @@
#define HAS_SHADOW_MAPS #define HAS_SHADOW_MAPS
#include <globals.glsl> #include <globals.glsl>
#include <light.glsl>
#include <cloud.glsl>
#include <lod.glsl>
in vec3 f_pos; layout(location = 0) in vec3 f_pos;
// in float dummy; // in float dummy;
// in vec3 f_col; // in vec3 f_col;
// in float f_ao; // in float f_ao;
// flat in uint f_pos_norm; // flat in uint f_pos_norm;
flat in vec3 f_norm; layout(location = 1) flat in vec3 f_norm;
/*centroid */in vec2 f_uv_pos; /*centroid */layout(location = 2) in vec2 f_uv_pos;
// in float f_alt; // in float f_alt;
// in vec4 f_shadow; // in vec4 f_shadow;
// in vec3 light_pos[2]; // in vec3 light_pos[2];
@ -35,7 +38,10 @@ flat in vec3 f_norm;
// const vec4 sun_pos = vec4(0.0); // const vec4 sun_pos = vec4(0.0);
// #endif // #endif
uniform sampler2D t_col_light; layout(set = 3, binding = 0)
uniform texture2D t_col_light;
layout(set = 3, binding = 1)
uniform sampler s_col_light;
//struct ShadowLocals { //struct ShadowLocals {
// mat4 shadowMatrices; // mat4 shadowMatrices;
@ -47,7 +53,7 @@ uniform sampler2D t_col_light;
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
//}; //};
layout (std140) layout (std140, set = 2, binding = 0)
uniform u_locals { uniform u_locals {
mat4 model_mat; mat4 model_mat;
vec4 highlight_col; vec4 highlight_col;
@ -65,16 +71,12 @@ struct BoneData {
mat4 normals_mat; mat4 normals_mat;
}; };
layout (std140) layout (std140, set = 2, binding = 1)
uniform u_bones { uniform u_bones {
BoneData bones[16]; BoneData bones[16];
}; };
#include <cloud.glsl> layout(location = 0) out vec4 tgt_color;
#include <light.glsl>
#include <lod.glsl>
out vec4 tgt_color;
void main() { void main() {
// vec2 texSize = textureSize(t_col_light, 0); // vec2 texSize = textureSize(t_col_light, 0);
@ -88,8 +90,7 @@ void main() {
float f_ao, f_glow; float f_ao, f_glow;
uint material = 0xFFu; uint material = 0xFFu;
vec3 f_col = greedy_extract_col_light_attr(t_col_light, f_uv_pos, f_ao, f_glow, material); vec3 f_col = greedy_extract_col_light_attr(t_col_light, s_col_light, f_uv_pos, f_ao, f_glow, material);
// float /*f_light*/f_ao = textureProj(t_col_light, vec3(f_uv_pos, texSize)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; // float /*f_light*/f_ao = textureProj(t_col_light, vec3(f_uv_pos, texSize)).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0;
// vec3 my_chunk_pos = (vec3((uvec3(f_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0; // vec3 my_chunk_pos = (vec3((uvec3(f_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0;
@ -131,7 +132,7 @@ void main() {
#endif #endif
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP)
vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy));
float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
#elif (SHADOW_MODE == SHADOW_MODE_NONE) #elif (SHADOW_MODE == SHADOW_MODE_NONE)
float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir);

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -17,15 +17,15 @@
#include <globals.glsl> #include <globals.glsl>
#include <lod.glsl> #include <lod.glsl>
in uint v_pos_norm; layout(location = 0) in uint v_pos_norm;
in uint v_atlas_pos; layout(location = 1) in uint v_atlas_pos;
// in vec3 v_norm; // in vec3 v_norm;
/* in uint v_col; /* in uint v_col;
// out vec3 light_pos[2]; // out vec3 light_pos[2];
in uint v_ao_bone; */ in uint v_ao_bone; */
layout (std140) layout (std140, set = 2, binding = 0)
uniform u_locals { uniform u_locals {
mat4 model_mat; mat4 model_mat;
vec4 highlight_col; vec4 highlight_col;
@ -40,10 +40,16 @@ uniform u_locals {
struct BoneData { struct BoneData {
mat4 bone_mat; mat4 bone_mat;
// This is actually a matrix, but we explicitly rely on being able to index into it
// in column major order, and some shader compilers seem to transpose the matrix to
// a different format when it's copied out of the array. So we shouldn't put it in
// a local variable (I think explicitly marking it as a vec4[4] works, but I'm not
// sure whether it optimizes the same, and in any case the fact that there's a
// format change suggests an actual wasteful copy is happening).
mat4 normals_mat; mat4 normals_mat;
}; };
layout (std140) layout (std140, set = 2, binding = 1)
uniform u_bones { uniform u_bones {
// Warning: might not actually be 16 elements long. Don't index out of bounds! // Warning: might not actually be 16 elements long. Don't index out of bounds!
BoneData bones[16]; BoneData bones[16];
@ -59,11 +65,11 @@ uniform u_bones {
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
//}; //};
out vec3 f_pos; layout(location = 0) out vec3 f_pos;
// flat out uint f_pos_norm; // flat out uint f_pos_norm;
flat out vec3 f_norm; layout(location = 1) flat out vec3 f_norm;
// float dummy; // float dummy;
/*centroid */out vec2 f_uv_pos; /*centroid */layout(location = 2) out vec2 f_uv_pos;
// out vec3 f_col; // out vec3 f_col;
// out float f_ao; // out float f_ao;
// out float f_alt; // out float f_alt;
@ -78,16 +84,14 @@ void main() {
/* uint bone_idx = (v_ao_bone >> 2) & 0x3Fu; */ /* uint bone_idx = (v_ao_bone >> 2) & 0x3Fu; */
uint bone_idx = (v_pos_norm >> 27) & 0xFu; uint bone_idx = (v_pos_norm >> 27) & 0xFu;
mat4 bone_mat = bones[bone_idx].bone_mat; // mat4 combined_mat = model_mat * bone_mat;
mat4 normals_mat = bones[bone_idx].normals_mat;
mat4 combined_mat = /*model_mat * */bone_mat;
vec3 pos = (vec3((uvec3(v_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0; vec3 pos = (vec3((uvec3(v_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0;
// vec4 bone_pos = bones[bone_idx].bone_mat * vec4(pos, 1); // vec4 bone_pos = bones[bone_idx].bone_mat * vec4(pos, 1);
f_pos = ( f_pos = (
combined_mat * bones[bone_idx].bone_mat *
vec4(pos, 1.0) vec4(pos, 1.0)
).xyz + (model_pos - focus_off.xyz); ).xyz + (model_pos - focus_off.xyz);
@ -110,7 +114,7 @@ void main() {
// vec3 norm = normals[normal_idx]; // vec3 norm = normals[normal_idx];
uint axis_idx = v_atlas_pos & 3u; uint axis_idx = v_atlas_pos & 3u;
vec3 norm = normals_mat[axis_idx].xyz; vec3 norm = bones[bone_idx].normals_mat[axis_idx].xyz;
// norm = normalize(norm); // norm = normalize(norm);
// vec3 norm = norm_mat * vec4(uvec3(1 << axis_idx) & uvec3(0x1u, 0x3u, 0x7u), 1); // vec3 norm = norm_mat * vec4(uvec3(1 << axis_idx) & uvec3(0x1u, 0x3u, 0x7u), 1);

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -21,8 +21,8 @@
#include <globals.glsl> #include <globals.glsl>
#include <random.glsl> #include <random.glsl>
in vec3 f_pos; layout(location = 0) in vec3 f_pos;
flat in uint f_pos_norm; layout(location = 1) flat in uint f_pos_norm;
// in vec3 f_col; // in vec3 f_col;
// in float f_light; // in float f_light;
// in vec3 light_pos[2]; // in vec3 light_pos[2];
@ -37,16 +37,14 @@ flat in uint f_pos_norm;
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
// }; // };
layout (std140) layout(std140, set = 2, binding = 0)
uniform u_locals { uniform u_locals {
vec3 model_offs; vec3 model_offs;
float load_time; float load_time;
ivec4 atlas_offs; ivec4 atlas_offs;
}; };
uniform sampler2D t_waves; layout(location = 0) out vec4 tgt_color;
out vec4 tgt_color;
#include <sky.glsl> #include <sky.glsl>
#include <light.glsl> #include <light.glsl>
@ -92,7 +90,7 @@ void main() {
#endif #endif
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP)
vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy));
float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
#elif (SHADOW_MODE == SHADOW_MODE_NONE) #elif (SHADOW_MODE == SHADOW_MODE_NONE)
float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir);

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -23,8 +23,8 @@
#include <globals.glsl> #include <globals.glsl>
#include <random.glsl> #include <random.glsl>
in vec3 f_pos; layout(location = 0) in vec3 f_pos;
flat in uint f_pos_norm; layout(location = 1) flat in uint f_pos_norm;
// in vec3 f_col; // in vec3 f_col;
// in float f_light; // in float f_light;
// in vec3 light_pos[2]; // in vec3 light_pos[2];
@ -39,16 +39,14 @@ flat in uint f_pos_norm;
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
//}; //};
layout (std140) layout(std140, set = 2, binding = 0)
uniform u_locals { uniform u_locals {
vec3 model_offs; vec3 model_offs;
float load_time; float load_time;
ivec4 atlas_offs; ivec4 atlas_offs;
}; };
uniform sampler2D t_waves; layout(location = 0) out vec4 tgt_color;
out vec4 tgt_color;
#include <cloud.glsl> #include <cloud.glsl>
#include <light.glsl> #include <light.glsl>
@ -65,25 +63,25 @@ float wave_height(vec3 pos) {
pos *= 0.5; pos *= 0.5;
vec3 big_warp = ( vec3 big_warp = (
texture(t_noise, fract(pos.xy * 0.03 + timer * 0.01)).xyz * 0.5 + texture(sampler2D(t_noise, s_noise), fract(pos.xy * 0.03 + timer * 0.01)).xyz * 0.5 +
texture(t_noise, fract(pos.yx * 0.03 - timer * 0.01)).xyz * 0.5 + texture(sampler2D(t_noise, s_noise), fract(pos.yx * 0.03 - timer * 0.01)).xyz * 0.5 +
vec3(0) vec3(0)
); );
vec3 warp = ( vec3 warp = (
texture(t_noise, fract(pos.yx * 0.1 + timer * 0.02)).xyz * 0.3 + texture(sampler2D(t_noise, s_noise), fract(pos.yx * 0.1 + timer * 0.02)).xyz * 0.3 +
texture(t_noise, fract(pos.yx * 0.1 - timer * 0.02)).xyz * 0.3 + texture(sampler2D(t_noise, s_noise), fract(pos.yx * 0.1 - timer * 0.02)).xyz * 0.3 +
vec3(0) vec3(0)
); );
float height = ( float height = (
(texture(t_noise, (pos.xy + pos.z) * 0.03 + big_warp.xy + timer * 0.05).y - 0.5) * 1.0 + (texture(sampler2D(t_noise, s_noise), (pos.xy + pos.z) * 0.03 + big_warp.xy + timer * 0.05).y - 0.5) * 1.0 +
(texture(t_noise, (pos.yx + pos.z) * 0.03 + big_warp.yx - timer * 0.05).y - 0.5) * 1.0 + (texture(sampler2D(t_noise, s_noise), (pos.yx + pos.z) * 0.03 + big_warp.yx - timer * 0.05).y - 0.5) * 1.0 +
(texture(t_noise, (pos.xy + pos.z) * 0.1 + warp.xy + timer * 0.1).x - 0.5) * 0.5 + (texture(sampler2D(t_noise, s_noise), (pos.xy + pos.z) * 0.1 + warp.xy + timer * 0.1).x - 0.5) * 0.5 +
(texture(t_noise, (pos.yx + pos.z) * 0.1 + warp.yx - timer * 0.1).x - 0.5) * 0.5 + (texture(sampler2D(t_noise, s_noise), (pos.yx + pos.z) * 0.1 + warp.yx - timer * 0.1).x - 0.5) * 0.5 +
(texture(t_noise, (pos.yx + pos.z) * 0.3 + warp.xy * 0.5 + timer * 0.1).x - 0.5) * 0.2 + (texture(sampler2D(t_noise, s_noise), (pos.yx + pos.z) * 0.3 + warp.xy * 0.5 + timer * 0.1).x - 0.5) * 0.2 +
(texture(t_noise, (pos.xy + pos.z) * 0.3 + warp.yx * 0.5 - timer * 0.1).x - 0.5) * 0.2 + (texture(sampler2D(t_noise, s_noise), (pos.xy + pos.z) * 0.3 + warp.yx * 0.5 - timer * 0.1).x - 0.5) * 0.2 +
(texture(t_noise, (pos.yx + pos.z) * 1.0 + warp.yx * 0.0 - timer * 0.1).x - 0.5) * 0.05 + (texture(sampler2D(t_noise, s_noise), (pos.yx + pos.z) * 1.0 + warp.yx * 0.0 - timer * 0.1).x - 0.5) * 0.05 +
0.0 0.0
); );
@ -191,7 +189,7 @@ void main() {
/* vec3 sun_dir = get_sun_dir(time_of_day.x); /* vec3 sun_dir = get_sun_dir(time_of_day.x);
vec3 moon_dir = get_moon_dir(time_of_day.x); */ vec3 moon_dir = get_moon_dir(time_of_day.x); */
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP)
vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy));
float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
#elif (SHADOW_MODE == SHADOW_MODE_NONE) #elif (SHADOW_MODE == SHADOW_MODE_NONE)
float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir);

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -20,10 +20,10 @@
#include <srgb.glsl> #include <srgb.glsl>
#include <random.glsl> #include <random.glsl>
in uint v_pos_norm; layout(location = 0) in uint v_pos_norm;
// in uint v_col_light; // in uint v_col_light;
layout (std140) layout(std140, set = 2, binding = 0)
uniform u_locals { uniform u_locals {
vec3 model_offs; vec3 model_offs;
float load_time; float load_time;
@ -40,8 +40,8 @@ uniform u_locals {
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
// }; // };
out vec3 f_pos; layout(location = 0) out vec3 f_pos;
flat out uint f_pos_norm; layout(location = 1) flat out uint f_pos_norm;
// out vec3 f_col; // out vec3 f_col;
// out float f_light; // out float f_light;
// out vec3 light_pos[2]; // out vec3 light_pos[2];

View File

@ -34,7 +34,7 @@ vec4 cloud_at(vec3 pos, float dist, out vec3 emission) {
// Mist sits close to the ground in valleys (TODO: use base_alt to put it closer to water) // Mist sits close to the ground in valleys (TODO: use base_alt to put it closer to water)
float mist_min_alt = 0.5; float mist_min_alt = 0.5;
#if (CLOUD_MODE >= CLOUD_MODE_MEDIUM) #if (CLOUD_MODE >= CLOUD_MODE_MEDIUM)
mist_min_alt = (texture(t_noise, pos.xy / 50000.0).x - 0.5) * 1.5 + 0.5; mist_min_alt = (textureLod(sampler2D(t_noise, s_noise), pos.xy / 50000.0, 0).x - 0.5) * 1.5 + 0.5;
#endif #endif
mist_min_alt = view_distance.z * 1.5 * (1.0 + mist_min_alt * 0.5) + alt * 0.5 + 250; mist_min_alt = view_distance.z * 1.5 * (1.0 + mist_min_alt * 0.5) + alt * 0.5 + 250;
const float MIST_FADE_HEIGHT = 1000; const float MIST_FADE_HEIGHT = 1000;
@ -146,9 +146,9 @@ vec4 cloud_at(vec3 pos, float dist, out vec3 emission) {
#if (CLOUD_MODE >= CLOUD_MODE_MEDIUM) #if (CLOUD_MODE >= CLOUD_MODE_MEDIUM)
emission_alt += (noise_3d(vec3(wind_pos.xy * 0.0005 + cloud_tendency * 0.2, emission_alt * 0.0001 + time_of_day.x * 0.001)) - 0.5) * 1000; emission_alt += (noise_3d(vec3(wind_pos.xy * 0.0005 + cloud_tendency * 0.2, emission_alt * 0.0001 + time_of_day.x * 0.001)) - 0.5) * 1000;
#endif #endif
float tail = (texture(t_noise, wind_pos.xy * 0.00005).x - 0.5) * 4 + (pos.z - emission_alt) * 0.0001; float tail = (textureLod(sampler2D(t_noise, s_noise), wind_pos.xy * 0.00005, 0).x - 0.5) * 4 + (pos.z - emission_alt) * 0.0001;
vec3 emission_col = vec3(0.8 + tail * 1.5, 0.5 - tail * 0.2, 0.3 + tail * 0.2); vec3 emission_col = vec3(0.8 + tail * 1.5, 0.5 - tail * 0.2, 0.3 + tail * 0.2);
float emission_nz = max(pow(texture(t_noise, wind_pos.xy * 0.000015).x, 8), 0.01) * 0.25 / (10.0 + abs(pos.z - emission_alt) / 80); float emission_nz = max(pow(textureLod(sampler2D(t_noise, s_noise), wind_pos.xy * 0.000015, 0).x, 8), 0.01) * 0.25 / (10.0 + abs(pos.z - emission_alt) / 80);
emission = emission_col * emission_nz * emission_strength * max(sun_dir.z, 0) * 500000 / (1000.0 + abs(pos.z - emission_alt) * 0.1); emission = emission_col * emission_nz * emission_strength * max(sun_dir.z, 0) * 500000 / (1000.0 + abs(pos.z - emission_alt) * 0.1);
} }
@ -196,7 +196,7 @@ vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, const float time_of
// improves visual quality for low cloud settings // improves visual quality for low cloud settings
float splay = 1.0; float splay = 1.0;
#if (CLOUD_MODE == CLOUD_MODE_MINIMAL) #if (CLOUD_MODE == CLOUD_MODE_MINIMAL)
splay += (texture(t_noise, vec2(atan2(dir.x, dir.y) * 2 / PI, dir.z) * 5.0 - time_of_day * 0.00005).x - 0.5) * 0.025 / (1.0 + pow(dir.z, 2) * 10); splay += (textureLod(sampler2D(t_noise, s_noise), vec2(atan2(dir.x, dir.y) * 2 / PI, dir.z) * 5.0 - time_of_day * 0.00005, 0).x - 0.5) * 0.025 / (1.0 + pow(dir.z, 2) * 10);
#endif #endif
/* const float RAYLEIGH = 0.25; */ /* const float RAYLEIGH = 0.25; */
@ -218,12 +218,13 @@ vec3 get_cloud_color(vec3 surf_color, vec3 dir, vec3 origin, const float time_of
cdist = step_to_dist(trunc(dist_to_step(cdist - 0.25, quality)), quality); cdist = step_to_dist(trunc(dist_to_step(cdist - 0.25, quality)), quality);
vec3 emission; vec3 emission;
vec4 sample = cloud_at(origin + dir * ldist * splay, ldist, emission); // `sample` is a reserved keyword
vec4 sample_ = cloud_at(origin + dir * ldist * splay, ldist, emission);
vec2 density_integrals = max(sample.zw, vec2(0)); vec2 density_integrals = max(sample_.zw, vec2(0));
float sun_access = max(sample.x, 0); float sun_access = max(sample_.x, 0);
float moon_access = max(sample.y, 0); float moon_access = max(sample_.y, 0);
float cloud_scatter_factor = density_integrals.x; float cloud_scatter_factor = density_integrals.x;
float global_scatter_factor = density_integrals.y; float global_scatter_factor = density_integrals.y;

View File

@ -1,5 +1,7 @@
layout (std140) #ifndef GLOBALS_GLSL
uniform u_globals { #define GLOBALS_GLSL
layout(std140, set = 0, binding = 0) uniform u_globals {
mat4 view_mat; mat4 view_mat;
mat4 proj_mat; mat4 proj_mat;
mat4 all_mat; mat4 all_mat;
@ -22,6 +24,7 @@ uniform u_globals {
// 1 - ThirdPerson // 1 - ThirdPerson
uint cam_mode; uint cam_mode;
float sprite_render_distance; float sprite_render_distance;
float globals_dummy; // Fix alignment.
}; };
// Specifies the pattern used in the player dithering // Specifies the pattern used in the player dithering
@ -33,3 +36,5 @@ mat4 threshold_matrix = mat4(
); );
float distance_divider = 2; float distance_divider = 2;
float shadow_dithering = 0.5; float shadow_dithering = 0.5;
#endif

View File

@ -7,16 +7,17 @@ struct Light {
// mat4 light_proj; // mat4 light_proj;
}; };
layout (std140) layout (std140, set = 0, binding = 3)
uniform u_lights { uniform u_lights {
Light lights[31]; // TODO: insert light max count constant here when loading the shaders
Light lights[20];
}; };
struct Shadow { struct Shadow {
vec4 shadow_pos_radius; vec4 shadow_pos_radius;
}; };
layout (std140) layout (std140, set = 0, binding = 4)
uniform u_shadows { uniform u_shadows {
Shadow shadows[24]; Shadow shadows[24];
}; };

View File

@ -1,15 +1,20 @@
#ifndef LOD_GLSL
#define LOD_GLSL
#include <random.glsl> #include <random.glsl>
#include <sky.glsl> #include <sky.glsl>
#include <srgb.glsl> #include <srgb.glsl>
uniform sampler2D t_alt; layout(set = 0, binding = 5) uniform texture2D t_alt;
uniform sampler2D t_horizon; layout(set = 0, binding = 6) uniform sampler s_alt;
layout(set = 0, binding = 7) uniform texture2D t_horizon;
layout(set = 0, binding = 8) uniform sampler s_horizon;
const float MIN_SHADOW = 0.33; const float MIN_SHADOW = 0.33;
vec2 pos_to_uv(sampler2D sampler, vec2 pos) { vec2 pos_to_uv(texture2D tex, sampler s, vec2 pos) {
// Want: (pixel + 0.5) / W // Want: (pixel + 0.5) / W
vec2 texSize = textureSize(sampler, 0); vec2 texSize = textureSize(sampler2D(tex, s), 0);
vec2 uv_pos = (focus_off.xy + pos + 16) / (32.0 * texSize); vec2 uv_pos = (focus_off.xy + pos + 16) / (32.0 * texSize);
return vec2(uv_pos.x, /*1.0 - */uv_pos.y); return vec2(uv_pos.x, /*1.0 - */uv_pos.y);
} }
@ -32,8 +37,9 @@ vec4 cubic(float v) {
} }
// NOTE: We assume the sampled coordinates are already in "texture pixels". // NOTE: We assume the sampled coordinates are already in "texture pixels".
vec4 textureBicubic(sampler2D sampler, vec2 texCoords) { vec4 textureBicubic(texture2D tex, sampler sampl, vec2 texCoords) {
vec2 texSize = textureSize(sampler, 0); // TODO: remove all textureSize calls and replace with constants
vec2 texSize = textureSize(sampler2D(tex, sampl), 0);
vec2 invTexSize = 1.0 / texSize; vec2 invTexSize = 1.0 / texSize;
/* texCoords.y = texSize.y - texCoords.y; */ /* texCoords.y = texSize.y - texCoords.y; */
@ -56,10 +62,57 @@ vec4 textureBicubic(sampler2D sampler, vec2 texCoords) {
/* // Correct for map rotaton. /* // Correct for map rotaton.
offset.zw = 1.0 - offset.zw; */ offset.zw = 1.0 - offset.zw; */
vec4 sample0 = texture(sampler, offset.xz); vec4 sample0 = texture(sampler2D(tex, sampl), offset.xz);
vec4 sample1 = texture(sampler, offset.yz); vec4 sample1 = texture(sampler2D(tex, sampl), offset.yz);
vec4 sample2 = texture(sampler, offset.xw); vec4 sample2 = texture(sampler2D(tex, sampl), offset.xw);
vec4 sample3 = texture(sampler, offset.yw); vec4 sample3 = texture(sampler2D(tex, sampl), offset.yw);
// vec4 sample0 = texelFetch(sampler, offset.xz, 0);
// vec4 sample1 = texelFetch(sampler, offset.yz, 0);
// vec4 sample2 = texelFetch(sampler, offset.xw, 0);
// vec4 sample3 = texelFetch(sampler, offset.yw, 0);
float sx = s.x / (s.x + s.y);
float sy = s.z / (s.z + s.w);
return mix(
mix(sample3, sample2, sx), mix(sample1, sample0, sx)
, sy);
}
// 16 bit version (each of the 2 8-bit components are combined after bilinear sampling)
// NOTE: We assume the sampled coordinates are already in "texture pixels".
vec2 textureBicubic16(texture2D tex, sampler sampl, vec2 texCoords) {
vec2 texSize = textureSize(sampler2D(tex, sampl), 0);
vec2 invTexSize = 1.0 / texSize;
/* texCoords.y = texSize.y - texCoords.y; */
texCoords = texCoords/* * texSize */ - 0.5;
vec2 fxy = fract(texCoords);
texCoords -= fxy;
vec4 xcubic = cubic(fxy.x);
vec4 ycubic = cubic(fxy.y);
vec4 c = texCoords.xxyy + vec2 (-0.5, +1.5).xyxy;
// vec4 c = texCoords.xxyy + vec2 (-1, +1).xyxy;
vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw);
vec4 offset = c + vec4 (xcubic.yw, ycubic.yw) / s;
offset *= invTexSize.xxyy;
/* // Correct for map rotaton.
offset.zw = 1.0 - offset.zw; */
vec4 sample0_v4 = texture(sampler2D(tex, sampl), offset.xz);
vec4 sample1_v4 = texture(sampler2D(tex, sampl), offset.yz);
vec4 sample2_v4 = texture(sampler2D(tex, sampl), offset.xw);
vec4 sample3_v4 = texture(sampler2D(tex, sampl), offset.yw);
vec2 sample0 = sample0_v4.rb / 256.0 + sample0_v4.ga;
vec2 sample1 = sample1_v4.rb / 256.0 + sample1_v4.ga;
vec2 sample2 = sample2_v4.rb / 256.0 + sample2_v4.ga;
vec2 sample3 = sample3_v4.rb / 256.0 + sample3_v4.ga;
// vec4 sample0 = texelFetch(sampler, offset.xz, 0); // vec4 sample0 = texelFetch(sampler, offset.xz, 0);
// vec4 sample1 = texelFetch(sampler, offset.yz, 0); // vec4 sample1 = texelFetch(sampler, offset.yz, 0);
// vec4 sample2 = texelFetch(sampler, offset.xw, 0); // vec4 sample2 = texelFetch(sampler, offset.xw, 0);
@ -74,7 +127,8 @@ vec4 textureBicubic(sampler2D sampler, vec2 texCoords) {
} }
float alt_at(vec2 pos) { float alt_at(vec2 pos) {
return (/*round*/(texture/*textureBicubic*/(t_alt, pos_to_uv(t_alt, pos)).r * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z); vec4 alt_sample = textureLod/*textureBicubic16*/(sampler2D(t_alt, s_alt), pos_to_uv(t_alt, s_alt, pos), 0);
return (/*round*/((alt_sample.r / 256.0 + alt_sample.g) * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z);
//+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0; //+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0;
// return 0.0 // return 0.0
@ -88,7 +142,7 @@ float alt_at_real(vec2 pos) {
// #if (FLUID_MODE == FLUID_MODE_CHEAP) // #if (FLUID_MODE == FLUID_MODE_CHEAP)
// return alt_at(pos); // return alt_at(pos);
// #elif (FLUID_MODE == FLUID_MODE_SHINY) // #elif (FLUID_MODE == FLUID_MODE_SHINY)
return (/*round*/(textureBicubic(t_alt, pos_to_tex(pos)).r * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z); return (/*round*/(textureBicubic16(t_alt, s_alt, pos_to_tex(pos)).r * (/*1300.0*//*1278.7266845703125*/view_distance.w)) + /*140.0*/view_distance.z - focus_off.z);
// #endif // #endif
//+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0; //+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0;
@ -204,7 +258,7 @@ vec2 splay(vec2 pos) {
const float SQRT_2 = sqrt(2.0) / 2.0; const float SQRT_2 = sqrt(2.0) / 2.0;
// /const float CBRT_2 = cbrt(2.0) / 2.0; // /const float CBRT_2 = cbrt(2.0) / 2.0;
// vec2 splayed = pos * (view_distance.x * SQRT_2 + pow(len * 0.5, 3.0) * (SPLAY_MULT - view_distance.x)); // vec2 splayed = pos * (view_distance.x * SQRT_2 + pow(len * 0.5, 3.0) * (SPLAY_MULT - view_distance.x));
vec2 splayed = pos * (view_distance.x * SQRT_2 + len_pow * (textureSize(t_alt, 0) * 32.0/* - view_distance.x*/)); vec2 splayed = pos * (view_distance.x * SQRT_2 + len_pow * (textureSize(sampler2D(t_alt, s_alt), 0) * 32.0/* - view_distance.x*/));
if (abs(pos.x) > 0.99 || abs(pos.y) > 0.99) { if (abs(pos.x) > 0.99 || abs(pos.y) > 0.99) {
splayed *= 10.0; splayed *= 10.0;
} }
@ -280,13 +334,18 @@ vec3 lod_pos(vec2 pos, vec2 focus_pos) {
} }
#ifdef HAS_LOD_FULL_INFO #ifdef HAS_LOD_FULL_INFO
uniform sampler2D t_map; layout(set = 0, binding = 10)
uniform texture2D t_map;
layout(set = 0, binding = 11)
uniform sampler s_map;
vec3 lod_col(vec2 pos) { vec3 lod_col(vec2 pos) {
//return vec3(0, 0.5, 0); //return vec3(0, 0.5, 0);
// return /*linear_to_srgb*/vec3(alt_at(pos), textureBicubic(t_map, pos_to_tex(pos)).gb); // return /*linear_to_srgb*/vec3(alt_at(pos), textureBicubic(t_map, pos_to_tex(pos)).gb);
return /*linear_to_srgb*/(textureBicubic(t_map, pos_to_tex(pos)).rgb) return /*linear_to_srgb*/(textureBicubic(t_map, s_map, pos_to_tex(pos)).rgb)
;//+ (texture(t_noise, pos * 0.04 + texture(t_noise, pos * 0.005).xy * 2.0 + texture(t_noise, pos * 0.06).xy * 0.6).x - 0.5) * 0.1; ;//+ (texture(t_noise, pos * 0.04 + texture(t_noise, pos * 0.005).xy * 2.0 + texture(t_noise, pos * 0.06).xy * 0.6).x - 0.5) * 0.1;
//+ (texture(t_noise, pos * 0.04 + texture(t_noise, pos * 0.005).xy * 2.0 + texture(t_noise, pos * 0.06).xy * 0.6).x - 0.5) * 0.1; //+ (texture(t_noise, pos * 0.04 + texture(t_noise, pos * 0.005).xy * 2.0 + texture(t_noise, pos * 0.06).xy * 0.6).x - 0.5) * 0.1;
} }
#endif #endif
#endif

View File

@ -1,4 +1,8 @@
uniform sampler2D t_noise; #ifndef RANDOM_GLSL
#define RANDOM_GLSL
layout(set = 0, binding = 1) uniform texture2D t_noise;
layout(set = 0, binding = 2) uniform sampler s_noise;
float hash(vec4 p) { float hash(vec4 p) {
p = fract(p * 0.3183099 + 0.1) - fract(p + 23.22121); p = fract(p * 0.3183099 + 0.1) - fract(p + 23.22121);
@ -28,7 +32,7 @@ float hash_fast(uvec3 q)
// 2D, but using shifted 2D textures // 2D, but using shifted 2D textures
float noise_2d(vec2 pos) { float noise_2d(vec2 pos) {
return texture(t_noise, pos).x; return textureLod(sampler2D(t_noise, s_noise), pos, 0).x;
} }
// 3D, but using shifted 2D textures // 3D, but using shifted 2D textures
@ -37,7 +41,7 @@ float noise_3d(vec3 pos) {
uint z = uint(trunc(pos.z)); uint z = uint(trunc(pos.z));
vec2 offs0 = vec2(hash_one(z), hash_one(z + 73u)); vec2 offs0 = vec2(hash_one(z), hash_one(z + 73u));
vec2 offs1 = vec2(hash_one(z + 1u), hash_one(z + 1u + 73u)); vec2 offs1 = vec2(hash_one(z + 1u), hash_one(z + 1u + 73u));
return mix(texture(t_noise, pos.xy + offs0).x, texture(t_noise, pos.xy + offs1).x, fract(pos.z)); return mix(textureLod(sampler2D(t_noise, s_noise), pos.xy + offs0, 0).x, textureLod(sampler2D(t_noise, s_noise), pos.xy + offs1, 0).x, fract(pos.z));
} }
// 3D version of `snoise` // 3D version of `snoise`
@ -100,3 +104,4 @@ vec3 smooth_rand(vec3 pos, float lerp_axis) {
vec3 r1 = rand_perm_3(vec3(pos.x, pos.y, pos.z) + floor(lerp_axis + 1.0)); vec3 r1 = rand_perm_3(vec3(pos.x, pos.y, pos.z) + floor(lerp_axis + 1.0));
return r0 + (r1 - r0) * fract(lerp_axis); return r0 + (r1 - r0) * fract(lerp_axis);
} }
#endif

View File

@ -1,22 +1,29 @@
#ifndef SHADOWS_GLSL
#define SHADOWS_GLSL
#ifdef HAS_SHADOW_MAPS #ifdef HAS_SHADOW_MAPS
#if (SHADOW_MODE == SHADOW_MODE_MAP) #if (SHADOW_MODE == SHADOW_MODE_MAP)
struct ShadowLocals { layout (std140, set = 0, binding = 9)
uniform u_light_shadows {
mat4 shadowMatrices; mat4 shadowMatrices;
mat4 texture_mat; mat4 texture_mat;
}; };
layout (std140) // Use with sampler2DShadow
uniform u_light_shadows { layout(set = 1, binding = 2)
ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; uniform texture2D t_directed_shadow_maps;
}; layout(set = 1, binding = 3)
uniform samplerShadow s_directed_shadow_maps;
uniform sampler2DShadow t_directed_shadow_maps;
// uniform sampler2DArrayShadow t_directed_shadow_maps; // uniform sampler2DArrayShadow t_directed_shadow_maps;
// uniform samplerCubeArrayShadow t_shadow_maps; // uniform samplerCubeArrayShadow t_shadow_maps;
// uniform samplerCubeArray t_shadow_maps; // uniform samplerCubeArray t_shadow_maps;
uniform samplerCubeShadow t_point_shadow_maps; // Use with samplerCubeShadow
layout(set = 1, binding = 0)
uniform textureCube t_point_shadow_maps;
layout(set = 1, binding = 1)
uniform samplerShadow s_point_shadow_maps;
// uniform samplerCube t_shadow_maps; // uniform samplerCube t_shadow_maps;
// uniform sampler2DArray t_directed_shadow_maps; // uniform sampler2DArray t_directed_shadow_maps;
@ -35,9 +42,13 @@ float VectorToDepth (vec3 Vec)
// float NormZComp = (screen_res.w+screen_res.z) / (screen_res.w-screen_res.z) - (2*screen_res.w*screen_res.z)/(screen_res.w-screen_res.z)/LocalZcomp; // float NormZComp = (screen_res.w+screen_res.z) / (screen_res.w-screen_res.z) - (2*screen_res.w*screen_res.z)/(screen_res.w-screen_res.z)/LocalZcomp;
// float NormZComp = 1.0 - shadow_proj_factors.y / shadow_proj_factors.x / LocalZcomp; // float NormZComp = 1.0 - shadow_proj_factors.y / shadow_proj_factors.x / LocalZcomp;
// -(1 + 2n/(f-n)) - 2(1 + n/(f-n)) * n/z
// -(1 + n/(f-n)) - (1 + n/(f-n)) * n/z
// f/(f-n) - fn/(f-n)/z
float NormZComp = shadow_proj_factors.x - shadow_proj_factors.y / LocalZcomp; float NormZComp = shadow_proj_factors.x - shadow_proj_factors.y / LocalZcomp;
// NormZComp = -1000.0 / (NormZComp + 10000.0); // NormZComp = -1000.0 / (NormZComp + 10000.0);
return (NormZComp + 1.0) * 0.5; // return (NormZComp + 1.0) * 0.5;
return NormZComp;
// float NormZComp = length(LocalZcomp); // float NormZComp = length(LocalZcomp);
// NormZComp = -NormZComp / screen_res.w; // NormZComp = -NormZComp / screen_res.w;
@ -64,7 +75,9 @@ float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /
{ {
float currentDepth = VectorToDepth(fragToLight);// + bias; float currentDepth = VectorToDepth(fragToLight);// + bias;
float visibility = texture(t_point_shadow_maps, vec4(fragToLight, currentDepth));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/); // currentDepth = -currentDepth * 0.5 + 0.5;
float visibility = texture(samplerCubeShadow(t_point_shadow_maps, s_point_shadow_maps), vec4(fragToLight, currentDepth));// / (screen_res.w/* - screen_res.z*/)/*1.0 -bias*//*-(currentDepth - bias) / screen_res.w*//*-screen_res.w*/);
/* if (visibility == 1.0 || visibility == 0.0) { /* if (visibility == 1.0 || visibility == 0.0) {
return visibility; return visibility;
} */ } */
@ -154,10 +167,45 @@ float ShadowCalculationDirected(in vec3 fragPos)//in vec4 /*light_pos[2]*/sun_po
} */ } */
// vec3 fragPos = sun_pos.xyz;// / sun_pos.w;//light_pos[lightIndex].xyz; // vec3 fragPos = sun_pos.xyz;// / sun_pos.w;//light_pos[lightIndex].xyz;
// sun_pos.z += sun_pos.w * bias; // sun_pos.z += sun_pos.w * bias;
mat4 texture_mat = shadowMats[0].texture_mat; vec4 sun_pos = texture_mat/*shadowMatrices*/ * vec4(fragPos, 1.0);
vec4 sun_pos = texture_mat * vec4(fragPos, 1.0); // sun_pos.xy = 0.5 * sun_pos.w + sun_pos.xy * 0.5;
// sun_pos.z -= sun_pos.w * bias; // sun_pos.xy = sun_pos.ww - sun_pos.xy;
float visibility = textureProj(t_directed_shadow_maps, sun_pos); // sun_pos.xyz /= abs(sun_pos.w);
// sun_pos.w = sign(sun_pos.w);
// sun_pos.xy = (sun_pos.xy + 1.0) * 0.5;
// vec4 orig_pos = warpViewMat * lightViewMat * vec4(fragPos, 1.0);
//
// vec4 shadow_pos;
// shadow_pos.xyz = (warpProjMat * orig_pos).xyz:
// shadow_pos.w = orig_pos.y;
//
// sun_pos.xy = 0.5 * (shadow_pos.xy + shadow_pos.w) = 0.5 * (shadow_pos.xy + orig_pos.yy);
// sun_pos.z = shadow_pos.z;
//
// sun_pos.w = sign(shadow_pos.w) = sign(orig_pos.y);
// sun_pos.xyz = sun_pos.xyz / shadow_pos.w = vec3(0.5 * shadow_pos.xy / orig_pos.yy + 0.5, shadow_pos.z / orig_pos.y)
// = vec3(0.5 * (2.0 * warp_pos.xy / orig_pos.yy - (max_warp_pos + min_warp_pos).xy) / (max_warp_pos - min_warp_pos).xy + 0.5,
// -(warp_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z )
// = vec3((warp_pos.x / orig_pos.y - min_warp_pos.x) / (max_warp_pos - min_warp_pos).x,
// (warp_pos.y / orig_pos.y - min_warp_pos.y) / (max_warp_pos - min_warp_pos).y,
// -(warp_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z )
// = vec3((near * orig_pos.x / orig_pos.y - min_warp_pos.x) / (max_warp_pos - min_warp_pos).x,
// (((far+near) - 2.0 * near * far / orig_pos.y)/(far-near) - min_warp_pos.y) / (max_warp_pos - min_warp_pos).y,
// -(near * orig_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z )
// = vec3((near * orig_pos.x / orig_pos.y - min_warp_pos.x) / (max_warp_pos - min_warp_pos).x,
// (2.0 * (1.0 - far / orig_pos.y)*near/(far-near) + 1.0 - min_warp_pos.y) / (max_warp_pos - min_warp_pos).y,
// -(near * orig_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z )
// = vec3((near * orig_pos.x / orig_pos.y - min_warp_pos.x) / (max_warp_pos - min_warp_pos).x,
// (2.0 * (1.0 - far / orig_pos.y)*near/(far-near) + 1.0 - 0.0) / (1.0 - 0.0),
// -(near * orig_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z )
// = vec3((near * orig_pos.x / orig_pos.y - min_warp_pos.x) / (max_warp_pos - min_warp_pos).x,
// 2.0 * (1.0 - far / orig_pos.y)*near/(far-near) + 1.0,
// -(near * orig_pos.z / orig_pos.y - min_warp_pos.z) / (max_warp_pos - min_warp_pos).z )
//
// orig_pos.y = n: warp_pos.y = 2*(1-f/n)*n/(f-n) + 1 = 2*(n-f)/(f-n) + 1 = 2 * -1 + 1 = -1, sun_pos.y = (-1 - -1) / 2 = 0
// orig_pos.y = f: warp_pos.y = 2*(1-f/f)*n/(f-n) + 1 = 2*(1-1)*n/(f-n) + 1 = 2 * 0 * n/(f-n) + 1 = 1, sun_pos.y = (1 - -1) / 2 = 1
//
float visibility = textureProj(sampler2DShadow(t_directed_shadow_maps, s_directed_shadow_maps), sun_pos);
/* float visibilityLeft = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, -diskRadius, 0.0), 1.0)); /* float visibilityLeft = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, -diskRadius, 0.0), 1.0));
float visibilityRight = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, diskRadius, 0.0), 1.0)); */ float visibilityRight = textureProj(t_directed_shadow_maps, sun_shadow.texture_mat * vec4(fragPos + vec3(0.0, diskRadius, 0.0), 1.0)); */
// float nearVisibility = textureProj(t_directed_shadow_maps + vec3(0.001, sun_pos)); // float nearVisibility = textureProj(t_directed_shadow_maps + vec3(0.001, sun_pos));
@ -216,3 +264,5 @@ float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /
return 1.0; return 1.0;
} }
#endif #endif
#endif

View File

@ -1,3 +1,6 @@
#ifndef SKY_GLSL
#define SKY_GLSL
#include <random.glsl> #include <random.glsl>
#include <srgb.glsl> #include <srgb.glsl>
#include <shadows.glsl> #include <shadows.glsl>
@ -84,7 +87,7 @@ vec2 wind_offset = vec2(time_of_day.x * wind_speed);
float cloud_scale = view_distance.z / 150.0; float cloud_scale = view_distance.z / 150.0;
float cloud_tendency_at(vec2 pos) { float cloud_tendency_at(vec2 pos) {
float nz = texture(t_noise, (pos + wind_offset) / 60000.0 / cloud_scale).x - 0.3; float nz = textureLod(sampler2D(t_noise, s_noise), (pos + wind_offset) / 60000.0 / cloud_scale, 0).x - 0.3;
nz = pow(clamp(nz, 0, 1), 3); nz = pow(clamp(nz, 0, 1), 3);
return nz; return nz;
} }
@ -425,7 +428,7 @@ vec3 get_sky_light(vec3 dir, float time_of_day, bool with_stars) {
mix( mix(
SKY_DUSK_TOP, SKY_DUSK_TOP,
SKY_NIGHT_TOP, SKY_NIGHT_TOP,
max(pow(sun_dir.z, 0.2), 0) pow(max(sun_dir.z, 0.0), 0.2)
) + star, ) + star,
SKY_DAY_TOP, SKY_DAY_TOP,
max(-sun_dir.z, 0) max(-sun_dir.z, 0)
@ -434,7 +437,7 @@ vec3 get_sky_light(vec3 dir, float time_of_day, bool with_stars) {
vec3 sky_mid = mix( vec3 sky_mid = mix(
mix( SKY_DUSK_MID, mix( SKY_DUSK_MID,
SKY_NIGHT_MID, SKY_NIGHT_MID,
max(pow(sun_dir.z, 0.1), 0) pow(max(sun_dir.z, 0.0), 0.1)
), ),
SKY_DAY_MID, SKY_DAY_MID,
max(-sun_dir.z, 0) max(-sun_dir.z, 0)
@ -444,7 +447,7 @@ vec3 get_sky_light(vec3 dir, float time_of_day, bool with_stars) {
mix( mix(
SKY_DUSK_BOT, SKY_DUSK_BOT,
SKY_NIGHT_BOT, SKY_NIGHT_BOT,
max(pow(sun_dir.z, 0.2), 0) pow(max(sun_dir.z, 0.0), 0.2)
), ),
SKY_DAY_BOT, SKY_DAY_BOT,
max(-sun_dir.z, 0) max(-sun_dir.z, 0)
@ -640,3 +643,5 @@ vec3 illuminate(float max_light, vec3 view_dir, /*vec3 max_light, */vec3 emitted
// float sum_col = color.r + color.g + color.b; // float sum_col = color.r + color.g + color.b;
// return /*srgb_to_linear*/(/*0.5*//*0.125 * */vec3(pow(color.x, gamma), pow(color.y, gamma), pow(color.z, gamma))); // return /*srgb_to_linear*/(/*0.5*//*0.125 * */vec3(pow(color.x, gamma), pow(color.y, gamma), pow(color.z, gamma)));
} }
#endif

View File

@ -1,3 +1,5 @@
#ifndef SRGB_GLSL
#define SRGB_GLSL
// Linear RGB, attenuation coefficients for water at roughly R, G, B wavelengths. // Linear RGB, attenuation coefficients for water at roughly R, G, B wavelengths.
// See https://en.wikipedia.org/wiki/Electromagnetic_absorption_by_water // See https://en.wikipedia.org/wiki/Electromagnetic_absorption_by_water
const vec3 MU_WATER = vec3(0.6, 0.04, 0.01); const vec3 MU_WATER = vec3(0.6, 0.04, 0.01);
@ -618,8 +620,8 @@ vec3 compute_attenuation_point(vec3 wpos, vec3 ray_dir, vec3 mu, float surface_a
//} //}
//#endif //#endif
vec3 greedy_extract_col_light_attr(sampler2D t_col_light, vec2 f_uv_pos, out float f_light, out float f_glow, out uint f_attr) { vec3 greedy_extract_col_light_attr(texture2D t_col_light, sampler s_col_light, vec2 f_uv_pos, out float f_light, out float f_glow, out uint f_attr) {
uvec4 f_col_light = uvec4(texelFetch(t_col_light, ivec2(f_uv_pos), 0) * 255); uvec4 f_col_light = uvec4(texelFetch(sampler2D(t_col_light, s_col_light), ivec2(f_uv_pos), 0) * 255);
vec3 f_col = vec3( vec3 f_col = vec3(
float(((f_col_light.r & 0x7u) << 1u) | (f_col_light.b & 0xF0u)), float(((f_col_light.r & 0x7u) << 1u) | (f_col_light.b & 0xF0u)),
float(f_col_light.a), float(f_col_light.a),
@ -628,9 +630,9 @@ vec3 greedy_extract_col_light_attr(sampler2D t_col_light, vec2 f_uv_pos, out flo
// TODO: Figure out how to use `texture` and modulation to avoid needing to do manual filtering // TODO: Figure out how to use `texture` and modulation to avoid needing to do manual filtering
vec2 light_00 = vec2(uvec2(f_col_light.rg) >> 3u); vec2 light_00 = vec2(uvec2(f_col_light.rg) >> 3u);
vec2 light_10 = vec2(uvec2(texelFetch(t_col_light, ivec2(f_uv_pos) + ivec2(1, 0), 0).rg * 255.0) >> 3u); vec2 light_10 = vec2(uvec2(texelFetch(sampler2D(t_col_light, s_col_light), ivec2(f_uv_pos) + ivec2(1, 0), 0).rg * 255.0) >> 3u);
vec2 light_01 = vec2(uvec2(texelFetch(t_col_light, ivec2(f_uv_pos) + ivec2(0, 1), 0).rg * 255.0) >> 3u); vec2 light_01 = vec2(uvec2(texelFetch(sampler2D(t_col_light, s_col_light), ivec2(f_uv_pos) + ivec2(0, 1), 0).rg * 255.0) >> 3u);
vec2 light_11 = vec2(uvec2(texelFetch(t_col_light, ivec2(f_uv_pos) + ivec2(1, 1), 0).rg * 255.0) >> 3u); vec2 light_11 = vec2(uvec2(texelFetch(sampler2D(t_col_light, s_col_light), ivec2(f_uv_pos) + ivec2(1, 1), 0).rg * 255.0) >> 3u);
vec2 light_0 = mix(light_00, light_01, fract(f_uv_pos.y)); vec2 light_0 = mix(light_00, light_01, fract(f_uv_pos.y));
vec2 light_1 = mix(light_10, light_11, fract(f_uv_pos.y)); vec2 light_1 = mix(light_10, light_11, fract(f_uv_pos.y));
vec2 light = mix(light_0, light_1, fract(f_uv_pos.x)); vec2 light = mix(light_0, light_1, fract(f_uv_pos.x));
@ -644,7 +646,8 @@ vec3 greedy_extract_col_light_attr(sampler2D t_col_light, vec2 f_uv_pos, out flo
return srgb_to_linear(f_col); return srgb_to_linear(f_col);
} }
vec3 greedy_extract_col_light_glow(sampler2D t_col_light, vec2 f_uv_pos, out float f_light, out float f_glow) { vec3 greedy_extract_col_light_glow(texture2D t_col_light, sampler s_col_light, vec2 f_uv_pos, out float f_light, out float f_glow) {
uint f_attr; uint f_attr;
return greedy_extract_col_light_attr(t_col_light, f_uv_pos, f_light, f_glow, f_attr); return greedy_extract_col_light_attr(t_col_light, s_col_light, f_uv_pos, f_light, f_glow, f_attr);
} }
#endif

View File

@ -1,48 +0,0 @@
// NOTE: We currently do nothing, and just rely on the default shader behavior.
//
// However, in the future we might apply some depth transforms here.
#version 330 core
// #extension ARB_texture_storage : enable
#include <constants.glsl>
#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION
#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
#if (FLUID_MODE == FLUID_MODE_CHEAP)
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
#elif (FLUID_MODE == FLUID_MODE_SHINY)
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
#endif
#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET
#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN
// // Currently, we only need globals for the far plane.
// #include <globals.glsl>
// // Currently, we only need lights for the light position
// #include <light.glsl>
// in vec3 FragPos; // FragPos from GS (output per emitvertex)
// flat in int FragLayer;
void main()
{
// Only need to do anything with point lights, since sun and moon should already have nonlinear
// distance.
/*if (FragLayer > 0) */{
// get distance between fragment and light source
// float lightDistance = length(FragPos - lights[FragLayer & 31].light_pos.xyz);
// // // map to [0;1] range by dividing by far_plane
// lightDistance = lightDistance / screen_res.w;//FragPos.w;//screen_res.w;
// // // write this as modified depth
// // // lightDistance = -1000.0 / (lightDistance + 10000.0);
// // // lightDistance /= screen_res.w;
// gl_FragDepth = lightDistance;// / /*FragPos.w;*/screen_res.w;//-1000.0 / (lightDistance + 1000.0);//lightDistance
}
}

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
// #extension ARB_texture_storage : enable // #extension ARB_texture_storage : enable
#include <constants.glsl> #include <constants.glsl>
@ -22,7 +22,13 @@
// Currently, we only need globals for focus_off. // Currently, we only need globals for focus_off.
#include <globals.glsl> #include <globals.glsl>
// For shadow locals. // For shadow locals.
#include <shadows.glsl> // #include <shadows.glsl>
layout (std140, set = 0, binding = 9)
uniform u_light_shadows {
mat4 shadowMatrices;
mat4 texture_mat;
};
/* Accurate packed shadow maps for many lights at once! /* Accurate packed shadow maps for many lights at once!
* *
@ -30,12 +36,13 @@
* *
* */ * */
in uint v_pos_norm; layout(location = 0) in uint v_pos_norm;
// in uint v_col_light; // in uint v_col_light;
// in vec4 v_pos; // in vec4 v_pos;
// layout(location = 1) in uint v_atlas_pos;
// Light projection matrices. // Light projection matrices.
layout (std140) layout (std140, set = 1, binding = 0)
uniform u_locals { uniform u_locals {
vec3 model_offs; vec3 model_offs;
float load_time; float load_time;
@ -47,17 +54,15 @@ uniform u_locals {
const int EXTRA_NEG_Z = 32768; const int EXTRA_NEG_Z = 32768;
void main() { void main() {
#if (SHADOW_MODE == SHADOW_MODE_MAP)
vec3 f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); vec3 f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z));
vec3 f_pos = f_chunk_pos + model_offs - focus_off.xyz; vec3 f_pos = f_chunk_pos + (model_offs - focus_off.xyz);
// f_pos = v_pos; // f_pos = v_pos;
// vec3 f_pos = f_chunk_pos + model_offs; // vec3 f_pos = f_chunk_pos + model_offs;
// gl_Position = v_pos + vec4(model_offs, 0.0); // gl_Position = v_pos + vec4(model_offs, 0.0);
gl_Position = /*all_mat * */shadowMats[/*layer_face*/0].shadowMatrices * vec4(f_pos/*, 1.0*/, /*float(((f_pos_norm >> 29) & 0x7u) ^ 0x1)*//*uintBitsToFloat(v_pos_norm)*/1.0); gl_Position = /*all_mat * */shadowMatrices * vec4(f_pos/*, 1.0*/, /*float(((f_pos_norm >> 29) & 0x7u) ^ 0x1)*//*uintBitsToFloat(v_pos_norm)*/1.0);
// gl_Position.z = -gl_Position.z; // gl_Position.z = -gl_Position.z;
// gl_Position.z = clamp(gl_Position.z, -abs(gl_Position.w), abs(gl_Position.w)); // gl_Position.z = clamp(gl_Position.z, -abs(gl_Position.w), abs(gl_Position.w));
// shadowMapCoord = lights[gl_InstanceID].light_pos * gl_Vertex; // shadowMapCoord = lights[gl_InstanceID].light_pos * gl_Vertex;
// vec4(v_pos, 0.0, 1.0); // vec4(v_pos, 0.0, 1.0);
#endif
} }

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
// #extension ARB_texture_storage : enable // #extension ARB_texture_storage : enable
#define FIGURE_SHADER #define FIGURE_SHADER
@ -24,7 +24,13 @@
// Currently, we only need globals for focus_off. // Currently, we only need globals for focus_off.
#include <globals.glsl> #include <globals.glsl>
// For shadow locals. // For shadow locals.
#include <shadows.glsl> // #include <shadows.glsl>
layout (std140, set = 0, binding = 9)
uniform u_light_shadows {
mat4 shadowMatrices;
mat4 texture_mat;
};
/* Accurate packed shadow maps for many lights at once! /* Accurate packed shadow maps for many lights at once!
* *
@ -32,12 +38,12 @@
* *
* */ * */
in uint v_pos_norm; layout(location = 0) in uint v_pos_norm;
in uint v_atlas_pos; layout(location = 1) in uint v_atlas_pos;
// in uint v_col_light; // in uint v_col_light;
// in vec4 v_pos; // in vec4 v_pos;
layout (std140) layout (std140, set = 1, binding = 0)
uniform u_locals { uniform u_locals {
mat4 model_mat; mat4 model_mat;
vec4 highlight_col; vec4 highlight_col;
@ -55,7 +61,7 @@ struct BoneData {
mat4 normals_mat; mat4 normals_mat;
}; };
layout (std140) layout (std140, set = 1, binding = 1)
uniform u_bones { uniform u_bones {
// Warning: might not actually be 16 elements long. Don't index out of bounds! // Warning: might not actually be 16 elements long. Don't index out of bounds!
BoneData bones[16]; BoneData bones[16];
@ -64,7 +70,6 @@ uniform u_bones {
// out vec4 shadowMapCoord; // out vec4 shadowMapCoord;
void main() { void main() {
#if (SHADOW_MODE == SHADOW_MODE_MAP)
uint bone_idx = (v_pos_norm >> 27) & 0xFu; uint bone_idx = (v_pos_norm >> 27) & 0xFu;
vec3 pos = (vec3((uvec3(v_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0; vec3 pos = (vec3((uvec3(v_pos_norm) >> uvec3(0, 9, 18)) & uvec3(0x1FFu)) - 256.0) / 2.0;
@ -73,6 +78,5 @@ void main() {
vec4(pos, 1.0) vec4(pos, 1.0)
).xyz + (model_pos - focus_off.xyz/* + vec3(0.0, 0.0, 0.0001)*/); ).xyz + (model_pos - focus_off.xyz/* + vec3(0.0, 0.0, 0.0001)*/);
gl_Position = shadowMats[/*layer_face*/0].shadowMatrices * vec4(f_pos, 1.0); gl_Position = shadowMatrices * vec4(f_pos, 1.0);
#endif
} }

View File

@ -2,7 +2,7 @@
// //
// However, in the future we might apply some depth transforms here. // However, in the future we might apply some depth transforms here.
#version 330 core #version 420 core
// #extension ARB_texture_storage : enable // #extension ARB_texture_storage : enable
#include <constants.glsl> #include <constants.glsl>

View File

@ -26,12 +26,12 @@
* *
* */ * */
in uint v_pos_norm; layout(location = 1) in uint v_pos_norm;
// in uint v_col_light; // in uint v_col_light;
// in vec4 v_pos; // in vec4 v_pos;
// Light projection matrices. // Light projection matrices.
layout (std140) layout (std140, set = 1, binding = 0)
uniform u_locals { uniform u_locals {
vec3 model_offs; vec3 model_offs;
float load_time; float load_time;

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -23,14 +23,14 @@
#include <cloud.glsl> #include <cloud.glsl>
#include <lod.glsl> #include <lod.glsl>
in vec3 f_pos; layout(location = 0) in vec3 f_pos;
in vec3 f_norm; layout(location = 1) in vec3 f_norm;
in float pull_down; layout(location = 2) in float pull_down;
// in vec2 v_pos_orig; // in vec2 v_pos_orig;
// in vec4 f_shadow; // in vec4 f_shadow;
// in vec4 f_square; // in vec4 f_square;
out vec4 tgt_color; layout(location = 0) out vec4 tgt_color;
/// const vec4 sun_pos = vec4(0); /// const vec4 sun_pos = vec4(0);
// const vec4 light_pos[2] = vec4[](vec4(0), vec4(0)/*, vec3(00), vec3(0), vec3(0), vec3(0)*/); // const vec4 light_pos[2] = vec4[](vec4(0), vec4(0)/*, vec3(00), vec3(0), vec3(0), vec3(0)*/);
@ -444,7 +444,7 @@ void main() {
#endif #endif
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP)
vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy));
float sun_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir); float sun_shade_frac = horizon_at2(f_shadow, shadow_alt, f_pos, sun_dir);
// float sun_shade_frac = 1.0; // float sun_shade_frac = 1.0;
#elif (SHADOW_MODE == SHADOW_MODE_NONE) #elif (SHADOW_MODE == SHADOW_MODE_NONE)

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -20,16 +20,11 @@
#include <srgb.glsl> #include <srgb.glsl>
#include <lod.glsl> #include <lod.glsl>
in vec2 v_pos; layout(location = 0) in vec2 v_pos;
layout (std140) layout(location = 0) out vec3 f_pos;
uniform u_locals { layout(location = 1) out vec3 f_norm;
vec4 nul; layout(location = 2) out float pull_down;
};
out vec3 f_pos;
out vec3 f_norm;
out float pull_down;
// out vec2 v_pos_orig; // out vec2 v_pos_orig;
// out vec4 f_square; // out vec4 f_square;
// out vec4 f_shadow; // out vec4 f_shadow;
@ -49,8 +44,11 @@ void main() {
// f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); // f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy));
float dist = distance(focus_pos.xy, f_pos.xy); // TODO: disabled because it isn't designed to work with reverse depth
pull_down = 0.2 / pow(dist / (view_distance.x * 0.9), 20.0); //float dist = distance(focus_pos.xy, f_pos.xy);
//pull_down = 0.2 / pow(dist / (view_distance.x * 0.9), 20.0);
pull_down = 1.0 / pow(distance(focus_pos.xy, f_pos.xy) / (view_distance.x * 0.95), 20.0);
f_pos.z -= pull_down; f_pos.z -= pull_down;
// f_pos.z -= 100.0 * pow(1.0 + 0.01 / view_distance.x, -pow(distance(focus_pos.xy, f_pos.xy), 2.0)); // f_pos.z -= 100.0 * pow(1.0 + 0.01 / view_distance.x, -pow(distance(focus_pos.xy, f_pos.xy), 2.0));
@ -100,7 +98,9 @@ void main() {
all_mat * all_mat *
vec4(f_pos/*newRay*/, 1); vec4(f_pos/*newRay*/, 1);
// Pull up the depth to avoid drawing over voxels (biased according to VD) // Pull up the depth to avoid drawing over voxels (biased according to VD)
gl_Position.z += 0.1 * clamp((view_distance.x * 1.0 - dist) * 0.01, 0, 1); // TODO: disabled because it isn't designed to work with reverse depth
//gl_Position.z += 0.1 * clamp((view_distance.x * 1.0 - dist) * 0.01, 0, 1);
// gl_Position.z = -gl_Position.z / gl_Position.w; // gl_Position.z = -gl_Position.z / gl_Position.w;
// gl_Position.z = -gl_Position.z / gl_Position.w; // gl_Position.z = -gl_Position.z / gl_Position.w;
// gl_Position.z = -gl_Position.z * gl_Position.w; // gl_Position.z = -gl_Position.z * gl_Position.w;

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -16,12 +16,12 @@
#include <globals.glsl> #include <globals.glsl>
in vec3 f_pos; layout(location = 0) in vec3 f_pos;
flat in vec3 f_norm; layout(location = 1) flat in vec3 f_norm;
in vec4 f_col; layout(location = 2) in vec4 f_col;
in float f_reflect; layout(location = 3) in float f_reflect;
out vec4 tgt_color; layout(location = 0) out vec4 tgt_color;
#include <sky.glsl> #include <sky.glsl>
#include <light.glsl> #include <light.glsl>
@ -40,7 +40,7 @@ void main() {
#endif #endif
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP)
vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy));
float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
#elif (SHADOW_MODE == SHADOW_MODE_NONE) #elif (SHADOW_MODE == SHADOW_MODE_NONE)
float sun_shade_frac = 1.0; float sun_shade_frac = 1.0;

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -17,22 +17,22 @@
#include <random.glsl> #include <random.glsl>
#include <lod.glsl> #include <lod.glsl>
in vec3 v_pos; layout(location = 0) in vec3 v_pos;
// in uint v_col; // in uint v_col;
in uint v_norm_ao; layout(location = 1) in uint v_norm_ao;
in vec3 inst_pos; layout(location = 2) in float inst_time;
in float inst_time; layout(location = 3) in float inst_lifespan;
in float inst_lifespan; layout(location = 4) in float inst_entropy;
in float inst_entropy; layout(location = 5) in int inst_mode;
in vec3 inst_dir; layout(location = 6) in vec3 inst_dir;
in int inst_mode; layout(location = 7) in vec3 inst_pos;
out vec3 f_pos; layout(location = 0) out vec3 f_pos;
flat out vec3 f_norm; layout(location = 1) flat out vec3 f_norm;
out vec4 f_col; layout(location = 2) out vec4 f_col;
out float f_ao; //layout(location = x) out float f_ao;
out float f_light; //layout(location = x) out float f_light;
out float f_reflect; layout(location = 3) out float f_reflect;
const float SCALE = 1.0 / 11.0; const float SCALE = 1.0 / 11.0;

View File

@ -0,0 +1,61 @@
#version 420 core
// #extension ARB_texture_storage : enable
#include <constants.glsl>
#define LIGHTING_TYPE LIGHTING_TYPE_REFLECTION
#define LIGHTING_REFLECTION_KIND LIGHTING_REFLECTION_KIND_GLOSSY
#if (FLUID_MODE == FLUID_MODE_CHEAP)
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_IMPORTANCE
#elif (FLUID_MODE == FLUID_MODE_SHINY)
#define LIGHTING_TRANSPORT_MODE LIGHTING_TRANSPORT_MODE_RADIANCE
#endif
#define LIGHTING_DISTRIBUTION_SCHEME LIGHTING_DISTRIBUTION_SCHEME_MICROFACET
#define LIGHTING_DISTRIBUTION LIGHTING_DISTRIBUTION_BECKMANN
// Currently, we only need globals for focus_off.
#include <globals.glsl>
/* Accurate packed shadow maps for many lights at once!
*
* Ideally, we would just write to a bitmask...
*
* */
layout(location = 0) in uint v_pos_norm;
// layout(location = 1) in uint v_atlas_pos;
// in uint v_col_light;
// in vec4 v_pos;
// Light projection matrices.
layout (std140, set = 1, binding = 0)
uniform u_locals {
vec3 model_offs;
float load_time;
ivec4 atlas_offs;
};
// out vec4 shadowMapCoord;
const int EXTRA_NEG_Z = 32768;
layout( push_constant ) uniform PointLightMatrix {
mat4 lightShadowMatrix;
};
void main() {
vec3 f_chunk_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z));
vec3 f_pos = f_chunk_pos + model_offs - focus_off.xyz;
// f_pos = v_pos;
// vec3 f_pos = f_chunk_pos + model_offs;
// gl_Position = v_pos + vec4(model_offs, 0.0);
// gl_Position = /*all_mat * */vec4(f_pos/*, 1.0*/, /*float(((f_pos_norm >> 29) & 0x7u) ^ 0x1)*//*uintBitsToFloat(v_pos_norm)*/1.0);
// shadowMapCoord = lights[gl_InstanceID].light_pos * gl_Vertex;
// vec4(v_pos, 0.0, 1.0);
gl_Position = lightShadowMatrix * vec4(f_pos, 1.0);
}

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -22,17 +22,21 @@
#include <srgb.glsl> #include <srgb.glsl>
#include <cloud.glsl> #include <cloud.glsl>
//uniform sampler2D src_depth; layout(set = 1, binding = 0)
uniform texture2D t_src_color;
layout(set = 1, binding = 1)
uniform sampler s_src_color;
in vec2 f_pos;
layout (std140) layout(location = 0) in vec2 uv;
layout (std140, set = 1, binding = 2)
uniform u_locals { uniform u_locals {
mat4 proj_mat_inv; mat4 proj_mat_inv;
mat4 view_mat_inv; mat4 view_mat_inv;
}; };
out vec4 tgt_color; layout(location = 0) out vec4 tgt_color;
vec3 rgb2hsv(vec3 c) { vec3 rgb2hsv(vec3 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
@ -145,33 +149,7 @@ vec3 _illuminate(float max_light, vec3 view_dir, /*vec3 max_light, */vec3 emitte
// return /*srgb_to_linear*/(/*0.5*//*0.125 * */vec3(pow(color.x, gamma), pow(color.y, gamma), pow(color.z, gamma))); // return /*srgb_to_linear*/(/*0.5*//*0.125 * */vec3(pow(color.x, gamma), pow(color.y, gamma), pow(color.z, gamma)));
} }
/*
float depth_at(vec2 uv) {
float buf_depth = texture(src_depth, uv).x;
vec4 clip_space = vec4(uv * 2.0 - 1.0, buf_depth, 1.0);
vec4 view_space = proj_mat_inv * clip_space;
view_space /= view_space.w;
return -view_space.z;
}
vec3 wpos_at(vec2 uv) {
float buf_depth = texture(src_depth, uv).x * 2.0 - 1.0;
mat4 inv = view_mat_inv * proj_mat_inv;//inverse(all_mat);
vec4 clip_space = vec4(uv * 2.0 - 1.0, buf_depth, 1.0);
vec4 view_space = inv * clip_space;
view_space /= view_space.w;
if (buf_depth == 1.0) {
vec3 direction = normalize(view_space.xyz);
return direction.xyz * 100000.0 + cam_pos.xyz;
} else {
return view_space.xyz;
}
}
*/
void main() { void main() {
vec2 uv = (f_pos + 1.0) * 0.5;
/* if (medium.x == 1u) { /* if (medium.x == 1u) {
uv = clamp(uv + vec2(sin(uv.y * 16.0 + tick.x), sin(uv.x * 24.0 + tick.x)) * 0.005, 0, 1); uv = clamp(uv + vec2(sin(uv.y * 16.0 + tick.x), sin(uv.x * 24.0 + tick.x)) * 0.005, 0, 1);
} */ } */
@ -202,7 +180,7 @@ void main() {
// float bright_color = (bright_color0 + bright_color1 + bright_color2 + bright_color3 + bright_color4) / 5.0; // float bright_color = (bright_color0 + bright_color1 + bright_color2 + bright_color3 + bright_color4) / 5.0;
vec4 aa_color = aa_apply(src_color, uv * screen_res.xy, screen_res.xy); vec4 aa_color = aa_apply(t_src_color, s_src_color, uv * screen_res.xy, screen_res.xy);
// Tonemapping // Tonemapping
float exposure_offset = 1.0; float exposure_offset = 1.0;

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -18,12 +18,16 @@
#include <globals.glsl> #include <globals.glsl>
in vec2 v_pos; layout(location = 0) out vec2 uv;
out vec2 f_pos;
void main() { void main() {
f_pos = v_pos; // Generate fullscreen triangle
vec2 v_pos = vec2(
float(gl_VertexIndex / 2) * 4.0 - 1.0,
float(gl_VertexIndex % 2) * 4.0 - 1.0
);
gl_Position = vec4(v_pos, -1.0, 1.0); uv = (v_pos * vec2(1.0, -1.0) + 1.0) * 0.5;
gl_Position = vec4(v_pos, 0.0, 1.0);
} }

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -20,14 +20,9 @@
#include <sky.glsl> #include <sky.glsl>
#include <lod.glsl> #include <lod.glsl>
in vec3 f_pos; layout(location = 0) in vec3 f_pos;
layout (std140) layout(location = 0) out vec4 tgt_color;
uniform u_locals {
vec4 nul;
};
out vec4 tgt_color;
void main() { void main() {
// tgt_color = vec4(MU_SCATTER, 1.0); // tgt_color = vec4(MU_SCATTER, 1.0);

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -18,31 +18,28 @@
#include <globals.glsl> #include <globals.glsl>
in vec3 v_pos; layout(location = 0) in vec3 v_pos;
layout (std140) layout(location = 0) out vec3 f_pos;
uniform u_locals {
vec4 nul;
};
out vec3 f_pos;
void main() { void main() {
/* vec3 v_pos = v_pos;
v_pos.y = -v_pos.y; */
f_pos = v_pos; f_pos = v_pos;
// TODO: Make this position-independent to avoid rounding error jittering // TODO: Make this position-independent to avoid rounding error jittering
// NOTE: we may or may not want to use an infinite projection here
//
// Essentially: using any finite projection is likely wrong here if we want
// to project out to infinity, but since we want to perturb the skybox as we
// move and we have stars now, the "right" answer is heavily dependent on
// how we compute cloud position and stuff.
//
// Infinite projections of cubemaps are nice because they can be oriented
// but still extend infinitely far.
gl_Position = gl_Position =
/* proj_mat *
view_mat * */
all_mat * all_mat *
/* proj_mat * vec4(v_pos + cam_pos.xyz, 1);
view_mat * */
vec4(/*100000 * */v_pos + cam_pos.xyz, 1);
// vec4(v_pos * (100000.0/* + 0.5*/) + cam_pos.xyz, 1);
// gl_Position = vec4(gl_Position.xy, sign(gl_Position.z) * gl_Position.w, gl_Position.w); // gl_Position = vec4(gl_Position.xy, sign(gl_Position.z) * gl_Position.w, gl_Position.w);
gl_Position.z = gl_Position.w; gl_Position.z = 0;
// gl_Position.z = gl_Position.w - 0.000001;//0.0; // gl_Position.z = gl_Position.w - 0.000001;//0.0;
// gl_Position.z = 1.0; // gl_Position.z = 1.0;
// gl_Position.z = -1.0; // gl_Position.z = -1.0;

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -16,31 +16,18 @@
#include <globals.glsl> #include <globals.glsl>
in vec3 f_pos; layout(location = 0) in vec3 f_pos;
flat in vec3 f_norm; layout(location = 1) flat in vec3 f_norm;
flat in float f_select; layout(location = 2) flat in float f_select;
// flat in vec3 f_pos_norm; layout(location = 3) in vec2 f_uv_pos;
in vec2 f_uv_pos; layout(location = 4) in vec2 f_inst_light;
in vec2 f_inst_light;
// flat in uint f_atlas_pos;
// in vec3 f_col;
// in float f_ao;
// in float f_light;
// in vec4 light_pos[2];
uniform sampler2D t_col_light; layout(set = 3, binding = 0)
uniform texture2D t_col_light;
layout(set = 3, binding = 1)
uniform sampler s_col_light;
//struct ShadowLocals { layout(location = 0) out vec4 tgt_color;
// mat4 shadowMatrices;
// mat4 texture_mat;
//};
//
//layout (std140)
//uniform u_light_shadows {
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
//};
out vec4 tgt_color;
#include <sky.glsl> #include <sky.glsl>
#include <light.glsl> #include <light.glsl>
@ -49,77 +36,31 @@ out vec4 tgt_color;
const float FADE_DIST = 32.0; const float FADE_DIST = 32.0;
void main() { void main() {
/* if (f_uv_pos.x < 757) {
discard;
} */
// vec2 f_uv_pos = vec2(768,1) + 0.5;
// vec2 f_uv_pos = vec2(760, 380);// + 0.5;
// vec2 f_uv_pos = vec2((uvec2(f_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu)) + 0.5;
/* if (f_uv_pos.x < 757) {
discard;
} */
// vec3 du = dFdx(f_pos);
// vec3 dv = dFdy(f_pos);
// vec3 f_norm = normalize(cross(du, dv));
float f_ao, f_glow; float f_ao, f_glow;
vec3 f_col = greedy_extract_col_light_glow(t_col_light, f_uv_pos, f_ao, f_glow); vec3 f_col = greedy_extract_col_light_glow(t_col_light, s_col_light, f_uv_pos, f_ao, f_glow);
// vec3 my_chunk_pos = f_pos_norm;
// tgt_color = vec4(hash(floor(vec4(my_chunk_pos.x, 0, 0, 0))), hash(floor(vec4(0, my_chunk_pos.y, 0, 1))), hash(floor(vec4(0, 0, my_chunk_pos.z, 2))), 1.0);
// tgt_color = vec4(f_uv_pos / texSize, 0.0, 1.0);
// tgt_color = vec4(f_col.rgb, 1.0);
// return;
// vec4 light_pos[2];
//#if (SHADOW_MODE == SHADOW_MODE_MAP)
// // for (uint i = 0u; i < light_shadow_count.z; ++i) {
// // light_pos[i] = /*vec3(*/shadowMats[i].texture_mat * vec4(f_pos, 1.0)/*)*/;
// // }
// vec4 sun_pos = /*vec3(*/shadowMats[0].texture_mat * vec4(f_pos, 1.0)/*)*/;
//#elif (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_NONE)
// vec4 sun_pos = vec4(0.0);
//#endif
vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz); vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz);
// vec4 vert_pos4 = view_mat * vec4(f_pos, 1.0);
// vec3 view_dir = normalize(-vec3(vert_pos4)/* / vert_pos4.w*/);
vec3 view_dir = -cam_to_frag; vec3 view_dir = -cam_to_frag;
/* vec3 sun_dir = get_sun_dir(time_of_day.x);
vec3 moon_dir = get_moon_dir(time_of_day.x); */
// float sun_light = get_sun_brightness(sun_dir);
// float moon_light = get_moon_brightness(moon_dir);
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY) #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP || FLUID_MODE == FLUID_MODE_SHINY)
float f_alt = alt_at(f_pos.xy); float f_alt = alt_at(f_pos.xy);
// float f_alt = f_pos.z;
#elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP) #elif (SHADOW_MODE == SHADOW_MODE_NONE || FLUID_MODE == FLUID_MODE_CHEAP)
float f_alt = f_pos.z; float f_alt = f_pos.z;
#endif #endif
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP)
vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy));
float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
// float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
#elif (SHADOW_MODE == SHADOW_MODE_NONE) #elif (SHADOW_MODE == SHADOW_MODE_NONE)
float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); float sun_shade_frac = 1.0;
#endif #endif
float moon_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, moon_dir); float moon_shade_frac = 1.0;
// float sun_shade_frac = horizon_at(f_pos, sun_dir);
// float moon_shade_frac = horizon_at(f_pos, moon_dir);
// Globbal illumination "estimate" used to light the faces of voxels which are parallel to the sun or moon (which is a very common occurrence).
// Will be attenuated by k_d, which is assumed to carry any additional ambient occlusion information (e.g. about shadowing).
// float ambient_sides = clamp(mix(0.5, 0.0, abs(dot(-f_norm, sun_dir)) * 10000.0), 0.0, 0.5);
// NOTE: current assumption is that moon and sun shouldn't be out at the sae time.
// This assumption is (or can at least easily be) wrong, but if we pretend it's true we avoids having to explicitly pass in a separate shadow
// for the sun and moon (since they have different brightnesses / colors so the shadows shouldn't attenuate equally).
// float shade_frac = sun_shade_frac + moon_shade_frac;
// DirectionalLight sun_info = get_sun_info(sun_dir, sun_shade_frac, light_pos);
float point_shadow = shadow_at(f_pos, f_norm); float point_shadow = shadow_at(f_pos, f_norm);
DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*sun_pos*/f_pos); DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, f_pos);
DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/); DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac);
vec3 surf_color = /*srgb_to_linear*//*linear_to_srgb*/(f_col); vec3 surf_color = f_col;
float alpha = 1.0; float alpha = 1.0;
const float n2 = 1.5; const float n2 = 1.5;
const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2); const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2);
@ -138,42 +79,11 @@ void main() {
sun_info.block = f_inst_light.x; sun_info.block = f_inst_light.x;
moon_info.block = f_inst_light.x; moon_info.block = f_inst_light.x;
// To account for prior saturation.
// float vert_light = pow(f_light, 1.5);
// vec3 light_frac = light_reflection_factor(f_norm/*vec3(0, 0, 1.0)*/, view_dir, vec3(0, 0, -1.0), vec3(1.0), vec3(R_s), alpha);
/* light_frac += light_reflection_factor(f_norm, view_dir, vec3(1.0, 0, 0.0), vec3(1.0), vec3(1.0), 2.0);
light_frac += light_reflection_factor(f_norm, view_dir, vec3(-1.0, 0, 0.0), vec3(1.0), vec3(1.0), 2.0);
light_frac += light_reflection_factor(f_norm, view_dir, vec3(0.0, -1.0, 0.0), vec3(1.0), vec3(1.0), 2.0);
light_frac += light_reflection_factor(f_norm, view_dir, vec3(0.0, 1.0, 0.0), vec3(1.0), vec3(1.0), 2.0); */
// vec3 light, diffuse_light, ambient_light;
// vec3 emitted_light, reflected_light;
// float point_shadow = shadow_at(f_pos,f_norm);
// vec3 point_light = light_at(f_pos, f_norm);
// vec3 surf_color = srgb_to_linear(vec3(0.2, 0.5, 1.0));
// vec3 cam_to_frag = normalize(f_pos - cam_pos.xyz);
float max_light = 0.0; float max_light = 0.0;
max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, /*time_of_day.x, *//*cam_to_frag*/view_dir, k_a/* * (shade_frac * 0.5 + light_frac * 0.5)*/, k_d, k_s, alpha, emitted_light, reflected_light); max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light);
// reflected_light *= /*vert_light * */point_shadow * shade_frac;
// emitted_light *= /*vert_light * */point_shadow * max(shade_frac, MIN_SHADOW);
// max_light *= /*vert_light * */point_shadow * shade_frac;
// emitted_light *= point_shadow;
// reflected_light *= point_shadow;
// max_light *= point_shadow;
// get_sun_diffuse(f_norm, time_of_day.x, light, diffuse_light, ambient_light, 1.0);
// float point_shadow = shadow_at(f_pos, f_norm);
// diffuse_light *= f_light * point_shadow;
// ambient_light *= f_light * point_shadow;
// light += point_light;
// diffuse_light += point_light;
// reflected_light += point_light;
max_light += lights_at(f_pos, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light); max_light += lights_at(f_pos, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light);
/* vec3 point_light = light_at(f_pos, f_norm);
emitted_light += point_light;
reflected_light += point_light; */
// float ao = /*pow(f_ao, 0.5)*/f_ao * 0.85 + 0.15;
vec3 glow = pow(f_inst_light.y, 3) * 4 * glow_light(f_pos); vec3 glow = pow(f_inst_light.y, 3) * 4 * glow_light(f_pos);
emitted_light += glow; emitted_light += glow;
@ -182,10 +92,9 @@ void main() {
reflected_light *= ao; reflected_light *= ao;
surf_color = illuminate(max_light, view_dir, surf_color * emitted_light, surf_color * reflected_light); surf_color = illuminate(max_light, view_dir, surf_color * emitted_light, surf_color * reflected_light);
// vec3 surf_color = illuminate(f_col, light, diffuse_light, ambient_light);
surf_color += f_select * (surf_color + 0.1) * vec3(0.15, 0.15, 0.15); surf_color += f_select * (surf_color + 0.1) * vec3(0.15, 0.15, 0.15);
// tgt_color = vec4(color, 1.0);
tgt_color = vec4(surf_color, 1.0 - clamp((distance(focus_pos.xy, f_pos.xy) - (sprite_render_distance - FADE_DIST)) / FADE_DIST, 0, 1)); tgt_color = vec4(surf_color, 1.0 - clamp((distance(focus_pos.xy, f_pos.xy) - (sprite_render_distance - FADE_DIST)) / FADE_DIST, 0, 1));
//tgt_color = vec4(-f_norm, 1.0);
} }

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -16,227 +16,114 @@
#include <srgb.glsl> #include <srgb.glsl>
#include <sky.glsl> #include <sky.glsl>
in vec3 v_pos; layout(location = 0) in vec4 inst_mat0;
in uint v_atlas_pos; layout(location = 1) in vec4 inst_mat1;
// in uint v_col; layout(location = 2) in vec4 inst_mat2;
in uint v_norm_ao; layout(location = 3) in vec4 inst_mat3;
in uint inst_pos_ori; // TODO: is there a better way to pack the various vertex attributes?
in vec4 inst_mat0; // TODO: ori is unused
in vec4 inst_mat1; layout(location = 4) in uint inst_pos_ori;
in vec4 inst_mat2; layout(location = 5) in uint inst_vert_page; // NOTE: this could fit in less bits
in vec4 inst_mat3; // TODO: do we need this many bits for light and glow?
in vec4 inst_light; layout(location = 6) in float inst_light;
in float inst_wind_sway; layout(location = 7) in float inst_glow;
layout(location = 8) in float model_wind_sway; // NOTE: this only varies per model
layout(location = 9) in float model_z_scale; // NOTE: this only varies per model
struct SpriteLocals { layout(set = 0, binding = 12) restrict readonly buffer sprite_verts {
mat4 mat; uvec2 verts[];
vec4 wind_sway;
vec4 offs;
}; };
layout (std140) layout (std140, set = 2, binding = 0)
uniform u_locals {
mat4 mat;
vec4 wind_sway;
vec4 offs;
// SpriteLocals sprites[8];
};
// struct Instance {
// mat4 inst_mat;
// vec3 inst_col;
// float inst_wind_sway;
// };
//
// layout (std140)
// uniform u_ibuf {
// Instance sprite_instances[/*MAX_LAYER_FACES*/512];
// };
//struct ShadowLocals {
// mat4 shadowMatrices;
// mat4 texture_mat;
//};
//
//layout (std140)
//uniform u_light_shadows {
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
//};
layout (std140)
uniform u_terrain_locals { uniform u_terrain_locals {
vec3 model_offs; vec3 model_offs;
float load_time; float load_time;
ivec4 atlas_offs; ivec4 atlas_offs;
}; };
out vec3 f_pos; // TODO: consider grouping into vec4's
flat out vec3 f_norm; layout(location = 0) out vec3 f_pos;
flat out float f_select; layout(location = 1) flat out vec3 f_norm;
// flat out vec3 f_pos_norm; layout(location = 2) flat out float f_select;
// out vec3 f_col; layout(location = 3) out vec2 f_uv_pos;
// out float f_ao; layout(location = 4) out vec2 f_inst_light;
out vec2 f_uv_pos;
out vec2 f_inst_light;
// flat out uint f_atlas_pos;
// out vec3 light_pos[2];
// out float f_light;
const float SCALE = 1.0 / 11.0; const float SCALE = 1.0 / 11.0;
const float SCALE_FACTOR = pow(SCALE, 1.3) * 0.2; const float SCALE_FACTOR = pow(SCALE, 1.3) * 0.2;
const int EXTRA_NEG_Z = 32768; const int EXTRA_NEG_Z = 32768;
const int VERT_EXTRA_NEG_Z = 128;
const uint VERT_PAGE_SIZE = 256;
void main() { void main() {
// vec3 inst_chunk_pos = vec3(ivec3((uvec3(inst_pos_ori) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z)); // Matrix to transform this sprite instance from model space to chunk space
// uint inst_ori = (inst_pos_ori >> 29) & 0x7u;
// SpriteLocals locals = sprites[inst_ori];
// SpriteLocals locals = sprites;
// mat4 inst_mat = locals.mat;
// float inst_wind_sway = locals.wind_sway.w;
// mat4 inst_mat = mat4(vec4(1, 0, 0, 0), vec4(0, 1, 0, 0), vec4(0, 0, 1, 0), vec4(5.5, 5.5, 0, 1));
// float inst_wind_sway = 0.0;
mat4 inst_mat; mat4 inst_mat;
inst_mat[0] = inst_mat0; inst_mat[0] = inst_mat0;
inst_mat[1] = inst_mat1; inst_mat[1] = inst_mat1;
inst_mat[2] = inst_mat2; inst_mat[2] = inst_mat2;
inst_mat[3] = inst_mat3; inst_mat[3] = inst_mat3;
/* Instance instances = sprite_instances[gl_InstanceID & 1023];
mat4 inst_mat = instances.inst_mat;
vec3 inst_col = instances.inst_col;
float inst_wind_sway = instances.inst_wind_sway; */
vec3 inst_offs = model_offs - focus_off.xyz;
// mat3 inst_mat;
// inst_mat[0] = inst_mat0.xyz;
// inst_mat[1] = inst_mat1.xyz;
// inst_mat[2] = inst_mat2.xyz;
// /* Instance instances = sprite_instances[gl_InstanceID & 1023];
// mat4 inst_mat = instances.inst_mat;
// vec3 inst_col = instances.inst_col;
// float inst_wind_sway = instances.inst_wind_sway; */
// float inst_wind_sway = wind_sway.w;
// vec3 inst_offs = model_offs - focus_off.xyz;
f_inst_light = inst_light.xy; // Worldpos of the chunk that this sprite is in
vec3 chunk_offs = model_offs - focus_off.xyz;
// vec3 sprite_pos = floor(inst_mat3.xyz * SCALE) + inst_offs; f_inst_light = vec2(inst_light, inst_glow);
// f_pos_norm = v_pos; // Index of the vertex data in the 1D vertex texture
int vertex_index = int(uint(gl_VertexIndex) % VERT_PAGE_SIZE + inst_vert_page * VERT_PAGE_SIZE);
uvec2 pos_atlas_pos_norm_ao = verts[vertex_index];
uint v_pos_norm = pos_atlas_pos_norm_ao.x;
uint v_atlas_pos = pos_atlas_pos_norm_ao.y;
// vec3 sprite_pos = (inst_mat * vec4(0, 0, 0, 1)).xyz; // Expand the model vertex position bits into float values
// vec3 sprite_pos = floor((inst_mat * vec4(0, 0, 0, 1)).xyz * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs; vec3 v_pos = vec3(ivec3((uvec3(v_pos_norm) >> uvec3(0, 8, 16)) & uvec3(0xFFu, 0xFFu, 0x0FFFu)) - ivec3(0, 0, VERT_EXTRA_NEG_Z));
// vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(0, 0, 0, 1)).xyz - /* wind_sway.xyz * */offs.xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) - inst_offs;
// vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(-offs.xyz, 1)).xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs;
// vec3 v_pos = vec3(gl_VertexID * 32, gl_VertexID % 32, 1.0); // Transform into chunk space and scale
// f_pos = v_pos + (model_offs - focus_off.xyz); f_pos = (inst_mat * vec4(v_pos, 1.0)).xyz;
// Transform info world space
// vec3 v_pos = /*inst_mat*//*locals.*/wind_sway.xyz * v_pos; f_pos += chunk_offs;
vec3 v_pos_ = /*inst_mat*//*locals.*//*sprites[0].*/wind_sway.xyz * v_pos;
// vec3 v_pos = (/*inst_mat*/locals.mat * vec4(v_pos, 1)).xyz + vec3(0.5, 0.5, 0.0);
// f_pos = v_pos * SCALE + (inst_chunk_pos + model_offs - focus_off.xyz);
// vec3 v_pos_ = (inst_mat * vec4(v_pos/* * SCALE*/, 1)).xyz;
// vec3 v_pos = (inst_mat * vec4(v_pos, 1)).xyz;
// f_pos = v_pos + (model_offs - focus_off.xyz);
f_pos = (inst_mat * vec4(v_pos_, 1.0)).xyz * SCALE + inst_offs;
// Terrain 'pop-in' effect // Terrain 'pop-in' effect
f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0)); f_pos.z -= 250.0 * (1.0 - min(1.0001 - 0.02 / pow(tick.x - load_time, 10.0), 1.0));
// f_pos = (inst_mat * v_pos_) * SCALE + sprite_pos;
// f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz + (model_offs - focus_off.xyz); // Wind sway effect
// f_pos = v_pos_ + (inst_chunk_pos + model_offs - focus_off.xyz + vec3(0.5, 0.5, 0.0)); f_pos += model_wind_sway * vec3(
// f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0));
// Wind waving
/* const float x_scale = sin(tick.x * 1.5 + f_pos.x * 0.1);
const float y_scale = sin(tick.x * 1.5 + f_pos.y * 0.1);
const float z_scale = pow(abs(v_pos_.z), 1.3) * SCALE_FACTOR;
const float xy_bias = sin(tick.x * 0.25);
const float z_bias = xy_bias * t_scale;
mat3 shear = mat4(
vec3(x_scale , 0.0, 0.0, 0.0),
vec3(0.0, y_scale, 0.0, 0.0),
vec3(0.0, 0.0, z_bias, 0.0),
vec3(0.0, 0.0, (1.0 / z_bias), 0.0)
); */
// const float x_scale = sin(tick.x * 1.5 + f_pos.x * 0.1);
// const float y_scale = sin(tick.x * 1.5 + f_pos.y * 0.1);
// const float z_scale = pow(abs(v_pos_.z), 1.3) * SCALE_FACTOR;
// const float xy_bias = sin(tick.x * 0.25);
// const float z_bias = xy_bias * t_scale;
// vec3 rotate = inst_wind_sway * vec3(
// sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35),
// sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25),
// 0.0
// ) * pow(abs(v_pos_.z/* + sprites[0].offs.z*/)/* * SCALE*/, 1.3) * /*0.2;*/SCALE_FACTOR;
//
// mat3 shear = mat4(
// vec3(x_scale * , 0.0, 0.0, 0.0),
// vec3(0.0, y_scale, 0.0, 0.0),
// vec3(0.0, 0.0, z_bias, 0.0),
// vec3(0.0, 0.0, (1.0 / z_bias), 0.0)
// );
/*if (wind_sway.w >= 0.4) */{
f_pos += /*inst_wind_sway*/wind_sway.w * vec3(
sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35), sin(tick.x * 1.5 + f_pos.y * 0.1) * sin(tick.x * 0.35),
sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25), sin(tick.x * 1.5 + f_pos.x * 0.1) * sin(tick.x * 0.25),
0.0 0.0
) * 4 * v_pos_.z * /*0.2;*/SCALE_FACTOR; // NOTE: could potentially replace `v_pos.z * model_z_scale` with a calculation using `inst_chunk_pos` from below
//) * pow(abs(v_pos.z * model_z_scale), 1.3) * SCALE_FACTOR;
) * v_pos.z * model_z_scale * SCALE_FACTOR;
// Determine normal
// TODO: do changes here effect perf on vulkan
// TODO: dx12 doesn't like dynamic index
// TODO: use mix?
// Shader@0x000001AABD89BEE0(112,43-53): error X4576: Input array signature parameter cannot be indexed dynamically.
//vec3 norm = (inst_mat[(v_pos_norm >> 30u) & 3u].xyz);
uint index = v_pos_norm >> 30u & 3u;
vec3 norm;
if (index == 0) {
norm = (inst_mat[0].xyz);
} else if (index == 1) {
norm = (inst_mat[1].xyz);
} else {
norm = (inst_mat[2].xyz);
} }
// First 3 normals are negative, next 3 are positive f_norm = normalize(mix(-norm, norm, v_pos_norm >> 29u & 1u));
// vec3 normals[6] = vec3[](vec3(-1,0,0), vec3(1,0,0), vec3(0,-1,0), vec3(0,1,0), vec3(0,0,-1), vec3(0,0,1));
// uint norm_idx = (v_norm_ao >> 0) & 0x7u;
// f_norm = (inst_mat * vec4(normals[], 0)).xyz;
// TODO: Consider adding a second, already-normalized (i.e. unscaled) matrix. // Expand atlas tex coords to floats
// vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u].xyz); // NOTE: Could defer to fragment shader if we are vert heavy
// vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u]); f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu));;
// vec3 norm = bone_data.normals_mat[axis_idx].xyz; // Position of the sprite block in the chunk
// norm = normalize(norm); // Used solely for highlighting the selected sprite
// norm = norm / SCALE_FACTOR / locals.wind_sway.xyz; vec3 inst_chunk_pos = vec3(ivec3((uvec3(inst_pos_ori) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z));
// norm = norm / (norm.x + norm.y + norm.z); // Select glowing
// vec3 norm = norm_mat * vec4(uvec3(1 << axis_idx) & uvec3(0x1u, 0x3u, 0x7u), 1); vec3 sprite_pos = inst_chunk_pos + chunk_offs;
f_select = (select_pos.w > 0 && select_pos.xyz == sprite_pos) ? 1.0 : 0.0;
// // Calculate normal here rather than for each pixel in the fragment shader
// f_norm = normalize((
// combined_mat *
// vec4(norm, 0)
// ).xyz);
vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u].xyz);
f_norm = mix(-norm, norm, v_norm_ao & 1u);
/* vec3 col = vec3((uvec3(v_col) >> uvec3(0, 8, 16)) & uvec3(0xFFu)) / 255.0;
f_col = srgb_to_linear(col) * srgb_to_linear(inst_col);
f_ao = float((v_norm_ao >> 3) & 0x3u) / 4.0; */
f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu));/* + 0.5*/;
// f_atlas_pos = v_atlas_pos;
/* for (uint i = 0u; i < light_shadow_count.z; ++i) {
light_pos[i] = vec3(shadowMats[i].texture_mat * vec4(f_pos, 1.0));
} */
// // Select glowing
// if (select_pos.w > 0 && select_pos.xyz == floor(sprite_pos)) {
// f_col *= 4.0;
// }
// f_light = 1.0;
// if (select_pos.w > 0) */{
vec3 sprite_pos = /*round*/floor(((inst_mat * vec4(-offs.xyz, 1)).xyz) * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs;
f_select = (select_pos.w > 0 && select_pos.xyz == sprite_pos/* - vec3(0.5, 0.5, 0.0) * SCALE*/) ? 1.0 : 0.0;
// }
gl_Position = gl_Position =
all_mat * all_mat *
vec4(f_pos, 1); vec4(f_pos, 1);
// gl_Position.z = -gl_Position.z;
// gl_Position.z = -gl_Position.z / gl_Position.w;
// gl_Position.z = -gl_Position.z / 100.0;
// gl_Position.z = -gl_Position.z / 100.0;
// gl_Position.z = -1000.0 / (gl_Position.z + 10000.0);
} }

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
// #extension GL_ARB_texture_storage : require // #extension GL_ARB_texture_storage : require
#include <constants.glsl> #include <constants.glsl>
@ -22,12 +22,12 @@
#include <globals.glsl> #include <globals.glsl>
#include <random.glsl> #include <random.glsl>
in vec3 f_pos; layout(location = 0) in vec3 f_pos;
// in float f_ao; // in float f_ao;
// in vec3 f_chunk_pos; // in vec3 f_chunk_pos;
// #ifdef FLUID_MODE_SHINY // #ifdef FLUID_MODE_SHINY
flat in uint f_pos_norm; layout(location = 1) flat in uint f_pos_norm;
flat in float f_load_time; layout(location = 2) flat in float f_load_time;
// #else // #else
// const uint f_pos_norm = 0u; // const uint f_pos_norm = 0u;
// #endif // #endif
@ -35,7 +35,7 @@ flat in float f_load_time;
// in vec4 f_shadow; // in vec4 f_shadow;
// in vec3 f_col; // in vec3 f_col;
// in float f_light; // in float f_light;
/*centroid */in vec2 f_uv_pos; /*centroid */layout(location = 3) in vec2 f_uv_pos;
// in vec3 light_pos[2]; // in vec3 light_pos[2];
// const vec3 light_pos[6] = vec3[](vec3(0), vec3(0), vec3(00), vec3(0), vec3(0), vec3(0)); // const vec3 light_pos[6] = vec3[](vec3(0), vec3(0), vec3(00), vec3(0), vec3(0), vec3(0));
@ -45,16 +45,19 @@ in vec4 sun_pos;
const vec4 sun_pos = vec4(0.0); const vec4 sun_pos = vec4(0.0);
#endif */ #endif */
uniform sampler2D t_col_light; layout(set = 3, binding = 0)
uniform texture2D t_col_light;
layout(set = 3, binding = 1)
uniform sampler s_col_light;
layout (std140) layout (std140, set = 2, binding = 0)
uniform u_locals { uniform u_locals {
vec3 model_offs; vec3 model_offs;
float load_time; float load_time;
ivec4 atlas_offs; ivec4 atlas_offs;
}; };
out vec4 tgt_color; layout(location = 0) out vec4 tgt_color;
#include <sky.glsl> #include <sky.glsl>
#include <light.glsl> #include <light.glsl>
@ -82,7 +85,7 @@ void main() {
// vec4 f_col_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0)));//(f_uv_pos/* + 0.5*/) / texSize); // vec4 f_col_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0)));//(f_uv_pos/* + 0.5*/) / texSize);
// float f_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0))).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; // float f_light = textureProj(t_col_light, vec3(f_uv_pos + 0.5, textureSize(t_col_light, 0))).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0;
float f_light, f_glow; float f_light, f_glow;
vec3 f_col = greedy_extract_col_light_glow(t_col_light, f_uv_pos, f_light, f_glow); vec3 f_col = greedy_extract_col_light_glow(t_col_light, s_col_light, f_uv_pos, f_light, f_glow);
//float f_light = (uint(texture(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0)).r * 255.0) & 0x1Fu) / 31.0; //float f_light = (uint(texture(t_col_light, (f_uv_pos + 0.5) / textureSize(t_col_light, 0)).r * 255.0) & 0x1Fu) / 31.0;
// vec2 texSize = textureSize(t_col_light, 0); // vec2 texSize = textureSize(t_col_light, 0);
// float f_light = texture(t_col_light, f_uv_pos/* + vec2(atlas_offs.xy)*/).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0; // float f_light = texture(t_col_light, f_uv_pos/* + vec2(atlas_offs.xy)*/).a;//1.0;//f_col_light.a * 4.0;// f_light = float(v_col_light & 0x3Fu) / 64.0;
@ -216,7 +219,7 @@ void main() {
// float f_alt = alt_at(f_pos.xy); // float f_alt = alt_at(f_pos.xy);
// vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); // vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy));
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP) #if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP)
vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy)); vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy));
float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir); float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
#elif (SHADOW_MODE == SHADOW_MODE_NONE) #elif (SHADOW_MODE == SHADOW_MODE_NONE)
float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir); float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir);

View File

@ -1,4 +1,4 @@
#version 330 core #version 420 core
#include <constants.glsl> #include <constants.glsl>
@ -24,14 +24,15 @@
#include <shadows.glsl> #include <shadows.glsl>
in uint v_pos_norm; layout(location = 0) in uint v_pos_norm;
// in uint v_col_light; // in uint v_col_light;
in uint v_atlas_pos; layout(location = 1) in uint v_atlas_pos;
layout (std140) layout (std140, set = 2, binding = 0)
uniform u_locals { uniform u_locals {
vec3 model_offs; vec3 model_offs;
float load_time; float load_time;
// TODO: consider whether these need to be signed
ivec4 atlas_offs; ivec4 atlas_offs;
}; };
@ -45,10 +46,10 @@ uniform u_locals {
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192]; // ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
//}; //};
out vec3 f_pos; layout(location = 0) out vec3 f_pos;
// #ifdef FLUID_MODE_SHINY // #ifdef FLUID_MODE_SHINY
flat out uint f_pos_norm; layout(location = 1) flat out uint f_pos_norm;
flat out float f_load_time; layout(location = 2) flat out float f_load_time;
// #if (SHADOW_MODE == SHADOW_MODE_MAP) // #if (SHADOW_MODE == SHADOW_MODE_MAP)
// out vec4 sun_pos; // out vec4 sun_pos;
@ -60,7 +61,7 @@ flat out float f_load_time;
// out vec3 f_col; // out vec3 f_col;
// out vec3 f_chunk_pos; // out vec3 f_chunk_pos;
// out float f_ao; // out float f_ao;
/*centroid */out vec2 f_uv_pos; /*centroid */layout(location = 3) out vec2 f_uv_pos;
// out vec3 light_pos[2]; // out vec3 light_pos[2];
// out float f_light; // out float f_light;
@ -155,7 +156,7 @@ void main() {
#ifdef HAS_SHADOW_MAPS #ifdef HAS_SHADOW_MAPS
gl_Position = gl_Position =
/*all_mat*/shadowMats[0].shadowMatrices/*texture_mat*/ * /*all_mat*/shadowMatrices/*texture_mat*/ *
vec4(f_pos/*newRay*/, 1); vec4(f_pos/*newRay*/, 1);
gl_Position.z = clamp(gl_Position.z, -abs(gl_Position.w), abs(gl_Position.w)); gl_Position.z = clamp(gl_Position.z, -abs(gl_Position.w), abs(gl_Position.w));
#else #else

View File

@ -1,28 +1,31 @@
#version 330 core #version 420 core
#include <globals.glsl> #include <globals.glsl>
in vec2 f_uv; layout(location = 0) in vec2 f_uv;
in vec4 f_color; layout(location = 1) in vec4 f_color;
flat in uint f_mode; layout(location = 2) flat in uint f_mode;
layout (std140) layout (std140, set = 1, binding = 0)
uniform u_locals { uniform u_locals {
vec4 w_pos; vec4 w_pos;
}; };
uniform sampler2D u_tex; layout(set = 2, binding = 0)
uniform texture2D t_tex;
layout(set = 2, binding = 1)
uniform sampler s_tex;
out vec4 tgt_color; layout(location = 0) out vec4 tgt_color;
void main() { void main() {
// Text // Text
if (f_mode == uint(0)) { if (f_mode == uint(0)) {
tgt_color = f_color * vec4(1.0, 1.0, 1.0, texture(u_tex, f_uv).a); tgt_color = f_color * vec4(1.0, 1.0, 1.0, texture(sampler2D(t_tex, s_tex), f_uv).a);
// Image // Image
// HACK: bit 0 is set for both ordinary and north-facing images. // HACK: bit 0 is set for both ordinary and north-facing images.
} else if ((f_mode & uint(1)) == uint(1)) { } else if ((f_mode & uint(1)) == uint(1)) {
tgt_color = f_color * texture(u_tex, f_uv); tgt_color = f_color * texture(sampler2D(t_tex, s_tex), f_uv);
// 2D Geometry // 2D Geometry
} else if (f_mode == uint(2)) { } else if (f_mode == uint(2)) {
tgt_color = f_color; tgt_color = f_color;

View File

@ -1,23 +1,26 @@
#version 330 core #version 420 core
#include <globals.glsl> #include <globals.glsl>
in vec2 v_pos; layout(location = 0) in vec2 v_pos;
in vec2 v_uv; layout(location = 1) in vec2 v_uv;
in vec2 v_center; layout(location = 2) in vec4 v_color;
in vec4 v_color; layout(location = 3) in vec2 v_center;
in uint v_mode; layout(location = 4) in uint v_mode;
layout (std140) layout (std140, set = 1, binding = 0)
uniform u_locals { uniform u_locals {
vec4 w_pos; vec4 w_pos;
}; };
uniform sampler2D u_tex; layout(set = 2, binding = 0)
uniform texture2D t_tex;
layout(set = 2, binding = 1)
uniform sampler s_tex;
out vec2 f_uv; layout(location = 0) out vec2 f_uv;
flat out uint f_mode; layout(location = 1) out vec4 f_color;
out vec4 f_color; layout(location = 2) flat out uint f_mode;
void main() { void main() {
f_color = v_color; f_color = v_color;
@ -30,13 +33,13 @@ void main() {
f_uv = v_uv; f_uv = v_uv;
// Fixed scale In-game element // Fixed scale In-game element
vec4 projected_pos = /*proj_mat * view_mat*/all_mat * vec4(w_pos.xyz - focus_off.xyz, 1.0); vec4 projected_pos = /*proj_mat * view_mat*/all_mat * vec4(w_pos.xyz - focus_off.xyz, 1.0);
gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos/* * projected_pos.w*/, -1.0, /*projected_pos.w*/1.0); gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos/* * projected_pos.w*/, 0.5, /*projected_pos.w*/1.0);
} else if (v_mode == uint(3)) { } else if (v_mode == uint(3)) {
// HACK: North facing source rectangle. // HACK: North facing source rectangle.
gl_Position = vec4(v_pos, -1.0, 1.0); gl_Position = vec4(v_pos, 0.5, 1.0);
vec2 look_at_dir = normalize(vec2(-view_mat[0][2], -view_mat[1][2])); vec2 look_at_dir = normalize(vec2(-view_mat[0][2], -view_mat[1][2]));
// TODO: Consider cleaning up matrix to something more efficient (e.g. a mat3). // TODO: Consider cleaning up matrix to something more efficient (e.g. a mat3).
vec2 aspect_ratio = textureSize(u_tex, 0).yx; vec2 aspect_ratio = textureSize(sampler2D(t_tex, s_tex), 0).yx;
mat2 look_at = mat2(look_at_dir.y, look_at_dir.x, -look_at_dir.x, look_at_dir.y); mat2 look_at = mat2(look_at_dir.y, look_at_dir.x, -look_at_dir.x, look_at_dir.y);
vec2 v_centered = (v_uv - v_center) / aspect_ratio; vec2 v_centered = (v_uv - v_center) / aspect_ratio;
vec2 v_rotated = look_at * v_centered; vec2 v_rotated = look_at * v_centered;
@ -50,11 +53,12 @@ void main() {
mat2 look_at = mat2(look_at_dir.y, -look_at_dir.x, look_at_dir.x, look_at_dir.y); mat2 look_at = mat2(look_at_dir.y, -look_at_dir.x, look_at_dir.x, look_at_dir.y);
vec2 v_centered = (v_pos - v_center) / aspect_ratio; vec2 v_centered = (v_pos - v_center) / aspect_ratio;
vec2 v_rotated = look_at * v_centered; vec2 v_rotated = look_at * v_centered;
gl_Position = vec4(aspect_ratio * v_rotated + v_center, -1.0, 1.0); gl_Position = vec4(aspect_ratio * v_rotated + v_center, 0.5, 1.0);
} else { } else {
// Interface element // Interface element
f_uv = v_uv; f_uv = v_uv;
gl_Position = vec4(v_pos, -1.0, 1.0); gl_Position = vec4(v_pos, 0.5, 1.0);
} }
f_mode = v_mode; f_mode = v_mode;
} }

BIN
assets/voxygen/texture/waves.png (Stored with Git LFS)

Binary file not shown.

View File

@ -58,7 +58,10 @@ macro_rules! span {
}; };
} }
pub struct DummySpan; #[cfg(feature = "tracy")]
pub struct ProfSpan(pub tracy_client::Span);
#[cfg(not(feature = "tracy"))]
pub struct ProfSpan;
/// Like the span macro but only used when profiling and not in regular tracing /// Like the span macro but only used when profiling and not in regular tracing
/// operations /// operations
@ -66,16 +69,16 @@ pub struct DummySpan;
macro_rules! prof_span { macro_rules! prof_span {
($guard_name:tt, $name:expr) => { ($guard_name:tt, $name:expr) => {
#[cfg(feature = "tracy")] #[cfg(feature = "tracy")]
let $guard_name = $crate::tracy_client::Span::new( let $guard_name = $crate::ProfSpan($crate::tracy_client::Span::new(
$name, $name,
"", "",
module_path!(), module_path!(),
line!(), line!(),
// No callstack since this has significant overhead // No callstack since this has significant overhead
0, 0,
); ));
#[cfg(not(feature = "tracy"))] #[cfg(not(feature = "tracy"))]
let $guard_name = $crate::DummySpan; let $guard_name = $crate::ProfSpan;
}; };
} }

View File

@ -47,7 +47,6 @@ where
// this crate would be veloren_voxygen=debug. // this crate would be veloren_voxygen=debug.
let base_exceptions = |env: EnvFilter| { let base_exceptions = |env: EnvFilter| {
env.add_directive("dot_vox::parser=warn".parse().unwrap()) env.add_directive("dot_vox::parser=warn".parse().unwrap())
.add_directive("gfx_device_gl=warn".parse().unwrap())
.add_directive("veloren_common::trade=info".parse().unwrap()) .add_directive("veloren_common::trade=info".parse().unwrap())
.add_directive("veloren_world::sim=info".parse().unwrap()) .add_directive("veloren_world::sim=info".parse().unwrap())
.add_directive("veloren_world::civ=info".parse().unwrap()) .add_directive("veloren_world::civ=info".parse().unwrap())
@ -58,6 +57,7 @@ where
.add_directive("h2=info".parse().unwrap()) .add_directive("h2=info".parse().unwrap())
.add_directive("tokio_util=info".parse().unwrap()) .add_directive("tokio_util=info".parse().unwrap())
.add_directive("rustls=info".parse().unwrap()) .add_directive("rustls=info".parse().unwrap())
.add_directive("wgpu_core::device=warn".parse().unwrap())
.add_directive("veloren_network_protocol=info".parse().unwrap()) .add_directive("veloren_network_protocol=info".parse().unwrap())
.add_directive("quinn_proto::connection=info".parse().unwrap()) .add_directive("quinn_proto::connection=info".parse().unwrap())
.add_directive( .add_directive(

View File

@ -22,14 +22,13 @@ runtimeLibs = ["libGL", "xorg.libX11", "xorg.libXcursor", "xorg.libXrandr", "xor
buildInputs = ["xorg.libxcb"] buildInputs = ["xorg.libxcb"]
[features] [features]
gl = ["gfx_device_gl", "gfx_gl"]
hot-anim = ["anim/use-dyn-lib"] hot-anim = ["anim/use-dyn-lib"]
singleplayer = ["server"] singleplayer = ["server"]
simd = ["vek/platform_intrinsics"] simd = ["vek/platform_intrinsics"]
tracy = ["common/tracy", "common-ecs/tracy", "common-frontend/tracy", "common-net/tracy", "common-systems/tracy", "common-state/tracy", "client/tracy"] tracy = ["profiling", "profiling/profile-with-tracy", "common/tracy", "common-ecs/tracy", "common-frontend/tracy", "common-net/tracy", "common-systems/tracy", "common-state/tracy", "client/tracy"]
plugins = ["client/plugins"] plugins = ["client/plugins"]
default = ["gl", "singleplayer", "native-dialog", "plugins", "simd"] default = ["singleplayer", "native-dialog", "plugins", "simd"]
[dependencies] [dependencies]
client = {package = "veloren-client", path = "../client"} client = {package = "veloren-client", path = "../client"}
@ -45,12 +44,11 @@ anim = {package = "veloren-voxygen-anim", path = "anim"}
i18n = {package = "veloren-i18n", path = "i18n"} i18n = {package = "veloren-i18n", path = "i18n"}
# Graphics # Graphics
gfx = "0.18.2"
gfx_device_gl = {version = "0.16.2", optional = true}
gfx_gl = {version = "0.6.1", optional = true}
glutin = "0.26.0"
old_school_gfx_glutin_ext = "0.26"
winit = {version = "0.24.0", features = ["serde"]} winit = {version = "0.24.0", features = ["serde"]}
wgpu = { version = "=0.8.0", features = ["trace", "cross"] }
wgpu-profiler = { git = "https://github.com/Imberflur/wgpu-profiler", tag = "wgpu-0.8" }
bytemuck = { version="1.4", features=["derive"] }
shaderc = "0.6.2"
# Ui # Ui
conrod_core = {git = "https://gitlab.com/veloren/conrod.git", branch="copypasta_0.7"} conrod_core = {git = "https://gitlab.com/veloren/conrod.git", branch="copypasta_0.7"}
@ -87,9 +85,7 @@ crossbeam-channel = "0.5"
directories-next = "2.0" directories-next = "2.0"
dot_vox = "4.0" dot_vox = "4.0"
enum-iterator = "0.6" enum-iterator = "0.6"
strum = "0.20" futures-executor = "0.3"
strum_macros = "0.20"
glsl-include = "0.3.1"
guillotiere = "0.6" guillotiere = "0.6"
hashbrown = {version = "0.9", features = ["rayon", "serde", "nightly"]} hashbrown = {version = "0.9", features = ["rayon", "serde", "nightly"]}
image = {version = "0.23.12", default-features = false, features = ["ico", "png"]} image = {version = "0.23.12", default-features = false, features = ["ico", "png"]}
@ -98,9 +94,12 @@ native-dialog = { version = "0.5.2", optional = true }
num = "0.4" num = "0.4"
ordered-float = { version = "2.0.1", default-features = false } ordered-float = { version = "2.0.1", default-features = false }
rand = "0.8" rand = "0.8"
rayon = "1.5"
rodio = {version = "0.13", default-features = false, features = ["vorbis"]} rodio = {version = "0.13", default-features = false, features = ["vorbis"]}
ron = {version = "0.6", default-features = false} ron = {version = "0.6", default-features = false}
serde = {version = "1.0", features = [ "rc", "derive" ]} serde = {version = "1.0", features = [ "rc", "derive" ]}
strum = "0.20"
strum_macros = "0.20"
treeculler = "0.2" treeculler = "0.2"
tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] } tokio = { version = "1", default-features = false, features = ["rt-multi-thread"] }
num_cpus = "1.0" num_cpus = "1.0"
@ -110,6 +109,7 @@ itertools = "0.10.0"
# Tracy # Tracy
tracing = "0.1" tracing = "0.1"
profiling = { version = "1.0.1", default-features = false, optional = true }
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
dispatch = "0.1.4" dispatch = "0.1.4"

View File

@ -20,3 +20,4 @@ libloading = {version = "0.7", optional = true}
notify = {version = "5.0.0-pre.2", optional = true} notify = {version = "5.0.0-pre.2", optional = true}
tracing = {version = "0.1", optional = true} tracing = {version = "0.1", optional = true}
vek = {version = "=0.14.1", features = ["serde"]} vek = {version = "=0.14.1", features = ["serde"]}
bytemuck = { version="1.4", features=["derive"] }

View File

@ -74,16 +74,19 @@ pub use dyn_lib::init;
use std::ffi::CStr; use std::ffi::CStr;
use self::vek::*; use self::vek::*;
use bytemuck::{Pod, Zeroable};
type MatRaw = [[f32; 4]; 4]; type MatRaw = [[f32; 4]; 4];
pub type FigureBoneData = (MatRaw, MatRaw); #[repr(C)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
pub struct FigureBoneData(pub MatRaw, pub MatRaw);
pub const MAX_BONE_COUNT: usize = 16; pub const MAX_BONE_COUNT: usize = 16;
fn make_bone(mat: Mat4<f32>) -> FigureBoneData { fn make_bone(mat: Mat4<f32>) -> FigureBoneData {
let normal = mat.map_cols(Vec4::normalized); let normal = mat.map_cols(Vec4::normalized);
(mat.into_col_arrays(), normal.into_col_arrays()) FigureBoneData(mat.into_col_arrays(), normal.into_col_arrays())
} }
pub type Bone = Transform<f32, f32, f32>; pub type Bone = Transform<f32, f32, f32>;

View File

@ -5,7 +5,7 @@ use common::{
use criterion::{black_box, criterion_group, criterion_main, Criterion}; use criterion::{black_box, criterion_group, criterion_main, Criterion};
use std::sync::Arc; use std::sync::Arc;
use vek::*; use vek::*;
use veloren_voxygen::{mesh::Meshable, scene::terrain::BlocksOfInterest}; use veloren_voxygen::{mesh::terrain::generate_mesh, scene::terrain::BlocksOfInterest};
use world::{sim, World}; use world::{sim, World};
const CENTER: Vec2<i32> = Vec2 { x: 512, y: 512 }; const CENTER: Vec2<i32> = Vec2 { x: 512, y: 512 };
@ -142,11 +142,10 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let (volume, range) = sample(Vec2::new(x, y)); let (volume, range) = sample(Vec2::new(x, y));
meshing_benches.bench_function(&format!("Terrain mesh {}, {}", x, y), move |b| { meshing_benches.bench_function(&format!("Terrain mesh {}, {}", x, y), move |b| {
b.iter(|| { b.iter(|| {
volume.generate_mesh(black_box(( generate_mesh(
range, black_box(&volume),
Vec2::new(8192, 8192), black_box((range, Vec2::new(8192, 8192), &BlocksOfInterest::default())),
&BlocksOfInterest::default(), )
)))
}) })
}); });
} }

View File

@ -55,7 +55,7 @@ use crate::{
ecs::{comp as vcomp, comp::HpFloaterList}, ecs::{comp as vcomp, comp::HpFloaterList},
hud::{img_ids::ImgsRot, prompt_dialog::DialogOutcomeEvent}, hud::{img_ids::ImgsRot, prompt_dialog::DialogOutcomeEvent},
i18n::Localization, i18n::Localization,
render::{Consts, Globals, Renderer}, render::UiDrawer,
scene::camera::{self, Camera}, scene::camera::{self, Camera},
session::{ session::{
settings_change::{Chat as ChatChange, Interface as InterfaceChange, SettingsChange}, settings_change::{Chat as ChatChange, Interface as InterfaceChange, SettingsChange},
@ -238,6 +238,7 @@ widget_ids! {
num_lights, num_lights,
num_figures, num_figures,
num_particles, num_particles,
gpu_timings[],
// Game Version // Game Version
version, version,
@ -2195,6 +2196,33 @@ impl Hud {
.font_size(self.fonts.cyri.scale(14)) .font_size(self.fonts.cyri.scale(14))
.set(self.ids.num_particles, ui_widgets); .set(self.ids.num_particles, ui_widgets);
// GPU timing for different pipelines
let gpu_timings = global_state.window.renderer().timings();
if !gpu_timings.is_empty() {
let num_timings = gpu_timings.len();
// Make sure we have enough ids
if self.ids.gpu_timings.len() < num_timings {
self.ids
.gpu_timings
.resize(num_timings, &mut ui_widgets.widget_id_generator());
}
for (i, timing) in gpu_timings.iter().enumerate() {
Text::new(&format!(
"{:16}{:.3} ms",
&format!("{}:", timing.1),
timing.2 * 1000.0,
))
.color(TEXT_COLOR)
.down(5.0)
.x_place_on(
ui_widgets.window,
conrod_core::position::Place::Start(Some(5.0 + 10.0 * timing.0 as f64)),
)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(14))
.set(self.ids.gpu_timings[i], ui_widgets);
}
}
// Help Window // Help Window
if let Some(help_key) = global_state.settings.controls.get_binding(GameInput::Help) { if let Some(help_key) = global_state.settings.controls.get_binding(GameInput::Help) {
Text::new( Text::new(
@ -2203,7 +2231,7 @@ impl Hud {
.replace("{key}", help_key.display_string(key_layout).as_str()), .replace("{key}", help_key.display_string(key_layout).as_str()),
) )
.color(TEXT_COLOR) .color(TEXT_COLOR)
.down_from(self.ids.num_particles, 5.0) .down(5.0)
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(14)) .font_size(self.fonts.cyri.scale(14))
.set(self.ids.help_info, ui_widgets); .set(self.ids.help_info, ui_widgets);
@ -3620,11 +3648,11 @@ impl Hud {
events events
} }
pub fn render(&self, renderer: &mut Renderer, globals: &Consts<Globals>) { pub fn render<'a>(&'a self, drawer: &mut UiDrawer<'_, 'a>) {
span!(_guard, "render", "Hud::render"); span!(_guard, "render", "Hud::render");
// Don't show anything if the UI is toggled off. // Don't show anything if the UI is toggled off.
if self.show.ui { if self.show.ui {
self.ui.render(renderer, Some(globals)); self.ui.render(drawer);
} }
} }

View File

@ -36,6 +36,8 @@ widget_ids! {
load_tips_button_label, load_tips_button_label,
debug_button, debug_button,
debug_button_label, debug_button_label,
hitboxes_button,
hitboxes_button_label,
ch_title, ch_title,
ch_transp_slider, ch_transp_slider,
ch_transp_value, ch_transp_value,
@ -239,9 +241,33 @@ impl<'a> Widget for Interface<'a> {
.color(TEXT_COLOR) .color(TEXT_COLOR)
.set(state.ids.debug_button_label, ui); .set(state.ids.debug_button_label, ui);
// Hitboxes
let show_hitboxes = ToggleButton::new(
self.global_state.settings.interface.toggle_hitboxes,
self.imgs.checkbox,
self.imgs.checkbox_checked,
)
.w_h(18.0, 18.0)
.down_from(state.ids.debug_button, 8.0)
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
.set(state.ids.hitboxes_button, ui);
if self.global_state.settings.interface.toggle_hitboxes != show_hitboxes {
events.push(ToggleHitboxes(show_hitboxes));
}
Text::new(&self.localized_strings.get("hud.settings.show_hitboxes"))
.right_from(state.ids.hitboxes_button, 10.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.graphics_for(state.ids.hitboxes_button)
.color(TEXT_COLOR)
.set(state.ids.hitboxes_button_label, ui);
// Ui Scale // Ui Scale
Text::new(&self.localized_strings.get("hud.settings.ui_scale")) Text::new(&self.localized_strings.get("hud.settings.ui_scale"))
.down_from(state.ids.debug_button, 20.0) .down_from(state.ids.hitboxes_button, 20.0)
.font_size(self.fonts.cyri.scale(18)) .font_size(self.fonts.cyri.scale(18))
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR) .color(TEXT_COLOR)

View File

@ -7,10 +7,10 @@ use crate::{
}, },
i18n::Localization, i18n::Localization,
render::{ render::{
AaMode, CloudMode, FluidMode, LightingMode, RenderMode, ShadowMapMode, ShadowMode, AaMode, CloudMode, FluidMode, LightingMode, PresentMode, RenderMode, ShadowMapMode,
UpscaleMode, ShadowMode, UpscaleMode,
}, },
session::settings_change::{Graphics as GraphicsChange, Graphics::*}, session::settings_change::Graphics as GraphicsChange,
settings::Fps, settings::Fps,
ui::{fonts::Fonts, ImageSlider, ToggleButton}, ui::{fonts::Fonts, ImageSlider, ToggleButton},
window::{FullScreenSettings, FullscreenMode}, window::{FullScreenSettings, FullscreenMode},
@ -35,6 +35,7 @@ widget_ids! {
window_scrollbar, window_scrollbar,
reset_graphics_button, reset_graphics_button,
fps_counter, fps_counter,
pipeline_recreation_text,
vd_slider, vd_slider,
vd_text, vd_text,
vd_value, vd_value,
@ -50,6 +51,8 @@ widget_ids! {
max_fps_slider, max_fps_slider,
max_fps_text, max_fps_text,
max_fps_value, max_fps_value,
present_mode_text,
present_mode_list,
fov_slider, fov_slider,
fov_text, fov_text,
fov_value, fov_value,
@ -80,6 +83,9 @@ widget_ids! {
refresh_rate, refresh_rate,
refresh_rate_label, refresh_rate_label,
// //
gpu_profiler_button,
gpu_profiler_label,
//
particles_button, particles_button,
particles_label, particles_label,
lossy_terrain_compression_button, lossy_terrain_compression_button,
@ -205,6 +211,24 @@ impl<'a> Widget for Video<'a> {
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(18)) .font_size(self.fonts.cyri.scale(18))
.set(state.ids.fps_counter, ui); .set(state.ids.fps_counter, ui);
// Pipeline recreation status
if let Some((total, complete)) = self
.global_state
.window
.renderer()
.pipeline_recreation_status()
{
Text::new(&format!("Rebuilding pipelines: ({}/{})", complete, total))
.down_from(state.ids.fps_counter, 10.0)
.align_right_of(state.ids.fps_counter)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
// TODO: make color pulse or something
.color(TEXT_COLOR)
.set(state.ids.pipeline_recreation_text, ui);
}
// View Distance // View Distance
Text::new(&self.localized_strings.get("hud.settings.view_distance")) Text::new(&self.localized_strings.get("hud.settings.view_distance"))
.top_left_with_margins_on(state.ids.window, 10.0, 10.0) .top_left_with_margins_on(state.ids.window, 10.0, 10.0)
@ -229,7 +253,7 @@ impl<'a> Widget for Video<'a> {
.pad_track((5.0, 5.0)) .pad_track((5.0, 5.0))
.set(state.ids.vd_slider, ui) .set(state.ids.vd_slider, ui)
{ {
events.push(AdjustViewDistance(new_val)); events.push(GraphicsChange::AdjustViewDistance(new_val));
} }
Text::new(&format!( Text::new(&format!(
@ -267,7 +291,7 @@ impl<'a> Widget for Video<'a> {
.pad_track((5.0, 5.0)) .pad_track((5.0, 5.0))
.set(state.ids.max_fps_slider, ui) .set(state.ids.max_fps_slider, ui)
{ {
events.push(ChangeMaxFPS(FPS_CHOICES[which])); events.push(GraphicsChange::ChangeMaxFPS(FPS_CHOICES[which]));
} }
Text::new(&self.global_state.settings.graphics.max_fps.to_string()) Text::new(&self.global_state.settings.graphics.max_fps.to_string())
@ -277,6 +301,53 @@ impl<'a> Widget for Video<'a> {
.color(TEXT_COLOR) .color(TEXT_COLOR)
.set(state.ids.max_fps_value, ui); .set(state.ids.max_fps_value, ui);
// Get render mode
let render_mode = &self.global_state.settings.graphics.render_mode;
// Present Mode
Text::new(&self.localized_strings.get("hud.settings.present_mode"))
.down_from(state.ids.vd_slider, 10.0)
.right_from(state.ids.max_fps_value, 30.0)
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)
.set(state.ids.present_mode_text, ui);
let mode_list = [
PresentMode::Fifo,
PresentMode::Mailbox,
PresentMode::Immediate,
];
let mode_label_list = [
&self.localized_strings.get("hud.settings.present_mode.fifo"),
&self
.localized_strings
.get("hud.settings.present_mode.mailbox"),
&self
.localized_strings
.get("hud.settings.present_mode.immediate"),
];
// Get which present mode is currently active
let selected = mode_list
.iter()
.position(|x| *x == render_mode.present_mode);
if let Some(clicked) = DropDownList::new(&mode_label_list, selected)
.w_h(120.0, 22.0)
.color(MENU_BG)
.label_color(TEXT_COLOR)
.label_font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.present_mode_text, 8.0)
.align_middle_x()
.set(state.ids.present_mode_list, ui)
{
events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode {
present_mode: mode_list[clicked],
..render_mode.clone()
})));
}
// FOV // FOV
Text::new(&self.localized_strings.get("hud.settings.fov")) Text::new(&self.localized_strings.get("hud.settings.fov"))
.down_from(state.ids.max_fps_slider, 10.0) .down_from(state.ids.max_fps_slider, 10.0)
@ -299,7 +370,7 @@ impl<'a> Widget for Video<'a> {
.pad_track((5.0, 5.0)) .pad_track((5.0, 5.0))
.set(state.ids.fov_slider, ui) .set(state.ids.fov_slider, ui)
{ {
events.push(ChangeFOV(new_val)); events.push(GraphicsChange::ChangeFOV(new_val));
} }
Text::new(&format!("{}", self.global_state.settings.graphics.fov)) Text::new(&format!("{}", self.global_state.settings.graphics.fov))
@ -332,7 +403,7 @@ impl<'a> Widget for Video<'a> {
.pad_track((5.0, 5.0)) .pad_track((5.0, 5.0))
.set(state.ids.lod_detail_slider, ui) .set(state.ids.lod_detail_slider, ui)
{ {
events.push(AdjustLodDetail( events.push(GraphicsChange::AdjustLodDetail(
(5.0f32.powf(new_val as f32 / 10.0) * 100.0) as u32, (5.0f32.powf(new_val as f32 / 10.0) * 100.0) as u32,
)); ));
} }
@ -369,7 +440,9 @@ impl<'a> Widget for Video<'a> {
.pad_track((5.0, 5.0)) .pad_track((5.0, 5.0))
.set(state.ids.gamma_slider, ui) .set(state.ids.gamma_slider, ui)
{ {
events.push(ChangeGamma(2.0f32.powf(new_val as f32 / 8.0))); events.push(GraphicsChange::ChangeGamma(
2.0f32.powf(new_val as f32 / 8.0),
));
} }
Text::new(&format!("{:.2}", self.global_state.settings.graphics.gamma)) Text::new(&format!("{:.2}", self.global_state.settings.graphics.gamma))
@ -394,7 +467,7 @@ impl<'a> Widget for Video<'a> {
.pad_track((5.0, 5.0)) .pad_track((5.0, 5.0))
.set(state.ids.exposure_slider, ui) .set(state.ids.exposure_slider, ui)
{ {
events.push(ChangeExposure(new_val as f32 / 16.0)); events.push(GraphicsChange::ChangeExposure(new_val as f32 / 16.0));
} }
Text::new(&self.localized_strings.get("hud.settings.exposure")) Text::new(&self.localized_strings.get("hud.settings.exposure"))
@ -432,7 +505,7 @@ impl<'a> Widget for Video<'a> {
.pad_track((5.0, 5.0)) .pad_track((5.0, 5.0))
.set(state.ids.ambiance_slider, ui) .set(state.ids.ambiance_slider, ui)
{ {
events.push(ChangeAmbiance(new_val as f32)); events.push(GraphicsChange::ChangeAmbiance(new_val as f32));
} }
Text::new(&self.localized_strings.get("hud.settings.ambiance")) Text::new(&self.localized_strings.get("hud.settings.ambiance"))
.up_from(state.ids.ambiance_slider, 8.0) .up_from(state.ids.ambiance_slider, 8.0)
@ -468,7 +541,7 @@ impl<'a> Widget for Video<'a> {
.pad_track((5.0, 5.0)) .pad_track((5.0, 5.0))
.set(state.ids.sprite_dist_slider, ui) .set(state.ids.sprite_dist_slider, ui)
{ {
events.push(AdjustSpriteRenderDistance(new_val)); events.push(GraphicsChange::AdjustSpriteRenderDistance(new_val));
} }
Text::new( Text::new(
&self &self
@ -508,7 +581,7 @@ impl<'a> Widget for Video<'a> {
.pad_track((5.0, 5.0)) .pad_track((5.0, 5.0))
.set(state.ids.figure_dist_slider, ui) .set(state.ids.figure_dist_slider, ui)
{ {
events.push(AdjustFigureLoDRenderDistance(new_val)); events.push(GraphicsChange::AdjustFigureLoDRenderDistance(new_val));
} }
Text::new( Text::new(
&self &self
@ -534,8 +607,6 @@ impl<'a> Widget for Video<'a> {
.color(TEXT_COLOR) .color(TEXT_COLOR)
.set(state.ids.figure_dist_value, ui); .set(state.ids.figure_dist_value, ui);
let render_mode = &self.global_state.settings.graphics.render_mode;
// AaMode // AaMode
Text::new(&self.localized_strings.get("hud.settings.antialiasing_mode")) Text::new(&self.localized_strings.get("hud.settings.antialiasing_mode"))
.down_from(state.ids.gamma_slider, 8.0) .down_from(state.ids.gamma_slider, 8.0)
@ -572,7 +643,7 @@ impl<'a> Widget for Video<'a> {
.down_from(state.ids.aa_mode_text, 8.0) .down_from(state.ids.aa_mode_text, 8.0)
.set(state.ids.aa_mode_list, ui) .set(state.ids.aa_mode_list, ui)
{ {
events.push(ChangeRenderMode(Box::new(RenderMode { events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode {
aa: mode_list[clicked], aa: mode_list[clicked],
..render_mode.clone() ..render_mode.clone()
}))); })));
@ -612,7 +683,7 @@ impl<'a> Widget for Video<'a> {
.down_from(state.ids.upscale_factor_text, 8.0) .down_from(state.ids.upscale_factor_text, 8.0)
.set(state.ids.upscale_factor_list, ui) .set(state.ids.upscale_factor_list, ui)
{ {
events.push(ChangeRenderMode(Box::new(RenderMode { events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode {
upscale_mode: UpscaleMode { upscale_mode: UpscaleMode {
factor: upscale_factors[clicked], factor: upscale_factors[clicked],
}, },
@ -670,7 +741,7 @@ impl<'a> Widget for Video<'a> {
.down_from(state.ids.cloud_mode_text, 8.0) .down_from(state.ids.cloud_mode_text, 8.0)
.set(state.ids.cloud_mode_list, ui) .set(state.ids.cloud_mode_list, ui)
{ {
events.push(ChangeRenderMode(Box::new(RenderMode { events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode {
cloud: mode_list[clicked], cloud: mode_list[clicked],
..render_mode.clone() ..render_mode.clone()
}))); })));
@ -709,7 +780,7 @@ impl<'a> Widget for Video<'a> {
.down_from(state.ids.fluid_mode_text, 8.0) .down_from(state.ids.fluid_mode_text, 8.0)
.set(state.ids.fluid_mode_list, ui) .set(state.ids.fluid_mode_list, ui)
{ {
events.push(ChangeRenderMode(Box::new(RenderMode { events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode {
fluid: mode_list[clicked], fluid: mode_list[clicked],
..render_mode.clone() ..render_mode.clone()
}))); })));
@ -755,7 +826,7 @@ impl<'a> Widget for Video<'a> {
.down_from(state.ids.lighting_mode_text, 8.0) .down_from(state.ids.lighting_mode_text, 8.0)
.set(state.ids.lighting_mode_list, ui) .set(state.ids.lighting_mode_list, ui)
{ {
events.push(ChangeRenderMode(Box::new(RenderMode { events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode {
lighting: mode_list[clicked], lighting: mode_list[clicked],
..render_mode.clone() ..render_mode.clone()
}))); })));
@ -802,7 +873,7 @@ impl<'a> Widget for Video<'a> {
.down_from(state.ids.shadow_mode_text, 8.0) .down_from(state.ids.shadow_mode_text, 8.0)
.set(state.ids.shadow_mode_list, ui) .set(state.ids.shadow_mode_list, ui)
{ {
events.push(ChangeRenderMode(Box::new(RenderMode { events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode {
shadow: mode_list[clicked], shadow: mode_list[clicked],
..render_mode.clone() ..render_mode.clone()
}))); })));
@ -835,7 +906,7 @@ impl<'a> Widget for Video<'a> {
.pad_track((5.0, 5.0)) .pad_track((5.0, 5.0))
.set(state.ids.shadow_mode_map_resolution_slider, ui) .set(state.ids.shadow_mode_map_resolution_slider, ui)
{ {
events.push(ChangeRenderMode(Box::new(RenderMode { events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode {
shadow: ShadowMode::Map(ShadowMapMode { shadow: ShadowMode::Map(ShadowMapMode {
resolution: 2.0f32.powf(f32::from(new_val) / 4.0), resolution: 2.0f32.powf(f32::from(new_val) / 4.0),
}), }),
@ -853,11 +924,37 @@ impl<'a> Widget for Video<'a> {
.set(state.ids.shadow_mode_map_resolution_value, ui); .set(state.ids.shadow_mode_map_resolution_value, ui);
} }
// GPU Profiler
Text::new(&self.localized_strings.get("hud.settings.gpu_profiler"))
.font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.shadow_mode_list, 8.0)
.color(TEXT_COLOR)
.set(state.ids.gpu_profiler_label, ui);
let gpu_profiler_enabled = ToggleButton::new(
render_mode.profiler_enabled,
self.imgs.checkbox,
self.imgs.checkbox_checked,
)
.w_h(18.0, 18.0)
.right_from(state.ids.gpu_profiler_label, 10.0)
.hover_images(self.imgs.checkbox_mo, self.imgs.checkbox_checked_mo)
.press_images(self.imgs.checkbox_press, self.imgs.checkbox_checked)
.set(state.ids.gpu_profiler_button, ui);
if render_mode.profiler_enabled != gpu_profiler_enabled {
events.push(GraphicsChange::ChangeRenderMode(Box::new(RenderMode {
profiler_enabled: gpu_profiler_enabled,
..render_mode.clone()
})));
}
// Particles // Particles
Text::new(&self.localized_strings.get("hud.settings.particles")) Text::new(&self.localized_strings.get("hud.settings.particles"))
.font_size(self.fonts.cyri.scale(14)) .font_size(self.fonts.cyri.scale(14))
.font_id(self.fonts.cyri.conrod_id) .font_id(self.fonts.cyri.conrod_id)
.down_from(state.ids.shadow_mode_list, 8.0) .down_from(state.ids.gpu_profiler_label, 8.0)
.color(TEXT_COLOR) .color(TEXT_COLOR)
.set(state.ids.particles_label, ui); .set(state.ids.particles_label, ui);
@ -873,7 +970,7 @@ impl<'a> Widget for Video<'a> {
.set(state.ids.particles_button, ui); .set(state.ids.particles_button, ui);
if self.global_state.settings.graphics.particles_enabled != particles_enabled { if self.global_state.settings.graphics.particles_enabled != particles_enabled {
events.push(ToggleParticlesEnabled(particles_enabled)); events.push(GraphicsChange::ToggleParticlesEnabled(particles_enabled));
} }
// Lossy terrain compression // Lossy terrain compression
@ -909,7 +1006,9 @@ impl<'a> Widget for Video<'a> {
.lossy_terrain_compression .lossy_terrain_compression
!= lossy_terrain_compression != lossy_terrain_compression
{ {
events.push(ToggleLossyTerrainCompression(lossy_terrain_compression)); events.push(GraphicsChange::ToggleLossyTerrainCompression(
lossy_terrain_compression,
));
} }
// Resolution // Resolution
@ -946,7 +1045,7 @@ impl<'a> Widget for Video<'a> {
.down_from(state.ids.resolution_label, 10.0) .down_from(state.ids.resolution_label, 10.0)
.set(state.ids.resolution, ui) .set(state.ids.resolution, ui)
{ {
events.push(ChangeFullscreenMode(FullScreenSettings { events.push(GraphicsChange::ChangeFullscreenMode(FullScreenSettings {
resolution: resolutions[clicked], resolution: resolutions[clicked],
..self.global_state.settings.graphics.fullscreen ..self.global_state.settings.graphics.fullscreen
})); }));
@ -1010,7 +1109,7 @@ impl<'a> Widget for Video<'a> {
.right_from(state.ids.resolution, 8.0) .right_from(state.ids.resolution, 8.0)
.set(state.ids.bit_depth, ui) .set(state.ids.bit_depth, ui)
{ {
events.push(ChangeFullscreenMode(FullScreenSettings { events.push(GraphicsChange::ChangeFullscreenMode(FullScreenSettings {
bit_depth: if clicked == 0 { bit_depth: if clicked == 0 {
None None
} else { } else {
@ -1064,7 +1163,7 @@ impl<'a> Widget for Video<'a> {
.right_from(state.ids.bit_depth, 8.0) .right_from(state.ids.bit_depth, 8.0)
.set(state.ids.refresh_rate, ui) .set(state.ids.refresh_rate, ui)
{ {
events.push(ChangeFullscreenMode(FullScreenSettings { events.push(GraphicsChange::ChangeFullscreenMode(FullScreenSettings {
refresh_rate: if clicked == 0 { refresh_rate: if clicked == 0 {
None None
} else { } else {
@ -1094,7 +1193,7 @@ impl<'a> Widget for Video<'a> {
.set(state.ids.fullscreen_button, ui); .set(state.ids.fullscreen_button, ui);
if self.global_state.settings.graphics.fullscreen.enabled != enabled { if self.global_state.settings.graphics.fullscreen.enabled != enabled {
events.push(ChangeFullscreenMode(FullScreenSettings { events.push(GraphicsChange::ChangeFullscreenMode(FullScreenSettings {
enabled, enabled,
..self.global_state.settings.graphics.fullscreen ..self.global_state.settings.graphics.fullscreen
})); }));
@ -1131,7 +1230,7 @@ impl<'a> Widget for Video<'a> {
.down_from(state.ids.fullscreen_mode_text, 8.0) .down_from(state.ids.fullscreen_mode_text, 8.0)
.set(state.ids.fullscreen_mode_list, ui) .set(state.ids.fullscreen_mode_list, ui)
{ {
events.push(ChangeFullscreenMode(FullScreenSettings { events.push(GraphicsChange::ChangeFullscreenMode(FullScreenSettings {
mode: mode_list[clicked], mode: mode_list[clicked],
..self.global_state.settings.graphics.fullscreen ..self.global_state.settings.graphics.fullscreen
})); }));
@ -1151,7 +1250,7 @@ impl<'a> Widget for Video<'a> {
.set(state.ids.save_window_size_button, ui) .set(state.ids.save_window_size_button, ui)
.was_clicked() .was_clicked()
{ {
events.push(AdjustWindowSize( events.push(GraphicsChange::AdjustWindowSize(
self.global_state self.global_state
.window .window
.logical_size() .logical_size()
@ -1175,7 +1274,7 @@ impl<'a> Widget for Video<'a> {
.set(state.ids.reset_graphics_button, ui) .set(state.ids.reset_graphics_button, ui)
.was_clicked() .was_clicked()
{ {
events.push(ResetGraphicsSettings); events.push(GraphicsChange::ResetGraphicsSettings);
} }
events events

View File

@ -2,7 +2,14 @@
#![allow(incomplete_features)] #![allow(incomplete_features)]
#![allow(clippy::option_map_unit_fn)] #![allow(clippy::option_map_unit_fn)]
#![deny(clippy::clone_on_ref_ptr)] #![deny(clippy::clone_on_ref_ptr)]
#![feature(array_map, bool_to_option, const_generics, drain_filter, once_cell)] #![feature(
array_map,
bool_to_option,
const_generics,
drain_filter,
once_cell,
trait_alias
)]
#![recursion_limit = "2048"] #![recursion_limit = "2048"]
#[macro_use] #[macro_use]

View File

@ -20,6 +20,7 @@ pub struct CharSelectionState {
char_selection_ui: CharSelectionUi, char_selection_ui: CharSelectionUi,
client: Rc<RefCell<Client>>, client: Rc<RefCell<Client>>,
scene: Scene, scene: Scene,
need_shadow_clear: bool,
} }
impl CharSelectionState { impl CharSelectionState {
@ -36,6 +37,7 @@ impl CharSelectionState {
char_selection_ui, char_selection_ui,
client, client,
scene, scene,
need_shadow_clear: false,
} }
} }
@ -71,6 +73,9 @@ impl PlayState for CharSelectionState {
// Set scale mode in case it was change // Set scale mode in case it was change
self.char_selection_ui self.char_selection_ui
.set_scale_mode(global_state.settings.interface.ui_scale); .set_scale_mode(global_state.settings.interface.ui_scale);
// Clear shadow textures since we don't render to them here
self.need_shadow_clear = true;
} }
fn tick(&mut self, global_state: &mut GlobalState, events: Vec<WinEvent>) -> PlayStateResult { fn tick(&mut self, global_state: &mut GlobalState, events: Vec<WinEvent>) -> PlayStateResult {
@ -228,15 +233,39 @@ impl PlayState for CharSelectionState {
fn name(&self) -> &'static str { "Character Selection" } fn name(&self) -> &'static str { "Character Selection" }
fn render(&mut self, renderer: &mut Renderer, _: &Settings) { fn render(&mut self, renderer: &mut Renderer, _: &Settings) {
let mut drawer = match renderer
.start_recording_frame(self.scene.global_bind_group())
.expect("Unrecoverable render error when starting a new frame!")
{
Some(d) => d,
// Couldn't get swap chain texture this fime
None => return,
};
if self.need_shadow_clear {
drawer.clear_shadows();
self.need_shadow_clear = false;
}
let client = self.client.borrow(); let client = self.client.borrow();
let (humanoid_body, loadout) = let (humanoid_body, loadout) =
Self::get_humanoid_body_inventory(&self.char_selection_ui, &client); Self::get_humanoid_body_inventory(&self.char_selection_ui, &client);
// Render the scene. if let Some(mut first_pass) = drawer.first_pass() {
self.scene self.scene
.render(renderer, client.get_tick(), humanoid_body, loadout); .render(&mut first_pass, client.get_tick(), humanoid_body, loadout);
}
// Clouds
if let Some(mut second_pass) = drawer.second_pass() {
second_pass.draw_clouds();
}
// PostProcess and UI
let mut third_pass = drawer.third_pass();
third_pass.draw_postprocess();
// Draw the UI to the screen. // Draw the UI to the screen.
self.char_selection_ui.render(renderer); if let Some(mut ui_drawer) = third_pass.draw_ui() {
self.char_selection_ui.render(&mut ui_drawer);
};
} }
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
i18n::{Localization, LocalizationHandle}, i18n::{Localization, LocalizationHandle},
render::Renderer, render::UiDrawer,
ui::{ ui::{
self, self,
fonts::IcedFonts as Fonts, fonts::IcedFonts as Fonts,
@ -1584,8 +1584,7 @@ impl CharSelectionUi {
events events
} }
// TODO: do we need globals? pub fn render<'a>(&'a self, drawer: &mut UiDrawer<'_, 'a>) { self.ui.render(drawer); }
pub fn render(&self, renderer: &mut Renderer) { self.ui.render(renderer); }
} }
#[derive(Default)] #[derive(Default)]

View File

@ -1,4 +1,5 @@
mod client_init; mod client_init;
mod scene;
mod ui; mod ui;
use super::char_selection::CharSelectionState; use super::char_selection::CharSelectionState;
@ -11,20 +12,41 @@ use crate::{
use client::{ use client::{
addr::ConnectionArgs, addr::ConnectionArgs,
error::{InitProtocolError, NetworkConnectError, NetworkError}, error::{InitProtocolError, NetworkConnectError, NetworkError},
ServerInfo, Client, ServerInfo,
}; };
use client_init::{ClientInit, Error as InitError, Msg as InitMsg}; use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
use common::comp; use common::comp;
use common_base::span; use common_base::span;
use scene::Scene;
use std::sync::Arc; use std::sync::Arc;
use tokio::runtime; use tokio::runtime;
use tracing::error; use tracing::error;
use ui::{Event as MainMenuEvent, MainMenuUi}; use ui::{Event as MainMenuEvent, MainMenuUi};
// TODO: show status messages for waiting on server creation, client init, and
// pipeline creation (we can show progress of pipeline creation)
enum InitState {
None,
// Waiting on the client initialization
Client(ClientInit),
// Client initialized but still waiting on Renderer pipeline creation
Pipeline(Box<Client>),
}
impl InitState {
fn client(&self) -> Option<&ClientInit> {
if let Self::Client(client_init) = &self {
Some(client_init)
} else {
None
}
}
}
pub struct MainMenuState { pub struct MainMenuState {
main_menu_ui: MainMenuUi, main_menu_ui: MainMenuUi,
// Used for client creation. init: InitState,
client_init: Option<ClientInit>, scene: Scene,
} }
impl MainMenuState { impl MainMenuState {
@ -32,7 +54,8 @@ impl MainMenuState {
pub fn new(global_state: &mut GlobalState) -> Self { pub fn new(global_state: &mut GlobalState) -> Self {
Self { Self {
main_menu_ui: MainMenuUi::new(global_state), main_menu_ui: MainMenuUi::new(global_state),
client_init: None, init: InitState::None,
scene: Scene::new(global_state.window.renderer_mut()),
} }
} }
} }
@ -74,14 +97,14 @@ impl PlayState for MainMenuState {
"singleplayer".to_owned(), "singleplayer".to_owned(),
"".to_owned(), "".to_owned(),
ConnectionArgs::Mpsc(14004), ConnectionArgs::Mpsc(14004),
&mut self.client_init, &mut self.init,
Some(runtime), Some(runtime),
); );
}, },
Ok(Err(e)) => { Ok(Err(e)) => {
error!(?e, "Could not start server"); error!(?e, "Could not start server");
global_state.singleplayer = None; global_state.singleplayer = None;
self.client_init = None; self.init = InitState::None;
self.main_menu_ui.cancel_connection(); self.main_menu_ui.cancel_connection();
self.main_menu_ui.show_info(format!("Error: {:?}", e)); self.main_menu_ui.show_info(format!("Error: {:?}", e));
}, },
@ -104,19 +127,14 @@ impl PlayState for MainMenuState {
} }
} }
// Poll client creation. // Poll client creation.
match self.client_init.as_ref().and_then(|init| init.poll()) { match self.init.client().and_then(|init| init.poll()) {
Some(InitMsg::Done(Ok(mut client))) => { Some(InitMsg::Done(Ok(mut client))) => {
self.client_init = None;
self.main_menu_ui.connected();
// Register voxygen components / resources // Register voxygen components / resources
crate::ecs::init(client.state_mut().ecs_mut()); crate::ecs::init(client.state_mut().ecs_mut());
return PlayStateResult::Push(Box::new(CharSelectionState::new( self.init = InitState::Pipeline(Box::new(client));
global_state,
std::rc::Rc::new(std::cell::RefCell::new(client)),
)));
}, },
Some(InitMsg::Done(Err(e))) => { Some(InitMsg::Done(Err(e))) => {
self.client_init = None; self.init = InitState::None;
tracing::trace!(?e, "raw Client Init error"); tracing::trace!(?e, "raw Client Init error");
let e = get_client_msg_error(e, &global_state.i18n); let e = get_client_msg_error(e, &global_state.i18n);
// Log error for possible additional use later or incase that the error // Log error for possible additional use later or incase that the error
@ -132,10 +150,7 @@ impl PlayState for MainMenuState {
.contains(&auth_server) .contains(&auth_server)
{ {
// Can't fail since we just polled it, it must be Some // Can't fail since we just polled it, it must be Some
self.client_init self.init.client().unwrap().auth_trust(auth_server, true);
.as_ref()
.unwrap()
.auth_trust(auth_server, true);
} else { } else {
// Show warning that auth server is not trusted and prompt for approval // Show warning that auth server is not trusted and prompt for approval
self.main_menu_ui.auth_trust_prompt(auth_server); self.main_menu_ui.auth_trust_prompt(auth_server);
@ -144,6 +159,64 @@ impl PlayState for MainMenuState {
None => {}, None => {},
} }
// Tick the client to keep the connection alive if we are waiting on pipelines
let localized_strings = &global_state.i18n.read();
if let InitState::Pipeline(client) = &mut self.init {
match client.tick(
comp::ControllerInputs::default(),
global_state.clock.dt(),
|_| {},
) {
Ok(events) => {
for event in events {
match event {
client::Event::SetViewDistance(vd) => {
global_state.settings.graphics.view_distance = vd;
global_state.settings.save_to_file_warn();
},
client::Event::Disconnect => {
global_state.info_message = Some(
localized_strings
.get("main.login.server_shut_down")
.to_owned(),
);
self.init = InitState::None;
},
_ => {},
}
}
},
Err(err) => {
global_state.info_message =
Some(localized_strings.get("common.connection_lost").to_owned());
error!(?err, "[main menu] Failed to tick the client");
self.init = InitState::None;
},
}
}
// Poll renderer pipeline creation
if let InitState::Pipeline(..) = &self.init {
// If not complete go to char select screen
if global_state
.window
.renderer()
.pipeline_creation_status()
.is_none()
{
// Always succeeds since we check above
if let InitState::Pipeline(client) =
core::mem::replace(&mut self.init, InitState::None)
{
self.main_menu_ui.connected();
return PlayStateResult::Push(Box::new(CharSelectionState::new(
global_state,
std::rc::Rc::new(std::cell::RefCell::new(*client)),
)));
}
}
}
// Maintain the UI. // Maintain the UI.
for event in self for event in self
.main_menu_ui .main_menu_ui
@ -180,19 +253,19 @@ impl PlayState for MainMenuState {
username, username,
password, password,
connection_args, connection_args,
&mut self.client_init, &mut self.init,
None, None,
); );
}, },
MainMenuEvent::CancelLoginAttempt => { MainMenuEvent::CancelLoginAttempt => {
// client_init contains Some(ClientInit), which spawns a thread which contains a // init contains InitState::Client(ClientInit), which spawns a thread which
// TcpStream::connect() call This call is blocking // contains a TcpStream::connect() call This call is
// TODO fix when the network rework happens // blocking TODO fix when the network rework happens
#[cfg(feature = "singleplayer")] #[cfg(feature = "singleplayer")]
{ {
global_state.singleplayer = None; global_state.singleplayer = None;
} }
self.client_init = None; self.init = InitState::None;
self.main_menu_ui.cancel_connection(); self.main_menu_ui.cancel_connection();
}, },
MainMenuEvent::ChangeLanguage(new_language) => { MainMenuEvent::ChangeLanguage(new_language) => {
@ -228,8 +301,8 @@ impl PlayState for MainMenuState {
.insert(auth_server.clone()); .insert(auth_server.clone());
global_state.settings.save_to_file_warn(); global_state.settings.save_to_file_warn();
} }
self.client_init self.init
.as_ref() .client()
.map(|init| init.auth_trust(auth_server, trust)); .map(|init| init.auth_trust(auth_server, trust));
}, },
} }
@ -245,8 +318,19 @@ impl PlayState for MainMenuState {
fn name(&self) -> &'static str { "Title" } fn name(&self) -> &'static str { "Title" }
fn render(&mut self, renderer: &mut Renderer, _: &Settings) { fn render(&mut self, renderer: &mut Renderer, _: &Settings) {
let mut drawer = match renderer
.start_recording_frame(self.scene.global_bind_group())
.expect("Unrecoverable render error when starting a new frame!")
{
Some(d) => d,
// Couldn't get swap chain texture this frame
None => return,
};
// Draw the UI to the screen. // Draw the UI to the screen.
self.main_menu_ui.render(renderer); if let Some(mut ui_drawer) = drawer.third_pass().draw_ui() {
self.main_menu_ui.render(&mut ui_drawer);
};
} }
} }
@ -354,7 +438,7 @@ fn attempt_login(
username: String, username: String,
password: String, password: String,
connection_args: ConnectionArgs, connection_args: ConnectionArgs,
client_init: &mut Option<ClientInit>, init: &mut InitState,
runtime: Option<Arc<runtime::Runtime>>, runtime: Option<Arc<runtime::Runtime>>,
) { ) {
if let Err(err) = comp::Player::alias_validate(&username) { if let Err(err) = comp::Player::alias_validate(&username) {
@ -363,8 +447,8 @@ fn attempt_login(
} }
// Don't try to connect if there is already a connection in progress. // Don't try to connect if there is already a connection in progress.
if client_init.is_none() { if let InitState::None = init {
*client_init = Some(ClientInit::new( *init = InitState::Client(ClientInit::new(
connection_args, connection_args,
username, username,
password, password,

View File

@ -0,0 +1,28 @@
use crate::render::{
GlobalModel, Globals, GlobalsBindGroup, Light, LodData, PointLightMatrix, Renderer, Shadow,
ShadowLocals,
};
pub struct Scene {
bind_group: GlobalsBindGroup,
}
impl Scene {
pub fn new(renderer: &mut Renderer) -> Self {
let global_data = GlobalModel {
globals: renderer.create_consts(&[Globals::default()]),
lights: renderer.create_consts(&[Light::default(); 32]),
shadows: renderer.create_consts(&[Shadow::default(); 32]),
shadow_mats: renderer.create_shadow_bound_locals(&[ShadowLocals::default()]),
point_light_matrices: Box::new([PointLightMatrix::default(); 126]),
};
let lod_data = LodData::dummy(renderer);
let bind_group = renderer.bind_globals(&global_data, &lod_data);
Self { bind_group }
}
pub fn global_bind_group(&self) -> &GlobalsBindGroup { &self.bind_group }
}

View File

@ -6,7 +6,7 @@ mod servers;
use crate::{ use crate::{
i18n::{LanguageMetadata, LocalizationHandle}, i18n::{LanguageMetadata, LocalizationHandle},
render::Renderer, render::UiDrawer,
ui::{ ui::{
self, self,
fonts::IcedFonts as Fonts, fonts::IcedFonts as Fonts,
@ -477,7 +477,7 @@ pub struct MainMenuUi {
controls: Controls, controls: Controls,
} }
impl<'a> MainMenuUi { impl MainMenuUi {
pub fn new(global_state: &mut GlobalState) -> Self { pub fn new(global_state: &mut GlobalState) -> Self {
// Load language // Load language
let i18n = &global_state.i18n.read(); let i18n = &global_state.i18n.read();
@ -583,5 +583,5 @@ impl<'a> MainMenuUi {
events events
} }
pub fn render(&self, renderer: &mut Renderer) { self.ui.render(renderer); } pub fn render<'a>(&'a self, drawer: &mut UiDrawer<'_, 'a>) { self.ui.render(drawer); }
} }

View File

@ -1,9 +1,7 @@
use crate::render::{self, mesh::Quad, ColLightFmt, ColLightInfo, TerrainPipeline}; use crate::render::{mesh::Quad, ColLightInfo, TerrainVertex, Vertex};
use common_base::span; use common_base::span;
use vek::*; use vek::*;
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type TodoRect = ( type TodoRect = (
Vec3<i32>, Vec3<i32>,
Vec2<Vec3<u16>>, Vec2<Vec3<u16>>,
@ -123,7 +121,7 @@ impl<'a> GreedyMesh<'a> {
small_size_threshold, small_size_threshold,
large_size_threshold, large_size_threshold,
}); });
let col_lights_size = Vec2::new(1u16, 1u16); let col_lights_size = Vec2::new(1, 1);
Self { Self {
atlas, atlas,
col_lights_size, col_lights_size,
@ -152,7 +150,7 @@ impl<'a> GreedyMesh<'a> {
FO: for<'r> FnMut(&'r mut D, Vec3<i32>) -> bool + 'a, FO: for<'r> FnMut(&'r mut D, Vec3<i32>) -> bool + 'a,
FS: for<'r> FnMut(&'r mut D, Vec3<i32>, Vec3<i32>, Vec2<Vec3<i32>>) -> Option<(bool, M)>, FS: for<'r> FnMut(&'r mut D, Vec3<i32>, Vec3<i32>, Vec2<Vec3<i32>>) -> Option<(bool, M)>,
FP: FnMut(Vec2<u16>, Vec2<Vec2<u16>>, Vec3<f32>, Vec2<Vec3<f32>>, Vec3<f32>, &M), FP: FnMut(Vec2<u16>, Vec2<Vec2<u16>>, Vec3<f32>, Vec2<Vec3<f32>>, Vec3<f32>, &M),
FT: for<'r> FnMut(&'r mut D, Vec3<i32>, u8, u8) -> <<ColLightFmt as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType + 'a, FT: for<'r> FnMut(&'r mut D, Vec3<i32>, u8, u8) -> [u8; 4] + 'a,
{ {
span!(_guard, "push", "GreedyMesh::push"); span!(_guard, "push", "GreedyMesh::push");
let cont = greedy_mesh( let cont = greedy_mesh(
@ -178,7 +176,7 @@ impl<'a> GreedyMesh<'a> {
let cur_size = self.col_lights_size; let cur_size = self.col_lights_size;
let col_lights = vec![ let col_lights = vec![
TerrainVertex::make_col_light(254, 0, Rgb::broadcast(254)); TerrainVertex::make_col_light(254, 0, Rgb::broadcast(254));
usize::from(cur_size.x) * usize::from(cur_size.y) cur_size.x as usize * cur_size.y as usize
]; ];
let mut col_lights_info = (col_lights, cur_size); let mut col_lights_info = (col_lights, cur_size);
self.suspended.into_iter().for_each(|cont| { self.suspended.into_iter().for_each(|cont| {
@ -213,7 +211,7 @@ where
FO: for<'r> FnMut(&'r mut D, Vec3<i32>) -> bool + 'a, FO: for<'r> FnMut(&'r mut D, Vec3<i32>) -> bool + 'a,
FS: for<'r> FnMut(&'r mut D, Vec3<i32>, Vec3<i32>, Vec2<Vec3<i32>>) -> Option<(bool, M)>, FS: for<'r> FnMut(&'r mut D, Vec3<i32>, Vec3<i32>, Vec2<Vec3<i32>>) -> Option<(bool, M)>,
FP: FnMut(Vec2<u16>, Vec2<Vec2<u16>>, Vec3<f32>, Vec2<Vec3<f32>>, Vec3<f32>, &M), FP: FnMut(Vec2<u16>, Vec2<Vec2<u16>>, Vec3<f32>, Vec2<Vec3<f32>>, Vec3<f32>, &M),
FT: for<'r> FnMut(&'r mut D, Vec3<i32>, u8, u8) -> <<ColLightFmt as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType + 'a, FT: for<'r> FnMut(&'r mut D, Vec3<i32>, u8, u8) -> [u8; 4] + 'a,
{ {
span!(_guard, "greedy_mesh"); span!(_guard, "greedy_mesh");
// TODO: Collect information to see if we can choose a good value here. // TODO: Collect information to see if we can choose a good value here.
@ -507,7 +505,7 @@ fn draw_col_lights<D>(
mut get_light: impl FnMut(&mut D, Vec3<i32>) -> f32, mut get_light: impl FnMut(&mut D, Vec3<i32>) -> f32,
mut get_glow: impl FnMut(&mut D, Vec3<i32>) -> f32, mut get_glow: impl FnMut(&mut D, Vec3<i32>) -> f32,
mut get_opacity: impl FnMut(&mut D, Vec3<i32>) -> bool, mut get_opacity: impl FnMut(&mut D, Vec3<i32>) -> bool,
mut make_face_texel: impl FnMut(&mut D, Vec3<i32>, u8, u8) -> <<ColLightFmt as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType, mut make_face_texel: impl FnMut(&mut D, Vec3<i32>, u8, u8) -> [u8; 4],
) { ) {
todo_rects.into_iter().for_each(|(pos, uv, rect, delta)| { todo_rects.into_iter().for_each(|(pos, uv, rect, delta)| {
// NOTE: Conversions are safe because width, height, and offset must be // NOTE: Conversions are safe because width, height, and offset must be
@ -520,7 +518,7 @@ fn draw_col_lights<D>(
let uv = uv.map(|e| e.map(i32::from)); let uv = uv.map(|e| e.map(i32::from));
let pos = pos + draw_delta; let pos = pos + draw_delta;
(0..height).for_each(|v| { (0..height).for_each(|v| {
let start = usize::from(cur_size.x) * usize::from(top + v) + usize::from(left); let start = cur_size.x as usize * usize::from(top + v) + usize::from(left);
(0..width) (0..width)
.zip(&mut col_lights[start..start + usize::from(width)]) .zip(&mut col_lights[start..start + usize::from(width)])
.for_each(|(u, col_light)| { .for_each(|(u, col_light)| {
@ -622,14 +620,14 @@ fn create_quad_greedy<M>(
push_quad(atlas_pos, dim, origin, draw_dim, norm, meta); push_quad(atlas_pos, dim, origin, draw_dim, norm, meta);
} }
pub fn create_quad<O: render::Pipeline, M>( pub fn create_quad<O: Vertex, M>(
atlas_pos: Vec2<u16>, atlas_pos: Vec2<u16>,
dim: Vec2<Vec2<u16>>, dim: Vec2<Vec2<u16>>,
origin: Vec3<f32>, origin: Vec3<f32>,
draw_dim: Vec2<Vec3<f32>>, draw_dim: Vec2<Vec3<f32>>,
norm: Vec3<f32>, norm: Vec3<f32>,
meta: &M, meta: &M,
create_vertex: impl Fn(Vec2<u16>, Vec3<f32>, Vec3<f32>, &M) -> O::Vertex, create_vertex: impl Fn(Vec2<u16>, Vec3<f32>, Vec3<f32>, &M) -> O,
) -> Quad<O> { ) -> Quad<O> {
Quad::new( Quad::new(
create_vertex(atlas_pos, origin, norm, meta), create_vertex(atlas_pos, origin, norm, meta),

View File

@ -2,25 +2,6 @@ pub mod greedy;
pub mod segment; pub mod segment;
pub mod terrain; pub mod terrain;
use crate::render::{self, Mesh}; use crate::render::Mesh;
pub type MeshGen<P, T, M> = ( pub type MeshGen<V, T, S, R> = (Mesh<V>, Mesh<T>, Mesh<S>, R);
Mesh<<M as Meshable<P, T>>::Pipeline>,
Mesh<<M as Meshable<P, T>>::TranslucentPipeline>,
Mesh<<M as Meshable<P, T>>::ShadowPipeline>,
<M as Meshable<P, T>>::Result,
);
/// FIXME: Remove this whole trait at some point. This "abstraction" is never
/// abstracted over, and is organized completely differently from how we
/// actually mesh things nowadays.
pub trait Meshable<P: render::Pipeline, T> {
type Pipeline: render::Pipeline;
type TranslucentPipeline: render::Pipeline;
type ShadowPipeline: render::Pipeline;
type Supplement;
type Result;
// Generate meshes - one opaque, one translucent, one shadow
fn generate_mesh(self, supp: Self::Supplement) -> MeshGen<P, T, Self>;
}

View File

@ -1,12 +1,9 @@
use crate::{ use crate::{
mesh::{ mesh::{
greedy::{self, GreedyConfig, GreedyMesh}, greedy::{self, GreedyConfig, GreedyMesh},
MeshGen, Meshable, MeshGen,
},
render::{
self, FigurePipeline, Mesh, ParticlePipeline, ShadowPipeline, SpritePipeline,
TerrainPipeline,
}, },
render::{Mesh, ParticleVertex, SpriteVertex, TerrainVertex},
scene::math, scene::math,
}; };
use common::{ use common::{
@ -16,38 +13,24 @@ use common::{
use core::convert::TryFrom; use core::convert::TryFrom;
use vek::*; use vek::*;
type SpriteVertex = <SpritePipeline as render::Pipeline>::Vertex; // /// NOTE: bone_idx must be in [0, 15] (may be bumped to [0, 31] at some
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex; // /// point).
type ParticleVertex = <ParticlePipeline as render::Pipeline>::Vertex; #[allow(clippy::or_fun_call)] // TODO: Pending review in #587
// TODO: this function name...
impl<'a: 'b, 'b, V: 'a> Meshable<FigurePipeline, &'b mut GreedyMesh<'a>> for V pub fn generate_mesh_base_vol_terrain<'a: 'b, 'b, V: 'a>(
where vol: V,
V: BaseVol<Vox = Cell> + ReadVol + SizedVol, (greedy, opaque_mesh, offs, scale, bone_idx): (
/* TODO: Use VolIterator instead of manually iterating
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
* &'a V: BaseVol<Vox=Cell>, */
{
type Pipeline = TerrainPipeline;
type Result = math::Aabb<f32>;
type ShadowPipeline = ShadowPipeline;
/// NOTE: bone_idx must be in [0, 15] (may be bumped to [0, 31] at some
/// point).
type Supplement = (
&'b mut GreedyMesh<'a>, &'b mut GreedyMesh<'a>,
&'b mut Mesh<Self::Pipeline>, &'b mut Mesh<TerrainVertex>,
Vec3<f32>, Vec3<f32>,
Vec3<f32>, Vec3<f32>,
u8, u8,
); ),
type TranslucentPipeline = FigurePipeline; ) -> MeshGen<TerrainVertex, TerrainVertex, TerrainVertex, math::Aabb<f32>>
where
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587 V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
fn generate_mesh( {
self,
(greedy, opaque_mesh, offs, scale, bone_idx): Self::Supplement,
) -> MeshGen<FigurePipeline, &'b mut GreedyMesh<'a>, Self> {
assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]"); assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]");
let max_size = greedy.max_size(); let max_size = greedy.max_size();
// NOTE: Required because we steal two bits from the normal in the shadow uint // NOTE: Required because we steal two bits from the normal in the shadow uint
// in order to store the bone index. The two bits are instead taken out // in order to store the bone index. The two bits are instead taken out
@ -55,8 +38,8 @@ where
// coordinate instead of 1 << 16. // coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 15); assert!(max_size.width.max(max_size.height) < 1 << 15);
let lower_bound = self.lower_bound(); let lower_bound = vol.lower_bound();
let upper_bound = self.upper_bound(); let upper_bound = vol.upper_bound();
assert!( assert!(
lower_bound.x <= upper_bound.x lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y && lower_bound.y <= upper_bound.y
@ -79,8 +62,7 @@ where
} }
}; };
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0; let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
let get_opacity = let get_opacity = |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
|vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| { let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
should_draw_greedy(pos, delta, uv, |vox| { should_draw_greedy(pos, delta, uv, |vox| {
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty()) vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
@ -91,7 +73,7 @@ where
}; };
greedy.push(GreedyConfig { greedy.push(GreedyConfig {
data: self, data: vol,
draw_delta, draw_delta,
greedy_size, greedy_size,
greedy_size_cross, greedy_size_cross,
@ -128,26 +110,19 @@ where
(Mesh::new(), Mesh::new(), Mesh::new(), bounds) (Mesh::new(), Mesh::new(), Mesh::new(), bounds)
} }
}
impl<'a: 'b, 'b, V: 'a> Meshable<SpritePipeline, &'b mut GreedyMesh<'a>> for V
where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
/* TODO: Use VolIterator instead of manually iterating
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
* &'a V: BaseVol<Vox=Cell>, */
{
type Pipeline = SpritePipeline;
type Result = ();
type ShadowPipeline = ShadowPipeline;
type Supplement = (&'b mut GreedyMesh<'a>, &'b mut Mesh<Self::Pipeline>, bool);
type TranslucentPipeline = SpritePipeline;
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587 #[allow(clippy::or_fun_call)] // TODO: Pending review in #587
fn generate_mesh( pub fn generate_mesh_base_vol_sprite<'a: 'b, 'b, V: 'a>(
self, vol: V,
(greedy, opaque_mesh, vertical_stripes): Self::Supplement, (greedy, opaque_mesh, vertical_stripes): (
) -> MeshGen<SpritePipeline, &'b mut GreedyMesh<'a>, Self> { &'b mut GreedyMesh<'a>,
&'b mut Mesh<SpriteVertex>,
bool,
),
) -> MeshGen<SpriteVertex, SpriteVertex, TerrainVertex, ()>
where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
{
let max_size = greedy.max_size(); let max_size = greedy.max_size();
// NOTE: Required because we steal two bits from the normal in the shadow uint // NOTE: Required because we steal two bits from the normal in the shadow uint
// in order to store the bone index. The two bits are instead taken out // in order to store the bone index. The two bits are instead taken out
@ -155,8 +130,8 @@ where
// coordinate instead of 1 << 16. // coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 16); assert!(max_size.width.max(max_size.height) < 1 << 16);
let lower_bound = self.lower_bound(); let lower_bound = vol.lower_bound();
let upper_bound = self.upper_bound(); let upper_bound = vol.upper_bound();
assert!( assert!(
lower_bound.x <= upper_bound.x lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y && lower_bound.y <= upper_bound.y
@ -181,8 +156,6 @@ where
let (flat, flat_get) = { let (flat, flat_get) = {
let (w, h, d) = (greedy_size + 2).into_tuple(); let (w, h, d) = (greedy_size + 2).into_tuple();
let flat = { let flat = {
let vol = self;
let mut flat = vec![Cell::empty(); (w * h * d) as usize]; let mut flat = vec![Cell::empty(); (w * h * d) as usize];
let mut i = 0; let mut i = 0;
for x in -1..greedy_size.x + 1 { for x in -1..greedy_size.x + 1 {
@ -224,9 +197,8 @@ where
} }
}; };
let get_glow = |_flat: &mut _, _pos: Vec3<i32>| 0.0; let get_glow = |_flat: &mut _, _pos: Vec3<i32>| 0.0;
let get_color = move |flat: &mut _, pos: Vec3<i32>| { let get_color =
flat_get(flat, pos).get_color().unwrap_or(Rgb::zero()) move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).get_color().unwrap_or(Rgb::zero());
};
let get_opacity = move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).is_empty(); let get_opacity = move |flat: &mut _, pos: Vec3<i32>| flat_get(flat, pos).is_empty();
let should_draw = move |flat: &mut _, pos: Vec3<i32>, delta: Vec3<i32>, uv| { let should_draw = move |flat: &mut _, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| flat_get(flat, vox)) should_draw_greedy_ao(vertical_stripes, pos, delta, uv, |vox| flat_get(flat, vox))
@ -265,26 +237,15 @@ where
(Mesh::new(), Mesh::new(), Mesh::new(), ()) (Mesh::new(), Mesh::new(), Mesh::new(), ())
} }
}
impl<'a: 'b, 'b, V: 'a> Meshable<ParticlePipeline, &'b mut GreedyMesh<'a>> for V
where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
/* TODO: Use VolIterator instead of manually iterating
* &'a V: IntoVolIterator<'a> + IntoFullVolIterator<'a>,
* &'a V: BaseVol<Vox=Cell>, */
{
type Pipeline = ParticlePipeline;
type Result = ();
type ShadowPipeline = ShadowPipeline;
type Supplement = &'b mut GreedyMesh<'a>;
type TranslucentPipeline = ParticlePipeline;
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587 #[allow(clippy::or_fun_call)] // TODO: Pending review in #587
fn generate_mesh( pub fn generate_mesh_base_vol_particle<'a: 'b, 'b, V: 'a>(
self, vol: V,
greedy: Self::Supplement, greedy: &'b mut GreedyMesh<'a>,
) -> MeshGen<ParticlePipeline, &'b mut GreedyMesh<'a>, Self> { ) -> MeshGen<ParticleVertex, ParticleVertex, TerrainVertex, ()>
where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
{
let max_size = greedy.max_size(); let max_size = greedy.max_size();
// NOTE: Required because we steal two bits from the normal in the shadow uint // NOTE: Required because we steal two bits from the normal in the shadow uint
// in order to store the bone index. The two bits are instead taken out // in order to store the bone index. The two bits are instead taken out
@ -292,8 +253,8 @@ where
// coordinate instead of 1 << 16. // coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 16); assert!(max_size.width.max(max_size.height) < 1 << 16);
let lower_bound = self.lower_bound(); let lower_bound = vol.lower_bound();
let upper_bound = self.upper_bound(); let upper_bound = vol.upper_bound();
assert!( assert!(
lower_bound.x <= upper_bound.x lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y && lower_bound.y <= upper_bound.y
@ -326,8 +287,7 @@ where
.and_then(|vox| vox.get_color()) .and_then(|vox| vox.get_color())
.unwrap_or(Rgb::zero()) .unwrap_or(Rgb::zero())
}; };
let get_opacity = let get_opacity = |vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
|vol: &mut V, pos: Vec3<i32>| vol.get(pos).map_or(true, |vox| vox.is_empty());
let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| { let should_draw = |vol: &mut V, pos: Vec3<i32>, delta: Vec3<i32>, uv| {
should_draw_greedy(pos, delta, uv, |vox| { should_draw_greedy(pos, delta, uv, |vox| {
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty()) vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
@ -337,7 +297,7 @@ where
let mut opaque_mesh = Mesh::new(); let mut opaque_mesh = Mesh::new();
greedy.push(GreedyConfig { greedy.push(GreedyConfig {
data: self, data: vol,
draw_delta, draw_delta,
greedy_size, greedy_size,
greedy_size_cross, greedy_size_cross,
@ -363,7 +323,6 @@ where
(opaque_mesh, Mesh::new(), Mesh::new(), ()) (opaque_mesh, Mesh::new(), Mesh::new(), ())
} }
}
fn should_draw_greedy( fn should_draw_greedy(
pos: Vec3<i32>, pos: Vec3<i32>,

View File

@ -3,9 +3,9 @@
use crate::{ use crate::{
mesh::{ mesh::{
greedy::{self, GreedyConfig, GreedyMesh}, greedy::{self, GreedyConfig, GreedyMesh},
MeshGen, Meshable, MeshGen,
}, },
render::{self, ColLightInfo, FluidPipeline, Mesh, ShadowPipeline, TerrainPipeline}, render::{ColLightInfo, FluidVertex, Mesh, TerrainVertex},
scene::terrain::BlocksOfInterest, scene::terrain::BlocksOfInterest,
}; };
use common::{ use common::{
@ -19,9 +19,6 @@ use std::{collections::VecDeque, fmt::Debug, sync::Arc};
use tracing::error; use tracing::error;
use vek::*; use vek::*;
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type FluidVertex = <FluidPipeline as render::Pipeline>::Vertex;
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
enum FaceKind { enum FaceKind {
/// Opaque face that is facing something non-opaque; either /// Opaque face that is facing something non-opaque; either
@ -227,29 +224,25 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
} }
} }
impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static> #[allow(clippy::collapsible_if)]
Meshable<TerrainPipeline, FluidPipeline> for &'a VolGrid2d<V> #[allow(clippy::many_single_char_names)]
{
type Pipeline = TerrainPipeline;
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
type Result = ( #[allow(clippy::needless_range_loop)] // TODO: Pending review in #587
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
pub fn generate_mesh<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>(
vol: &'a VolGrid2d<V>,
(range, max_texture_size, _boi): (Aabb<i32>, Vec2<u16>, &'a BlocksOfInterest),
) -> MeshGen<
TerrainVertex,
FluidVertex,
TerrainVertex,
(
Aabb<f32>, Aabb<f32>,
ColLightInfo, ColLightInfo,
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>, Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>, Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
); ),
type ShadowPipeline = ShadowPipeline; > {
type Supplement = (Aabb<i32>, Vec2<u16>, &'a BlocksOfInterest);
type TranslucentPipeline = FluidPipeline;
#[allow(clippy::collapsible_if)]
#[allow(clippy::many_single_char_names)]
#[allow(clippy::needless_range_loop)] // TODO: Pending review in #587
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
fn generate_mesh(
self,
(range, max_texture_size, _boi): Self::Supplement,
) -> MeshGen<TerrainPipeline, FluidPipeline, Self> {
span!( span!(
_guard, _guard,
"generate_mesh", "generate_mesh",
@ -261,13 +254,13 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
// let glow_blocks = boi.lights // let glow_blocks = boi.lights
// .iter() // .iter()
// .map(|(pos, glow)| (*pos + range.min.xy(), *glow)); // .map(|(pos, glow)| (*pos + range.min.xy(), *glow));
/* DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST) /* DefaultVolIterator::new(vol, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST)
.filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */ .filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */
let mut glow_blocks = Vec::new(); let mut glow_blocks = Vec::new();
// TODO: This expensive, use BlocksOfInterest instead // TODO: This expensive, use BlocksOfInterest instead
let mut volume = self.cached(); let mut volume = vol.cached();
for x in -MAX_LIGHT_DIST..range.size().w + MAX_LIGHT_DIST { for x in -MAX_LIGHT_DIST..range.size().w + MAX_LIGHT_DIST {
for y in -MAX_LIGHT_DIST..range.size().h + MAX_LIGHT_DIST { for y in -MAX_LIGHT_DIST..range.size().h + MAX_LIGHT_DIST {
for z in -1..range.size().d + 1 { for z in -1..range.size().d + 1 {
@ -282,8 +275,8 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
} }
// Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0) // Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0)
let light = calc_light(true, SUNLIGHT, range, self, core::iter::empty()); let light = calc_light(true, SUNLIGHT, range, vol, core::iter::empty());
let glow = calc_light(false, 0, range, self, glow_blocks.into_iter()); let glow = calc_light(false, 0, range, vol, glow_blocks.into_iter());
let mut opaque_limits = None::<Limits>; let mut opaque_limits = None::<Limits>;
let mut fluid_limits = None::<Limits>; let mut fluid_limits = None::<Limits>;
@ -294,10 +287,8 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
// z can range from -1..range.size().d + 1 // z can range from -1..range.size().d + 1
let d = d + 2; let d = d + 2;
let flat = { let flat = {
let mut volume = self.cached(); let mut volume = vol.cached();
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty); const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty);
// TODO: Once we can manage it sensibly, consider using something like // TODO: Once we can manage it sensibly, consider using something like
// Option<Block> instead of just assuming air. // Option<Block> instead of just assuming air.
let mut flat = vec![AIR; (w * h * d) as usize]; let mut flat = vec![AIR; (w * h * d) as usize];
@ -390,8 +381,7 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
} }
}; };
let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min); let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min);
let get_color = let get_color = |_: &mut (), pos: Vec3<i32>| flat_get(pos).get_color().unwrap_or(Rgb::zero());
|_: &mut (), pos: Vec3<i32>| flat_get(pos).get_color().unwrap_or(Rgb::zero());
let get_opacity = |_: &mut (), pos: Vec3<i32>| !flat_get(pos).is_opaque(); let get_opacity = |_: &mut (), pos: Vec3<i32>| !flat_get(pos).is_opaque();
let flat_get = |pos| flat_get(pos); let flat_get = |pos| flat_get(pos);
let should_draw = |_: &mut (), pos: Vec3<i32>, delta: Vec3<i32>, _uv| { let should_draw = |_: &mut (), pos: Vec3<i32>, delta: Vec3<i32>, _uv| {
@ -399,9 +389,8 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
}; };
// NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16. // NOTE: Conversion to f32 is fine since this i32 is actually in bounds for u16.
let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32); let mesh_delta = Vec3::new(0.0, 0.0, (z_start + range.min.z) as f32);
let create_opaque = |atlas_pos, pos, norm, meta| { let create_opaque =
TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta) |atlas_pos, pos, norm, meta| TerrainVertex::new(atlas_pos, pos + mesh_delta, norm, meta);
};
let create_transparent = |_atlas_pos, pos, norm| FluidVertex::new(pos + mesh_delta, norm); let create_transparent = |_atlas_pos, pos, norm| FluidVertex::new(pos + mesh_delta, norm);
let mut greedy = GreedyMesh::new(max_size); let mut greedy = GreedyMesh::new(max_size);
@ -464,7 +453,6 @@ impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
), ),
) )
} }
}
/// NOTE: Make sure to reflect any changes to how meshing is performanced in /// NOTE: Make sure to reflect any changes to how meshing is performanced in
/// [scene::terrain::Terrain::skip_remesh]. /// [scene::terrain::Terrain::skip_remesh].

View File

@ -0,0 +1,14 @@
pub struct Bound<T> {
pub(super) bind_group: wgpu::BindGroup,
pub(super) with: T,
}
impl<T> std::ops::Deref for Bound<T> {
type Target = T;
fn deref(&self) -> &Self::Target { &self.with }
}
impl<T> std::ops::DerefMut for Bound<T> {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.with }
}

View File

@ -0,0 +1,63 @@
use bytemuck::Pod;
use wgpu::util::DeviceExt;
pub struct Buffer<T: Copy + Pod> {
pub(super) buf: wgpu::Buffer,
// Size in number of elements
// TODO: determine if this is a good name
len: usize,
phantom_data: std::marker::PhantomData<T>,
}
impl<T: Copy + Pod> Buffer<T> {
pub fn new(device: &wgpu::Device, usage: wgpu::BufferUsage, data: &[T]) -> Self {
let contents = bytemuck::cast_slice(data);
Self {
buf: device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: None,
contents,
usage,
}),
len: data.len(),
phantom_data: std::marker::PhantomData,
}
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize { self.len }
}
pub struct DynamicBuffer<T: Copy + Pod>(Buffer<T>);
impl<T: Copy + Pod> DynamicBuffer<T> {
pub fn new(device: &wgpu::Device, len: usize, usage: wgpu::BufferUsage) -> Self {
let buffer = Buffer {
buf: device.create_buffer(&wgpu::BufferDescriptor {
label: None,
mapped_at_creation: false,
size: len as u64 * std::mem::size_of::<T>() as u64,
usage: usage | wgpu::BufferUsage::COPY_DST,
}),
len,
phantom_data: std::marker::PhantomData,
};
Self(buffer)
}
pub fn update(&self, queue: &wgpu::Queue, vals: &[T], offset: usize) {
if !vals.is_empty() {
queue.write_buffer(
&self.buf,
offset as u64 * std::mem::size_of::<T>() as u64,
bytemuck::cast_slice(vals),
)
}
}
}
impl<T: Copy + Pod> std::ops::Deref for DynamicBuffer<T> {
type Target = Buffer<T>;
fn deref(&self) -> &Self::Target { &self.0 }
}

View File

@ -1,36 +1,26 @@
use super::{gfx_backend, RenderError}; use super::buffer::DynamicBuffer;
use gfx::{self, traits::FactoryExt}; use bytemuck::Pod;
/// A handle to a series of constants sitting on the GPU. This is used to hold /// A handle to a series of constants sitting on the GPU. This is used to hold
/// information used in the rendering process that does not change throughout a /// information used in the rendering process that does not change throughout a
/// single render pass. /// single render pass.
#[derive(Clone)] pub struct Consts<T: Copy + Pod> {
pub struct Consts<T: Copy + gfx::traits::Pod> { buf: DynamicBuffer<T>,
pub buf: gfx::handle::Buffer<gfx_backend::Resources, T>,
} }
impl<T: Copy + gfx::traits::Pod> Consts<T> { impl<T: Copy + Pod> Consts<T> {
/// Create a new `Const<T>`. /// Create a new `Const<T>`.
pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Self { pub fn new(device: &wgpu::Device, len: usize) -> Self {
Self { Self {
buf: factory.create_constant_buffer(len), // TODO: examine if all our consts need to be updateable
buf: DynamicBuffer::new(device, len, wgpu::BufferUsage::UNIFORM),
} }
} }
/// Update the GPU-side value represented by this constant handle. /// Update the GPU-side value represented by this constant handle.
pub fn update(&mut self, queue: &wgpu::Queue, vals: &[T], offset: usize) {
self.buf.update(queue, vals, offset)
}
pub fn update( pub fn buf(&self) -> &wgpu::Buffer { &self.buf.buf }
&mut self,
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
vals: &[T],
offset: usize,
) -> Result<(), RenderError> {
if vals.is_empty() {
Ok(())
} else {
encoder
.update_buffer(&self.buf, vals, offset)
.map_err(RenderError::UpdateError)
}
}
} }

View File

@ -1,74 +1,54 @@
/// Used to represent one of many possible errors that may be omitted by the /// Used to represent one of many possible errors that may be omitted by the
/// rendering subsystem. /// rendering subsystem.
#[derive(Debug)]
pub enum RenderError { pub enum RenderError {
PipelineError(gfx::PipelineStateError<String>), RequestDeviceError(wgpu::RequestDeviceError),
UpdateError(gfx::UpdateError<usize>), MappingError(wgpu::BufferAsyncError),
TexUpdateError(gfx::UpdateError<[u16; 3]>), SwapChainError(wgpu::SwapChainError),
CombinedError(gfx::CombinedError),
BufferCreationError(gfx::buffer::CreationError),
IncludeError(glsl_include::Error),
MappingError(gfx::mapping::Error),
CopyError(gfx::CopyError<[u16; 3], usize>),
CustomError(String), CustomError(String),
CouldNotFindAdapter,
ErrorInitializingCompiler,
ShaderError(String, shaderc::Error),
} }
impl From<gfx::PipelineStateError<String>> for RenderError { use std::fmt;
fn from(err: gfx::PipelineStateError<String>) -> Self { Self::PipelineError(err) } impl fmt::Debug for RenderError {
} fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
impl From<gfx::PipelineStateError<&str>> for RenderError { Self::RequestDeviceError(err) => {
fn from(err: gfx::PipelineStateError<&str>) -> Self { f.debug_tuple("RequestDeviceError").field(err).finish()
match err {
gfx::PipelineStateError::DescriptorInit(err) => {
gfx::PipelineStateError::DescriptorInit(err)
}, },
err => err, Self::MappingError(err) => f.debug_tuple("MappingError").field(err).finish(),
} Self::SwapChainError(err) => f
.into() .debug_tuple("SwapChainError")
// Use Display formatting for this error since they have nice descriptions
.field(&format!("{}", err))
.finish(),
Self::CustomError(err) => f.debug_tuple("CustomError").field(err).finish(),
Self::CouldNotFindAdapter => f.debug_tuple("CouldNotFindAdapter").finish(),
Self::ErrorInitializingCompiler => f.debug_tuple("ErrorInitializingCompiler").finish(),
Self::ShaderError(shader_name, err) => write!(
f,
"\"{}\" shader failed to compile due to the following error: {}",
shader_name, err
),
} }
} }
impl From<gfx::shade::ProgramError> for RenderError {
fn from(err: gfx::shade::ProgramError) -> Self {
gfx::PipelineStateError::<String>::Program(err).into()
}
}
impl From<gfx::UpdateError<usize>> for RenderError {
fn from(err: gfx::UpdateError<usize>) -> Self { Self::UpdateError(err) }
} }
impl From<gfx::UpdateError<[u16; 3]>> for RenderError { impl From<wgpu::RequestDeviceError> for RenderError {
fn from(err: gfx::UpdateError<[u16; 3]>) -> Self { Self::TexUpdateError(err) } fn from(err: wgpu::RequestDeviceError) -> Self { Self::RequestDeviceError(err) }
} }
impl From<gfx::CombinedError> for RenderError { impl From<wgpu::BufferAsyncError> for RenderError {
fn from(err: gfx::CombinedError) -> Self { Self::CombinedError(err) } fn from(err: wgpu::BufferAsyncError) -> Self { Self::MappingError(err) }
} }
impl From<gfx::TargetViewError> for RenderError { impl From<wgpu::SwapChainError> for RenderError {
fn from(err: gfx::TargetViewError) -> Self { Self::CombinedError(err.into()) } fn from(err: wgpu::SwapChainError) -> Self { Self::SwapChainError(err) }
} }
impl From<gfx::ResourceViewError> for RenderError { impl From<(&str, shaderc::Error)> for RenderError {
fn from(err: gfx::ResourceViewError) -> Self { Self::CombinedError(err.into()) } fn from((shader_name, err): (&str, shaderc::Error)) -> Self {
Self::ShaderError(shader_name.into(), err)
} }
impl From<gfx::texture::CreationError> for RenderError {
fn from(err: gfx::texture::CreationError) -> Self { Self::CombinedError(err.into()) }
}
impl From<gfx::buffer::CreationError> for RenderError {
fn from(err: gfx::buffer::CreationError) -> Self { Self::BufferCreationError(err) }
}
impl From<glsl_include::Error> for RenderError {
fn from(err: glsl_include::Error) -> Self { Self::IncludeError(err) }
}
impl From<gfx::mapping::Error> for RenderError {
fn from(err: gfx::mapping::Error) -> Self { Self::MappingError(err) }
}
impl From<gfx::CopyError<[u16; 3], usize>> for RenderError {
fn from(err: gfx::CopyError<[u16; 3], usize>) -> Self { Self::CopyError(err) }
} }

View File

@ -1,34 +1,26 @@
use super::{gfx_backend, RenderError}; use super::buffer::DynamicBuffer;
use gfx::{ use bytemuck::Pod;
self,
buffer::Role,
memory::{Bind, Usage},
Factory,
};
/// Represents a mesh that has been sent to the GPU. /// Represents a mesh that has been sent to the GPU.
pub struct Instances<T: Copy + gfx::traits::Pod> { pub struct Instances<T: Copy + Pod> {
pub ibuf: gfx::handle::Buffer<gfx_backend::Resources, T>, buf: DynamicBuffer<T>,
} }
impl<T: Copy + gfx::traits::Pod> Instances<T> { impl<T: Copy + Pod> Instances<T> {
pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Result<Self, RenderError> { pub fn new(device: &wgpu::Device, len: usize) -> Self {
Ok(Self { Self {
ibuf: factory // TODO: examine if we have Instances that are not updated (e.g. sprites) and if there
.create_buffer(len, Role::Vertex, Usage::Dynamic, Bind::empty()) // would be any gains from separating those out
.map_err(RenderError::BufferCreationError)?, buf: DynamicBuffer::new(device, len, wgpu::BufferUsage::VERTEX),
}) }
} }
pub fn count(&self) -> usize { self.ibuf.len() } // TODO: count vs len naming scheme??
pub fn count(&self) -> usize { self.buf.len() }
pub fn update( pub fn update(&mut self, queue: &wgpu::Queue, vals: &[T], offset: usize) {
&mut self, self.buf.update(queue, vals, offset)
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>,
instances: &[T],
) -> Result<(), RenderError> {
encoder
.update_buffer(&self.ibuf, instances, 0)
.map_err(RenderError::UpdateError)
} }
pub fn buf(&self) -> &wgpu::Buffer { &self.buf.buf }
} }

View File

@ -1,15 +1,12 @@
use super::Pipeline; use super::Vertex;
use core::{iter::FromIterator, ops::Range}; use core::{iter::FromIterator, ops::Range};
/// A `Vec`-based mesh structure used to store mesh data on the CPU. /// A `Vec`-based mesh structure used to store mesh data on the CPU.
pub struct Mesh<P: Pipeline> { pub struct Mesh<V: Vertex> {
verts: Vec<P::Vertex>, verts: Vec<V>,
} }
impl<P: Pipeline> Clone for Mesh<P> impl<V: Vertex> Clone for Mesh<V> {
where
P::Vertex: Clone,
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self { Self {
verts: self.verts.clone(), verts: self.verts.clone(),
@ -17,7 +14,7 @@ where
} }
} }
impl<P: Pipeline> Mesh<P> { impl<V: Vertex> Mesh<V> {
/// Create a new `Mesh`. /// Create a new `Mesh`.
#[allow(clippy::new_without_default)] // TODO: Pending review in #587 #[allow(clippy::new_without_default)] // TODO: Pending review in #587
pub fn new() -> Self { Self { verts: Vec::new() } } pub fn new() -> Self { Self { verts: Vec::new() } }
@ -26,83 +23,103 @@ impl<P: Pipeline> Mesh<P> {
pub fn clear(&mut self) { self.verts.clear(); } pub fn clear(&mut self) { self.verts.clear(); }
/// Get a slice referencing the vertices of this mesh. /// Get a slice referencing the vertices of this mesh.
pub fn vertices(&self) -> &[P::Vertex] { &self.verts } pub fn vertices(&self) -> &[V] { &self.verts }
/// Get a mutable slice referencing the vertices of this mesh. /// Get a mutable slice referencing the vertices of this mesh.
pub fn vertices_mut(&mut self) -> &mut [P::Vertex] { &mut self.verts } pub fn vertices_mut(&mut self) -> &mut [V] { &mut self.verts }
/// Get a mutable vec referencing the vertices of this mesh.
pub fn vertices_mut_vec(&mut self) -> &mut Vec<V> { &mut self.verts }
/// Push a new vertex onto the end of this mesh. /// Push a new vertex onto the end of this mesh.
pub fn push(&mut self, vert: P::Vertex) { self.verts.push(vert); } pub fn push(&mut self, vert: V) { self.verts.push(vert); }
/// Push a new polygon onto the end of this mesh. /// Push a new polygon onto the end of this mesh.
pub fn push_tri(&mut self, tri: Tri<P>) { pub fn push_tri(&mut self, tri: Tri<V>) {
self.verts.push(tri.a); self.verts.push(tri.a);
self.verts.push(tri.b); self.verts.push(tri.b);
self.verts.push(tri.c); self.verts.push(tri.c);
} }
/// Push a new quad onto the end of this mesh. /// Push a new quad onto the end of this mesh.
pub fn push_quad(&mut self, quad: Quad<P>) { pub fn push_quad(&mut self, quad: Quad<V>) {
// A quad is composed of two triangles. The code below converts the former to // A quad is composed of two triangles. The code below converts the former to
// the latter. // the latter.
if V::QUADS_INDEX.is_some() {
// Tri 1 // 0, 1, 2, 2, 1, 3
self.verts.push(quad.a.clone()); // b, c, a, a, c, d
self.verts.push(quad.b); self.verts.push(quad.b);
self.verts.push(quad.c.clone()); self.verts.push(quad.c);
self.verts.push(quad.a);
self.verts.push(quad.d);
} else {
// Tri 1
self.verts.push(quad.a);
self.verts.push(quad.b);
self.verts.push(quad.c);
// Tri 2 // Tri 2
self.verts.push(quad.c); self.verts.push(quad.c);
self.verts.push(quad.d); self.verts.push(quad.d);
self.verts.push(quad.a); self.verts.push(quad.a);
} }
}
/// Overwrite a quad /// Overwrite a quad
pub fn replace_quad(&mut self, index: usize, quad: Quad<P>) { pub fn replace_quad(&mut self, index: usize, quad: Quad<V>) {
if V::QUADS_INDEX.is_some() {
debug_assert!(index % 4 == 0);
assert!(index + 3 < self.verts.len());
self.verts[index] = quad.b;
self.verts[index + 1] = quad.c;
self.verts[index + 2] = quad.a;
self.verts[index + 3] = quad.d;
} else {
debug_assert!(index % 3 == 0); debug_assert!(index % 3 == 0);
assert!(index + 5 < self.verts.len()); assert!(index + 5 < self.verts.len());
// Tri 1 // Tri 1
self.verts[index] = quad.a.clone(); self.verts[index] = quad.a;
self.verts[index + 1] = quad.b; self.verts[index + 1] = quad.b;
self.verts[index + 2] = quad.c.clone(); self.verts[index + 2] = quad.c;
// Tri 2 // Tri 2
self.verts[index + 3] = quad.c; self.verts[index + 3] = quad.c;
self.verts[index + 4] = quad.d; self.verts[index + 4] = quad.d;
self.verts[index + 5] = quad.a; self.verts[index + 5] = quad.a;
} }
}
/// Push the vertices of another mesh onto the end of this mesh. /// Push the vertices of another mesh onto the end of this mesh.
pub fn push_mesh(&mut self, other: &Mesh<P>) { self.verts.extend_from_slice(other.vertices()); } pub fn push_mesh(&mut self, other: &Mesh<V>) { self.verts.extend_from_slice(other.vertices()); }
/// Map and push the vertices of another mesh onto the end of this mesh. /// Map and push the vertices of another mesh onto the end of this mesh.
pub fn push_mesh_map<F: FnMut(P::Vertex) -> P::Vertex>(&mut self, other: &Mesh<P>, mut f: F) { pub fn push_mesh_map<F: FnMut(V) -> V>(&mut self, other: &Mesh<V>, mut f: F) {
// Reserve enough space in our Vec. This isn't necessary, but it tends to reduce // Reserve enough space in our Vec. This isn't necessary, but it tends to reduce
// the number of required (re)allocations. // the number of required (re)allocations.
self.verts.reserve(other.vertices().len()); self.verts.reserve(other.vertices().len());
for vert in other.vertices() { for vert in other.vertices() {
self.verts.push(f(vert.clone())); self.verts.push(f(*vert));
} }
} }
pub fn iter(&self) -> std::slice::Iter<P::Vertex> { self.verts.iter() } pub fn iter(&self) -> std::slice::Iter<V> { self.verts.iter() }
/// NOTE: Panics if vertex_range is out of bounds of vertices. /// NOTE: Panics if vertex_range is out of bounds of vertices.
pub fn iter_mut(&mut self, vertex_range: Range<usize>) -> std::slice::IterMut<P::Vertex> { pub fn iter_mut(&mut self, vertex_range: Range<usize>) -> std::slice::IterMut<V> {
self.verts[vertex_range].iter_mut() self.verts[vertex_range].iter_mut()
} }
} }
impl<P: Pipeline> IntoIterator for Mesh<P> { impl<V: Vertex> IntoIterator for Mesh<V> {
type IntoIter = std::vec::IntoIter<P::Vertex>; type IntoIter = std::vec::IntoIter<V>;
type Item = P::Vertex; type Item = V;
fn into_iter(self) -> Self::IntoIter { self.verts.into_iter() } fn into_iter(self) -> Self::IntoIter { self.verts.into_iter() }
} }
impl<P: Pipeline> FromIterator<Tri<P>> for Mesh<P> { impl<V: Vertex> FromIterator<Tri<V>> for Mesh<V> {
fn from_iter<I: IntoIterator<Item = Tri<P>>>(tris: I) -> Self { fn from_iter<I: IntoIterator<Item = Tri<V>>>(tris: I) -> Self {
tris.into_iter().fold(Self::new(), |mut this, tri| { tris.into_iter().fold(Self::new(), |mut this, tri| {
this.push_tri(tri); this.push_tri(tri);
this this
@ -110,8 +127,8 @@ impl<P: Pipeline> FromIterator<Tri<P>> for Mesh<P> {
} }
} }
impl<P: Pipeline> FromIterator<Quad<P>> for Mesh<P> { impl<V: Vertex> FromIterator<Quad<V>> for Mesh<V> {
fn from_iter<I: IntoIterator<Item = Quad<P>>>(quads: I) -> Self { fn from_iter<I: IntoIterator<Item = Quad<V>>>(quads: I) -> Self {
quads.into_iter().fold(Self::new(), |mut this, quad| { quads.into_iter().fold(Self::new(), |mut this, quad| {
this.push_quad(quad); this.push_quad(quad);
this this
@ -120,40 +137,35 @@ impl<P: Pipeline> FromIterator<Quad<P>> for Mesh<P> {
} }
/// Represents a triangle stored on the CPU. /// Represents a triangle stored on the CPU.
pub struct Tri<P: Pipeline> { pub struct Tri<V: Vertex> {
a: P::Vertex, a: V,
b: P::Vertex, b: V,
c: P::Vertex, c: V,
} }
impl<P: Pipeline> Tri<P> { impl<V: Vertex> Tri<V> {
pub fn new(a: P::Vertex, b: P::Vertex, c: P::Vertex) -> Self { Self { a, b, c } } pub fn new(a: V, b: V, c: V) -> Self { Self { a, b, c } }
} }
/// Represents a quad stored on the CPU. /// Represents a quad stored on the CPU.
pub struct Quad<P: Pipeline> { pub struct Quad<V: Vertex> {
a: P::Vertex, a: V,
b: P::Vertex, b: V,
c: P::Vertex, c: V,
d: P::Vertex, d: V,
} }
impl<P: Pipeline> Quad<P> { impl<V: Vertex> Quad<V> {
pub fn new(a: P::Vertex, b: P::Vertex, c: P::Vertex, d: P::Vertex) -> Self { pub fn new(a: V, b: V, c: V, d: V) -> Self { Self { a, b, c, d } }
Self { a, b, c, d }
}
pub fn rotated_by(self, n: usize) -> Self pub fn rotated_by(self, n: usize) -> Self {
where
P::Vertex: Clone,
{
let verts = [self.a, self.b, self.c, self.d]; let verts = [self.a, self.b, self.c, self.d];
Self { Self {
a: verts[n % 4].clone(), a: verts[n % 4],
b: verts[(1 + n) % 4].clone(), b: verts[(1 + n) % 4],
c: verts[(2 + n) % 4].clone(), c: verts[(2 + n) % 4],
d: verts[(3 + n) % 4].clone(), d: verts[(3 + n) % 4],
} }
} }
} }

View File

@ -1,3 +1,5 @@
pub mod bound;
mod buffer;
#[allow(clippy::single_component_path_imports)] // TODO: Pending review in #587 #[allow(clippy::single_component_path_imports)] // TODO: Pending review in #587
pub mod consts; pub mod consts;
mod error; mod error;
@ -10,58 +12,55 @@ pub mod texture;
// Reexports // Reexports
pub use self::{ pub use self::{
bound::Bound,
buffer::Buffer,
consts::Consts, consts::Consts,
error::RenderError, error::RenderError,
instances::Instances, instances::Instances,
mesh::{Mesh, Quad, Tri}, mesh::{Mesh, Quad, Tri},
model::{DynamicModel, Model}, model::{DynamicModel, Model, SubModel},
pipelines::{ pipelines::{
clouds::{create_mesh as create_clouds_mesh, CloudsPipeline, Locals as CloudsLocals}, clouds::Locals as CloudsLocals,
debug::{DebugPipeline, Locals as DebugLocals, Vertex as DebugVertex},
figure::{ figure::{
BoneData as FigureBoneData, BoneMeshes, FigureModel, FigurePipeline, BoneData as FigureBoneData, BoneMeshes, FigureLayout, FigureModel,
Locals as FigureLocals, Locals as FigureLocals,
}, },
fluid::FluidPipeline, fluid::Vertex as FluidVertex,
lod_terrain::{Locals as LodTerrainLocals, LodData, LodTerrainPipeline}, lod_terrain::{LodData, Vertex as LodTerrainVertex},
particle::{Instance as ParticleInstance, ParticlePipeline}, particle::{Instance as ParticleInstance, Vertex as ParticleVertex},
postprocess::{ postprocess::Locals as PostProcessLocals,
create_mesh as create_pp_mesh, Locals as PostProcessLocals, PostProcessPipeline, shadow::{Locals as ShadowLocals, PointLightMatrix},
skybox::{create_mesh as create_skybox_mesh, Vertex as SkyboxVertex},
sprite::{
Instance as SpriteInstance, SpriteGlobalsBindGroup, SpriteVerts,
Vertex as SpriteVertex, VERT_PAGE_SIZE as SPRITE_VERT_PAGE_SIZE,
}, },
shadow::{Locals as ShadowLocals, ShadowPipeline}, terrain::{Locals as TerrainLocals, TerrainLayout, Vertex as TerrainVertex},
skybox::{create_mesh as create_skybox_mesh, Locals as SkyboxLocals, SkyboxPipeline},
sprite::{Instance as SpriteInstance, Locals as SpriteLocals, SpritePipeline},
terrain::{Locals as TerrainLocals, TerrainPipeline},
ui::{ ui::{
create_quad as create_ui_quad, create_quad as create_ui_quad,
create_quad_vert_gradient as create_ui_quad_vert_gradient, create_tri as create_ui_tri, create_quad_vert_gradient as create_ui_quad_vert_gradient, create_tri as create_ui_tri,
Locals as UiLocals, Mode as UiMode, UiPipeline, BoundLocals as UiBoundLocals, Locals as UiLocals, Mode as UiMode,
TextureBindGroup as UiTextureBindGroup, Vertex as UiVertex,
}, },
GlobalModel, Globals, Light, Shadow, GlobalModel, Globals, GlobalsBindGroup, GlobalsLayouts, Light, Shadow,
}, },
renderer::{ renderer::{
ColLightFmt, ColLightInfo, LodAltFmt, LodColorFmt, LodTextureFmt, Renderer, drawer::{
ShadowDepthStencilFmt, TgtColorFmt, TgtDepthStencilFmt, WinColorFmt, WinDepthFmt, DebugDrawer, Drawer, FigureDrawer, FigureShadowDrawer, FirstPassDrawer, ParticleDrawer,
PreparedUiDrawer, SecondPassDrawer, ShadowPassDrawer, SpriteDrawer, TerrainDrawer,
TerrainShadowDrawer, ThirdPassDrawer, UiDrawer,
},
ColLightInfo, Renderer,
}, },
texture::Texture, texture::Texture,
}; };
pub use gfx::texture::{FilterMethod, WrapMode}; pub use wgpu::{AddressMode, FilterMode};
#[cfg(feature = "gl")] pub trait Vertex: Clone + bytemuck::Pod {
use gfx_device_gl as gfx_backend; const STRIDE: wgpu::BufferAddress;
// Whether these types of verts use the quad index buffer for drawing them
/// Used to represent a specific rendering configuration. const QUADS_INDEX: Option<wgpu::IndexFormat>;
///
/// Note that pipelines are tied to the
/// rendering backend, and as such it is necessary to modify the rendering
/// subsystem when adding new pipelines - custom pipelines are not currently an
/// objective of the rendering subsystem.
///
/// # Examples
///
/// - `SkyboxPipeline`
/// - `FigurePipeline`
pub trait Pipeline {
type Vertex: Clone + gfx::traits::Pod + gfx::pso::buffer::Structure<gfx::format::Format>;
} }
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -243,6 +242,30 @@ impl Default for UpscaleMode {
fn default() -> Self { Self { factor: 1.0 } } fn default() -> Self { Self { factor: 1.0 } }
} }
/// Present modes
/// See https://docs.rs/wgpu/0.7.0/wgpu/enum.PresentMode.html
#[derive(PartialEq, Clone, Copy, Debug, Serialize, Deserialize)]
pub enum PresentMode {
Fifo,
Mailbox,
#[serde(other)]
Immediate,
}
impl Default for PresentMode {
fn default() -> Self { Self::Immediate }
}
impl From<PresentMode> for wgpu::PresentMode {
fn from(mode: PresentMode) -> Self {
match mode {
PresentMode::Fifo => wgpu::PresentMode::Fifo,
PresentMode::Mailbox => wgpu::PresentMode::Mailbox,
PresentMode::Immediate => wgpu::PresentMode::Immediate,
}
}
}
/// Render modes /// Render modes
#[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize)] #[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
#[serde(default)] #[serde(default)]
@ -253,4 +276,6 @@ pub struct RenderMode {
pub lighting: LightingMode, pub lighting: LightingMode,
pub shadow: ShadowMode, pub shadow: ShadowMode,
pub upscale_mode: UpscaleMode, pub upscale_mode: UpscaleMode,
pub present_mode: PresentMode,
pub profiler_enabled: bool,
} }

View File

@ -1,69 +1,89 @@
use super::{gfx_backend, mesh::Mesh, Pipeline, RenderError}; use super::{
use gfx::{ buffer::{Buffer, DynamicBuffer},
buffer::Role, mesh::Mesh,
memory::{Bind, Usage}, Vertex,
traits::FactoryExt,
Factory,
}; };
use std::ops::Range; use std::ops::Range;
/// Represents a mesh that has been sent to the GPU. /// Represents a mesh that has been sent to the GPU.
pub struct Model<P: Pipeline> { pub struct SubModel<'a, V: Vertex> {
pub vbuf: gfx::handle::Buffer<gfx_backend::Resources, P::Vertex>,
pub vertex_range: Range<u32>, pub vertex_range: Range<u32>,
buf: &'a wgpu::Buffer,
phantom_data: std::marker::PhantomData<V>,
} }
impl<P: Pipeline> Model<P> { impl<'a, V: Vertex> SubModel<'a, V> {
pub fn new(factory: &mut gfx_backend::Factory, mesh: &Mesh<P>) -> Self { pub(super) fn buf(&self) -> wgpu::BufferSlice<'a> {
Self { let start = self.vertex_range.start as wgpu::BufferAddress * V::STRIDE;
vbuf: factory.create_vertex_buffer(mesh.vertices()), let end = self.vertex_range.end as wgpu::BufferAddress * V::STRIDE;
vertex_range: 0..mesh.vertices().len() as u32, self.buf.slice(start..end)
}
} }
pub fn vertex_range(&self) -> Range<u32> { self.vertex_range.clone() } #[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> u32 { self.vertex_range.end - self.vertex_range.start }
/// Create a model with a slice of a portion of this model to send to the
/// renderer.
pub fn submodel(&self, vertex_range: Range<u32>) -> Model<P> {
Model {
vbuf: self.vbuf.clone(),
vertex_range,
}
}
} }
/// Represents a mesh on the GPU which can be updated dynamically. /// Represents a mesh that has been sent to the GPU.
pub struct DynamicModel<P: Pipeline> { pub struct Model<V: Vertex> {
pub vbuf: gfx::handle::Buffer<gfx_backend::Resources, P::Vertex>, vbuf: Buffer<V>,
} }
impl<P: Pipeline> DynamicModel<P> { impl<V: Vertex> Model<V> {
pub fn new(factory: &mut gfx_backend::Factory, size: usize) -> Result<Self, RenderError> { /// Returns None if the provided mesh is empty
Ok(Self { pub fn new(device: &wgpu::Device, mesh: &Mesh<V>) -> Option<Self> {
vbuf: factory if mesh.vertices().is_empty() {
.create_buffer(size, Role::Vertex, Usage::Dynamic, Bind::empty()) return None;
.map_err(RenderError::BufferCreationError)?, }
Some(Self {
vbuf: Buffer::new(device, wgpu::BufferUsage::VERTEX, mesh.vertices()),
}) })
} }
/// Create a model with a slice of a portion of this model to send to the /// Create a model with a slice of a portion of this model to send to the
/// renderer. /// renderer.
pub fn submodel(&self, vertex_range: Range<u32>) -> Model<P> { pub fn submodel(&self, vertex_range: Range<u32>) -> SubModel<V> {
Model { SubModel {
vbuf: self.vbuf.clone(),
vertex_range, vertex_range,
buf: self.buf(),
phantom_data: std::marker::PhantomData,
} }
} }
pub fn update( pub(super) fn buf(&self) -> &wgpu::Buffer { &self.vbuf.buf }
&self,
encoder: &mut gfx::Encoder<gfx_backend::Resources, gfx_backend::CommandBuffer>, #[allow(clippy::len_without_is_empty)]
mesh: &Mesh<P>, pub fn len(&self) -> usize { self.vbuf.len() }
offset: usize, }
) -> Result<(), RenderError> {
encoder /// Represents a mesh that has been sent to the GPU.
.update_buffer(&self.vbuf, mesh.vertices(), offset) pub struct DynamicModel<V: Vertex> {
.map_err(RenderError::UpdateError) vbuf: DynamicBuffer<V>,
}
impl<V: Vertex> DynamicModel<V> {
pub fn new(device: &wgpu::Device, size: usize) -> Self {
Self {
vbuf: DynamicBuffer::new(device, size, wgpu::BufferUsage::VERTEX),
} }
} }
pub fn update(&self, queue: &wgpu::Queue, mesh: &Mesh<V>, offset: usize) {
self.vbuf.update(queue, mesh.vertices(), offset)
}
/// Create a model with a slice of a portion of this model to send to the
/// renderer.
pub fn submodel(&self, vertex_range: Range<u32>) -> SubModel<V> {
SubModel {
vertex_range,
buf: self.buf(),
phantom_data: std::marker::PhantomData,
}
}
pub fn buf(&self) -> &wgpu::Buffer { &self.vbuf.buf }
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize { self.vbuf.len() }
}

View File

@ -0,0 +1,123 @@
pub struct BindGroup {
pub(in super::super) bind_group: wgpu::BindGroup,
}
pub struct BlitLayout {
pub layout: wgpu::BindGroupLayout,
}
impl BlitLayout {
pub fn new(device: &wgpu::Device) -> Self {
Self {
layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[
// Color source
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
],
}),
}
}
pub fn bind(
&self,
device: &wgpu::Device,
src_color: &wgpu::TextureView,
sampler: &wgpu::Sampler,
) -> BindGroup {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(src_color),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(sampler),
},
],
});
BindGroup { bind_group }
}
}
pub struct BlitPipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl BlitPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
sc_desc: &wgpu::SwapChainDescriptor,
layout: &BlitLayout,
) -> Self {
common_base::span!(_guard, "BlitPipeline::new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Blit pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&layout.layout],
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Blit pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: sc_desc.format,
blend: None,
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
}
}
}

View File

@ -1,40 +1,15 @@
use super::{ use super::{
super::{Mesh, Pipeline, TgtColorFmt, TgtDepthStencilFmt, Tri}, super::{AaMode, Consts},
Globals, GlobalsLayouts,
};
use gfx::{
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
gfx_pipeline_inner, gfx_vertex_struct_meta,
}; };
use bytemuck::{Pod, Zeroable};
use vek::*; use vek::*;
gfx_defines! { #[repr(C)]
vertex Vertex { #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pos: [f32; 2] = "v_pos", pub struct Locals {
} proj_mat_inv: [[f32; 4]; 4],
view_mat_inv: [[f32; 4]; 4],
constant Locals {
proj_mat_inv: [[f32; 4]; 4] = "proj_mat_inv",
view_mat_inv: [[f32; 4]; 4] = "view_mat_inv",
}
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
map: gfx::TextureSampler<[f32; 4]> = "t_map",
alt: gfx::TextureSampler<[f32; 2]> = "t_alt",
horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon",
color_sampler: gfx::TextureSampler<<TgtColorFmt as gfx::format::Formatted>::View> = "src_color",
depth_sampler: gfx::TextureSampler<<TgtDepthStencilFmt as gfx::format::Formatted>::View> = "src_depth",
noise: gfx::TextureSampler<f32> = "t_noise",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
}
} }
impl Default for Locals { impl Default for Locals {
@ -50,28 +25,180 @@ impl Locals {
} }
} }
pub struct CloudsPipeline; pub struct BindGroup {
pub(in super::super) bind_group: wgpu::BindGroup,
impl Pipeline for CloudsPipeline {
type Vertex = Vertex;
} }
pub fn create_mesh() -> Mesh<CloudsPipeline> { pub struct CloudsLayout {
let mut mesh = Mesh::new(); pub layout: wgpu::BindGroupLayout,
}
#[rustfmt::skip]
mesh.push_tri(Tri::new( impl CloudsLayout {
Vertex { pos: [ 1.0, -1.0] }, pub fn new(device: &wgpu::Device) -> Self {
Vertex { pos: [-1.0, 1.0] }, Self {
Vertex { pos: [-1.0, -1.0] }, layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
)); label: None,
entries: &[
#[rustfmt::skip] // Color source
mesh.push_tri(Tri::new( wgpu::BindGroupLayoutEntry {
Vertex { pos: [1.0, -1.0] }, binding: 0,
Vertex { pos: [1.0, 1.0] }, visibility: wgpu::ShaderStage::FRAGMENT,
Vertex { pos: [-1.0, 1.0] }, ty: wgpu::BindingType::Texture {
)); sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
mesh multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// Depth source
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// Locals
wgpu::BindGroupLayoutEntry {
binding: 4,
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
}),
}
}
pub fn bind(
&self,
device: &wgpu::Device,
src_color: &wgpu::TextureView,
src_depth: &wgpu::TextureView,
sampler: &wgpu::Sampler,
depth_sampler: &wgpu::Sampler,
locals: &Consts<Locals>,
) -> BindGroup {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(src_color),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(sampler),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::TextureView(src_depth),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::Sampler(depth_sampler),
},
wgpu::BindGroupEntry {
binding: 4,
resource: locals.buf().as_entire_binding(),
},
],
});
BindGroup { bind_group }
}
}
pub struct CloudsPipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl CloudsPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
layout: &CloudsLayout,
aa_mode: AaMode,
) -> Self {
common_base::span!(_guard, "CloudsPipeline::new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Clouds pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &layout.layout],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Clouds pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: None,
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
}
}
} }

View File

@ -0,0 +1,172 @@
use super::super::{AaMode, Bound, Consts, GlobalsLayouts, Vertex as VertexTrait};
use bytemuck::{Pod, Zeroable};
use std::mem;
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pub pos: [f32; 3],
}
impl Vertex {
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
wgpu::VertexBufferLayout {
array_stride: Self::STRIDE,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &[wgpu::VertexAttribute {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
}],
}
}
}
impl VertexTrait for Vertex {
const QUADS_INDEX: Option<wgpu::IndexFormat> = None;
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
/// pos is [f32; 4] instead of [f32; 3] so that Locals's size is a multiple
/// of 8 bytes (which is required by gfx), the last component is ignored
/// by the shader
pub pos: [f32; 4],
pub color: [f32; 4],
}
pub type BoundLocals = Bound<Consts<Locals>>;
impl From<Vec3<f32>> for Vertex {
fn from(pos: Vec3<f32>) -> Vertex {
Vertex {
pos: [pos.x, pos.y, pos.z],
}
}
}
pub struct DebugPipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl DebugPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
global_layouts: &GlobalsLayouts,
layout: &DebugLayout,
aa_mode: AaMode,
) -> Self {
common_base::span!(_guard, "DebugPipeline::new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Debug pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&global_layouts.globals, &layout.locals],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Debug pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[Vertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::GreaterEqual,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
}
}
}
pub struct DebugLayout {
pub locals: wgpu::BindGroupLayout,
}
impl DebugLayout {
pub fn new(device: &wgpu::Device) -> Self {
Self {
locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
}),
}
}
pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts<Locals>) -> BoundLocals {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.locals,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: locals.buf().as_entire_binding(),
}],
});
BoundLocals {
bind_group,
with: locals,
}
}
}

View File

@ -1,57 +1,31 @@
use super::{ use super::{
super::{Mesh, Model, Pipeline, TerrainPipeline, TgtColorFmt, TgtDepthStencilFmt}, super::{AaMode, Bound, Consts, GlobalsLayouts, Mesh, Model},
shadow, Globals, Light, Shadow, terrain::Vertex,
}; };
use crate::mesh::greedy::GreedyMesh; use crate::mesh::greedy::GreedyMesh;
use gfx::{ use bytemuck::{Pod, Zeroable};
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
gfx_pipeline_inner, state::ColorMask,
};
use vek::*; use vek::*;
gfx_defines! { #[repr(C)]
constant Locals { #[derive(Copy, Clone, Debug, Zeroable, Pod)]
model_mat: [[f32; 4]; 4] = "model_mat", pub struct Locals {
highlight_col: [f32; 4] = "highlight_col", model_mat: [[f32; 4]; 4],
model_light: [f32; 4] = "model_light", highlight_col: [f32; 4],
model_glow: [f32; 4] = "model_glow", model_light: [f32; 4],
atlas_offs: [i32; 4] = "atlas_offs", model_glow: [f32; 4],
model_pos: [f32; 3] = "model_pos", atlas_offs: [i32; 4],
flags: u32 = "flags", model_pos: [f32; 3],
flags: u32,
} }
constant BoneData { #[repr(C)]
bone_mat: [[f32; 4]; 4] = "bone_mat", #[derive(Copy, Clone, Debug, Zeroable, Pod)]
normals_mat: [[f32; 4]; 4] = "normals_mat", pub struct BoneData {
bone_mat: [[f32; 4]; 4],
normals_mat: [[f32; 4]; 4],
} }
pipeline pipe { pub type BoundLocals = Bound<(Consts<Locals>, Consts<BoneData>)>;
vbuf: gfx::VertexBuffer<<TerrainPipeline as Pipeline>::Vertex> = (),
// abuf: gfx::VertexBuffer<<TerrainPipeline as Pipeline>::Vertex> = (),
col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light",
locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
bones: gfx::ConstantBuffer<BoneData> = "u_bones",
lights: gfx::ConstantBuffer<Light> = "u_lights",
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
point_shadow_maps: gfx::TextureSampler<f32> = "t_point_shadow_maps",
directed_shadow_maps: gfx::TextureSampler<f32> = "t_directed_shadow_maps",
alt: gfx::TextureSampler<[f32; 2]> = "t_alt",
horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon",
noise: gfx::TextureSampler<f32> = "t_noise",
// Shadow stuff
light_shadows: gfx::ConstantBuffer<shadow::Locals> = "u_light_shadows",
tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA),
tgt_depth_stencil: gfx::DepthTarget<TgtDepthStencilFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
// tgt_depth_stencil: gfx::DepthStencilTarget<TgtDepthStencilFmt> = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Replace))),
}
}
impl Locals { impl Locals {
pub fn new( pub fn new(
@ -105,14 +79,8 @@ impl Default for BoneData {
fn default() -> Self { Self::new(anim::vek::Mat4::identity(), anim::vek::Mat4::identity()) } fn default() -> Self { Self::new(anim::vek::Mat4::identity(), anim::vek::Mat4::identity()) }
} }
pub struct FigurePipeline;
impl Pipeline for FigurePipeline {
type Vertex = <TerrainPipeline as Pipeline>::Vertex;
}
pub struct FigureModel { pub struct FigureModel {
pub opaque: Model<TerrainPipeline>, pub opaque: Model<Vertex>,
/* TODO: Consider using mipmaps instead of storing multiple texture atlases for different /* TODO: Consider using mipmaps instead of storing multiple texture atlases for different
* LOD levels. */ * LOD levels. */
} }
@ -129,4 +97,157 @@ impl FigureModel {
} }
} }
pub type BoneMeshes = (Mesh<TerrainPipeline>, anim::vek::Aabb<f32>); pub type BoneMeshes = (Mesh<Vertex>, anim::vek::Aabb<f32>);
pub struct FigureLayout {
pub locals: wgpu::BindGroupLayout,
}
impl FigureLayout {
pub fn new(device: &wgpu::Device) -> Self {
Self {
locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[
// locals
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
// bone data
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
}),
}
}
pub fn bind_locals(
&self,
device: &wgpu::Device,
locals: Consts<Locals>,
bone_data: Consts<BoneData>,
) -> BoundLocals {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.locals,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: locals.buf().as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: bone_data.buf().as_entire_binding(),
},
],
});
BoundLocals {
bind_group,
with: (locals, bone_data),
}
}
}
pub struct FigurePipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl FigurePipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
layout: &FigureLayout,
aa_mode: AaMode,
) -> Self {
common_base::span!(_guard, "FigurePipeline::new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Figure pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[
&global_layout.globals,
&global_layout.shadow_textures,
&layout.locals,
&global_layout.col_light,
],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Figure pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[Vertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::GreaterEqual,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: None,
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
}
}
}

View File

@ -1,42 +1,12 @@
use super::{ use super::super::{AaMode, GlobalsLayouts, TerrainLayout, Vertex as VertexTrait};
super::{Pipeline, TerrainLocals, TgtColorFmt, TgtDepthStencilFmt}, use bytemuck::{Pod, Zeroable};
shadow, Globals, Light, Shadow, use std::mem;
};
use gfx::{
self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner,
gfx_vertex_struct_meta, state::ColorMask,
};
use vek::*; use vek::*;
gfx_defines! { #[repr(C)]
vertex Vertex { #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pos_norm: u32 = "v_pos_norm", pub struct Vertex {
} pos_norm: u32,
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
locals: gfx::ConstantBuffer<TerrainLocals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
lights: gfx::ConstantBuffer<Light> = "u_lights",
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
point_shadow_maps: gfx::TextureSampler<f32> = "t_point_shadow_maps",
directed_shadow_maps: gfx::TextureSampler<f32> = "t_directed_shadow_maps",
alt: gfx::TextureSampler<[f32; 2]> = "t_alt",
horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon",
noise: gfx::TextureSampler<f32> = "t_noise",
waves: gfx::TextureSampler<[f32; 4]> = "t_waves",
// Shadow stuff
light_shadows: gfx::ConstantBuffer<shadow::Locals> = "u_light_shadows",
tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA),
tgt_depth_stencil: gfx::DepthTarget<TgtDepthStencilFmt> = gfx::preset::depth::LESS_EQUAL_TEST,
// tgt_depth_stencil: gfx::DepthStencilTarget<TgtDepthStencilFmt> = (gfx::preset::depth::LESS_EQUAL_TEST,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))),
}
} }
impl Vertex { impl Vertex {
@ -61,10 +31,116 @@ impl Vertex {
| (norm_bits & 0x7) << 29, | (norm_bits & 0x7) << 29,
} }
} }
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
const ATTRIBUTES: [wgpu::VertexAttribute; 1] = wgpu::vertex_attr_array![0 => Uint32];
wgpu::VertexBufferLayout {
array_stride: Self::STRIDE,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &ATTRIBUTES,
}
}
} }
pub struct FluidPipeline; impl VertexTrait for Vertex {
const QUADS_INDEX: Option<wgpu::IndexFormat> = Some(wgpu::IndexFormat::Uint16);
impl Pipeline for FluidPipeline { const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
type Vertex = Vertex; }
pub struct FluidPipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl FluidPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
terrain_layout: &TerrainLayout,
aa_mode: AaMode,
) -> Self {
common_base::span!(_guard, "FluidPipeline::new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Fluid pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[
&global_layout.globals,
&global_layout.shadow_textures,
&terrain_layout.locals,
],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Fluid pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[Vertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: false,
depth_compare: wgpu::CompareFunction::GreaterEqual,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
}
}
} }

View File

@ -1,40 +1,12 @@
use super::{ use super::super::{AaMode, GlobalsLayouts, Renderer, Texture, Vertex as VertexTrait};
super::{ use bytemuck::{Pod, Zeroable};
LodAltFmt, LodColorFmt, LodTextureFmt, Pipeline, Renderer, Texture, TgtColorFmt, use std::mem;
TgtDepthStencilFmt,
},
Globals,
};
use gfx::{
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
gfx_pipeline_inner, gfx_vertex_struct_meta, texture::SamplerInfo,
};
use vek::*; use vek::*;
gfx_defines! { #[repr(C)]
vertex Vertex { #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pos: [f32; 2] = "v_pos", pub struct Vertex {
} pos: [f32; 2],
constant Locals {
nul: [f32; 4] = "nul",
}
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
map: gfx::TextureSampler<[f32; 4]> = "t_map",
alt: gfx::TextureSampler<[f32; 2]> = "t_alt",
horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon",
noise: gfx::TextureSampler<f32> = "t_noise",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
tgt_depth_stencil: gfx::DepthTarget<TgtDepthStencilFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
// tgt_depth_stencil: gfx::DepthStencilTarget<TgtDepthStencilFmt> = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))),
}
} }
impl Vertex { impl Vertex {
@ -43,75 +15,210 @@ impl Vertex {
pos: pos.into_array(), pos: pos.into_array(),
} }
} }
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
const ATTRIBUTES: [wgpu::VertexAttribute; 1] = wgpu::vertex_attr_array![0 => Float32x2];
wgpu::VertexBufferLayout {
array_stride: Self::STRIDE,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &ATTRIBUTES,
}
}
} }
impl Locals { impl VertexTrait for Vertex {
pub fn default() -> Self { Self { nul: [0.0; 4] } } const QUADS_INDEX: Option<wgpu::IndexFormat> = Some(wgpu::IndexFormat::Uint32);
} const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
pub struct LodTerrainPipeline;
impl Pipeline for LodTerrainPipeline {
type Vertex = Vertex;
} }
pub struct LodData { pub struct LodData {
pub map: Texture<LodColorFmt>, pub map: Texture,
pub alt: Texture<LodAltFmt>, pub alt: Texture,
pub horizon: Texture<LodTextureFmt>, pub horizon: Texture,
pub tgt_detail: u32, pub tgt_detail: u32,
} }
impl LodData { impl LodData {
pub fn dummy(renderer: &mut Renderer) -> Self {
let map_size = Vec2::new(1, 1);
//let map_border = [0.0, 0.0, 0.0, 0.0];
let map_image = [0];
let alt_image = [0];
let horizon_image = [0x_00_01_00_01];
Self::new(
renderer,
map_size,
&map_image,
&alt_image,
&horizon_image,
1,
//map_border.into(),
)
}
pub fn new( pub fn new(
renderer: &mut Renderer, renderer: &mut Renderer,
map_size: Vec2<u16>, map_size: Vec2<u32>,
lod_base: &[u32], lod_base: &[u32],
lod_alt: &[u32], lod_alt: &[u32],
lod_horizon: &[u32], lod_horizon: &[u32],
tgt_detail: u32, tgt_detail: u32,
border_color: gfx::texture::PackedColor, //border_color: gfx::texture::PackedColor,
) -> Self { ) -> Self {
let kind = gfx::texture::Kind::D2(map_size.x, map_size.y, gfx::texture::AaMode::Single); let mut create_texture = |format, data, filter| {
let info = gfx::texture::SamplerInfo::new( let texture_info = wgpu::TextureDescriptor {
gfx::texture::FilterMethod::Bilinear, label: None,
gfx::texture::WrapMode::Border, size: wgpu::Extent3d {
width: map_size.x,
height: map_size.y,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format,
usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
};
let sampler_info = wgpu::SamplerDescriptor {
label: None,
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: filter,
min_filter: filter,
mipmap_filter: wgpu::FilterMode::Nearest,
border_color: Some(wgpu::SamplerBorderColor::TransparentBlack),
..Default::default()
};
let view_info = wgpu::TextureViewDescriptor {
label: None,
format: Some(format),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
};
renderer.create_texture_with_data_raw(
&texture_info,
&view_info,
&sampler_info,
bytemuck::cast_slice(data),
)
};
let map = create_texture(
wgpu::TextureFormat::Rgba8UnormSrgb,
lod_base,
wgpu::FilterMode::Linear,
); );
// SamplerInfo {
// border: border_color,
let alt = create_texture(
wgpu::TextureFormat::Rgba8Unorm,
lod_alt,
wgpu::FilterMode::Linear,
);
// SamplerInfo {
// border: [0.0, 0.0, 0.0, 0.0].into(),
let horizon = create_texture(
wgpu::TextureFormat::Rgba8Unorm,
lod_horizon,
wgpu::FilterMode::Linear,
);
// SamplerInfo {
// border: [1.0, 0.0, 1.0, 0.0].into(),
Self { Self {
map: renderer map,
.create_texture_immutable_raw( alt,
kind, horizon,
gfx::texture::Mipmap::Provided,
&[gfx::memory::cast_slice(lod_base)],
SamplerInfo {
border: border_color,
..info
},
)
.expect("Failed to generate map texture"),
alt: renderer
.create_texture_immutable_raw(
kind,
gfx::texture::Mipmap::Provided,
&[gfx::memory::cast_slice(lod_alt)],
SamplerInfo {
border: [0.0, 0.0, 0.0, 0.0].into(),
..info
},
)
.expect("Failed to generate alt texture"),
horizon: renderer
.create_texture_immutable_raw(
kind,
gfx::texture::Mipmap::Provided,
&[gfx::memory::cast_slice(lod_horizon)],
SamplerInfo {
border: [1.0, 0.0, 1.0, 0.0].into(),
..info
},
)
.expect("Failed to generate horizon texture"),
tgt_detail, tgt_detail,
} }
} }
} }
pub struct LodTerrainPipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl LodTerrainPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
aa_mode: AaMode,
) -> Self {
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Lod terrain pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &global_layout.shadow_textures],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Lod terrain pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[Vertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::GreaterEqual,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: None,
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
}
}
}

View File

@ -1,4 +1,6 @@
pub mod blit;
pub mod clouds; pub mod clouds;
pub mod debug;
pub mod figure; pub mod figure;
pub mod fluid; pub mod fluid;
pub mod lod_terrain; pub mod lod_terrain;
@ -10,56 +12,70 @@ pub mod sprite;
pub mod terrain; pub mod terrain;
pub mod ui; pub mod ui;
use super::Consts; use super::{Consts, Texture};
use crate::scene::camera::CameraMode; use crate::scene::camera::CameraMode;
use bytemuck::{Pod, Zeroable};
use common::terrain::BlockKind; use common::terrain::BlockKind;
use gfx::{self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta};
use vek::*; use vek::*;
pub const MAX_POINT_LIGHT_COUNT: usize = 31; // TODO: auto insert these into shaders
pub const MAX_POINT_LIGHT_COUNT: usize = 20;
pub const MAX_FIGURE_SHADOW_COUNT: usize = 24; pub const MAX_FIGURE_SHADOW_COUNT: usize = 24;
pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6; pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6;
gfx_defines! { #[repr(C)]
constant Globals { #[derive(Copy, Clone, Debug, Zeroable, Pod)]
view_mat: [[f32; 4]; 4] = "view_mat", pub struct Globals {
proj_mat: [[f32; 4]; 4] = "proj_mat", /// Transformation from world coordinate space (with focus_off as the
all_mat: [[f32; 4]; 4] = "all_mat", /// origin) to the camera space
cam_pos: [f32; 4] = "cam_pos", view_mat: [[f32; 4]; 4],
focus_off: [f32; 4] = "focus_off", proj_mat: [[f32; 4]; 4],
focus_pos: [f32; 4] = "focus_pos", /// proj_mat * view_mat
/// NOTE: view_distance.x is the horizontal view distance, view_distance.y is the LOD all_mat: [[f32; 4]; 4],
/// detail, view_distance.z is the /// Offset of the camera from the focus position
/// minimum height over any land chunk (i.e. the sea level), and view_distance.w is the cam_pos: [f32; 4],
/// maximum height over this minimum height. /// Integer portion of the focus position in world coordinates
focus_off: [f32; 4],
/// Fractions portion of the focus position
focus_pos: [f32; 4],
/// NOTE: view_distance.x is the horizontal view distance, view_distance.y
/// is the LOD detail, view_distance.z is the
/// minimum height over any land chunk (i.e. the sea level), and
/// view_distance.w is the maximum height over this minimum height.
/// ///
/// TODO: Fix whatever alignment issue requires these uniforms to be aligned. /// TODO: Fix whatever alignment issue requires these uniforms to be
view_distance: [f32; 4] = "view_distance", /// aligned.
time_of_day: [f32; 4] = "time_of_day", // TODO: Make this f64. view_distance: [f32; 4],
sun_dir: [f32; 4] = "sun_dir", time_of_day: [f32; 4], // TODO: Make this f64.
moon_dir: [f32; 4] = "moon_dir", sun_dir: [f32; 4],
tick: [f32; 4] = "tick", moon_dir: [f32; 4],
tick: [f32; 4],
/// x, y represent the resolution of the screen; /// x, y represent the resolution of the screen;
/// w, z represent the near and far planes of the shadow map. /// w, z represent the near and far planes of the shadow map.
screen_res: [f32; 4] = "screen_res", screen_res: [f32; 4],
light_shadow_count: [u32; 4] = "light_shadow_count", light_shadow_count: [u32; 4],
shadow_proj_factors: [f32; 4] = "shadow_proj_factors", shadow_proj_factors: [f32; 4],
medium: [u32; 4] = "medium", medium: [u32; 4],
select_pos: [i32; 4] = "select_pos", select_pos: [i32; 4],
gamma_exposure: [f32; 4] = "gamma_exposure", gamma_exposure: [f32; 4],
ambiance: f32 = "ambiance", ambiance: f32,
cam_mode: u32 = "cam_mode", cam_mode: u32,
sprite_render_distance: f32 = "sprite_render_distance", sprite_render_distance: f32,
/// To keep 16-byte-aligned.
globals_dummy: f32,
} }
constant Light { #[repr(C)]
pos: [f32; 4] = "light_pos", #[derive(Copy, Clone, Debug, Zeroable, Pod)]
col: [f32; 4] = "light_col", pub struct Light {
pub pos: [f32; 4],
pub col: [f32; 4],
} }
constant Shadow { #[repr(C)]
pos_radius: [f32; 4] = "shadow_pos_radius", #[derive(Copy, Clone, Debug, Zeroable, Pod)]
} pub struct Shadow {
pos_radius: [f32; 4],
} }
impl Globals { impl Globals {
@ -108,15 +124,16 @@ impl Globals {
shadow_planes.x, shadow_planes.x,
shadow_planes.y, shadow_planes.y,
], ],
// TODO: why do we accept values greater than the max?
light_shadow_count: [ light_shadow_count: [
(light_count % (MAX_POINT_LIGHT_COUNT + 1)) as u32, usize::min(light_count, MAX_POINT_LIGHT_COUNT) as u32,
(shadow_count % (MAX_FIGURE_SHADOW_COUNT + 1)) as u32, usize::min(shadow_count, MAX_FIGURE_SHADOW_COUNT) as u32,
(directed_light_count % (MAX_DIRECTED_LIGHT_COUNT + 1)) as u32, usize::min(directed_light_count, MAX_DIRECTED_LIGHT_COUNT) as u32,
0, 0,
], ],
shadow_proj_factors: [ shadow_proj_factors: [
(shadow_planes.y + shadow_planes.x) / (shadow_planes.y - shadow_planes.x), shadow_planes.y / (shadow_planes.y - shadow_planes.x),
(2.0 * shadow_planes.y * shadow_planes.x) / (shadow_planes.y - shadow_planes.x), shadow_planes.y * shadow_planes.x / (shadow_planes.y - shadow_planes.x),
0.0, 0.0,
0.0, 0.0,
], ],
@ -129,6 +146,7 @@ impl Globals {
ambiance, ambiance,
cam_mode: cam_mode as u32, cam_mode: cam_mode as u32,
sprite_render_distance, sprite_render_distance,
globals_dummy: 0.0,
} }
} }
@ -214,8 +232,388 @@ impl Default for Shadow {
// Global scene data spread across several arrays. // Global scene data spread across several arrays.
pub struct GlobalModel { pub struct GlobalModel {
// TODO: enforce that these are the lengths in the shaders??
pub globals: Consts<Globals>, pub globals: Consts<Globals>,
pub lights: Consts<Light>, pub lights: Consts<Light>,
pub shadows: Consts<Shadow>, pub shadows: Consts<Shadow>,
pub shadow_mats: Consts<shadow::Locals>, pub shadow_mats: shadow::BoundLocals,
pub point_light_matrices: Box<[shadow::PointLightMatrix; 126]>,
}
pub struct GlobalsBindGroup {
pub(super) bind_group: wgpu::BindGroup,
}
pub struct ShadowTexturesBindGroup {
pub(super) bind_group: wgpu::BindGroup,
}
pub struct GlobalsLayouts {
pub globals: wgpu::BindGroupLayout,
pub col_light: wgpu::BindGroupLayout,
pub shadow_textures: wgpu::BindGroupLayout,
}
pub struct ColLights<Locals> {
pub(super) bind_group: wgpu::BindGroup,
pub texture: Texture,
phantom: std::marker::PhantomData<Locals>,
}
impl GlobalsLayouts {
pub fn base_globals_layout() -> Vec<wgpu::BindGroupLayoutEntry> {
vec![
// Global uniform
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
// Noise tex
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// Light uniform
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
// Shadow uniform
wgpu::BindGroupLayoutEntry {
binding: 4,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
// Alt texture
wgpu::BindGroupLayoutEntry {
binding: 5,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 6,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// Horizon texture
wgpu::BindGroupLayoutEntry {
binding: 7,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 8,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// light shadows (ie shadows from a light?)
wgpu::BindGroupLayoutEntry {
binding: 9,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
// TODO: is this relevant?
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
// lod map (t_map)
wgpu::BindGroupLayoutEntry {
binding: 10,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 11,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
]
}
pub fn new(device: &wgpu::Device) -> Self {
let globals = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Globals layout"),
entries: &Self::base_globals_layout(),
});
let col_light = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[
// col lights
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
],
});
let shadow_textures = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[
// point shadow_maps
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Depth,
view_dimension: wgpu::TextureViewDimension::Cube,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: true,
},
count: None,
},
// directed shadow maps
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Depth,
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 3,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: true,
},
count: None,
},
],
});
Self {
globals,
col_light,
shadow_textures,
}
}
// Note: this allocation serves the purpose of not having to duplicate code
pub fn bind_base_globals<'a>(
global_model: &'a GlobalModel,
lod_data: &'a lod_terrain::LodData,
noise: &'a Texture,
) -> Vec<wgpu::BindGroupEntry<'a>> {
vec![
// Global uniform
wgpu::BindGroupEntry {
binding: 0,
resource: global_model.globals.buf().as_entire_binding(),
},
// Noise tex
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::TextureView(&noise.view),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::Sampler(&noise.sampler),
},
// Light uniform
wgpu::BindGroupEntry {
binding: 3,
resource: global_model.lights.buf().as_entire_binding(),
},
// Shadow uniform
wgpu::BindGroupEntry {
binding: 4,
resource: global_model.shadows.buf().as_entire_binding(),
},
// Alt texture
wgpu::BindGroupEntry {
binding: 5,
resource: wgpu::BindingResource::TextureView(&lod_data.alt.view),
},
wgpu::BindGroupEntry {
binding: 6,
resource: wgpu::BindingResource::Sampler(&lod_data.alt.sampler),
},
// Horizon texture
wgpu::BindGroupEntry {
binding: 7,
resource: wgpu::BindingResource::TextureView(&lod_data.horizon.view),
},
wgpu::BindGroupEntry {
binding: 8,
resource: wgpu::BindingResource::Sampler(&lod_data.horizon.sampler),
},
// light shadows
wgpu::BindGroupEntry {
binding: 9,
resource: global_model.shadow_mats.buf().as_entire_binding(),
},
// lod map (t_map)
wgpu::BindGroupEntry {
binding: 10,
resource: wgpu::BindingResource::TextureView(&lod_data.map.view),
},
wgpu::BindGroupEntry {
binding: 11,
resource: wgpu::BindingResource::Sampler(&lod_data.map.sampler),
},
]
}
pub fn bind(
&self,
device: &wgpu::Device,
global_model: &GlobalModel,
lod_data: &lod_terrain::LodData,
noise: &Texture,
) -> GlobalsBindGroup {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.globals,
entries: &Self::bind_base_globals(global_model, lod_data, noise),
});
GlobalsBindGroup { bind_group }
}
pub fn bind_shadow_textures(
&self,
device: &wgpu::Device,
point_shadow_map: &Texture,
directed_shadow_map: &Texture,
) -> ShadowTexturesBindGroup {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.shadow_textures,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&point_shadow_map.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&point_shadow_map.sampler),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::TextureView(&directed_shadow_map.view),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::Sampler(&directed_shadow_map.sampler),
},
],
});
ShadowTexturesBindGroup { bind_group }
}
pub fn bind_col_light<Locals>(
&self,
device: &wgpu::Device,
col_light: Texture,
) -> ColLights<Locals> {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.col_light,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&col_light.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&col_light.sampler),
},
],
});
ColLights {
texture: col_light,
bind_group,
phantom: std::marker::PhantomData,
}
}
} }

View File

@ -1,79 +1,18 @@
use super::{ use super::super::{AaMode, GlobalsLayouts, Vertex as VertexTrait};
super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt}, use bytemuck::{Pod, Zeroable};
shadow, Globals, Light, Shadow, use std::mem;
};
use gfx::{
self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner,
gfx_vertex_struct_meta, state::ColorMask,
};
use vek::*; use vek::*;
gfx_defines! { #[repr(C)]
vertex Vertex { #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pos: [f32; 3] = "v_pos", pub struct Vertex {
pub pos: [f32; 3],
// ____BBBBBBBBGGGGGGGGRRRRRRRR // ____BBBBBBBBGGGGGGGGRRRRRRRR
// col: u32 = "v_col", // col: u32 = "v_col",
// ...AANNN // ...AANNN
// A = AO // A = AO
// N = Normal // N = Normal
norm_ao: u32 = "v_norm_ao", norm_ao: u32,
}
vertex Instance {
// created_at time, so we can calculate time relativity, needed for relative animation.
// can save 32 bits per instance, for particles that are not relatively animated.
inst_time: f32 = "inst_time",
// The lifespan in seconds of the particle
inst_lifespan: f32 = "inst_lifespan",
// a seed value for randomness
// can save 32 bits per instance, for particles that don't need randomness/uniqueness.
inst_entropy: f32 = "inst_entropy",
// modes should probably be seperate shaders, as a part of scaling and optimisation efforts.
// can save 32 bits per instance, and have cleaner tailor made code.
inst_mode: i32 = "inst_mode",
// A direction for particles to move in
inst_dir: [f32; 3] = "inst_dir",
// a triangle is: f32 x 3 x 3 x 1 = 288 bits
// a quad is: f32 x 3 x 3 x 2 = 576 bits
// a cube is: f32 x 3 x 3 x 12 = 3456 bits
// this vec is: f32 x 3 x 1 x 1 = 96 bits (per instance!)
// consider using a throw-away mesh and
// positioning the vertex vertices instead,
// if we have:
// - a triangle mesh, and 3 or more instances.
// - a quad mesh, and 6 or more instances.
// - a cube mesh, and 36 or more instances.
inst_pos: [f32; 3] = "inst_pos",
}
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
ibuf: gfx::InstanceBuffer<Instance> = (),
globals: gfx::ConstantBuffer<Globals> = "u_globals",
lights: gfx::ConstantBuffer<Light> = "u_lights",
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
point_shadow_maps: gfx::TextureSampler<f32> = "t_point_shadow_maps",
directed_shadow_maps: gfx::TextureSampler<f32> = "t_directed_shadow_maps",
alt: gfx::TextureSampler<[f32; 2]> = "t_alt",
horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon",
noise: gfx::TextureSampler<f32> = "t_noise",
// Shadow stuff
light_shadows: gfx::ConstantBuffer<shadow::Locals> = "u_light_shadows",
tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA),
tgt_depth_stencil: gfx::DepthTarget<TgtDepthStencilFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
// tgt_depth_stencil: gfx::DepthStencilTarget<TgtDepthStencilFmt> = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))),
}
} }
impl Vertex { impl Vertex {
@ -92,6 +31,21 @@ impl Vertex {
norm_ao: norm_bits, norm_ao: norm_bits,
} }
} }
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
const ATTRIBUTES: [wgpu::VertexAttribute; 2] =
wgpu::vertex_attr_array![0 => Float32x3, 1 => Uint32];
wgpu::VertexBufferLayout {
array_stride: Self::STRIDE,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &ATTRIBUTES,
}
}
}
impl VertexTrait for Vertex {
const QUADS_INDEX: Option<wgpu::IndexFormat> = Some(wgpu::IndexFormat::Uint16);
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -131,6 +85,40 @@ impl ParticleMode {
pub fn into_uint(self) -> u32 { self as u32 } pub fn into_uint(self) -> u32 { self as u32 }
} }
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Instance {
// created_at time, so we can calculate time relativity, needed for relative animation.
// can save 32 bits per instance, for particles that are not relatively animated.
inst_time: f32,
// The lifespan in seconds of the particle
inst_lifespan: f32,
// a seed value for randomness
// can save 32 bits per instance, for particles that don't need randomness/uniqueness.
inst_entropy: f32,
// modes should probably be seperate shaders, as a part of scaling and optimisation efforts.
// can save 32 bits per instance, and have cleaner tailor made code.
inst_mode: i32,
// A direction for particles to move in
inst_dir: [f32; 3],
// a triangle is: f32 x 3 x 3 x 1 = 288 bits
// a quad is: f32 x 3 x 3 x 2 = 576 bits
// a cube is: f32 x 3 x 3 x 12 = 3456 bits
// this vec is: f32 x 3 x 1 x 1 = 96 bits (per instance!)
// consider using a throw-away mesh and
// positioning the vertex verticies instead,
// if we have:
// - a triangle mesh, and 3 or more instances.
// - a quad mesh, and 6 or more instances.
// - a cube mesh, and 36 or more instances.
inst_pos: [f32; 3],
}
impl Instance { impl Instance {
pub fn new( pub fn new(
inst_time: f64, inst_time: f64,
@ -166,14 +154,111 @@ impl Instance {
inst_dir: (inst_pos2 - inst_pos).into_array(), inst_dir: (inst_pos2 - inst_pos).into_array(),
} }
} }
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
const ATTRIBUTES: [wgpu::VertexAttribute; 6] = wgpu::vertex_attr_array![2 => Float32, 3 => Float32, 4 => Float32, 5 => Sint32, 6 => Float32x3, 7 => Float32x3];
wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Instance,
attributes: &ATTRIBUTES,
}
}
} }
impl Default for Instance { impl Default for Instance {
fn default() -> Self { Self::new(0.0, 0.0, ParticleMode::CampfireSmoke, Vec3::zero()) } fn default() -> Self { Self::new(0.0, 0.0, ParticleMode::CampfireSmoke, Vec3::zero()) }
} }
pub struct ParticlePipeline; pub struct ParticlePipeline {
pub pipeline: wgpu::RenderPipeline,
impl Pipeline for ParticlePipeline { }
type Vertex = Vertex;
impl ParticlePipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
aa_mode: AaMode,
) -> Self {
common_base::span!(_guard, "ParticlePipeline::new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Particle pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &global_layout.shadow_textures],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Particle pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[Vertex::desc(), Instance::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::GreaterEqual,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
// TODO: use a constant and/or pass in this format on pipeline construction
format: wgpu::TextureFormat::Rgba16Float,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
}
}
} }

View File

@ -1,40 +1,12 @@
use super::{ use super::super::{Consts, GlobalsLayouts};
super::{Mesh, Pipeline, TgtColorFmt, TgtDepthStencilFmt, Tri, WinColorFmt}, use bytemuck::{Pod, Zeroable};
Globals,
};
use gfx::{
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
gfx_pipeline_inner, gfx_vertex_struct_meta,
};
use vek::*; use vek::*;
gfx_defines! { #[repr(C)]
vertex Vertex { #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pos: [f32; 2] = "v_pos", pub struct Locals {
} proj_mat_inv: [[f32; 4]; 4],
view_mat_inv: [[f32; 4]; 4],
constant Locals {
proj_mat_inv: [[f32; 4]; 4] = "proj_mat_inv",
view_mat_inv: [[f32; 4]; 4] = "view_mat_inv",
}
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
map: gfx::TextureSampler<[f32; 4]> = "t_map",
alt: gfx::TextureSampler<[f32; 2]> = "t_alt",
horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon",
color_sampler: gfx::TextureSampler<<TgtColorFmt as gfx::format::Formatted>::View> = "src_color",
depth_sampler: gfx::TextureSampler<<TgtDepthStencilFmt as gfx::format::Formatted>::View> = "src_depth",
noise: gfx::TextureSampler<f32> = "t_noise",
tgt_color: gfx::RenderTarget<WinColorFmt> = "tgt_color",
}
} }
impl Default for Locals { impl Default for Locals {
@ -50,28 +22,143 @@ impl Locals {
} }
} }
pub struct PostProcessPipeline; pub struct BindGroup {
pub(in super::super) bind_group: wgpu::BindGroup,
impl Pipeline for PostProcessPipeline {
type Vertex = Vertex;
} }
pub fn create_mesh() -> Mesh<PostProcessPipeline> { pub struct PostProcessLayout {
let mut mesh = Mesh::new(); pub layout: wgpu::BindGroupLayout,
}
#[rustfmt::skip]
mesh.push_tri(Tri::new( impl PostProcessLayout {
Vertex { pos: [ 1.0, -1.0] }, pub fn new(device: &wgpu::Device) -> Self {
Vertex { pos: [-1.0, 1.0] }, Self {
Vertex { pos: [-1.0, -1.0] }, layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
)); label: None,
entries: &[
#[rustfmt::skip] // src color
mesh.push_tri(Tri::new( wgpu::BindGroupLayoutEntry {
Vertex { pos: [1.0, -1.0] }, binding: 0,
Vertex { pos: [1.0, 1.0] }, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
Vertex { pos: [-1.0, 1.0] }, ty: wgpu::BindingType::Texture {
)); sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
mesh multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
// Locals
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
}),
}
}
pub fn bind(
&self,
device: &wgpu::Device,
src_color: &wgpu::TextureView,
sampler: &wgpu::Sampler,
locals: &Consts<Locals>,
) -> BindGroup {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(src_color),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(sampler),
},
wgpu::BindGroupEntry {
binding: 2,
resource: locals.buf().as_entire_binding(),
},
],
});
BindGroup { bind_group }
}
}
pub struct PostProcessPipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl PostProcessPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
sc_desc: &wgpu::SwapChainDescriptor,
global_layout: &GlobalsLayouts,
layout: &PostProcessLayout,
) -> Self {
common_base::span!(_guard, "PostProcessPipeline::new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Post process pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &layout.layout],
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Post process pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: sc_desc.format,
blend: None,
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
}
}
} }

View File

@ -1,54 +1,15 @@
use super::{ use super::super::{
super::{ AaMode, Bound, ColLightInfo, Consts, FigureLayout, GlobalsLayouts, Renderer, TerrainLayout,
ColLightFmt, ColLightInfo, Pipeline, RenderError, Renderer, ShadowDepthStencilFmt, TerrainVertex, Texture,
TerrainLocals, Texture,
},
figure, terrain, Globals,
};
use gfx::{
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
gfx_pipeline_inner,
}; };
use bytemuck::{Pod, Zeroable};
use vek::*; use vek::*;
gfx_defines! { #[repr(C)]
constant Locals { #[derive(Copy, Clone, Debug, Zeroable, Pod)]
shadow_matrices: [[f32; 4]; 4] = "shadowMatrices", pub struct Locals {
texture_mats: [[f32; 4]; 4] = "texture_mat", shadow_matrices: [[f32; 4]; 4],
} texture_mats: [[f32; 4]; 4],
pipeline pipe {
// Terrain vertex stuff
vbuf: gfx::VertexBuffer<terrain::Vertex> = (),
locals: gfx::ConstantBuffer<TerrainLocals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
// Shadow stuff
light_shadows: gfx::ConstantBuffer<Locals> = "u_light_shadows",
tgt_depth_stencil: gfx::DepthTarget<ShadowDepthStencilFmt> = gfx::state::Depth {
fun: gfx::state::Comparison::Less,
write: true,
},
}
pipeline figure_pipe {
// Terrain vertex stuff
vbuf: gfx::VertexBuffer<terrain::Vertex> = (),
locals: gfx::ConstantBuffer<figure::Locals> = "u_locals",
bones: gfx::ConstantBuffer<figure::BoneData> = "u_bones",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
// Shadow stuff
light_shadows: gfx::ConstantBuffer<Locals> = "u_light_shadows",
tgt_depth_stencil: gfx::DepthTarget<ShadowDepthStencilFmt> = gfx::state::Depth {
fun: gfx::state::Comparison::Less,
write: true,
},
}
} }
impl Locals { impl Locals {
@ -62,29 +23,326 @@ impl Locals {
pub fn default() -> Self { Self::new(Mat4::identity(), Mat4::identity()) } pub fn default() -> Self { Self::new(Mat4::identity(), Mat4::identity()) }
} }
pub struct ShadowPipeline; pub type BoundLocals = Bound<Consts<Locals>>;
pub struct ShadowLayout {
pub locals: wgpu::BindGroupLayout,
}
impl ShadowLayout {
pub fn new(device: &wgpu::Device) -> Self {
Self {
locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
}),
}
}
pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts<Locals>) -> BoundLocals {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.locals,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: locals.buf().as_entire_binding(),
}],
});
BoundLocals {
bind_group,
with: locals,
}
}
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct PointLightMatrix([[f32; 4]; 4]);
impl PointLightMatrix {
pub fn new(shadow_mat: Mat4<f32>) -> Self { Self(shadow_mat.into_col_arrays()) }
pub fn default() -> Self { Self::new(Mat4::identity()) }
}
impl ShadowPipeline {
pub fn create_col_lights( pub fn create_col_lights(
renderer: &mut Renderer, renderer: &mut Renderer,
(col_lights, col_lights_size): &ColLightInfo, (col_lights, col_lights_size): &ColLightInfo,
) -> Result<Texture<ColLightFmt>, RenderError> { ) -> Texture {
renderer.create_texture_immutable_raw( let texture_info = wgpu::TextureDescriptor {
gfx::texture::Kind::D2( label: None,
col_lights_size.x, size: wgpu::Extent3d {
col_lights_size.y, width: u32::from(col_lights_size.x),
gfx::texture::AaMode::Single, height: u32::from(col_lights_size.y),
), depth_or_array_layers: 1,
gfx::texture::Mipmap::Provided, },
&[col_lights], mip_level_count: 1,
gfx::texture::SamplerInfo::new( sample_count: 1,
gfx::texture::FilterMethod::Bilinear, dimension: wgpu::TextureDimension::D2,
gfx::texture::WrapMode::Clamp, format: wgpu::TextureFormat::Rgba8Unorm,
), usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,
};
let sampler_info = wgpu::SamplerDescriptor {
label: None,
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Nearest,
border_color: Some(wgpu::SamplerBorderColor::TransparentBlack),
..Default::default()
};
let view_info = wgpu::TextureViewDescriptor {
label: None,
format: Some(wgpu::TextureFormat::Rgba8Unorm),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
};
renderer.create_texture_with_data_raw(
&texture_info,
&view_info,
&sampler_info,
bytemuck::cast_slice(&col_lights),
) )
} }
pub struct ShadowFigurePipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl ShadowFigurePipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
figure_layout: &FigureLayout,
aa_mode: AaMode,
) -> Self {
common_base::span!(_guard, "new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Directed figure shadow pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &figure_layout.locals],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Directed shadow figure pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[TerrainVertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Front),
clamp_depth: true,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24Plus,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: None,
});
Self {
pipeline: render_pipeline,
}
}
} }
impl Pipeline for ShadowPipeline { pub struct ShadowPipeline {
type Vertex = terrain::Vertex; pub pipeline: wgpu::RenderPipeline,
}
impl ShadowPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
terrain_layout: &TerrainLayout,
aa_mode: AaMode,
) -> Self {
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Directed shadow pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &terrain_layout.locals],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Directed shadow pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[TerrainVertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Front),
clamp_depth: true,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24Plus,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: None,
});
Self {
pipeline: render_pipeline,
}
}
}
pub struct PointShadowPipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl PointShadowPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
terrain_layout: &TerrainLayout,
aa_mode: AaMode,
) -> Self {
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Point shadow pipeline layout"),
push_constant_ranges: &[wgpu::PushConstantRange {
stages: wgpu::ShaderStage::all(),
range: 0..64,
}],
bind_group_layouts: &[&global_layout.globals, &terrain_layout.locals],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Point shadow pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[TerrainVertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth24Plus,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: None,
});
Self {
pipeline: render_pipeline,
}
}
} }

View File

@ -1,53 +1,120 @@
use super::{ use super::super::{AaMode, GlobalsLayouts, Mesh, Quad, Vertex as VertexTrait};
super::{Mesh, Pipeline, Quad, TgtColorFmt, TgtDepthStencilFmt}, use bytemuck::{Pod, Zeroable};
Globals, use std::mem;
};
use gfx::{ #[repr(C)]
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, #[derive(Copy, Clone, Debug, Zeroable, Pod)]
gfx_pipeline_inner, gfx_vertex_struct_meta, pub struct Vertex {
pub pos: [f32; 3],
}
impl Vertex {
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
wgpu::VertexBufferLayout {
array_stride: Self::STRIDE,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &[wgpu::VertexAttribute {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
}],
}
}
}
impl VertexTrait for Vertex {
const QUADS_INDEX: Option<wgpu::IndexFormat> = None;
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
}
// TODO: does skybox still do anything with new cloud shaders?
pub struct SkyboxPipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl SkyboxPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
layouts: &GlobalsLayouts,
aa_mode: AaMode,
) -> Self {
common_base::span!(_guard, "SkyboxPipeline::new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Skybox pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&layouts.globals, &layouts.shadow_textures],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
}; };
gfx_defines! { let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
vertex Vertex { label: Some("Skybox pipeline"),
pos: [f32; 3] = "v_pos", layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[Vertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::GreaterEqual,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: None,
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
} }
constant Locals {
nul: [f32; 4] = "nul",
}
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
alt: gfx::TextureSampler<[f32; 2]> = "t_alt",
horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon",
noise: gfx::TextureSampler<f32> = "t_noise",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
tgt_depth_stencil: gfx::DepthTarget<TgtDepthStencilFmt> = gfx::preset::depth::LESS_EQUAL_TEST,
// tgt_depth_stencil: gfx::DepthStencilTarget<TgtDepthStencilFmt> = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))),
} }
} }
impl Locals { #[rustfmt::skip]
pub fn default() -> Self { Self { nul: [0.0; 4] } } pub fn create_mesh() -> Mesh<Vertex> {
}
pub struct SkyboxPipeline;
impl Pipeline for SkyboxPipeline {
type Vertex = Vertex;
}
pub fn create_mesh() -> Mesh<SkyboxPipeline> {
let mut mesh = Mesh::new(); let mut mesh = Mesh::new();
// -x // -x
#[rustfmt::skip]
mesh.push_quad(Quad::new( mesh.push_quad(Quad::new(
Vertex { pos: [-1.0, -1.0, -1.0] }, Vertex { pos: [-1.0, -1.0, -1.0] },
Vertex { pos: [-1.0, 1.0, -1.0] }, Vertex { pos: [-1.0, 1.0, -1.0] },
@ -55,7 +122,6 @@ pub fn create_mesh() -> Mesh<SkyboxPipeline> {
Vertex { pos: [-1.0, -1.0, 1.0] }, Vertex { pos: [-1.0, -1.0, 1.0] },
)); ));
// +x // +x
#[rustfmt::skip]
mesh.push_quad(Quad::new( mesh.push_quad(Quad::new(
Vertex { pos: [ 1.0, -1.0, 1.0] }, Vertex { pos: [ 1.0, -1.0, 1.0] },
Vertex { pos: [ 1.0, 1.0, 1.0] }, Vertex { pos: [ 1.0, 1.0, 1.0] },
@ -63,7 +129,6 @@ pub fn create_mesh() -> Mesh<SkyboxPipeline> {
Vertex { pos: [ 1.0, -1.0, -1.0] }, Vertex { pos: [ 1.0, -1.0, -1.0] },
)); ));
// -y // -y
#[rustfmt::skip]
mesh.push_quad(Quad::new( mesh.push_quad(Quad::new(
Vertex { pos: [ 1.0, -1.0, -1.0] }, Vertex { pos: [ 1.0, -1.0, -1.0] },
Vertex { pos: [-1.0, -1.0, -1.0] }, Vertex { pos: [-1.0, -1.0, -1.0] },
@ -71,7 +136,6 @@ pub fn create_mesh() -> Mesh<SkyboxPipeline> {
Vertex { pos: [ 1.0, -1.0, 1.0] }, Vertex { pos: [ 1.0, -1.0, 1.0] },
)); ));
// +y // +y
#[rustfmt::skip]
mesh.push_quad(Quad::new( mesh.push_quad(Quad::new(
Vertex { pos: [ 1.0, 1.0, 1.0] }, Vertex { pos: [ 1.0, 1.0, 1.0] },
Vertex { pos: [-1.0, 1.0, 1.0] }, Vertex { pos: [-1.0, 1.0, 1.0] },
@ -79,7 +143,6 @@ pub fn create_mesh() -> Mesh<SkyboxPipeline> {
Vertex { pos: [ 1.0, 1.0, -1.0] }, Vertex { pos: [ 1.0, 1.0, -1.0] },
)); ));
// -z // -z
#[rustfmt::skip]
mesh.push_quad(Quad::new( mesh.push_quad(Quad::new(
Vertex { pos: [-1.0, -1.0, -1.0] }, Vertex { pos: [-1.0, -1.0, -1.0] },
Vertex { pos: [ 1.0, -1.0, -1.0] }, Vertex { pos: [ 1.0, -1.0, -1.0] },
@ -87,7 +150,6 @@ pub fn create_mesh() -> Mesh<SkyboxPipeline> {
Vertex { pos: [-1.0, 1.0, -1.0] }, Vertex { pos: [-1.0, 1.0, -1.0] },
)); ));
// +z // +z
#[rustfmt::skip]
mesh.push_quad(Quad::new( mesh.push_quad(Quad::new(
Vertex { pos: [-1.0, 1.0, 1.0] }, Vertex { pos: [-1.0, 1.0, 1.0] },
Vertex { pos: [ 1.0, 1.0, 1.0] }, Vertex { pos: [ 1.0, 1.0, 1.0] },

View File

@ -1,98 +1,49 @@
use super::{ use super::{
super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt}, super::{
shadow, terrain, Globals, Light, Shadow, buffer::Buffer, AaMode, GlobalsLayouts, Mesh, TerrainLayout, Texture, Vertex as VertexTrait,
}; },
use core::fmt; lod_terrain, GlobalModel,
use gfx::{
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
gfx_pipeline_inner, gfx_vertex_struct_meta, state::ColorMask,
}; };
use bytemuck::{Pod, Zeroable};
use std::mem;
use vek::*; use vek::*;
gfx_defines! { pub const VERT_PAGE_SIZE: u32 = 256;
vertex Vertex {
pos: [f32; 3] = "v_pos", #[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos_norm: u32,
// Because we try to restrict terrain sprite data to a 128×128 block // Because we try to restrict terrain sprite data to a 128×128 block
// we need an offset into the texture atlas. // we need an offset into the texture atlas.
atlas_pos: u32 = "v_atlas_pos", atlas_pos: u32,
// ____BBBBBBBBGGGGGGGGRRRRRRRR /* ____BBBBBBBBGGGGGGGGRRRRRRRR
// col: u32 = "v_col", * col: u32 = "v_col",
// ...AANNN * .....NNN
// A = AO * A = AO
// N = Normal * N = Normal
norm_ao: u32 = "v_norm_ao", *norm: u32, */
} }
constant Locals { // TODO: fix?
// Each matrix performs rotatation, translation, and scaling, relative to the sprite /*impl fmt::Display for Vertex {
// origin, for all sprite instances. The matrix will be in an array indexed by the
// sprite instance's orientation (0 through 7).
mat: [[f32; 4]; 4] = "mat",
wind_sway: [f32; 4] = "wind_sway",
offs: [f32; 4] = "offs",
}
vertex/*constant*/ Instance {
// Terrain block position and orientation
pos_ori: u32 = "inst_pos_ori",
inst_mat0: [f32; 4] = "inst_mat0",
inst_mat1: [f32; 4] = "inst_mat1",
inst_mat2: [f32; 4] = "inst_mat2",
inst_mat3: [f32; 4] = "inst_mat3",
inst_light: [f32; 4] = "inst_light",
inst_wind_sway: f32 = "inst_wind_sway",
}
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
ibuf: gfx::InstanceBuffer<Instance> = (),
col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light",
locals: gfx::ConstantBuffer<Locals> = "u_locals",
// A sprite instance is a cross between a sprite and a terrain chunk.
terrain_locals: gfx::ConstantBuffer<terrain::Locals> = "u_terrain_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
lights: gfx::ConstantBuffer<Light> = "u_lights",
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
point_shadow_maps: gfx::TextureSampler<f32> = "t_point_shadow_maps",
directed_shadow_maps: gfx::TextureSampler<f32> = "t_directed_shadow_maps",
alt: gfx::TextureSampler<[f32; 2]> = "t_alt",
horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon",
noise: gfx::TextureSampler<f32> = "t_noise",
// Shadow stuff
light_shadows: gfx::ConstantBuffer<shadow::Locals> = "u_light_shadows",
tgt_color: gfx::BlendTarget<TgtColorFmt> = ("tgt_color", ColorMask::all(), gfx::preset::blend::ALPHA),
tgt_depth_stencil: gfx::DepthTarget<TgtDepthStencilFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
// tgt_depth_stencil: gfx::DepthStencilTarget<TgtDepthStencilFmt> = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))),
}
}
impl fmt::Display for Vertex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Vertex") f.debug_struct("Vertex")
.field("pos", &Vec3::<f32>::from(self.pos)) .field("pos_norm", &Vec3::<f32>::from(self.pos))
.field( .field(
"atlas_pos", "atlas_pos",
&Vec2::new(self.atlas_pos & 0xFFFF, (self.atlas_pos >> 16) & 0xFFFF), &Vec2::new(self.atlas_pos & 0xFFFF, (self.atlas_pos >> 16) & 0xFFFF),
) )
.field("norm_ao", &self.norm_ao)
.finish() .finish()
} }
} }*/
impl Vertex { impl Vertex {
// NOTE: Limit to 16 (x) × 16 (y) × 32 (z). // NOTE: Limit to 16 (x) × 16 (y) × 32 (z).
#[allow(clippy::collapsible_else_if)] #[allow(clippy::collapsible_else_if)]
pub fn new( pub fn new(atlas_pos: Vec2<u16>, pos: Vec3<f32>, norm: Vec3<f32>) -> Self {
atlas_pos: Vec2<u16>, const VERT_EXTRA_NEG_Z: i32 = 128; // NOTE: change if number of bits changes below, also we might not need this if meshing always produces positives values for sprites (I have no idea)
pos: Vec3<f32>,
norm: Vec3<f32>, /* , col: Rgb<f32>, ao: f32 */
) -> Self {
let norm_bits = if norm.x != 0.0 { let norm_bits = if norm.x != 0.0 {
if norm.x < 0.0 { 0 } else { 1 } if norm.x < 0.0 { 0 } else { 1 }
} else if norm.y != 0.0 { } else if norm.y != 0.0 {
@ -107,60 +58,287 @@ impl Vertex {
// | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12 // | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12
// | if meta { 1 } else { 0 } << 28 // | if meta { 1 } else { 0 } << 28
// | (norm_bits & 0x7) << 29, // | (norm_bits & 0x7) << 29,
pos: pos.into_array(), pos_norm: ((pos.x as u32) & 0x00FF) // NOTE: temp hack, this doesn't need 8 bits
| ((pos.y as u32) & 0x00FF) << 8
| (((pos.z as i32 + VERT_EXTRA_NEG_Z).max(0).min(1 << 12) as u32) & 0x0FFF) << 16
| (norm_bits & 0x7) << 29,
atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) | ((atlas_pos.y as u32) & 0xFFFF) << 16, atlas_pos: ((atlas_pos.x as u32) & 0xFFFF) | ((atlas_pos.y as u32) & 0xFFFF) << 16,
norm_ao: norm_bits,
} }
} }
} }
impl Default for Vertex {
fn default() -> Self { Self::new(Vec2::zero(), Vec3::zero(), Vec3::zero()) }
}
impl VertexTrait for Vertex {
const QUADS_INDEX: Option<wgpu::IndexFormat> = Some(wgpu::IndexFormat::Uint16);
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
}
pub struct SpriteVerts(Buffer<Vertex>);
//pub struct SpriteVerts(Texture);
pub(in super::super) fn create_verts_buffer(
device: &wgpu::Device,
mesh: Mesh<Vertex>,
) -> SpriteVerts {
// TODO: type Buffer by wgpu::BufferUsage
SpriteVerts(Buffer::new(
&device,
wgpu::BufferUsage::STORAGE,
mesh.vertices(),
))
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Instance {
inst_mat0: [f32; 4],
inst_mat1: [f32; 4],
inst_mat2: [f32; 4],
inst_mat3: [f32; 4],
pos_ori: u32,
inst_vert_page: u32,
inst_light: f32,
inst_glow: f32,
model_wind_sway: f32,
model_z_scale: f32,
}
impl Instance { impl Instance {
pub fn new( pub fn new(
mat: Mat4<f32>, mat: Mat4<f32>,
wind_sway: f32, wind_sway: f32,
z_scale: f32,
pos: Vec3<i32>, pos: Vec3<i32>,
ori_bits: u8, ori_bits: u8,
light: f32, light: f32,
glow: f32, glow: f32,
vert_page: u32,
) -> Self { ) -> Self {
const EXTRA_NEG_Z: i32 = 32768; const EXTRA_NEG_Z: i32 = 32768;
let mat_arr = mat.into_col_arrays(); let mat_arr = mat.into_col_arrays();
Self { Self {
pos_ori: ((pos.x as u32) & 0x003F)
| ((pos.y as u32) & 0x003F) << 6
| (((pos + EXTRA_NEG_Z).z.max(0).min(1 << 16) as u32) & 0xFFFF) << 12
| (u32::from(ori_bits) & 0x7) << 29,
inst_mat0: mat_arr[0], inst_mat0: mat_arr[0],
inst_mat1: mat_arr[1], inst_mat1: mat_arr[1],
inst_mat2: mat_arr[2], inst_mat2: mat_arr[2],
inst_mat3: mat_arr[3], inst_mat3: mat_arr[3],
inst_light: [light, glow, 1.0, 1.0], pos_ori: ((pos.x as u32) & 0x003F)
inst_wind_sway: wind_sway, | ((pos.y as u32) & 0x003F) << 6
| (((pos.z + EXTRA_NEG_Z).max(0).min(1 << 16) as u32) & 0xFFFF) << 12
| (u32::from(ori_bits) & 0x7) << 29,
inst_vert_page: vert_page,
inst_light: light,
inst_glow: glow,
model_wind_sway: wind_sway,
model_z_scale: z_scale,
}
}
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
const ATTRIBUTES: [wgpu::VertexAttribute; 10] = wgpu::vertex_attr_array![
0 => Float32x4,
1 => Float32x4,
2 => Float32x4,
3 => Float32x4,
4 => Uint32,
5 => Uint32,
6 => Float32,
7 => Float32,
8 => Float32,
9 => Float32,
];
wgpu::VertexBufferLayout {
array_stride: mem::size_of::<Self>() as wgpu::BufferAddress,
step_mode: wgpu::InputStepMode::Instance,
attributes: &ATTRIBUTES,
} }
} }
} }
impl Default for Instance { impl Default for Instance {
fn default() -> Self { Self::new(Mat4::identity(), 0.0, Vec3::zero(), 0, 1.0, 0.0) } fn default() -> Self { Self::new(Mat4::identity(), 0.0, 0.0, Vec3::zero(), 0, 1.0, 0.0, 0) }
} }
impl Default for Locals { // TODO: ColLightsWrapper instead?
fn default() -> Self { Self::new(Mat4::identity(), Vec3::one(), Vec3::zero(), 0.0) } pub struct Locals;
pub struct SpriteGlobalsBindGroup {
pub(in super::super) bind_group: wgpu::BindGroup,
} }
impl Locals { pub struct SpriteLayout {
pub fn new(mat: Mat4<f32>, scale: Vec3<f32>, offs: Vec3<f32>, wind_sway: f32) -> Self { pub globals: wgpu::BindGroupLayout,
}
impl SpriteLayout {
pub fn new(device: &wgpu::Device) -> Self {
let mut entries = GlobalsLayouts::base_globals_layout();
debug_assert_eq!(12, entries.len()); // To remember to adjust the bindings below
entries.extend_from_slice(&[
// sprite_verts
wgpu::BindGroupLayoutEntry {
binding: 12,
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: core::num::NonZeroU64::new(
core::mem::size_of::<Vertex>() as u64
),
},
count: None,
},
]);
Self { Self {
mat: mat.into_col_arrays(), globals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
wind_sway: [scale.x, scale.y, scale.z, wind_sway], label: None,
offs: [offs.x, offs.y, offs.z, 0.0], entries: &entries,
} }),
} }
} }
pub struct SpritePipeline; fn bind_globals_inner(
&self,
device: &wgpu::Device,
global_model: &GlobalModel,
lod_data: &lod_terrain::LodData,
noise: &Texture,
sprite_verts: &SpriteVerts,
) -> wgpu::BindGroup {
let mut entries = GlobalsLayouts::bind_base_globals(global_model, lod_data, noise);
impl Pipeline for SpritePipeline { entries.extend_from_slice(&[
type Vertex = Vertex; // sprite_verts
wgpu::BindGroupEntry {
binding: 12,
resource: sprite_verts.0.buf.as_entire_binding(),
},
]);
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.globals,
entries: &entries,
})
}
pub fn bind_globals(
&self,
device: &wgpu::Device,
global_model: &GlobalModel,
lod_data: &lod_terrain::LodData,
noise: &Texture,
sprite_verts: &SpriteVerts,
) -> SpriteGlobalsBindGroup {
let bind_group =
self.bind_globals_inner(device, global_model, lod_data, noise, sprite_verts);
SpriteGlobalsBindGroup { bind_group }
}
}
pub struct SpritePipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl SpritePipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
layout: &SpriteLayout,
terrain_layout: &TerrainLayout,
aa_mode: AaMode,
) -> Self {
common_base::span!(_guard, "SpritePipeline::new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Sprite pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[
&layout.globals,
&global_layout.shadow_textures,
&terrain_layout.locals,
// Note: mergable with globals
&global_layout.col_light,
],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Sprite pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[Instance::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::GreaterEqual,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
// TODO: can we remove sprite transparency?
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
}
}
} }

View File

@ -1,49 +1,13 @@
use super::{ use super::super::{AaMode, Bound, Consts, GlobalsLayouts, Vertex as VertexTrait};
super::{ColLightFmt, Pipeline, TgtColorFmt, TgtDepthStencilFmt}, use bytemuck::{Pod, Zeroable};
shadow, Globals, Light, Shadow, use std::mem;
};
use gfx::{
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
gfx_pipeline_inner, gfx_vertex_struct_meta,
};
use vek::*; use vek::*;
gfx_defines! { #[repr(C)]
vertex Vertex { #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pos_norm: u32 = "v_pos_norm", pub struct Vertex {
atlas_pos: u32 = "v_atlas_pos", pos_norm: u32,
} atlas_pos: u32,
constant Locals {
model_offs: [f32; 3] = "model_offs",
load_time: f32 = "load_time",
atlas_offs: [i32; 4] = "atlas_offs",
}
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
col_lights: gfx::TextureSampler<[f32; 4]> = "t_col_light",
locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
lights: gfx::ConstantBuffer<Light> = "u_lights",
shadows: gfx::ConstantBuffer<Shadow> = "u_shadows",
point_shadow_maps: gfx::TextureSampler<f32> = "t_point_shadow_maps",
directed_shadow_maps: gfx::TextureSampler<f32> = "t_directed_shadow_maps",
alt: gfx::TextureSampler<[f32; 2]> = "t_alt",
horizon: gfx::TextureSampler<[f32; 4]> = "t_horizon",
noise: gfx::TextureSampler<f32> = "t_noise",
// Shadow stuff
light_shadows: gfx::ConstantBuffer<shadow::Locals> = "u_light_shadows",
tgt_color: gfx::RenderTarget<TgtColorFmt> = "tgt_color",
tgt_depth_stencil: gfx::DepthTarget<TgtDepthStencilFmt> = gfx::preset::depth::LESS_EQUAL_WRITE,
// tgt_depth_stencil: gfx::DepthStencilTarget<TgtDepthStencilFmt> = (gfx::preset::depth::LESS_EQUAL_WRITE,Stencil::new(Comparison::Always,0xff,(StencilOp::Keep,StencilOp::Keep,StencilOp::Keep))),
}
} }
impl Vertex { impl Vertex {
@ -104,8 +68,7 @@ impl Vertex {
// 0 to 31 // 0 to 31
glow: u8, glow: u8,
col: Rgb<u8>, col: Rgb<u8>,
) -> <<ColLightFmt as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType ) -> [u8; 4] {
{
//[col.r, col.g, col.b, light] //[col.r, col.g, col.b, light]
// It would be nice for this to be cleaner, but we want to squeeze 5 fields into // It would be nice for this to be cleaner, but we want to squeeze 5 fields into
// 4. We can do this because both `light` and `glow` go from 0 to 31, // 4. We can do this because both `light` and `glow` go from 0 to 31,
@ -141,8 +104,7 @@ impl Vertex {
glowy: bool, glowy: bool,
shiny: bool, shiny: bool,
col: Rgb<u8>, col: Rgb<u8>,
) -> <<ColLightFmt as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType ) -> [u8; 4] {
{
let attr = 0 | ((glowy as u8) << 0) | ((shiny as u8) << 1); let attr = 0 | ((glowy as u8) << 0) | ((shiny as u8) << 1);
[ [
(light.min(31) << 3) | ((col.r >> 1) & 0b111), (light.min(31) << 3) | ((col.r >> 1) & 0b111),
@ -151,9 +113,48 @@ impl Vertex {
col.g, // Green is lucky, it remains unscathed col.g, // Green is lucky, it remains unscathed
] ]
} }
/// Set the bone_idx for an existing figure vertex.
pub fn set_bone_idx(&mut self, bone_idx: u8) {
self.pos_norm = (self.pos_norm & !(0xF << 27)) | ((bone_idx as u32 & 0xF) << 27);
}
pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
const ATTRIBUTES: [wgpu::VertexAttribute; 2] =
wgpu::vertex_attr_array![0 => Uint32,1 => Uint32];
wgpu::VertexBufferLayout {
array_stride: Self::STRIDE,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &ATTRIBUTES,
}
}
}
impl VertexTrait for Vertex {
// Note: I think it's u32 due to figures??
// potentiall optimize by splitting
const QUADS_INDEX: Option<wgpu::IndexFormat> = Some(wgpu::IndexFormat::Uint32);
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
// TODO: new function and private fields??
pub struct Locals {
model_offs: [f32; 3],
load_time: f32,
atlas_offs: [i32; 4],
} }
impl Locals { impl Locals {
pub fn new(model_offs: Vec3<f32>, atlas_offs: Vec2<u32>, load_time: f32) -> Self {
Self {
model_offs: model_offs.into_array(),
load_time,
atlas_offs: Vec4::new(atlas_offs.x as i32, atlas_offs.y as i32, 0, 0).into_array(),
}
}
pub fn default() -> Self { pub fn default() -> Self {
Self { Self {
model_offs: [0.0; 3], model_offs: [0.0; 3],
@ -163,8 +164,135 @@ impl Locals {
} }
} }
pub struct TerrainPipeline; pub type BoundLocals = Bound<Consts<Locals>>;
impl Pipeline for TerrainPipeline { pub struct TerrainLayout {
type Vertex = Vertex; pub locals: wgpu::BindGroupLayout,
}
impl TerrainLayout {
pub fn new(device: &wgpu::Device) -> Self {
Self {
locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[
// locals
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
}),
}
}
pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts<Locals>) -> BoundLocals {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.locals,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: locals.buf().as_entire_binding(),
}],
});
BoundLocals {
bind_group,
with: locals,
}
}
}
pub struct TerrainPipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl TerrainPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
global_layout: &GlobalsLayouts,
layout: &TerrainLayout,
aa_mode: AaMode,
) -> Self {
common_base::span!(_guard, "TerrainPipeline::new");
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Terrain pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[
&global_layout.globals,
&global_layout.shadow_textures,
&layout.locals,
&global_layout.col_light,
],
});
let samples = match aa_mode {
AaMode::None | AaMode::Fxaa => 1,
AaMode::MsaaX4 => 4,
AaMode::MsaaX8 => 8,
AaMode::MsaaX16 => 16,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Terrain pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[Vertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::GreaterEqual,
stencil: wgpu::StencilState {
front: wgpu::StencilFaceState::IGNORE,
back: wgpu::StencilFaceState::IGNORE,
read_mask: !0,
write_mask: !0,
},
bias: wgpu::DepthBiasState {
constant: 0,
slope_scale: 0.0,
clamp: 0.0,
},
}),
multisample: wgpu::MultisampleState {
count: samples,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Rgba16Float,
blend: None,
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
}
}
} }

View File

@ -1,41 +1,38 @@
use super::super::{Globals, Pipeline, Quad, Tri, WinColorFmt, WinDepthFmt}; use super::super::{Bound, Consts, GlobalsLayouts, Quad, Texture, Tri, Vertex as VertexTrait};
use gfx::{ use bytemuck::{Pod, Zeroable};
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, use std::mem;
gfx_pipeline_inner, gfx_vertex_struct_meta,
};
use vek::*; use vek::*;
gfx_defines! { #[repr(C)]
vertex Vertex { #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pos: [f32; 2] = "v_pos", pub struct Vertex {
uv: [f32; 2] = "v_uv", pos: [f32; 2],
color: [f32; 4] = "v_color", uv: [f32; 2],
center: [f32; 2] = "v_center", color: [f32; 4],
mode: u32 = "v_mode", center: [f32; 2],
mode: u32,
} }
constant Locals { impl Vertex {
pos: [f32; 4] = "w_pos", fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
const ATTRIBUTES: [wgpu::VertexAttribute; 5] = wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Float32x4, 3 => Float32x2, 4 => Uint32];
wgpu::VertexBufferLayout {
array_stride: Self::STRIDE,
step_mode: wgpu::InputStepMode::Vertex,
attributes: &ATTRIBUTES,
} }
pipeline pipe {
vbuf: gfx::VertexBuffer<Vertex> = (),
locals: gfx::ConstantBuffer<Locals> = "u_locals",
globals: gfx::ConstantBuffer<Globals> = "u_globals",
tex: gfx::TextureSampler<[f32; 4]> = "u_tex",
scissor: gfx::Scissor = (),
tgt_color: gfx::BlendTarget<WinColorFmt> = ("tgt_color", gfx::state::ColorMask::all(), gfx::preset::blend::ALPHA),
tgt_depth: gfx::DepthTarget<WinDepthFmt> = gfx::preset::depth::LESS_EQUAL_TEST,
} }
} }
pub struct UiPipeline; impl VertexTrait for Vertex {
const QUADS_INDEX: Option<wgpu::IndexFormat> = None;
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
}
impl Pipeline for UiPipeline { #[repr(C)]
type Vertex = Vertex; #[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
pos: [f32; 4],
} }
impl From<Vec4<f32>> for Locals { impl From<Vec4<f32>> for Locals {
@ -87,12 +84,177 @@ impl Mode {
} }
} }
pub type BoundLocals = Bound<Consts<Locals>>;
pub struct TextureBindGroup {
pub(in super::super) bind_group: wgpu::BindGroup,
}
pub struct UiLayout {
pub locals: wgpu::BindGroupLayout,
pub texture: wgpu::BindGroupLayout,
}
impl UiLayout {
pub fn new(device: &wgpu::Device) -> Self {
Self {
locals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[
// locals
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
}),
texture: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[
// texture
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: true,
comparison: false,
},
count: None,
},
],
}),
}
}
pub fn bind_locals(&self, device: &wgpu::Device, locals: Consts<Locals>) -> BoundLocals {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.locals,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: locals.buf().as_entire_binding(),
}],
});
BoundLocals {
bind_group,
with: locals,
}
}
pub fn bind_texture(&self, device: &wgpu::Device, texture: &Texture) -> TextureBindGroup {
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.texture,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&texture.view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&texture.sampler),
},
],
});
TextureBindGroup { bind_group }
}
}
pub struct UiPipeline {
pub pipeline: wgpu::RenderPipeline,
}
impl UiPipeline {
pub fn new(
device: &wgpu::Device,
vs_module: &wgpu::ShaderModule,
fs_module: &wgpu::ShaderModule,
sc_desc: &wgpu::SwapChainDescriptor,
global_layout: &GlobalsLayouts,
layout: &UiLayout,
) -> Self {
let render_pipeline_layout =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Ui pipeline layout"),
push_constant_ranges: &[],
bind_group_layouts: &[&global_layout.globals, &layout.locals, &layout.texture],
});
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("UI pipeline"),
layout: Some(&render_pipeline_layout),
vertex: wgpu::VertexState {
module: vs_module,
entry_point: "main",
buffers: &[Vertex::desc()],
},
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(wgpu::Face::Back),
clamp_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
fragment: Some(wgpu::FragmentState {
module: fs_module,
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: sc_desc.format,
blend: Some(wgpu::BlendState {
color: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::SrcAlpha,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
operation: wgpu::BlendOperation::Add,
},
alpha: wgpu::BlendComponent {
src_factor: wgpu::BlendFactor::One,
dst_factor: wgpu::BlendFactor::One,
operation: wgpu::BlendOperation::Add,
},
}),
write_mask: wgpu::ColorWrite::ALL,
}],
}),
});
Self {
pipeline: render_pipeline,
}
}
}
pub fn create_quad( pub fn create_quad(
rect: Aabr<f32>, rect: Aabr<f32>,
uv_rect: Aabr<f32>, uv_rect: Aabr<f32>,
color: Rgba<f32>, color: Rgba<f32>,
mode: Mode, mode: Mode,
) -> Quad<UiPipeline> { ) -> Quad<Vertex> {
create_quad_vert_gradient(rect, uv_rect, color, color, mode) create_quad_vert_gradient(rect, uv_rect, color, color, mode)
} }
@ -103,7 +265,7 @@ pub fn create_quad_vert_gradient(
top_color: Rgba<f32>, top_color: Rgba<f32>,
bottom_color: Rgba<f32>, bottom_color: Rgba<f32>,
mode: Mode, mode: Mode,
) -> Quad<UiPipeline> { ) -> Quad<Vertex> {
let top_color = top_color.into_array(); let top_color = top_color.into_array();
let bottom_color = bottom_color.into_array(); let bottom_color = bottom_color.into_array();
@ -152,7 +314,7 @@ pub fn create_tri(
uv_tri: [[f32; 2]; 3], uv_tri: [[f32; 2]; 3],
color: Rgba<f32>, color: Rgba<f32>,
mode: Mode, mode: Mode,
) -> Tri<UiPipeline> { ) -> Tri<Vertex> {
let center = [0.0, 0.0]; let center = [0.0, 0.0];
let mode_val = mode.value(); let mode_val = mode.value();
let v = |pos, uv| Vertex { let v = |pos, uv| Vertex {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
use super::{
super::{
pipelines::{
debug, figure, lod_terrain, shadow, sprite, terrain, ui, ColLights, GlobalModel,
GlobalsBindGroup,
},
texture::Texture,
},
Renderer,
};
impl Renderer {
pub fn bind_globals(
&self,
global_model: &GlobalModel,
lod_data: &lod_terrain::LodData,
) -> GlobalsBindGroup {
self.layouts
.global
.bind(&self.device, global_model, lod_data, &self.noise_tex)
}
pub fn bind_sprite_globals(
&self,
global_model: &GlobalModel,
lod_data: &lod_terrain::LodData,
sprite_verts: &sprite::SpriteVerts,
) -> sprite::SpriteGlobalsBindGroup {
self.layouts.sprite.bind_globals(
&self.device,
global_model,
lod_data,
&self.noise_tex,
sprite_verts,
)
}
pub fn create_debug_bound_locals(&mut self, vals: &[debug::Locals]) -> debug::BoundLocals {
let locals = self.create_consts(vals);
self.layouts.debug.bind_locals(&self.device, locals)
}
pub fn create_ui_bound_locals(&mut self, vals: &[ui::Locals]) -> ui::BoundLocals {
let locals = self.create_consts(vals);
self.layouts.ui.bind_locals(&self.device, locals)
}
pub fn ui_bind_texture(&self, texture: &Texture) -> ui::TextureBindGroup {
self.layouts.ui.bind_texture(&self.device, texture)
}
pub fn create_figure_bound_locals(
&mut self,
locals: &[figure::Locals],
bone_data: &[figure::BoneData],
) -> figure::BoundLocals {
let locals = self.create_consts(locals);
let bone_data = self.create_consts(bone_data);
self.layouts
.figure
.bind_locals(&self.device, locals, bone_data)
}
pub fn create_terrain_bound_locals(
&mut self,
locals: &[terrain::Locals],
) -> terrain::BoundLocals {
let locals = self.create_consts(locals);
self.layouts.terrain.bind_locals(&self.device, locals)
}
pub fn create_shadow_bound_locals(&mut self, locals: &[shadow::Locals]) -> shadow::BoundLocals {
let locals = self.create_consts(locals);
self.layouts.shadow.bind_locals(&self.device, locals)
}
pub fn figure_bind_col_light(&self, col_light: Texture) -> ColLights<figure::Locals> {
self.layouts.global.bind_col_light(&self.device, col_light)
}
pub fn terrain_bind_col_light(&self, col_light: Texture) -> ColLights<terrain::Locals> {
self.layouts.global.bind_col_light(&self.device, col_light)
}
pub fn sprite_bind_col_light(&self, col_light: Texture) -> ColLights<sprite::Locals> {
self.layouts.global.bind_col_light(&self.device, col_light)
}
}

View File

@ -0,0 +1,869 @@
use super::{
super::{
buffer::Buffer,
instances::Instances,
model::{DynamicModel, Model, SubModel},
pipelines::{
blit, clouds, debug, figure, fluid, lod_terrain, particle, shadow, skybox, sprite,
terrain, ui, ColLights, GlobalsBindGroup,
},
},
Renderer, ShadowMap, ShadowMapRenderer,
};
use core::{num::NonZeroU32, ops::Range};
use std::sync::Arc;
use vek::Aabr;
use wgpu_profiler::scope::{ManualOwningScope, OwningScope, Scope};
// Currently available pipelines
enum Pipelines<'frame> {
Interface(&'frame super::InterfacePipelines),
All(&'frame super::Pipelines),
// Should never be in this state for now but we need this to accound for super::State::Nothing
None,
}
impl<'frame> Pipelines<'frame> {
fn ui(&self) -> Option<&ui::UiPipeline> {
match self {
Pipelines::Interface(pipelines) => Some(&pipelines.ui),
Pipelines::All(pipelines) => Some(&pipelines.ui),
Pipelines::None => None,
}
}
fn blit(&self) -> Option<&blit::BlitPipeline> {
match self {
Pipelines::Interface(pipelines) => Some(&pipelines.blit),
Pipelines::All(pipelines) => Some(&pipelines.blit),
Pipelines::None => None,
}
}
fn all(&self) -> Option<&super::Pipelines> {
match self {
Pipelines::All(pipelines) => Some(pipelines),
Pipelines::Interface(_) | Pipelines::None => None,
}
}
}
// Borrow the fields we need from the renderer so that the GpuProfiler can be
// disjointly borrowed mutably
struct RendererBorrow<'frame> {
queue: &'frame wgpu::Queue,
device: &'frame wgpu::Device,
shadow: Option<&'frame super::Shadow>,
pipelines: Pipelines<'frame>,
locals: &'frame super::locals::Locals,
views: &'frame super::Views,
mode: &'frame super::super::RenderMode,
quad_index_buffer_u16: &'frame Buffer<u16>,
quad_index_buffer_u32: &'frame Buffer<u32>,
}
pub struct Drawer<'frame> {
encoder: Option<ManualOwningScope<'frame, wgpu::CommandEncoder>>,
borrow: RendererBorrow<'frame>,
swap_tex: wgpu::SwapChainTexture,
globals: &'frame GlobalsBindGroup,
// Texture and other info for taking a screenshot
// Writes to this instead in the third pass if it is present
taking_screenshot: Option<super::screenshot::TakeScreenshot>,
}
impl<'frame> Drawer<'frame> {
pub fn new(
encoder: wgpu::CommandEncoder,
renderer: &'frame mut Renderer,
swap_tex: wgpu::SwapChainTexture,
globals: &'frame GlobalsBindGroup,
) -> Self {
let taking_screenshot = renderer.take_screenshot.take().map(|screenshot_fn| {
super::screenshot::TakeScreenshot::new(
&renderer.device,
&renderer.layouts.blit,
&renderer.sampler,
&renderer.sc_desc,
screenshot_fn,
)
});
let (pipelines, shadow) = match &renderer.state {
super::State::Interface { pipelines, .. } => (Pipelines::Interface(pipelines), None),
super::State::Complete {
pipelines, shadow, ..
} => (Pipelines::All(pipelines), Some(shadow)),
super::State::Nothing => (Pipelines::None, None),
};
let borrow = RendererBorrow {
queue: &renderer.queue,
device: &renderer.device,
shadow,
pipelines,
locals: &renderer.locals,
views: &renderer.views,
mode: &renderer.mode,
quad_index_buffer_u16: &renderer.quad_index_buffer_u16,
quad_index_buffer_u32: &renderer.quad_index_buffer_u32,
};
let encoder =
ManualOwningScope::start("frame", &mut renderer.profiler, encoder, borrow.device);
Self {
encoder: Some(encoder),
borrow,
swap_tex,
globals,
taking_screenshot,
}
}
/// Get the render mode.
pub fn render_mode(&self) -> &super::super::RenderMode { self.borrow.mode }
/// Returns None if the shadow renderer is not enabled at some level or the
/// pipelines are not available yet
pub fn shadow_pass(&mut self) -> Option<ShadowPassDrawer> {
if !self.borrow.mode.shadow.is_map() {
return None;
}
if let ShadowMap::Enabled(ref shadow_renderer) = self.borrow.shadow?.map {
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut render_pass =
encoder.scoped_render_pass("shadow_pass", device, &wgpu::RenderPassDescriptor {
label: Some("shadow pass"),
color_attachments: &[],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &shadow_renderer.directed_depth.view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
});
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
Some(ShadowPassDrawer {
render_pass,
borrow: &self.borrow,
shadow_renderer,
})
} else {
None
}
}
/// Returns None if all the pipelines are not available
pub fn first_pass(&mut self) -> Option<FirstPassDrawer> {
let pipelines = self.borrow.pipelines.all()?;
// Note: this becomes Some once pipeline creation is complete even if shadows
// are not enabled
let shadow = self.borrow.shadow?;
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut render_pass =
encoder.scoped_render_pass("first_pass", device, &wgpu::RenderPassDescriptor {
label: Some("first pass"),
color_attachments: &[wgpu::RenderPassColorAttachment {
view: &self.borrow.views.tgt_color,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
}],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &self.borrow.views.tgt_depth,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(0.0),
store: true,
}),
stencil_ops: None,
}),
});
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
render_pass.set_bind_group(1, &shadow.bind.bind_group, &[]);
Some(FirstPassDrawer {
render_pass,
borrow: &self.borrow,
pipelines,
globals: self.globals,
})
}
/// Returns None if the clouds pipeline is not available
pub fn second_pass(&mut self) -> Option<SecondPassDrawer> {
let pipeline = &self.borrow.pipelines.all()?.clouds;
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut render_pass =
encoder.scoped_render_pass("second_pass", device, &wgpu::RenderPassDescriptor {
label: Some("second pass (clouds)"),
color_attachments: &[wgpu::RenderPassColorAttachment {
view: &self.borrow.views.tgt_color_pp,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
}],
depth_stencil_attachment: None,
});
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
Some(SecondPassDrawer {
render_pass,
borrow: &self.borrow,
pipeline,
})
}
pub fn third_pass(&mut self) -> ThirdPassDrawer {
let encoder = self.encoder.as_mut().unwrap();
let device = self.borrow.device;
let mut render_pass =
encoder.scoped_render_pass("third_pass", device, &wgpu::RenderPassDescriptor {
label: Some("third pass (postprocess + ui)"),
color_attachments: &[wgpu::RenderPassColorAttachment {
// If a screenshot was requested render to that as an intermediate texture
// instead
view: self
.taking_screenshot
.as_ref()
.map_or(&self.swap_tex.view, |s| s.texture_view()),
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
}],
depth_stencil_attachment: None,
});
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
ThirdPassDrawer {
render_pass,
borrow: &self.borrow,
}
}
/// Does nothing if the shadow pipelines are not available or shadow map
/// rendering is disabled
pub fn draw_point_shadows<'data: 'frame>(
&mut self,
matrices: &[shadow::PointLightMatrix; 126],
chunks: impl Clone
+ Iterator<Item = (&'data Model<terrain::Vertex>, &'data terrain::BoundLocals)>,
) {
if !self.borrow.mode.shadow.is_map() {
return;
}
if let Some(ShadowMap::Enabled(ref shadow_renderer)) = self.borrow.shadow.map(|s| &s.map) {
let device = self.borrow.device;
let mut encoder = self
.encoder
.as_mut()
.unwrap()
.scope("point shadows", device);
const STRIDE: usize = std::mem::size_of::<shadow::PointLightMatrix>();
let data = bytemuck::cast_slice(matrices);
for face in 0..6 {
// TODO: view creation cost?
let view =
shadow_renderer
.point_depth
.tex
.create_view(&wgpu::TextureViewDescriptor {
label: Some("Point shadow cubemap face"),
format: None,
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::DepthOnly,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: face,
array_layer_count: NonZeroU32::new(1),
});
let label = format!("point shadow face-{} pass", face);
let mut render_pass =
encoder.scoped_render_pass(&label, device, &wgpu::RenderPassDescriptor {
label: Some(&label),
color_attachments: &[],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
});
render_pass.set_pipeline(&shadow_renderer.point_pipeline.pipeline);
set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, &self.borrow);
render_pass.set_bind_group(0, &self.globals.bind_group, &[]);
(0../*20*/1).for_each(|point_light| {
render_pass.set_push_constants(
wgpu::ShaderStage::all(),
0,
&data[(6 * (point_light + 1) * STRIDE + face as usize * STRIDE)
..(6 * (point_light + 1) * STRIDE + (face + 1) as usize * STRIDE)],
);
chunks.clone().for_each(|(model, locals)| {
render_pass.set_bind_group(1, &locals.bind_group, &[]);
render_pass.set_vertex_buffer(0, model.buf().slice(..));
render_pass.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1);
});
});
}
}
}
/// Clear all the shadow textures, useful if directed shadows (shadow_pass)
/// and point light shadows (draw_point_shadows) are unused and thus the
/// textures will otherwise not be cleared after either their
/// initialization or their last use
/// NOTE: could simply use the above passes except `draw_point_shadows`
/// requires an array of matrices that could be a pain to construct
/// simply for clearing
///
/// Does nothing if the shadow pipelines are not available (although they
/// aren't used here they are needed for the ShadowMap to exist)
pub fn clear_shadows(&mut self) {
if let Some(ShadowMap::Enabled(ref shadow_renderer)) = self.borrow.shadow.map(|s| &s.map) {
let device = self.borrow.device;
let encoder = self.encoder.as_mut().unwrap();
let _ = encoder.scoped_render_pass(
"clear_directed_shadow",
device,
&wgpu::RenderPassDescriptor {
label: Some("clear directed shadow pass"),
color_attachments: &[],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &shadow_renderer.directed_depth.view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
},
);
for face in 0..6 {
// TODO: view creation cost?
let view =
shadow_renderer
.point_depth
.tex
.create_view(&wgpu::TextureViewDescriptor {
label: Some("Point shadow cubemap face"),
format: None,
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::DepthOnly,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: face,
array_layer_count: NonZeroU32::new(1),
});
let label = format!("clear point shadow face-{} pass", face);
let _ = encoder.scoped_render_pass(&label, device, &wgpu::RenderPassDescriptor {
label: Some(&label),
color_attachments: &[],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
});
}
}
}
}
impl<'frame> Drop for Drawer<'frame> {
fn drop(&mut self) {
let mut encoder = self.encoder.take().unwrap();
// If taking a screenshot and the blit pipeline is available
// NOTE: blit pipeline should always be available for now so we don't report an
// error if it isn't
if let Some((screenshot, blit)) = self
.taking_screenshot
.take()
.zip(self.borrow.pipelines.blit())
{
// Image needs to be copied from the screenshot texture to the swapchain texture
let mut render_pass = encoder.scoped_render_pass(
"screenshot blit",
self.borrow.device,
&wgpu::RenderPassDescriptor {
label: Some("Blit screenshot pass"),
color_attachments: &[wgpu::RenderPassColorAttachment {
view: &self.swap_tex.view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
store: true,
},
}],
depth_stencil_attachment: None,
},
);
render_pass.set_pipeline(&blit.pipeline);
render_pass.set_bind_group(0, &screenshot.bind_group(), &[]);
render_pass.draw(0..3, 0..1);
drop(render_pass);
// Issues a command to copy from the texture to a buffer and then sends the
// buffer off to another thread to be mapped and processed
screenshot.download_and_handle(&mut encoder);
}
let (mut encoder, profiler) = encoder.end_scope();
profiler.resolve_queries(&mut encoder);
// It is recommended to only do one submit per frame
self.borrow.queue.submit(std::iter::once(encoder.finish()));
profiler
.end_frame()
.expect("Gpu profiler error! Maybe there was an unclosed scope?");
}
}
// Shadow pass
pub struct ShadowPassDrawer<'pass> {
render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
borrow: &'pass RendererBorrow<'pass>,
shadow_renderer: &'pass ShadowMapRenderer,
}
impl<'pass> ShadowPassDrawer<'pass> {
pub fn draw_figure_shadows(&mut self) -> FigureShadowDrawer<'_, 'pass> {
let mut render_pass = self
.render_pass
.scope("direcred_figure_shadows", self.borrow.device);
render_pass.set_pipeline(&self.shadow_renderer.figure_directed_pipeline.pipeline);
set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, &self.borrow);
FigureShadowDrawer { render_pass }
}
pub fn draw_terrain_shadows(&mut self) -> TerrainShadowDrawer<'_, 'pass> {
let mut render_pass = self
.render_pass
.scope("direcred_terrain_shadows", self.borrow.device);
render_pass.set_pipeline(&self.shadow_renderer.terrain_directed_pipeline.pipeline);
set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, &self.borrow);
TerrainShadowDrawer { render_pass }
}
}
pub struct FigureShadowDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> FigureShadowDrawer<'pass_ref, 'pass> {
pub fn draw<'data: 'pass>(
&mut self,
model: SubModel<'data, terrain::Vertex>,
locals: &'data figure::BoundLocals,
) {
self.render_pass.set_bind_group(1, &locals.bind_group, &[]);
self.render_pass.set_vertex_buffer(0, model.buf());
self.render_pass
.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1);
}
}
pub struct TerrainShadowDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> TerrainShadowDrawer<'pass_ref, 'pass> {
pub fn draw<'data: 'pass>(
&mut self,
model: &'data Model<terrain::Vertex>,
locals: &'data terrain::BoundLocals,
) {
self.render_pass.set_bind_group(1, &locals.bind_group, &[]);
self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
self.render_pass
.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1);
}
}
// First pass
pub struct FirstPassDrawer<'pass> {
pub(super) render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
borrow: &'pass RendererBorrow<'pass>,
pipelines: &'pass super::Pipelines,
globals: &'pass GlobalsBindGroup,
}
impl<'pass> FirstPassDrawer<'pass> {
pub fn draw_skybox<'data: 'pass>(&mut self, model: &'data Model<skybox::Vertex>) {
let mut render_pass = self.render_pass.scope("skybox", self.borrow.device);
render_pass.set_pipeline(&self.pipelines.skybox.pipeline);
set_quad_index_buffer::<skybox::Vertex>(&mut render_pass, &self.borrow);
render_pass.set_vertex_buffer(0, model.buf().slice(..));
render_pass.draw(0..model.len() as u32, 0..1);
}
pub fn draw_debug(&mut self) -> DebugDrawer<'_, 'pass> {
let mut render_pass = self.render_pass.scope("debug", self.borrow.device);
render_pass.set_pipeline(&self.pipelines.debug.pipeline);
set_quad_index_buffer::<debug::Vertex>(&mut render_pass, &self.borrow);
DebugDrawer { render_pass }
}
pub fn draw_lod_terrain<'data: 'pass>(&mut self, model: &'data Model<lod_terrain::Vertex>) {
let mut render_pass = self.render_pass.scope("lod_terrain", self.borrow.device);
render_pass.set_pipeline(&self.pipelines.lod_terrain.pipeline);
set_quad_index_buffer::<lod_terrain::Vertex>(&mut render_pass, &self.borrow);
render_pass.set_vertex_buffer(0, model.buf().slice(..));
render_pass.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1);
}
pub fn draw_figures(&mut self) -> FigureDrawer<'_, 'pass> {
let mut render_pass = self.render_pass.scope("figures", self.borrow.device);
render_pass.set_pipeline(&self.pipelines.figure.pipeline);
// Note: figures use the same vertex type as the terrain
set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, &self.borrow);
FigureDrawer { render_pass }
}
pub fn draw_terrain(&mut self) -> TerrainDrawer<'_, 'pass> {
let mut render_pass = self.render_pass.scope("terrain", self.borrow.device);
render_pass.set_pipeline(&self.pipelines.terrain.pipeline);
set_quad_index_buffer::<terrain::Vertex>(&mut render_pass, &self.borrow);
TerrainDrawer {
render_pass,
col_lights: None,
}
}
pub fn draw_particles(&mut self) -> ParticleDrawer<'_, 'pass> {
let mut render_pass = self.render_pass.scope("particles", self.borrow.device);
render_pass.set_pipeline(&self.pipelines.particle.pipeline);
set_quad_index_buffer::<particle::Vertex>(&mut render_pass, &self.borrow);
ParticleDrawer { render_pass }
}
pub fn draw_sprites<'data: 'pass>(
&mut self,
globals: &'data sprite::SpriteGlobalsBindGroup,
col_lights: &'data ColLights<sprite::Locals>,
) -> SpriteDrawer<'_, 'pass> {
let mut render_pass = self.render_pass.scope("sprites", self.borrow.device);
render_pass.set_pipeline(&self.pipelines.sprite.pipeline);
set_quad_index_buffer::<sprite::Vertex>(&mut render_pass, &self.borrow);
render_pass.set_bind_group(0, &globals.bind_group, &[]);
render_pass.set_bind_group(3, &col_lights.bind_group, &[]);
SpriteDrawer {
render_pass,
globals: self.globals,
}
}
pub fn draw_fluid(&mut self) -> FluidDrawer<'_, 'pass> {
let mut render_pass = self.render_pass.scope("fluid", self.borrow.device);
render_pass.set_pipeline(&self.pipelines.fluid.pipeline);
set_quad_index_buffer::<fluid::Vertex>(&mut render_pass, &self.borrow);
FluidDrawer { render_pass }
}
}
pub struct DebugDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> DebugDrawer<'pass_ref, 'pass> {
pub fn draw<'data: 'pass>(
&mut self,
model: &'data Model<debug::Vertex>,
locals: &'data debug::BoundLocals,
) {
self.render_pass.set_bind_group(1, &locals.bind_group, &[]);
self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
self.render_pass.draw(0..model.len() as u32, 0..1);
}
}
pub struct FigureDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> FigureDrawer<'pass_ref, 'pass> {
pub fn draw<'data: 'pass>(
&mut self,
model: SubModel<'data, terrain::Vertex>,
locals: &'data figure::BoundLocals,
// TODO: don't rebind this every time once they are shared between figures
col_lights: &'data ColLights<figure::Locals>,
) {
self.render_pass.set_bind_group(2, &locals.bind_group, &[]);
self.render_pass
.set_bind_group(3, &col_lights.bind_group, &[]);
self.render_pass.set_vertex_buffer(0, model.buf());
self.render_pass
.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1);
}
}
pub struct TerrainDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
col_lights: Option<&'pass_ref Arc<ColLights<terrain::Locals>>>,
}
impl<'pass_ref, 'pass: 'pass_ref> TerrainDrawer<'pass_ref, 'pass> {
pub fn draw<'data: 'pass>(
&mut self,
model: &'data Model<terrain::Vertex>,
col_lights: &'data Arc<ColLights<terrain::Locals>>,
locals: &'data terrain::BoundLocals,
) {
if self.col_lights
// Check if we are still using the same atlas texture as the previous drawn
// chunk
.filter(|current_col_lights| Arc::ptr_eq(current_col_lights, col_lights))
.is_none()
{
self.render_pass
.set_bind_group(3, &col_lights.bind_group, &[]); // TODO: put this in slot 2
self.col_lights = Some(col_lights);
};
self.render_pass.set_bind_group(2, &locals.bind_group, &[]); // TODO: put this in slot 3
self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
self.render_pass
.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1);
}
}
pub struct ParticleDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> ParticleDrawer<'pass_ref, 'pass> {
// Note: if we ever need to draw less than the whole model, these APIs can be
// changed
pub fn draw<'data: 'pass>(
&mut self,
model: &'data Model<particle::Vertex>,
instances: &'data Instances<particle::Instance>,
) {
self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
self.render_pass
.set_vertex_buffer(1, instances.buf().slice(..));
self.render_pass
// TODO: since we cast to u32 maybe this should returned by the len/count functions?
.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..instances.count() as u32);
}
}
pub struct SpriteDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
globals: &'pass GlobalsBindGroup,
}
impl<'pass_ref, 'pass: 'pass_ref> SpriteDrawer<'pass_ref, 'pass> {
pub fn draw<'data: 'pass>(
&mut self,
terrain_locals: &'data terrain::BoundLocals,
instances: &'data Instances<sprite::Instance>,
) {
self.render_pass
.set_bind_group(2, &terrain_locals.bind_group, &[]);
self.render_pass
.set_vertex_buffer(0, instances.buf().slice(..));
self.render_pass.draw_indexed(
0..sprite::VERT_PAGE_SIZE / 4 * 6,
0,
0..instances.count() as u32,
);
}
}
impl<'pass_ref, 'pass: 'pass_ref> Drop for SpriteDrawer<'pass_ref, 'pass> {
fn drop(&mut self) {
// Reset to regular globals
self.render_pass
.set_bind_group(0, &self.globals.bind_group, &[]);
}
}
pub struct FluidDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
impl<'pass_ref, 'pass: 'pass_ref> FluidDrawer<'pass_ref, 'pass> {
pub fn draw<'data: 'pass>(
&mut self,
model: &'data Model<fluid::Vertex>,
locals: &'data terrain::BoundLocals,
) {
self.render_pass.set_vertex_buffer(0, model.buf().slice(..));
self.render_pass.set_bind_group(2, &locals.bind_group, &[]);
self.render_pass
.draw_indexed(0..model.len() as u32 / 4 * 6, 0, 0..1);
}
}
// Second pass: clouds
pub struct SecondPassDrawer<'pass> {
render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
borrow: &'pass RendererBorrow<'pass>,
pipeline: &'pass clouds::CloudsPipeline,
}
impl<'pass> SecondPassDrawer<'pass> {
pub fn draw_clouds(&mut self) {
self.render_pass.set_pipeline(&self.pipeline.pipeline);
self.render_pass
.set_bind_group(1, &self.borrow.locals.clouds_bind.bind_group, &[]);
self.render_pass.draw(0..3, 0..1);
}
}
/// Third pass: postprocess + ui
pub struct ThirdPassDrawer<'pass> {
render_pass: OwningScope<'pass, wgpu::RenderPass<'pass>>,
borrow: &'pass RendererBorrow<'pass>,
}
impl<'pass> ThirdPassDrawer<'pass> {
/// Does nothing if the postprocess pipeline is not available
pub fn draw_postprocess(&mut self) {
let postprocess = match self.borrow.pipelines.all() {
Some(p) => &p.postprocess,
None => return,
};
let mut render_pass = self.render_pass.scope("postprocess", self.borrow.device);
render_pass.set_pipeline(&postprocess.pipeline);
render_pass.set_bind_group(1, &self.borrow.locals.postprocess_bind.bind_group, &[]);
render_pass.draw(0..3, 0..1);
}
/// Returns None if the UI pipeline is not available (note: this should
/// never be the case for now)
pub fn draw_ui(&mut self) -> Option<UiDrawer<'_, 'pass>> {
let ui = self.borrow.pipelines.ui()?;
let mut render_pass = self.render_pass.scope("ui", self.borrow.device);
render_pass.set_pipeline(&ui.pipeline);
set_quad_index_buffer::<ui::Vertex>(&mut render_pass, &self.borrow);
Some(UiDrawer { render_pass })
}
}
pub struct UiDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: Scope<'pass_ref, wgpu::RenderPass<'pass>>,
}
pub struct PreparedUiDrawer<'pass_ref, 'pass: 'pass_ref> {
render_pass: &'pass_ref mut wgpu::RenderPass<'pass>,
}
impl<'pass_ref, 'pass: 'pass_ref> UiDrawer<'pass_ref, 'pass> {
/// Set vertex buffer, initial scissor, and locals
/// These can be changed later but this ensures that they don't have to be
/// set with every draw call
pub fn prepare<'data: 'pass>(
&mut self,
locals: &'data ui::BoundLocals,
buf: &'data DynamicModel<ui::Vertex>,
scissor: Aabr<u16>,
) -> PreparedUiDrawer<'_, 'pass> {
// Note: not actually prepared yet
// we do this to avoid having to write extra code for the set functions
let mut prepared = PreparedUiDrawer {
render_pass: &mut *self.render_pass,
};
// Prepare
prepared.set_locals(locals);
prepared.set_model(buf);
prepared.set_scissor(scissor);
prepared
}
}
impl<'pass_ref, 'pass: 'pass_ref> PreparedUiDrawer<'pass_ref, 'pass> {
pub fn set_locals<'data: 'pass>(&mut self, locals: &'data ui::BoundLocals) {
self.render_pass.set_bind_group(1, &locals.bind_group, &[]);
}
pub fn set_model<'data: 'pass>(&mut self, model: &'data DynamicModel<ui::Vertex>) {
self.render_pass.set_vertex_buffer(0, model.buf().slice(..))
}
pub fn set_scissor(&mut self, scissor: Aabr<u16>) {
let Aabr { min, max } = scissor;
self.render_pass.set_scissor_rect(
min.x as u32,
min.y as u32,
(max.x - min.x) as u32,
(max.y - min.y) as u32,
);
}
pub fn draw<'data: 'pass>(&mut self, texture: &'data ui::TextureBindGroup, verts: Range<u32>) {
self.render_pass.set_bind_group(2, &texture.bind_group, &[]);
self.render_pass.draw(verts, 0..1);
}
}
fn set_quad_index_buffer<'a, V: super::super::Vertex>(
pass: &mut wgpu::RenderPass<'a>,
borrow: &RendererBorrow<'a>,
) {
if let Some(format) = V::QUADS_INDEX {
let slice = match format {
wgpu::IndexFormat::Uint16 => borrow.quad_index_buffer_u16.buf.slice(..),
wgpu::IndexFormat::Uint32 => borrow.quad_index_buffer_u32.buf.slice(..),
};
pass.set_index_buffer(slice, format);
}
}

View File

@ -0,0 +1,75 @@
use super::{
super::{
consts::Consts,
pipelines::{clouds, postprocess},
},
Layouts,
};
pub struct Locals {
pub clouds: Consts<clouds::Locals>,
pub clouds_bind: clouds::BindGroup,
pub postprocess: Consts<postprocess::Locals>,
pub postprocess_bind: postprocess::BindGroup,
}
impl Locals {
pub(super) fn new(
device: &wgpu::Device,
layouts: &Layouts,
clouds_locals: Consts<clouds::Locals>,
postprocess_locals: Consts<postprocess::Locals>,
tgt_color_view: &wgpu::TextureView,
tgt_depth_view: &wgpu::TextureView,
tgt_color_pp_view: &wgpu::TextureView,
sampler: &wgpu::Sampler,
depth_sampler: &wgpu::Sampler,
) -> Self {
let clouds_bind = layouts.clouds.bind(
device,
tgt_color_view,
tgt_depth_view,
sampler,
depth_sampler,
&clouds_locals,
);
let postprocess_bind =
layouts
.postprocess
.bind(device, tgt_color_pp_view, sampler, &postprocess_locals);
Self {
clouds: clouds_locals,
clouds_bind,
postprocess: postprocess_locals,
postprocess_bind,
}
}
pub(super) fn rebind(
&mut self,
device: &wgpu::Device,
layouts: &Layouts,
// Call when these are recreated and need to be rebound
// e.g. resizing
tgt_color_view: &wgpu::TextureView,
tgt_depth_view: &wgpu::TextureView,
tgt_color_pp_view: &wgpu::TextureView,
sampler: &wgpu::Sampler,
depth_sampler: &wgpu::Sampler,
) {
self.clouds_bind = layouts.clouds.bind(
device,
tgt_color_view,
tgt_depth_view,
sampler,
depth_sampler,
&self.clouds,
);
self.postprocess_bind =
layouts
.postprocess
.bind(device, tgt_color_pp_view, sampler, &self.postprocess);
}
}

View File

@ -0,0 +1,914 @@
use super::{
super::{
pipelines::{
blit, clouds, debug, figure, fluid, lod_terrain, particle, postprocess, shadow, skybox,
sprite, terrain, ui,
},
AaMode, CloudMode, FluidMode, LightingMode, RenderError, RenderMode, ShadowMode,
},
shaders::Shaders,
Layouts,
};
use common_base::prof_span;
use std::sync::Arc;
/// All the pipelines
pub struct Pipelines {
pub debug: debug::DebugPipeline,
pub figure: figure::FigurePipeline,
pub fluid: fluid::FluidPipeline,
pub lod_terrain: lod_terrain::LodTerrainPipeline,
pub particle: particle::ParticlePipeline,
pub clouds: clouds::CloudsPipeline,
pub postprocess: postprocess::PostProcessPipeline,
// Consider reenabling at some time
// player_shadow: figure::FigurePipeline,
pub skybox: skybox::SkyboxPipeline,
pub sprite: sprite::SpritePipeline,
pub terrain: terrain::TerrainPipeline,
pub ui: ui::UiPipeline,
pub blit: blit::BlitPipeline,
}
/// Pipelines that are needed to render 3D stuff in-game
/// Use to decouple interface pipeline creation when initializing the renderer
pub struct IngamePipelines {
debug: debug::DebugPipeline,
figure: figure::FigurePipeline,
fluid: fluid::FluidPipeline,
lod_terrain: lod_terrain::LodTerrainPipeline,
particle: particle::ParticlePipeline,
clouds: clouds::CloudsPipeline,
postprocess: postprocess::PostProcessPipeline,
// Consider reenabling at some time
// player_shadow: figure::FigurePipeline,
skybox: skybox::SkyboxPipeline,
sprite: sprite::SpritePipeline,
terrain: terrain::TerrainPipeline,
}
pub struct ShadowPipelines {
pub point: Option<shadow::PointShadowPipeline>,
pub directed: Option<shadow::ShadowPipeline>,
pub figure: Option<shadow::ShadowFigurePipeline>,
}
pub struct IngameAndShadowPipelines {
pub ingame: IngamePipelines,
pub shadow: ShadowPipelines,
}
/// Pipelines neccesary to display the UI and take screenshots
/// Use to decouple interface pipeline creation when initializing the renderer
pub struct InterfacePipelines {
pub ui: ui::UiPipeline,
pub blit: blit::BlitPipeline,
}
impl Pipelines {
pub fn consolidate(interface: InterfacePipelines, ingame: IngamePipelines) -> Self {
Self {
debug: ingame.debug,
figure: ingame.figure,
fluid: ingame.fluid,
lod_terrain: ingame.lod_terrain,
particle: ingame.particle,
clouds: ingame.clouds,
postprocess: ingame.postprocess,
//player_shadow: ingame.player_shadow,
skybox: ingame.skybox,
sprite: ingame.sprite,
terrain: ingame.terrain,
ui: interface.ui,
blit: interface.blit,
}
}
}
/// Processed shaders ready for use in pipeline creation
struct ShaderModules {
skybox_vert: wgpu::ShaderModule,
skybox_frag: wgpu::ShaderModule,
debug_vert: wgpu::ShaderModule,
debug_frag: wgpu::ShaderModule,
figure_vert: wgpu::ShaderModule,
figure_frag: wgpu::ShaderModule,
terrain_vert: wgpu::ShaderModule,
terrain_frag: wgpu::ShaderModule,
fluid_vert: wgpu::ShaderModule,
fluid_frag: wgpu::ShaderModule,
sprite_vert: wgpu::ShaderModule,
sprite_frag: wgpu::ShaderModule,
particle_vert: wgpu::ShaderModule,
particle_frag: wgpu::ShaderModule,
ui_vert: wgpu::ShaderModule,
ui_frag: wgpu::ShaderModule,
lod_terrain_vert: wgpu::ShaderModule,
lod_terrain_frag: wgpu::ShaderModule,
clouds_vert: wgpu::ShaderModule,
clouds_frag: wgpu::ShaderModule,
postprocess_vert: wgpu::ShaderModule,
postprocess_frag: wgpu::ShaderModule,
blit_vert: wgpu::ShaderModule,
blit_frag: wgpu::ShaderModule,
point_light_shadows_vert: wgpu::ShaderModule,
light_shadows_directed_vert: wgpu::ShaderModule,
light_shadows_figure_vert: wgpu::ShaderModule,
}
impl ShaderModules {
pub fn new(
device: &wgpu::Device,
shaders: &Shaders,
mode: &RenderMode,
has_shadow_views: bool,
) -> Result<Self, RenderError> {
prof_span!(_guard, "ShaderModules::new");
use shaderc::{CompileOptions, Compiler, OptimizationLevel, ResolvedInclude, ShaderKind};
let constants = shaders.get("include.constants").unwrap();
let globals = shaders.get("include.globals").unwrap();
let sky = shaders.get("include.sky").unwrap();
let light = shaders.get("include.light").unwrap();
let srgb = shaders.get("include.srgb").unwrap();
let random = shaders.get("include.random").unwrap();
let lod = shaders.get("include.lod").unwrap();
let shadows = shaders.get("include.shadows").unwrap();
// We dynamically add extra configuration settings to the constants file.
let constants = format!(
r#"
{}
#define VOXYGEN_COMPUTATION_PREFERENCE {}
#define FLUID_MODE {}
#define CLOUD_MODE {}
#define LIGHTING_ALGORITHM {}
#define SHADOW_MODE {}
"#,
&constants.0,
// TODO: Configurable vertex/fragment shader preference.
"VOXYGEN_COMPUTATION_PREFERENCE_FRAGMENT",
match mode.fluid {
FluidMode::Cheap => "FLUID_MODE_CHEAP",
FluidMode::Shiny => "FLUID_MODE_SHINY",
},
match mode.cloud {
CloudMode::None => "CLOUD_MODE_NONE",
CloudMode::Minimal => "CLOUD_MODE_MINIMAL",
CloudMode::Low => "CLOUD_MODE_LOW",
CloudMode::Medium => "CLOUD_MODE_MEDIUM",
CloudMode::High => "CLOUD_MODE_HIGH",
CloudMode::Ultra => "CLOUD_MODE_ULTRA",
},
match mode.lighting {
LightingMode::Ashikhmin => "LIGHTING_ALGORITHM_ASHIKHMIN",
LightingMode::BlinnPhong => "LIGHTING_ALGORITHM_BLINN_PHONG",
LightingMode::Lambertian => "LIGHTING_ALGORITHM_LAMBERTIAN",
},
match mode.shadow {
ShadowMode::None => "SHADOW_MODE_NONE",
ShadowMode::Map(_) if has_shadow_views => "SHADOW_MODE_MAP",
ShadowMode::Cheap | ShadowMode::Map(_) => "SHADOW_MODE_CHEAP",
},
);
let anti_alias = shaders
.get(match mode.aa {
AaMode::None => "antialias.none",
AaMode::Fxaa => "antialias.fxaa",
AaMode::MsaaX4 => "antialias.msaa-x4",
AaMode::MsaaX8 => "antialias.msaa-x8",
AaMode::MsaaX16 => "antialias.msaa-x16",
})
.unwrap();
let cloud = shaders
.get(match mode.cloud {
CloudMode::None => "include.cloud.none",
_ => "include.cloud.regular",
})
.unwrap();
let mut compiler = Compiler::new().ok_or(RenderError::ErrorInitializingCompiler)?;
let mut options = CompileOptions::new().ok_or(RenderError::ErrorInitializingCompiler)?;
options.set_optimization_level(OptimizationLevel::Performance);
options.set_forced_version_profile(430, shaderc::GlslProfile::Core);
options.set_include_callback(move |name, _, shader_name, _| {
Ok(ResolvedInclude {
resolved_name: name.to_string(),
content: match name {
"constants.glsl" => constants.clone(),
"globals.glsl" => globals.0.to_owned(),
"shadows.glsl" => shadows.0.to_owned(),
"sky.glsl" => sky.0.to_owned(),
"light.glsl" => light.0.to_owned(),
"srgb.glsl" => srgb.0.to_owned(),
"random.glsl" => random.0.to_owned(),
"lod.glsl" => lod.0.to_owned(),
"anti-aliasing.glsl" => anti_alias.0.to_owned(),
"cloud.glsl" => cloud.0.to_owned(),
other => {
return Err(format!(
"Include {} in {} is not defined",
other, shader_name
));
},
},
})
});
let mut create_shader = |name, kind| {
let glsl = &shaders
.get(name)
.unwrap_or_else(|| panic!("Can't retrieve shader: {}", name))
.0;
let file_name = format!("{}.glsl", name);
create_shader_module(device, &mut compiler, glsl, kind, &file_name, &options)
};
let selected_fluid_shader = ["fluid-frag.", match mode.fluid {
FluidMode::Cheap => "cheap",
FluidMode::Shiny => "shiny",
}]
.concat();
Ok(Self {
skybox_vert: create_shader("skybox-vert", ShaderKind::Vertex)?,
skybox_frag: create_shader("skybox-frag", ShaderKind::Fragment)?,
debug_vert: create_shader("debug-vert", ShaderKind::Vertex)?,
debug_frag: create_shader("debug-frag", ShaderKind::Fragment)?,
figure_vert: create_shader("figure-vert", ShaderKind::Vertex)?,
figure_frag: create_shader("figure-frag", ShaderKind::Fragment)?,
terrain_vert: create_shader("terrain-vert", ShaderKind::Vertex)?,
terrain_frag: create_shader("terrain-frag", ShaderKind::Fragment)?,
fluid_vert: create_shader("fluid-vert", ShaderKind::Vertex)?,
fluid_frag: create_shader(&selected_fluid_shader, ShaderKind::Fragment)?,
sprite_vert: create_shader("sprite-vert", ShaderKind::Vertex)?,
sprite_frag: create_shader("sprite-frag", ShaderKind::Fragment)?,
particle_vert: create_shader("particle-vert", ShaderKind::Vertex)?,
particle_frag: create_shader("particle-frag", ShaderKind::Fragment)?,
ui_vert: create_shader("ui-vert", ShaderKind::Vertex)?,
ui_frag: create_shader("ui-frag", ShaderKind::Fragment)?,
lod_terrain_vert: create_shader("lod-terrain-vert", ShaderKind::Vertex)?,
lod_terrain_frag: create_shader("lod-terrain-frag", ShaderKind::Fragment)?,
clouds_vert: create_shader("clouds-vert", ShaderKind::Vertex)?,
clouds_frag: create_shader("clouds-frag", ShaderKind::Fragment)?,
postprocess_vert: create_shader("postprocess-vert", ShaderKind::Vertex)?,
postprocess_frag: create_shader("postprocess-frag", ShaderKind::Fragment)?,
blit_vert: create_shader("blit-vert", ShaderKind::Vertex)?,
blit_frag: create_shader("blit-frag", ShaderKind::Fragment)?,
point_light_shadows_vert: create_shader(
"point-light-shadows-vert",
ShaderKind::Vertex,
)?,
light_shadows_directed_vert: create_shader(
"light-shadows-directed-vert",
ShaderKind::Vertex,
)?,
light_shadows_figure_vert: create_shader(
"light-shadows-figure-vert",
ShaderKind::Vertex,
)?,
})
}
}
fn create_shader_module(
device: &wgpu::Device,
compiler: &mut shaderc::Compiler,
source: &str,
kind: shaderc::ShaderKind,
file_name: &str,
options: &shaderc::CompileOptions,
) -> Result<wgpu::ShaderModule, RenderError> {
prof_span!(_guard, "create_shader_modules");
use std::borrow::Cow;
let spv = compiler
.compile_into_spirv(source, kind, file_name, "main", Some(options))
.map_err(|e| (file_name, e))?;
let label = [file_name, "\n\n", source].concat();
Ok(device.create_shader_module(&wgpu::ShaderModuleDescriptor {
label: Some(&label),
source: wgpu::ShaderSource::SpirV(Cow::Borrowed(spv.as_binary())),
flags: wgpu::ShaderFlags::empty(),
// TODO: renable // flags: wgpu::ShaderFlags::VALIDATION,
}))
}
/// Things needed to create a pipeline
#[derive(Clone, Copy)]
struct PipelineNeeds<'a> {
device: &'a wgpu::Device,
layouts: &'a Layouts,
shaders: &'a ShaderModules,
mode: &'a RenderMode,
sc_desc: &'a wgpu::SwapChainDescriptor,
}
/// Creates InterfacePipelines in parallel
fn create_interface_pipelines(
needs: PipelineNeeds,
pool: &rayon::ThreadPool,
tasks: [Task; 2],
) -> InterfacePipelines {
prof_span!(_guard, "create_interface_pipelines");
let [ui_task, blit_task] = tasks;
// Construct a pipeline for rendering UI elements
let create_ui = || {
ui_task.run(
|| {
ui::UiPipeline::new(
needs.device,
&needs.shaders.ui_vert,
&needs.shaders.ui_frag,
needs.sc_desc,
&needs.layouts.global,
&needs.layouts.ui,
)
},
"ui pipeline creation",
)
};
// Construct a pipeline for blitting, used during screenshotting
let create_blit = || {
blit_task.run(
|| {
blit::BlitPipeline::new(
needs.device,
&needs.shaders.blit_vert,
&needs.shaders.blit_frag,
needs.sc_desc,
&needs.layouts.blit,
)
},
"blit pipeline creation",
)
};
let (ui, blit) = pool.join(create_ui, create_blit);
InterfacePipelines { ui, blit }
}
/// Create IngamePipelines and shadow pipelines in parallel
fn create_ingame_and_shadow_pipelines(
needs: PipelineNeeds,
pool: &rayon::ThreadPool,
tasks: [Task; 13],
) -> IngameAndShadowPipelines {
prof_span!(_guard, "create_ingame_and_shadow_pipelines");
let PipelineNeeds {
device,
layouts,
shaders,
mode,
sc_desc,
} = needs;
let [
debug_task,
skybox_task,
figure_task,
terrain_task,
fluid_task,
sprite_task,
particle_task,
lod_terrain_task,
clouds_task,
postprocess_task,
// TODO: if these are ever actually optionally done, counting them
// as tasks to do beforehand seems kind of iffy since they will just
// be skipped
point_shadow_task,
terrain_directed_shadow_task,
figure_directed_shadow_task,
] = tasks;
// TODO: pass in format of target color buffer
// Pipeline for rendering debug shapes
let create_debug = || {
debug_task.run(
|| {
debug::DebugPipeline::new(
device,
&shaders.debug_vert,
&shaders.debug_frag,
&layouts.global,
&layouts.debug,
mode.aa,
)
},
"debug pipeline creation",
)
};
// Pipeline for rendering skyboxes
let create_skybox = || {
skybox_task.run(
|| {
skybox::SkyboxPipeline::new(
device,
&shaders.skybox_vert,
&shaders.skybox_frag,
&layouts.global,
mode.aa,
)
},
"skybox pipeline creation",
)
};
// Pipeline for rendering figures
let create_figure = || {
figure_task.run(
|| {
figure::FigurePipeline::new(
device,
&shaders.figure_vert,
&shaders.figure_frag,
&layouts.global,
&layouts.figure,
mode.aa,
)
},
"figure pipeline creation",
)
};
// Pipeline for rendering terrain
let create_terrain = || {
terrain_task.run(
|| {
terrain::TerrainPipeline::new(
device,
&shaders.terrain_vert,
&shaders.terrain_frag,
&layouts.global,
&layouts.terrain,
mode.aa,
)
},
"terrain pipeline creation",
)
};
// Pipeline for rendering fluids
let create_fluid = || {
fluid_task.run(
|| {
fluid::FluidPipeline::new(
device,
&shaders.fluid_vert,
&shaders.fluid_frag,
&layouts.global,
&layouts.terrain,
mode.aa,
)
},
"fluid pipeline creation",
)
};
// Pipeline for rendering sprites
let create_sprite = || {
sprite_task.run(
|| {
sprite::SpritePipeline::new(
device,
&shaders.sprite_vert,
&shaders.sprite_frag,
&layouts.global,
&layouts.sprite,
&layouts.terrain,
mode.aa,
)
},
"sprite pipeline creation",
)
};
// Pipeline for rendering particles
let create_particle = || {
particle_task.run(
|| {
particle::ParticlePipeline::new(
device,
&shaders.particle_vert,
&shaders.particle_frag,
&layouts.global,
mode.aa,
)
},
"particle pipeline creation",
)
};
// Pipeline for rendering terrain
let create_lod_terrain = || {
lod_terrain_task.run(
|| {
lod_terrain::LodTerrainPipeline::new(
device,
&shaders.lod_terrain_vert,
&shaders.lod_terrain_frag,
&layouts.global,
mode.aa,
)
},
"lod terrain pipeline creation",
)
};
// Pipeline for rendering our clouds (a kind of post-processing)
let create_clouds = || {
clouds_task.run(
|| {
clouds::CloudsPipeline::new(
device,
&shaders.clouds_vert,
&shaders.clouds_frag,
&layouts.global,
&layouts.clouds,
mode.aa,
)
},
"clouds pipeline creation",
)
};
// Pipeline for rendering our post-processing
let create_postprocess = || {
postprocess_task.run(
|| {
postprocess::PostProcessPipeline::new(
device,
&shaders.postprocess_vert,
&shaders.postprocess_frag,
sc_desc,
&layouts.global,
&layouts.postprocess,
)
},
"postprocess pipeline creation",
)
};
//
// // Pipeline for rendering the player silhouette
// let player_shadow_pipeline = create_pipeline(
// factory,
// figure::pipe::Init {
// tgt_depth: (gfx::preset::depth::PASS_TEST/*,
// Stencil::new(
// Comparison::Equal,
// 0xff,
// (StencilOp::Keep, StencilOp::Keep, StencilOp::Keep),
// ),*/),
// ..figure::pipe::new()
// },
// &figure_vert,
// &Glsl::load_watched(
// "voxygen.shaders.player-shadow-frag",
// shader_reload_indicator,
// )
// .unwrap(),
// &include_ctx,
// gfx::state::CullFace::Back,
// )?;
// Pipeline for rendering point light terrain shadow maps.
let create_point_shadow = || {
point_shadow_task.run(
|| {
shadow::PointShadowPipeline::new(
device,
&shaders.point_light_shadows_vert,
&layouts.global,
&layouts.terrain,
mode.aa,
)
},
"point shadow pipeline creation",
)
};
// Pipeline for rendering directional light terrain shadow maps.
let create_terrain_directed_shadow = || {
terrain_directed_shadow_task.run(
|| {
shadow::ShadowPipeline::new(
device,
&shaders.light_shadows_directed_vert,
&layouts.global,
&layouts.terrain,
mode.aa,
)
},
"terrain directed shadow pipeline creation",
)
};
// Pipeline for rendering directional light figure shadow maps.
let create_figure_directed_shadow = || {
figure_directed_shadow_task.run(
|| {
shadow::ShadowFigurePipeline::new(
device,
&shaders.light_shadows_figure_vert,
&layouts.global,
&layouts.figure,
mode.aa,
)
},
"figure directed shadow pipeline creation",
)
};
let j1 = || pool.join(create_debug, || pool.join(create_skybox, create_figure));
let j2 = || pool.join(create_terrain, create_fluid);
let j3 = || pool.join(create_sprite, create_particle);
let j4 = || pool.join(create_lod_terrain, create_clouds);
let j5 = || pool.join(create_postprocess, create_point_shadow);
let j6 = || {
pool.join(
create_terrain_directed_shadow,
create_figure_directed_shadow,
)
};
// Ignore this
let (
(
((debug, (skybox, figure)), (terrain, fluid)),
((sprite, particle), (lod_terrain, clouds)),
),
((postprocess, point_shadow), (terrain_directed_shadow, figure_directed_shadow)),
) = pool.join(
|| pool.join(|| pool.join(j1, j2), || pool.join(j3, j4)),
|| pool.join(j5, j6),
);
IngameAndShadowPipelines {
ingame: IngamePipelines {
debug,
figure,
fluid,
lod_terrain,
particle,
clouds,
postprocess,
skybox,
sprite,
terrain,
// player_shadow_pipeline,
},
// TODO: skip creating these if the shadow map setting is not enabled
shadow: ShadowPipelines {
point: Some(point_shadow),
directed: Some(terrain_directed_shadow),
figure: Some(figure_directed_shadow),
},
}
}
/// Creates all the pipelines used to render.
/// Use this for the initial creation.
/// It blocks the main thread to create the interface pipelines while moving the
/// creation of other pipelines into the background
/// NOTE: this tries to use all the CPU cores to complete as soon as possible
pub(super) fn initial_create_pipelines(
device: Arc<wgpu::Device>,
layouts: Arc<Layouts>,
shaders: Shaders,
mode: RenderMode,
sc_desc: wgpu::SwapChainDescriptor,
has_shadow_views: bool,
) -> Result<
(
InterfacePipelines,
PipelineCreation<IngameAndShadowPipelines>,
),
RenderError,
> {
prof_span!(_guard, "initial_create_pipelines");
// Process shaders into modules
let shader_modules = ShaderModules::new(&device, &shaders, &mode, has_shadow_views)?;
// Create threadpool for parallel portion
let pool = rayon::ThreadPoolBuilder::new()
.thread_name(|n| format!("pipeline-creation-{}", n))
.build()
.unwrap();
let needs = PipelineNeeds {
device: &device,
layouts: &layouts,
shaders: &shader_modules,
mode: &mode,
sc_desc: &sc_desc,
};
// Create interface pipelines while blocking the main thread
// Note: we use a throwaway Progress tracker here since we don't need to track
// the progress
let interface_pipelines =
create_interface_pipelines(needs, &pool, Progress::new().create_tasks());
let pool = Arc::new(pool);
let send_pool = Arc::clone(&pool);
// Track pipeline creation progress
let progress = Arc::new(Progress::new());
let (pipeline_send, pipeline_recv) = crossbeam_channel::bounded(0);
let pipeline_creation = PipelineCreation {
progress: Arc::clone(&progress),
recv: pipeline_recv,
};
// Start background compilation
pool.spawn(move || {
let pool = &*send_pool;
let needs = PipelineNeeds {
device: &device,
layouts: &layouts,
shaders: &shader_modules,
mode: &mode,
sc_desc: &sc_desc,
};
let pipelines = create_ingame_and_shadow_pipelines(needs, &pool, progress.create_tasks());
pipeline_send.send(pipelines).expect("Channel disconnected");
});
Ok((interface_pipelines, pipeline_creation))
}
/// Creates all the pipelines used to render.
/// Use this to recreate all the pipelines in the background.
/// TODO: report progress
/// NOTE: this tries to use all the CPU cores to complete as soon as possible
pub(super) fn recreate_pipelines(
device: Arc<wgpu::Device>,
layouts: Arc<Layouts>,
shaders: Shaders,
mode: RenderMode,
sc_desc: wgpu::SwapChainDescriptor,
has_shadow_views: bool,
) -> PipelineCreation<Result<(Pipelines, ShadowPipelines), RenderError>> {
prof_span!(_guard, "recreate_pipelines");
// Create threadpool for parallel portion
let pool = rayon::ThreadPoolBuilder::new()
.thread_name(|n| format!("pipeline-recreation-{}", n))
.build()
.unwrap();
let pool = Arc::new(pool);
let send_pool = Arc::clone(&pool);
// Track pipeline creation progress
let progress = Arc::new(Progress::new());
let (result_send, result_recv) = crossbeam_channel::bounded(0);
let pipeline_creation = PipelineCreation {
progress: Arc::clone(&progress),
recv: result_recv,
};
// Start background compilation
pool.spawn(move || {
let pool = &*send_pool;
// Create tasks upfront so the total counter will be accurate
let shader_task = progress.create_task();
let interface_tasks = progress.create_tasks();
let ingame_and_shadow_tasks = progress.create_tasks();
// Process shaders into modules
let guard = shader_task.start("process shaders");
let shader_modules = match ShaderModules::new(&device, &shaders, &mode, has_shadow_views) {
Ok(modules) => modules,
Err(err) => {
result_send.send(Err(err)).expect("Channel disconnected");
return;
},
};
drop(guard);
let needs = PipelineNeeds {
device: &device,
layouts: &layouts,
shaders: &shader_modules,
mode: &mode,
sc_desc: &sc_desc,
};
// Create interface pipelines
let interface = create_interface_pipelines(needs, &pool, interface_tasks);
// Create the rest of the pipelines
let IngameAndShadowPipelines { ingame, shadow } =
create_ingame_and_shadow_pipelines(needs, &pool, ingame_and_shadow_tasks);
// Send them
result_send
.send(Ok((Pipelines::consolidate(interface, ingame), shadow)))
.expect("Channel disconnected");
});
pipeline_creation
}
use core::sync::atomic::{AtomicUsize, Ordering};
/// Represents future task that has not been started
/// Dropping this will mark the task as complete though
struct Task<'a> {
progress: &'a Progress,
}
/// Represents in-progress task, drop when complete
// NOTE: fields are unused because they are only used for their Drop impls
struct StartedTask<'a> {
_span: common_base::ProfSpan,
_task: Task<'a>,
}
#[derive(Default)]
struct Progress {
total: AtomicUsize,
complete: AtomicUsize,
// Note: could easily add a "started counter" if that would be useful
}
impl Progress {
pub fn new() -> Self { Self::default() }
/// Creates a task incrementing the total number of tasks
/// NOTE: all tasks should be created as upfront as possible so that the
/// total reflects the amount of tasks that will need to be completed
pub fn create_task(&self) -> Task {
self.total.fetch_add(1, Ordering::Relaxed);
Task { progress: &self }
}
/// Helper method for creating tasks to do in bulk
pub fn create_tasks<const N: usize>(&self) -> [Task; N] { [(); N].map(|()| self.create_task()) }
}
impl<'a> Task<'a> {
/// Start a task.
/// The name is used for profiling.
fn start(self, _name: &str) -> StartedTask<'a> {
// _name only used when tracy feature is activated
StartedTask {
_span: {
prof_span!(guard, _name);
guard
},
_task: self,
}
}
/// Convenience function to run the provided closure as the task
/// Completing the task when this function returns
fn run<T>(self, task: impl FnOnce() -> T, name: &str) -> T {
let _guard = self.start(name);
task()
}
}
impl Drop for Task<'_> {
fn drop(&mut self) { self.progress.complete.fetch_add(1, Ordering::Relaxed); }
}
pub struct PipelineCreation<T> {
progress: Arc<Progress>,
recv: crossbeam_channel::Receiver<T>,
}
impl<T> PipelineCreation<T> {
/// Returns the number of pipelines being built and completed
/// (total, complete)
/// NOTE: there is no guarantee that `total >= complete` due to relaxed
/// atomics but this property should hold most of the time
pub fn status(&self) -> (usize, usize) {
let progress = &*self.progress;
(
progress.total.load(Ordering::Relaxed),
progress.complete.load(Ordering::Relaxed),
)
}
/// Checks if the pipelines were completed and returns the result if they
/// were
pub fn try_complete(self) -> Result<T, Self> {
use crossbeam_channel::TryRecvError;
match self.recv.try_recv() {
// Yay!
Ok(t) => Ok(t),
// Normal error, we have not gotten anything yet
Err(TryRecvError::Empty) => Err(self),
// How rude!
Err(TryRecvError::Disconnected) => {
panic!(
"Background thread panicked or dropped the sender without sending anything!"
);
},
}
}
}

View File

@ -0,0 +1,185 @@
use super::super::pipelines::blit;
use tracing::error;
pub type ScreenshotFn = Box<dyn FnOnce(image::DynamicImage) + Send>;
pub struct TakeScreenshot {
bind_group: blit::BindGroup,
view: wgpu::TextureView,
texture: wgpu::Texture,
buffer: wgpu::Buffer,
screenshot_fn: ScreenshotFn,
// Dimensions used for copying from the screenshot texture to a buffer
width: u32,
height: u32,
bytes_per_pixel: u8,
}
impl TakeScreenshot {
pub fn new(
device: &wgpu::Device,
blit_layout: &blit::BlitLayout,
sampler: &wgpu::Sampler,
// Used to determine the resolution and texture format
sc_desc: &wgpu::SwapChainDescriptor,
// Function that is given the image after downloading it from the GPU
// This is executed in a background thread
screenshot_fn: ScreenshotFn,
) -> Self {
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("screenshot tex"),
size: wgpu::Extent3d {
width: sc_desc.width,
height: sc_desc.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: sc_desc.format,
usage: wgpu::TextureUsage::COPY_SRC
| wgpu::TextureUsage::SAMPLED
| wgpu::TextureUsage::RENDER_ATTACHMENT,
});
let view = texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("screenshot tex view"),
format: Some(sc_desc.format),
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
mip_level_count: None,
base_array_layer: 0,
array_layer_count: None,
});
let bind_group = blit_layout.bind(device, &view, sampler);
let bytes_per_pixel = sc_desc.format.describe().block_size;
let padded_bytes_per_row = padded_bytes_per_row(sc_desc.width, bytes_per_pixel);
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("screenshot download buffer"),
size: (padded_bytes_per_row * sc_desc.height) as u64,
usage: wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::MAP_READ,
mapped_at_creation: false,
});
Self {
bind_group,
texture,
view,
buffer,
screenshot_fn,
width: sc_desc.width,
height: sc_desc.height,
bytes_per_pixel,
}
}
/// Get the texture view for the screenshot
/// This can then be used as a render attachment
pub fn texture_view(&self) -> &wgpu::TextureView { &self.view }
/// Get the bind group used for blitting the screenshot to the current
/// swapchain image
pub fn bind_group(&self) -> &wgpu::BindGroup { &self.bind_group.bind_group }
/// NOTE: spawns thread
/// Call this after rendering to the screenshot texture
pub fn download_and_handle(self, encoder: &mut wgpu::CommandEncoder) {
// Calculate padded bytes per row
let padded_bytes_per_row = padded_bytes_per_row(self.width, self.bytes_per_pixel);
// Copy image to a buffer
encoder.copy_texture_to_buffer(
wgpu::ImageCopyTexture {
texture: &self.texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
},
wgpu::ImageCopyBuffer {
buffer: &self.buffer,
layout: wgpu::ImageDataLayout {
offset: 0,
bytes_per_row: core::num::NonZeroU32::new(padded_bytes_per_row),
rows_per_image: None,
},
},
wgpu::Extent3d {
width: self.width,
height: self.height,
depth_or_array_layers: 1,
},
);
// Send buffer to another thread for async mapping, downloading, and passing to
// the given handler function (which probably saves it to the disk)
std::thread::Builder::new()
.name("screenshot".into())
.spawn(move || {
self.download_and_handle_internal();
})
.expect("Failed to spawn screenshot thread");
}
fn download_and_handle_internal(self) {
// Calculate padded bytes per row
let padded_bytes_per_row = padded_bytes_per_row(self.width, self.bytes_per_pixel);
let singlethread_rt = match tokio::runtime::Builder::new_current_thread().build() {
Ok(rt) => rt,
Err(err) => {
error!(?err, "Could not create tokio runtime");
return;
},
};
// Map buffer
let buffer_slice = self.buffer.slice(..);
let buffer_map_future = buffer_slice.map_async(wgpu::MapMode::Read);
// Wait on buffer mapping
let pixel_bytes = match singlethread_rt.block_on(buffer_map_future) {
// Buffer is mapped and we can read it
Ok(()) => {
// Copy to a Vec
let padded_buffer = buffer_slice.get_mapped_range();
let mut pixel_bytes = Vec::new();
padded_buffer
.chunks(padded_bytes_per_row as usize)
.map(|padded_chunk| {
&padded_chunk[..self.width as usize * self.bytes_per_pixel as usize]
})
.for_each(|row| pixel_bytes.extend_from_slice(row));
pixel_bytes
},
// Error
Err(err) => {
error!(
?err,
"Failed to map buffer for downloading a screenshot from the GPU"
);
return;
},
};
// Construct image
// TODO: support other formats
let image = image::ImageBuffer::<image::Bgra<u8>, Vec<u8>>::from_vec(
self.width,
self.height,
pixel_bytes,
)
.expect("Failed to create ImageBuffer! Buffer was not large enough. This should not occur");
let image = image::DynamicImage::ImageBgra8(image);
// Call supplied handler
(self.screenshot_fn)(image);
}
}
// Graphics API requires a specific alignment for buffer copies
fn padded_bytes_per_row(width: u32, bytes_per_pixel: u8) -> u32 {
let align = wgpu::COPY_BYTES_PER_ROW_ALIGNMENT;
let unpadded_bytes_per_row = width * bytes_per_pixel as u32;
let padding = (align - unpadded_bytes_per_row % align) % align;
unpadded_bytes_per_row + padding
}

View File

@ -0,0 +1,96 @@
use common::assets::{self, AssetExt, AssetHandle};
use hashbrown::HashMap;
/// Load from a GLSL file.
pub struct Glsl(pub String);
impl From<String> for Glsl {
fn from(s: String) -> Glsl { Glsl(s) }
}
impl assets::Asset for Glsl {
type Loader = assets::LoadFrom<String, assets::StringLoader>;
const EXTENSION: &'static str = "glsl";
}
// Note: we use this clone to send the shaders to a background thread
// TODO: use Arc-ed asset and clone that instead
#[derive(Clone)]
pub struct Shaders {
shaders: HashMap<String, AssetHandle<Glsl>>,
}
impl assets::Compound for Shaders {
// TODO: Taking the specifier argument as a base for shaders specifiers
// would allow to use several shaders groups easily
fn load<S: assets::source::Source>(
_: &assets::AssetCache<S>,
_: &str,
) -> Result<Shaders, assets::Error> {
let shaders = [
"include.constants",
"include.globals",
"include.sky",
"include.light",
"include.srgb",
"include.random",
"include.lod",
"include.shadows",
"antialias.none",
"antialias.fxaa",
"antialias.msaa-x4",
"antialias.msaa-x8",
"antialias.msaa-x16",
"include.cloud.none",
"include.cloud.regular",
"figure-vert",
"light-shadows-figure-vert",
"light-shadows-directed-vert",
"point-light-shadows-vert",
"skybox-vert",
"skybox-frag",
"debug-vert",
"debug-frag",
"figure-frag",
"terrain-vert",
"terrain-frag",
"fluid-vert",
"fluid-frag.cheap",
"fluid-frag.shiny",
"sprite-vert",
"sprite-frag",
"particle-vert",
"particle-frag",
"ui-vert",
"ui-frag",
"lod-terrain-vert",
"lod-terrain-frag",
"clouds-vert",
"clouds-frag",
"postprocess-vert",
"postprocess-frag",
"blit-vert",
"blit-frag",
//"player-shadow-frag",
//"light-shadows-geom",
];
let shaders = shaders
.iter()
.map(|shader| {
let full_specifier = ["voxygen.shaders.", shader].concat();
let asset = AssetExt::load(&full_specifier)?;
Ok((String::from(*shader), asset))
})
.collect::<Result<HashMap<_, _>, assets::Error>>()?;
Ok(Self { shaders })
}
}
impl Shaders {
pub fn get(&self, shader: &str) -> Option<impl core::ops::Deref<Target = Glsl>> {
self.shaders.get(shader).map(|a| a.read())
}
}

Some files were not shown because too many files have changed in this diff Show More