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

This reverts commit e7a766b310, reversing
changes made to 24c256b681.
This commit is contained in:
Avi Weinstock 2021-06-06 12:00:24 -04:00
parent 85b8d4e7c0
commit 917b3e3c44
124 changed files with 6134 additions and 10784 deletions

View File

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

View File

@ -37,7 +37,6 @@ before_script:
- export VELOREN_ASSETS="$(pwd)/assets"
- echo "VELOREN_ASSETS=$VELOREN_ASSETS"
- export RUSTFLAGS="-D warnings"
- export SHADERC_LIB_DIR=/shaderc/combined/
- 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

View File

@ -75,25 +75,17 @@ coverage:
.twindows:
image: registry.gitlab.com/veloren/veloren-docker-ci/cache/release-windows:${CACHE_IMAGE_TAG}
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
- 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
- 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 /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:
paths:
- veloren-server-cli.exe
- veloren-voxygen.exe
- assets/
- LICENSE
- libgcc_s_seh-1.dll
- libstdc++-6.dll
- libwinpthread-1.dll
expire_in: 1 week
.tmacos:

View File

@ -65,8 +65,6 @@ 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
- ChargedRanged attacks (such as some bow attacks) use an FOV zoom effect to indicate charge.
- 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

782
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -98,6 +98,11 @@ incremental = true
inherits = 'release'
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]
systems = ["x86_64-linux"]
@ -108,40 +113,3 @@ key = "veloren-nix.cachix.org-1:zokfKJqVsNV6kI/oJdLF6TYBdNPYGSb+diMVQPn/5Rc="
[workspace.metadata.nix.crateOverride.veloren-network]
buildInputs = ["openssl"]
nativeBuildInputs = ["pkg-config"]
[workspace.metadata.nix.crateOverride.shaderc-sys]
buildInputs = ["shaderc"]
nativeBuildInputs = ["cmake", "python3", "gnumake"]
[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,7 +10,6 @@
"hud.settings.press_behavior.hold": "Hold",
"hud.settings.help_window": "Help Window",
"hud.settings.debug_info": "Debug Info",
"hud.settings.show_hitboxes": "Show hitboxes",
"hud.settings.tips_on_startup": "Tips-On-Startup",
"hud.settings.ui_scale": "UI-Scale",
"hud.settings.relative_scaling": "Relative Scaling",
@ -58,10 +57,6 @@
"hud.settings.sprites_view_distance": "Sprites View Distance",
"hud.settings.figures_view_distance": "Entities View Distance",
"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.gamma": "Gamma",
"hud.settings.exposure": "Exposure",
@ -81,7 +76,6 @@
"hud.settings.fullscreen_mode": "Fullscreen Mode",
"hud.settings.fullscreen_mode.exclusive": "Exclusive",
"hud.settings.fullscreen_mode.borderless": "Borderless",
"hud.settings.gpu_profiler": "Enable GPU timing (not supported everywhere)",
"hud.settings.particles": "Particles",
"hud.settings.lossy_terrain_compression": "Lossy terrain compression",
"hud.settings.resolution": "Resolution",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,16 +0,0 @@
#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

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

View File

@ -1,4 +1,4 @@
#version 420 core
#version 330 core
#include <constants.glsl>
@ -18,17 +18,12 @@
#include <globals.glsl>
layout(location = 0) out vec2 uv;
in vec2 v_pos;
out vec2 f_pos;
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
);
f_pos = v_pos;
// 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);
gl_Position = vec4(v_pos, -1.0, 1.0);
}

View File

@ -1,19 +0,0 @@
#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

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

View File

@ -1,4 +1,4 @@
#version 420 core
#version 330 core
#include <constants.glsl>
@ -17,15 +17,15 @@
#include <globals.glsl>
#include <lod.glsl>
layout(location = 0) in uint v_pos_norm;
layout(location = 1) in uint v_atlas_pos;
in uint v_pos_norm;
in uint v_atlas_pos;
// in vec3 v_norm;
/* in uint v_col;
// out vec3 light_pos[2];
in uint v_ao_bone; */
layout (std140, set = 2, binding = 0)
layout (std140)
uniform u_locals {
mat4 model_mat;
vec4 highlight_col;
@ -40,16 +40,10 @@ uniform u_locals {
struct BoneData {
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;
};
layout (std140, set = 2, binding = 1)
layout (std140)
uniform u_bones {
// Warning: might not actually be 16 elements long. Don't index out of bounds!
BoneData bones[16];
@ -65,11 +59,11 @@ uniform u_bones {
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
//};
layout(location = 0) out vec3 f_pos;
out vec3 f_pos;
// flat out uint f_pos_norm;
layout(location = 1) flat out vec3 f_norm;
flat out vec3 f_norm;
// float dummy;
/*centroid */layout(location = 2) out vec2 f_uv_pos;
/*centroid */out vec2 f_uv_pos;
// out vec3 f_col;
// out float f_ao;
// out float f_alt;
@ -84,14 +78,16 @@ void main() {
/* uint bone_idx = (v_ao_bone >> 2) & 0x3Fu; */
uint bone_idx = (v_pos_norm >> 27) & 0xFu;
// mat4 combined_mat = model_mat * bone_mat;
mat4 bone_mat = bones[bone_idx].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;
// vec4 bone_pos = bones[bone_idx].bone_mat * vec4(pos, 1);
f_pos = (
bones[bone_idx].bone_mat *
combined_mat *
vec4(pos, 1.0)
).xyz + (model_pos - focus_off.xyz);
@ -114,7 +110,7 @@ void main() {
// vec3 norm = normals[normal_idx];
uint axis_idx = v_atlas_pos & 3u;
vec3 norm = bones[bone_idx].normals_mat[axis_idx].xyz;
vec3 norm = normals_mat[axis_idx].xyz;
// norm = normalize(norm);
// vec3 norm = norm_mat * vec4(uvec3(1 << axis_idx) & uvec3(0x1u, 0x3u, 0x7u), 1);

View File

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

View File

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

View File

@ -1,4 +1,4 @@
#version 420 core
#version 330 core
#include <constants.glsl>
@ -20,10 +20,10 @@
#include <srgb.glsl>
#include <random.glsl>
layout(location = 0) in uint v_pos_norm;
in uint v_pos_norm;
// in uint v_col_light;
layout(std140, set = 2, binding = 0)
layout (std140)
uniform u_locals {
vec3 model_offs;
float load_time;
@ -40,8 +40,8 @@ uniform u_locals {
// ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
// };
layout(location = 0) out vec3 f_pos;
layout(location = 1) flat out uint f_pos_norm;
out vec3 f_pos;
flat out uint f_pos_norm;
// out vec3 f_col;
// out float f_light;
// 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)
float mist_min_alt = 0.5;
#if (CLOUD_MODE >= CLOUD_MODE_MEDIUM)
mist_min_alt = (textureLod(sampler2D(t_noise, s_noise), pos.xy / 50000.0, 0).x - 0.5) * 1.5 + 0.5;
mist_min_alt = (texture(t_noise, pos.xy / 50000.0).x - 0.5) * 1.5 + 0.5;
#endif
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;
@ -146,9 +146,9 @@ vec4 cloud_at(vec3 pos, float dist, out vec3 emission) {
#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;
#endif
float tail = (textureLod(sampler2D(t_noise, s_noise), wind_pos.xy * 0.00005, 0).x - 0.5) * 4 + (pos.z - emission_alt) * 0.0001;
float tail = (texture(t_noise, wind_pos.xy * 0.00005).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);
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);
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);
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
float splay = 1.0;
#if (CLOUD_MODE == CLOUD_MODE_MINIMAL)
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);
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);
#endif
/* const float RAYLEIGH = 0.25; */
@ -218,13 +218,12 @@ 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);
vec3 emission;
// `sample` is a reserved keyword
vec4 sample_ = cloud_at(origin + dir * ldist * splay, ldist, emission);
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 moon_access = max(sample_.y, 0);
float sun_access = max(sample.x, 0);
float moon_access = max(sample.y, 0);
float cloud_scatter_factor = density_integrals.x;
float global_scatter_factor = density_integrals.y;

View File

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

View File

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

View File

@ -1,20 +1,15 @@
#ifndef LOD_GLSL
#define LOD_GLSL
#include <random.glsl>
#include <sky.glsl>
#include <srgb.glsl>
layout(set = 0, binding = 5) uniform texture2D t_alt;
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;
uniform sampler2D t_alt;
uniform sampler2D t_horizon;
const float MIN_SHADOW = 0.33;
vec2 pos_to_uv(texture2D tex, sampler s, vec2 pos) {
vec2 pos_to_uv(sampler2D sampler, vec2 pos) {
// Want: (pixel + 0.5) / W
vec2 texSize = textureSize(sampler2D(tex, s), 0);
vec2 texSize = textureSize(sampler, 0);
vec2 uv_pos = (focus_off.xy + pos + 16) / (32.0 * texSize);
return vec2(uv_pos.x, /*1.0 - */uv_pos.y);
}
@ -37,9 +32,8 @@ vec4 cubic(float v) {
}
// NOTE: We assume the sampled coordinates are already in "texture pixels".
vec4 textureBicubic(texture2D tex, sampler sampl, vec2 texCoords) {
// TODO: remove all textureSize calls and replace with constants
vec2 texSize = textureSize(sampler2D(tex, sampl), 0);
vec4 textureBicubic(sampler2D sampler, vec2 texCoords) {
vec2 texSize = textureSize(sampler, 0);
vec2 invTexSize = 1.0 / texSize;
/* texCoords.y = texSize.y - texCoords.y; */
@ -62,57 +56,10 @@ vec4 textureBicubic(texture2D tex, sampler sampl, vec2 texCoords) {
/* // Correct for map rotaton.
offset.zw = 1.0 - offset.zw; */
vec4 sample0 = texture(sampler2D(tex, sampl), offset.xz);
vec4 sample1 = texture(sampler2D(tex, sampl), offset.yz);
vec4 sample2 = texture(sampler2D(tex, sampl), offset.xw);
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 = texture(sampler, offset.xz);
vec4 sample1 = texture(sampler, offset.yz);
vec4 sample2 = texture(sampler, offset.xw);
vec4 sample3 = texture(sampler, offset.yw);
// vec4 sample0 = texelFetch(sampler, offset.xz, 0);
// vec4 sample1 = texelFetch(sampler, offset.yz, 0);
// vec4 sample2 = texelFetch(sampler, offset.xw, 0);
@ -127,9 +74,8 @@ vec2 textureBicubic16(texture2D tex, sampler sampl, vec2 texCoords) {
}
float alt_at(vec2 pos) {
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;
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);
//+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0;
// return 0.0
// + pow(texture(t_noise, pos * 0.00005).x * 1.4, 3.0) * 1000.0
@ -142,7 +88,7 @@ float alt_at_real(vec2 pos) {
// #if (FLUID_MODE == FLUID_MODE_CHEAP)
// return alt_at(pos);
// #elif (FLUID_MODE == FLUID_MODE_SHINY)
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);
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);
// #endif
//+ (texture(t_noise, pos * 0.002).x - 0.5) * 64.0;
@ -258,7 +204,7 @@ vec2 splay(vec2 pos) {
const float SQRT_2 = sqrt(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 + len_pow * (textureSize(sampler2D(t_alt, s_alt), 0) * 32.0/* - view_distance.x*/));
vec2 splayed = pos * (view_distance.x * SQRT_2 + len_pow * (textureSize(t_alt, 0) * 32.0/* - view_distance.x*/));
if (abs(pos.x) > 0.99 || abs(pos.y) > 0.99) {
splayed *= 10.0;
}
@ -334,18 +280,13 @@ vec3 lod_pos(vec2 pos, vec2 focus_pos) {
}
#ifdef HAS_LOD_FULL_INFO
layout(set = 0, binding = 10)
uniform texture2D t_map;
layout(set = 0, binding = 11)
uniform sampler s_map;
uniform sampler2D t_map;
vec3 lod_col(vec2 pos) {
//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*/(textureBicubic(t_map, s_map, pos_to_tex(pos)).rgb)
return /*linear_to_srgb*/(textureBicubic(t_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;
}
#endif
#endif

View File

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

View File

@ -1,29 +1,22 @@
#ifndef SHADOWS_GLSL
#define SHADOWS_GLSL
#ifdef HAS_SHADOW_MAPS
#if (SHADOW_MODE == SHADOW_MODE_MAP)
layout (std140, set = 0, binding = 9)
uniform u_light_shadows {
#if (SHADOW_MODE == SHADOW_MODE_MAP)
struct ShadowLocals {
mat4 shadowMatrices;
mat4 texture_mat;
};
// Use with sampler2DShadow
layout(set = 1, binding = 2)
uniform texture2D t_directed_shadow_maps;
layout(set = 1, binding = 3)
uniform samplerShadow s_directed_shadow_maps;
layout (std140)
uniform u_light_shadows {
ShadowLocals shadowMats[/*MAX_LAYER_FACES*/192];
};
uniform sampler2DShadow t_directed_shadow_maps;
// uniform sampler2DArrayShadow t_directed_shadow_maps;
// uniform samplerCubeArrayShadow t_shadow_maps;
// uniform samplerCubeArray t_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 samplerCubeShadow t_point_shadow_maps;
// uniform samplerCube t_shadow_maps;
// uniform sampler2DArray t_directed_shadow_maps;
@ -42,13 +35,9 @@ 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 = 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;
// NormZComp = -1000.0 / (NormZComp + 10000.0);
// return (NormZComp + 1.0) * 0.5;
return NormZComp;
return (NormZComp + 1.0) * 0.5;
// float NormZComp = length(LocalZcomp);
// NormZComp = -NormZComp / screen_res.w;
@ -75,9 +64,7 @@ float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /
{
float currentDepth = VectorToDepth(fragToLight);// + bias;
// 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*/);
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*/);
/* if (visibility == 1.0 || visibility == 0.0) {
return visibility;
} */
@ -167,45 +154,10 @@ float ShadowCalculationDirected(in vec3 fragPos)//in vec4 /*light_pos[2]*/sun_po
} */
// vec3 fragPos = sun_pos.xyz;// / sun_pos.w;//light_pos[lightIndex].xyz;
// sun_pos.z += sun_pos.w * bias;
vec4 sun_pos = texture_mat/*shadowMatrices*/ * vec4(fragPos, 1.0);
// sun_pos.xy = 0.5 * sun_pos.w + sun_pos.xy * 0.5;
// sun_pos.xy = sun_pos.ww - sun_pos.xy;
// 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);
mat4 texture_mat = shadowMats[0].texture_mat;
vec4 sun_pos = texture_mat * vec4(fragPos, 1.0);
// sun_pos.z -= sun_pos.w * bias;
float visibility = textureProj(t_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 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));
@ -264,5 +216,3 @@ float ShadowCalculationPoint(uint lightIndex, vec3 fragToLight, vec3 fragNorm, /
return 1.0;
}
#endif
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
#version 420 core
#version 330 core
#include <constants.glsl>
@ -23,14 +23,14 @@
#include <cloud.glsl>
#include <lod.glsl>
layout(location = 0) in vec3 f_pos;
layout(location = 1) in vec3 f_norm;
layout(location = 2) in float pull_down;
in vec3 f_pos;
in vec3 f_norm;
in float pull_down;
// in vec2 v_pos_orig;
// in vec4 f_shadow;
// in vec4 f_square;
layout(location = 0) out vec4 tgt_color;
out vec4 tgt_color;
/// const vec4 sun_pos = vec4(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
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP)
vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy));
vec4 f_shadow = textureBicubic(t_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 = 1.0;
#elif (SHADOW_MODE == SHADOW_MODE_NONE)

View File

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

View File

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

View File

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

View File

@ -1,61 +0,0 @@
#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 420 core
#version 330 core
#include <constants.glsl>
@ -22,21 +22,17 @@
#include <srgb.glsl>
#include <cloud.glsl>
layout(set = 1, binding = 0)
uniform texture2D t_src_color;
layout(set = 1, binding = 1)
uniform sampler s_src_color;
//uniform sampler2D src_depth;
in vec2 f_pos;
layout(location = 0) in vec2 uv;
layout (std140, set = 1, binding = 2)
layout (std140)
uniform u_locals {
mat4 proj_mat_inv;
mat4 view_mat_inv;
};
layout(location = 0) out vec4 tgt_color;
out vec4 tgt_color;
vec3 rgb2hsv(vec3 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
@ -149,7 +145,33 @@ 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)));
}
/*
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() {
vec2 uv = (f_pos + 1.0) * 0.5;
/* 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);
} */
@ -180,7 +202,7 @@ void main() {
// float bright_color = (bright_color0 + bright_color1 + bright_color2 + bright_color3 + bright_color4) / 5.0;
vec4 aa_color = aa_apply(t_src_color, s_src_color, uv * screen_res.xy, screen_res.xy);
vec4 aa_color = aa_apply(src_color, uv * screen_res.xy, screen_res.xy);
// Tonemapping
float exposure_offset = 1.0;

View File

@ -1,4 +1,4 @@
#version 420 core
#version 330 core
#include <constants.glsl>
@ -18,16 +18,12 @@
#include <globals.glsl>
layout(location = 0) out vec2 uv;
in vec2 v_pos;
out vec2 f_pos;
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
);
f_pos = v_pos;
uv = (v_pos * vec2(1.0, -1.0) + 1.0) * 0.5;
gl_Position = vec4(v_pos, 0.0, 1.0);
gl_Position = vec4(v_pos, -1.0, 1.0);
}

View File

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

View File

@ -1,4 +1,4 @@
#version 420 core
#version 330 core
#include <constants.glsl>
@ -18,28 +18,31 @@
#include <globals.glsl>
layout(location = 0) in vec3 v_pos;
in vec3 v_pos;
layout(location = 0) out vec3 f_pos;
layout (std140)
uniform u_locals {
vec4 nul;
};
out vec3 f_pos;
void main() {
/* vec3 v_pos = v_pos;
v_pos.y = -v_pos.y; */
f_pos = v_pos;
// 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 =
/* proj_mat *
view_mat * */
all_mat *
vec4(v_pos + cam_pos.xyz, 1);
/* proj_mat *
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.z = 0;
gl_Position.z = gl_Position.w;
// gl_Position.z = gl_Position.w - 0.000001;//0.0;
// gl_Position.z = 1.0;
// gl_Position.z = -1.0;

View File

@ -1,4 +1,4 @@
#version 420 core
#version 330 core
#include <constants.glsl>
@ -16,18 +16,31 @@
#include <globals.glsl>
layout(location = 0) in vec3 f_pos;
layout(location = 1) flat in vec3 f_norm;
layout(location = 2) flat in float f_select;
layout(location = 3) in vec2 f_uv_pos;
layout(location = 4) in vec2 f_inst_light;
in vec3 f_pos;
flat in vec3 f_norm;
flat in float f_select;
// flat in vec3 f_pos_norm;
in vec2 f_uv_pos;
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];
layout(set = 3, binding = 0)
uniform texture2D t_col_light;
layout(set = 3, binding = 1)
uniform sampler s_col_light;
uniform sampler2D t_col_light;
layout(location = 0) out vec4 tgt_color;
//struct ShadowLocals {
// 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 <light.glsl>
@ -36,31 +49,77 @@ layout(location = 0) out vec4 tgt_color;
const float FADE_DIST = 32.0;
void main() {
float 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);
/* 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;
vec3 f_col = greedy_extract_col_light_glow(t_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);
// 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 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)
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)
float f_alt = f_pos.z;
#endif
#if (SHADOW_MODE == SHADOW_MODE_CHEAP || SHADOW_MODE == SHADOW_MODE_MAP)
vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy));
vec4 f_shadow = textureBicubic(t_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 = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
#elif (SHADOW_MODE == SHADOW_MODE_NONE)
float sun_shade_frac = 1.0;
float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
#endif
float moon_shade_frac = 1.0;
float moon_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, moon_dir);
// 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);
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);
DirectionalLight sun_info = get_sun_info(sun_dir, point_shadow * sun_shade_frac, /*sun_pos*/f_pos);
DirectionalLight moon_info = get_moon_info(moon_dir, point_shadow * moon_shade_frac/*, light_pos*/);
vec3 surf_color = f_col;
vec3 surf_color = /*srgb_to_linear*//*linear_to_srgb*/(f_col);
float alpha = 1.0;
const float n2 = 1.5;
const float R_s2s0 = pow((1.0 - n2) / (1.0 + n2), 2);
@ -79,11 +138,42 @@ void main() {
sun_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;
max_light += get_sun_diffuse2(sun_info, moon_info, f_norm, view_dir, k_a, k_d, k_s, alpha, emitted_light, reflected_light);
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);
// 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);
/* 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);
emitted_light += glow;
@ -92,9 +182,10 @@ void main() {
reflected_light *= ao;
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);
// 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(-f_norm, 1.0);
}

View File

@ -1,4 +1,4 @@
#version 420 core
#version 330 core
#include <constants.glsl>
@ -16,114 +16,227 @@
#include <srgb.glsl>
#include <sky.glsl>
layout(location = 0) in vec4 inst_mat0;
layout(location = 1) in vec4 inst_mat1;
layout(location = 2) in vec4 inst_mat2;
layout(location = 3) in vec4 inst_mat3;
// TODO: is there a better way to pack the various vertex attributes?
// TODO: ori is unused
layout(location = 4) in uint inst_pos_ori;
layout(location = 5) in uint inst_vert_page; // NOTE: this could fit in less bits
// TODO: do we need this many bits for light and glow?
layout(location = 6) in float inst_light;
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
in vec3 v_pos;
in uint v_atlas_pos;
// in uint v_col;
in uint v_norm_ao;
in uint inst_pos_ori;
in vec4 inst_mat0;
in vec4 inst_mat1;
in vec4 inst_mat2;
in vec4 inst_mat3;
in vec4 inst_light;
in float inst_wind_sway;
layout(set = 0, binding = 12) restrict readonly buffer sprite_verts {
uvec2 verts[];
struct SpriteLocals {
mat4 mat;
vec4 wind_sway;
vec4 offs;
};
layout (std140, set = 2, binding = 0)
layout (std140)
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 {
vec3 model_offs;
float load_time;
ivec4 atlas_offs;
};
// TODO: consider grouping into vec4's
layout(location = 0) out vec3 f_pos;
layout(location = 1) flat out vec3 f_norm;
layout(location = 2) flat out float f_select;
layout(location = 3) out vec2 f_uv_pos;
layout(location = 4) out vec2 f_inst_light;
out vec3 f_pos;
flat out vec3 f_norm;
flat out float f_select;
// flat out vec3 f_pos_norm;
// out vec3 f_col;
// out float f_ao;
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_FACTOR = pow(SCALE, 1.3) * 0.2;
const int EXTRA_NEG_Z = 32768;
const int VERT_EXTRA_NEG_Z = 128;
const uint VERT_PAGE_SIZE = 256;
void main() {
// Matrix to transform this sprite instance from model space to chunk space
// vec3 inst_chunk_pos = vec3(ivec3((uvec3(inst_pos_ori) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z));
// 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;
inst_mat[0] = inst_mat0;
inst_mat[1] = inst_mat1;
inst_mat[2] = inst_mat2;
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;
// Worldpos of the chunk that this sprite is in
vec3 chunk_offs = model_offs - focus_off.xyz;
f_inst_light = inst_light.xy;
f_inst_light = vec2(inst_light, inst_glow);
// vec3 sprite_pos = floor(inst_mat3.xyz * SCALE) + inst_offs;
// 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;
// f_pos_norm = v_pos;
// Expand the model vertex position bits into float values
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 = (inst_mat * vec4(0, 0, 0, 1)).xyz;
// vec3 sprite_pos = floor((inst_mat * vec4(0, 0, 0, 1)).xyz * SCALE/* - vec3(0.5, 0.5, 0.0)*/) + inst_offs;
// 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;
// Transform into chunk space and scale
f_pos = (inst_mat * vec4(v_pos, 1.0)).xyz;
// Transform info world space
f_pos += chunk_offs;
// vec3 v_pos = vec3(gl_VertexID * 32, gl_VertexID % 32, 1.0);
// f_pos = v_pos + (model_offs - focus_off.xyz);
// vec3 v_pos = /*inst_mat*//*locals.*/wind_sway.xyz * v_pos;
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
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;
// Wind sway effect
f_pos += model_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
// 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;
// f_pos = (inst_mat * vec4(v_pos * SCALE, 1)).xyz + (model_offs - focus_off.xyz);
// f_pos = v_pos_ + (inst_chunk_pos + model_offs - focus_off.xyz + vec3(0.5, 0.5, 0.0));
// f_pos.z -= min(32.0, 25.0 * pow(distance(focus_pos.xy, f_pos.xy) / view_distance.x, 20.0));
// 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);
// 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.x * 0.1) * sin(tick.x * 0.25),
0.0
) * 4 * v_pos_.z * /*0.2;*/SCALE_FACTOR;
}
f_norm = normalize(mix(-norm, norm, v_pos_norm >> 29u & 1u));
// First 3 normals are negative, next 3 are positive
// 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;
// Expand atlas tex coords to floats
// NOTE: Could defer to fragment shader if we are vert heavy
f_uv_pos = vec2((uvec2(v_atlas_pos) >> uvec2(0, 16)) & uvec2(0xFFFFu, 0xFFFFu));;
// TODO: Consider adding a second, already-normalized (i.e. unscaled) matrix.
// vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u].xyz);
// vec3 norm = /*normalize*/(inst_mat/*locals.mat*/[(v_norm_ao >> 1u) & 3u]);
// Position of the sprite block in the chunk
// Used solely for highlighting the selected sprite
vec3 inst_chunk_pos = vec3(ivec3((uvec3(inst_pos_ori) >> uvec3(0, 6, 12)) & uvec3(0x3Fu, 0x3Fu, 0xFFFFu)) - ivec3(0, 0, EXTRA_NEG_Z));
// Select glowing
vec3 sprite_pos = inst_chunk_pos + chunk_offs;
f_select = (select_pos.w > 0 && select_pos.xyz == sprite_pos) ? 1.0 : 0.0;
// vec3 norm = bone_data.normals_mat[axis_idx].xyz;
// norm = normalize(norm);
// norm = norm / SCALE_FACTOR / locals.wind_sway.xyz;
// norm = norm / (norm.x + norm.y + norm.z);
// vec3 norm = norm_mat * vec4(uvec3(1 << axis_idx) & uvec3(0x1u, 0x3u, 0x7u), 1);
// // 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 =
all_mat *
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 420 core
#version 330 core
// #extension GL_ARB_texture_storage : require
#include <constants.glsl>
@ -22,12 +22,12 @@
#include <globals.glsl>
#include <random.glsl>
layout(location = 0) in vec3 f_pos;
in vec3 f_pos;
// in float f_ao;
// in vec3 f_chunk_pos;
// #ifdef FLUID_MODE_SHINY
layout(location = 1) flat in uint f_pos_norm;
layout(location = 2) flat in float f_load_time;
flat in uint f_pos_norm;
flat in float f_load_time;
// #else
// const uint f_pos_norm = 0u;
// #endif
@ -35,7 +35,7 @@ layout(location = 2) flat in float f_load_time;
// in vec4 f_shadow;
// in vec3 f_col;
// in float f_light;
/*centroid */layout(location = 3) in vec2 f_uv_pos;
/*centroid */in vec2 f_uv_pos;
// in vec3 light_pos[2];
// const vec3 light_pos[6] = vec3[](vec3(0), vec3(0), vec3(00), vec3(0), vec3(0), vec3(0));
@ -45,19 +45,16 @@ in vec4 sun_pos;
const vec4 sun_pos = vec4(0.0);
#endif */
layout(set = 3, binding = 0)
uniform texture2D t_col_light;
layout(set = 3, binding = 1)
uniform sampler s_col_light;
uniform sampler2D t_col_light;
layout (std140, set = 2, binding = 0)
layout (std140)
uniform u_locals {
vec3 model_offs;
float load_time;
ivec4 atlas_offs;
};
layout(location = 0) out vec4 tgt_color;
out vec4 tgt_color;
#include <sky.glsl>
#include <light.glsl>
@ -85,7 +82,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);
// 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;
vec3 f_col = greedy_extract_col_light_glow(t_col_light, s_col_light, f_uv_pos, f_light, f_glow);
vec3 f_col = greedy_extract_col_light_glow(t_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;
// 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;
@ -219,7 +216,7 @@ void main() {
// float f_alt = alt_at(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)
vec4 f_shadow = textureBicubic(t_horizon, s_horizon, pos_to_tex(f_pos.xy));
vec4 f_shadow = textureBicubic(t_horizon, pos_to_tex(f_pos.xy));
float sun_shade_frac = horizon_at2(f_shadow, f_alt, f_pos, sun_dir);
#elif (SHADOW_MODE == SHADOW_MODE_NONE)
float sun_shade_frac = 1.0;//horizon_at2(f_shadow, f_alt, f_pos, sun_dir);

View File

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

View File

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

View File

@ -1,26 +1,23 @@
#version 420 core
#version 330 core
#include <globals.glsl>
layout(location = 0) in vec2 v_pos;
layout(location = 1) in vec2 v_uv;
layout(location = 2) in vec4 v_color;
layout(location = 3) in vec2 v_center;
layout(location = 4) in uint v_mode;
in vec2 v_pos;
in vec2 v_uv;
in vec2 v_center;
in vec4 v_color;
in uint v_mode;
layout (std140, set = 1, binding = 0)
layout (std140)
uniform u_locals {
vec4 w_pos;
};
layout(set = 2, binding = 0)
uniform texture2D t_tex;
layout(set = 2, binding = 1)
uniform sampler s_tex;
uniform sampler2D u_tex;
layout(location = 0) out vec2 f_uv;
layout(location = 1) out vec4 f_color;
layout(location = 2) flat out uint f_mode;
out vec2 f_uv;
flat out uint f_mode;
out vec4 f_color;
void main() {
f_color = v_color;
@ -33,13 +30,13 @@ void main() {
f_uv = v_uv;
// Fixed scale In-game element
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*/, 0.5, /*projected_pos.w*/1.0);
gl_Position = vec4(projected_pos.xy / projected_pos.w + v_pos/* * projected_pos.w*/, -1.0, /*projected_pos.w*/1.0);
} else if (v_mode == uint(3)) {
// HACK: North facing source rectangle.
gl_Position = vec4(v_pos, 0.5, 1.0);
gl_Position = vec4(v_pos, -1.0, 1.0);
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).
vec2 aspect_ratio = textureSize(sampler2D(t_tex, s_tex), 0).yx;
vec2 aspect_ratio = textureSize(u_tex, 0).yx;
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_rotated = look_at * v_centered;
@ -53,12 +50,11 @@ void main() {
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_rotated = look_at * v_centered;
gl_Position = vec4(aspect_ratio * v_rotated + v_center, 0.5, 1.0);
gl_Position = vec4(aspect_ratio * v_rotated + v_center, -1.0, 1.0);
} else {
// Interface element
f_uv = v_uv;
gl_Position = vec4(v_pos, 0.5, 1.0);
gl_Position = vec4(v_pos, -1.0, 1.0);
}
f_mode = v_mode;
}

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

Binary file not shown.

View File

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

View File

@ -47,6 +47,7 @@ where
// this crate would be veloren_voxygen=debug.
let base_exceptions = |env: EnvFilter| {
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_world::sim=info".parse().unwrap())
.add_directive("veloren_world::civ=info".parse().unwrap())
@ -57,8 +58,6 @@ where
.add_directive("h2=info".parse().unwrap())
.add_directive("tokio_util=info".parse().unwrap())
.add_directive("rustls=info".parse().unwrap())
.add_directive("wgpu_core=info".parse().unwrap())
.add_directive("wgpu_core::device=warn".parse().unwrap())
.add_directive("veloren_network_protocol=info".parse().unwrap())
.add_directive("quinn_proto::connection=info".parse().unwrap())
.add_directive(

View File

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

View File

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

View File

@ -74,19 +74,16 @@ pub use dyn_lib::init;
use std::ffi::CStr;
use self::vek::*;
use bytemuck::{Pod, Zeroable};
type MatRaw = [[f32; 4]; 4];
#[repr(C)]
#[derive(Debug, Clone, Copy, Pod, Zeroable, Default)]
pub struct FigureBoneData(pub MatRaw, pub MatRaw);
pub type FigureBoneData = (MatRaw, MatRaw);
pub const MAX_BONE_COUNT: usize = 16;
fn make_bone(mat: Mat4<f32>) -> FigureBoneData {
let normal = mat.map_cols(Vec4::normalized);
FigureBoneData(mat.into_col_arrays(), normal.into_col_arrays())
(mat.into_col_arrays(), normal.into_col_arrays())
}
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 std::sync::Arc;
use vek::*;
use veloren_voxygen::{mesh::terrain::generate_mesh, scene::terrain::BlocksOfInterest};
use veloren_voxygen::{mesh::Meshable, scene::terrain::BlocksOfInterest};
use world::{sim, World};
const CENTER: Vec2<i32> = Vec2 { x: 512, y: 512 };
@ -142,10 +142,11 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let (volume, range) = sample(Vec2::new(x, y));
meshing_benches.bench_function(&format!("Terrain mesh {}, {}", x, y), move |b| {
b.iter(|| {
generate_mesh(
black_box(&volume),
black_box((range, Vec2::new(8192, 8192), &BlocksOfInterest::default())),
)
volume.generate_mesh(black_box((
range,
Vec2::new(8192, 8192),
&BlocksOfInterest::default(),
)))
})
});
}

View File

@ -55,7 +55,7 @@ use crate::{
ecs::{comp as vcomp, comp::HpFloaterList},
hud::{img_ids::ImgsRot, prompt_dialog::DialogOutcomeEvent},
i18n::Localization,
render::UiDrawer,
render::{Consts, Globals, Renderer},
scene::camera::{self, Camera},
session::{
settings_change::{Chat as ChatChange, Interface as InterfaceChange, SettingsChange},
@ -238,7 +238,6 @@ widget_ids! {
num_lights,
num_figures,
num_particles,
gpu_timings[],
// Game Version
version,
@ -2195,33 +2194,6 @@ impl Hud {
.font_size(self.fonts.cyri.scale(14))
.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
if let Some(help_key) = global_state.settings.controls.get_binding(GameInput::Help) {
Text::new(
@ -2230,7 +2202,7 @@ impl Hud {
.replace("{key}", help_key.display_string(key_layout).as_str()),
)
.color(TEXT_COLOR)
.down(5.0)
.down_from(self.ids.num_particles, 5.0)
.font_id(self.fonts.cyri.conrod_id)
.font_size(self.fonts.cyri.scale(14))
.set(self.ids.help_info, ui_widgets);
@ -3647,11 +3619,11 @@ impl Hud {
events
}
pub fn render<'a>(&'a self, drawer: &mut UiDrawer<'_, 'a>) {
pub fn render(&self, renderer: &mut Renderer, globals: &Consts<Globals>) {
span!(_guard, "render", "Hud::render");
// Don't show anything if the UI is toggled off.
if self.show.ui {
self.ui.render(drawer);
self.ui.render(renderer, Some(globals));
}
}

View File

@ -36,8 +36,6 @@ widget_ids! {
load_tips_button_label,
debug_button,
debug_button_label,
hitboxes_button,
hitboxes_button_label,
ch_title,
ch_transp_slider,
ch_transp_value,
@ -241,33 +239,9 @@ impl<'a> Widget for Interface<'a> {
.color(TEXT_COLOR)
.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
Text::new(&self.localized_strings.get("hud.settings.ui_scale"))
.down_from(state.ids.hitboxes_button, 20.0)
.down_from(state.ids.debug_button, 20.0)
.font_size(self.fonts.cyri.scale(18))
.font_id(self.fonts.cyri.conrod_id)
.color(TEXT_COLOR)

View File

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

View File

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

View File

@ -20,7 +20,6 @@ pub struct CharSelectionState {
char_selection_ui: CharSelectionUi,
client: Rc<RefCell<Client>>,
scene: Scene,
need_shadow_clear: bool,
}
impl CharSelectionState {
@ -37,7 +36,6 @@ impl CharSelectionState {
char_selection_ui,
client,
scene,
need_shadow_clear: false,
}
}
@ -73,9 +71,6 @@ impl PlayState for CharSelectionState {
// Set scale mode in case it was change
self.char_selection_ui
.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 {
@ -235,39 +230,15 @@ impl PlayState for CharSelectionState {
fn capped_fps(&self) -> bool { true }
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 (humanoid_body, loadout) =
Self::get_humanoid_body_inventory(&self.char_selection_ui, &client);
if let Some(mut first_pass) = drawer.first_pass() {
self.scene
.render(&mut first_pass, client.get_tick(), humanoid_body, loadout);
}
// Render the scene.
self.scene
.render(renderer, 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.
if let Some(mut ui_drawer) = third_pass.draw_ui() {
self.char_selection_ui.render(&mut ui_drawer);
};
self.char_selection_ui.render(renderer);
}
}

View File

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

View File

@ -1,5 +1,4 @@
mod client_init;
mod scene;
mod ui;
use super::char_selection::CharSelectionState;
@ -12,41 +11,20 @@ use crate::{
use client::{
addr::ConnectionArgs,
error::{InitProtocolError, NetworkConnectError, NetworkError},
Client, ServerInfo,
ServerInfo,
};
use client_init::{ClientInit, Error as InitError, Msg as InitMsg};
use common::comp;
use common_base::span;
use scene::Scene;
use std::sync::Arc;
use tokio::runtime;
use tracing::error;
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 {
main_menu_ui: MainMenuUi,
init: InitState,
scene: Scene,
// Used for client creation.
client_init: Option<ClientInit>,
}
impl MainMenuState {
@ -54,8 +32,7 @@ impl MainMenuState {
pub fn new(global_state: &mut GlobalState) -> Self {
Self {
main_menu_ui: MainMenuUi::new(global_state),
init: InitState::None,
scene: Scene::new(global_state.window.renderer_mut()),
client_init: None,
}
}
}
@ -97,14 +74,14 @@ impl PlayState for MainMenuState {
"singleplayer".to_owned(),
"".to_owned(),
ConnectionArgs::Mpsc(14004),
&mut self.init,
&mut self.client_init,
Some(runtime),
);
},
Ok(Err(e)) => {
error!(?e, "Could not start server");
global_state.singleplayer = None;
self.init = InitState::None;
self.client_init = None;
self.main_menu_ui.cancel_connection();
self.main_menu_ui.show_info(format!("Error: {:?}", e));
},
@ -127,14 +104,19 @@ impl PlayState for MainMenuState {
}
}
// Poll client creation.
match self.init.client().and_then(|init| init.poll()) {
match self.client_init.as_ref().and_then(|init| init.poll()) {
Some(InitMsg::Done(Ok(mut client))) => {
self.client_init = None;
self.main_menu_ui.connected();
// Register voxygen components / resources
crate::ecs::init(client.state_mut().ecs_mut());
self.init = InitState::Pipeline(Box::new(client));
return PlayStateResult::Push(Box::new(CharSelectionState::new(
global_state,
std::rc::Rc::new(std::cell::RefCell::new(client)),
)));
},
Some(InitMsg::Done(Err(e))) => {
self.init = InitState::None;
self.client_init = None;
tracing::trace!(?e, "raw Client Init error");
let e = get_client_msg_error(e, &global_state.i18n);
// Log error for possible additional use later or incase that the error
@ -150,7 +132,10 @@ impl PlayState for MainMenuState {
.contains(&auth_server)
{
// Can't fail since we just polled it, it must be Some
self.init.client().unwrap().auth_trust(auth_server, true);
self.client_init
.as_ref()
.unwrap()
.auth_trust(auth_server, true);
} else {
// Show warning that auth server is not trusted and prompt for approval
self.main_menu_ui.auth_trust_prompt(auth_server);
@ -159,64 +144,6 @@ impl PlayState for MainMenuState {
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.
for event in self
.main_menu_ui
@ -253,19 +180,19 @@ impl PlayState for MainMenuState {
username,
password,
connection_args,
&mut self.init,
&mut self.client_init,
None,
);
},
MainMenuEvent::CancelLoginAttempt => {
// init contains InitState::Client(ClientInit), which spawns a thread which
// contains a TcpStream::connect() call This call is
// blocking TODO fix when the network rework happens
// client_init contains Some(ClientInit), which spawns a thread which contains a
// TcpStream::connect() call This call is blocking
// TODO fix when the network rework happens
#[cfg(feature = "singleplayer")]
{
global_state.singleplayer = None;
}
self.init = InitState::None;
self.client_init = None;
self.main_menu_ui.cancel_connection();
},
MainMenuEvent::ChangeLanguage(new_language) => {
@ -301,8 +228,8 @@ impl PlayState for MainMenuState {
.insert(auth_server.clone());
global_state.settings.save_to_file_warn();
}
self.init
.client()
self.client_init
.as_ref()
.map(|init| init.auth_trust(auth_server, trust));
},
}
@ -320,19 +247,8 @@ impl PlayState for MainMenuState {
fn capped_fps(&self) -> bool { true }
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.
if let Some(mut ui_drawer) = drawer.third_pass().draw_ui() {
self.main_menu_ui.render(&mut ui_drawer);
};
self.main_menu_ui.render(renderer);
}
}
@ -440,7 +356,7 @@ fn attempt_login(
username: String,
password: String,
connection_args: ConnectionArgs,
init: &mut InitState,
client_init: &mut Option<ClientInit>,
runtime: Option<Arc<runtime::Runtime>>,
) {
if let Err(err) = comp::Player::alias_validate(&username) {
@ -449,8 +365,8 @@ fn attempt_login(
}
// Don't try to connect if there is already a connection in progress.
if let InitState::None = init {
*init = InitState::Client(ClientInit::new(
if client_init.is_none() {
*client_init = Some(ClientInit::new(
connection_args,
username,
password,

View File

@ -1,28 +0,0 @@
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::{
i18n::{LanguageMetadata, LocalizationHandle},
render::UiDrawer,
render::Renderer,
ui::{
self,
fonts::IcedFonts as Fonts,
@ -477,7 +477,7 @@ pub struct MainMenuUi {
controls: Controls,
}
impl MainMenuUi {
impl<'a> MainMenuUi {
pub fn new(global_state: &mut GlobalState) -> Self {
// Load language
let i18n = &global_state.i18n.read();
@ -583,5 +583,5 @@ impl MainMenuUi {
events
}
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); }
}

View File

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

View File

@ -2,6 +2,25 @@ pub mod greedy;
pub mod segment;
pub mod terrain;
use crate::render::Mesh;
use crate::render::{self, Mesh};
pub type MeshGen<V, T, S, R> = (Mesh<V>, Mesh<T>, Mesh<S>, R);
pub type MeshGen<P, T, M> = (
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,9 +1,12 @@
use crate::{
mesh::{
greedy::{self, GreedyConfig, GreedyMesh},
MeshGen,
MeshGen, Meshable,
},
render::{
self, FigurePipeline, Mesh, ParticlePipeline, ShadowPipeline, SpritePipeline,
TerrainPipeline,
},
render::{Mesh, ParticleVertex, SpriteVertex, TerrainVertex},
scene::math,
};
use common::{
@ -13,315 +16,353 @@ use common::{
use core::convert::TryFrom;
use vek::*;
// /// NOTE: bone_idx must be in [0, 15] (may be bumped to [0, 31] at some
// /// point).
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
// TODO: this function name...
pub fn generate_mesh_base_vol_terrain<'a: 'b, 'b, V: 'a>(
vol: V,
(greedy, opaque_mesh, offs, scale, bone_idx): (
type SpriteVertex = <SpritePipeline as render::Pipeline>::Vertex;
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type ParticleVertex = <ParticlePipeline as render::Pipeline>::Vertex;
impl<'a: 'b, 'b, V: 'a> Meshable<FigurePipeline, &'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 = 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 Mesh<TerrainVertex>,
&'b mut Mesh<Self::Pipeline>,
Vec3<f32>,
Vec3<f32>,
u8,
),
) -> MeshGen<TerrainVertex, TerrainVertex, TerrainVertex, math::Aabb<f32>>
where
V: BaseVol<Vox = Cell> + ReadVol + SizedVol,
{
assert!(bone_idx <= 15, "Bone index for figures must be in [0, 15]");
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 15);
let lower_bound = vol.lower_bound();
let upper_bound = vol.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
// NOTE: Figure sizes should be no more than 512 along each axis.
let greedy_size = upper_bound - lower_bound + 1;
assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512);
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = greedy_size;
let draw_delta = lower_bound;
type TranslucentPipeline = FigurePipeline;
let get_light = |vol: &mut V, pos: Vec3<i32>| {
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
1.0
} else {
0.0
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
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]");
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 15);
let lower_bound = self.lower_bound();
let upper_bound = self.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
// NOTE: Figure sizes should be no more than 512 along each axis.
let greedy_size = upper_bound - lower_bound + 1;
assert!(greedy_size.x <= 512 && greedy_size.y <= 512 && greedy_size.z <= 512);
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = greedy_size;
let draw_delta = lower_bound;
let get_light = |vol: &mut V, pos: Vec3<i32>| {
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
1.0
} else {
0.0
}
};
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
let get_opacity =
|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| {
should_draw_greedy(pos, delta, uv, |vox| {
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
})
};
let create_opaque = |atlas_pos, pos, norm| {
TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx)
};
greedy.push(GreedyConfig {
data: self,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
));
},
make_face_texel: |vol: &mut V, pos, light, _| {
let cell = vol.get(pos).ok();
let (glowy, shiny) = cell
.map(|c| (c.is_glowy(), c.is_shiny()))
.unwrap_or_default();
let col = cell.and_then(|vox| vox.get_color()).unwrap_or(Rgb::zero());
TerrainVertex::make_col_light_figure(light, glowy, shiny, col)
},
});
let bounds = math::Aabb {
// NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16.
min: math::Vec3::from((lower_bound.as_::<f32>() + offs) * scale),
max: math::Vec3::from((upper_bound.as_::<f32>() + offs) * scale),
}
};
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
let get_opacity = |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| {
should_draw_greedy(pos, delta, uv, |vox| {
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
})
};
let create_opaque = |atlas_pos, pos, norm| {
TerrainVertex::new_figure(atlas_pos, (pos + offs) * scale, norm, bone_idx)
};
.made_valid();
greedy.push(GreedyConfig {
data: vol,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
));
},
make_face_texel: |vol: &mut V, pos, light, _| {
let cell = vol.get(pos).ok();
let (glowy, shiny) = cell
.map(|c| (c.is_glowy(), c.is_shiny()))
.unwrap_or_default();
let col = cell.and_then(|vox| vox.get_color()).unwrap_or(Rgb::zero());
TerrainVertex::make_col_light_figure(light, glowy, shiny, col)
},
});
let bounds = math::Aabb {
// NOTE: Casts are safe since lower_bound and upper_bound both fit in a i16.
min: math::Vec3::from((lower_bound.as_::<f32>() + offs) * scale),
max: math::Vec3::from((upper_bound.as_::<f32>() + offs) * scale),
(Mesh::new(), Mesh::new(), Mesh::new(), bounds)
}
.made_valid();
(Mesh::new(), Mesh::new(), Mesh::new(), bounds)
}
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
pub fn generate_mesh_base_vol_sprite<'a: 'b, 'b, V: 'a>(
vol: V,
(greedy, opaque_mesh, vertical_stripes): (
&'b mut GreedyMesh<'a>,
&'b mut Mesh<SpriteVertex>,
bool,
),
) -> MeshGen<SpriteVertex, SpriteVertex, TerrainVertex, ()>
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>, */
{
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 16);
type Pipeline = SpritePipeline;
type Result = ();
type ShadowPipeline = ShadowPipeline;
type Supplement = (&'b mut GreedyMesh<'a>, &'b mut Mesh<Self::Pipeline>, bool);
type TranslucentPipeline = SpritePipeline;
let lower_bound = vol.lower_bound();
let upper_bound = vol.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
// Lower bound coordinates must fit in an i16 (which means upper bound
// coordinates fit as integers in a f23).
assert!(
i16::try_from(lower_bound.x).is_ok()
&& i16::try_from(lower_bound.y).is_ok()
&& i16::try_from(lower_bound.z).is_ok(),
"Sprite offsets should fit in i16",
);
let greedy_size = upper_bound - lower_bound + 1;
// TODO: Should this be 16, 16, 64?
assert!(
greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 64,
"Sprite size out of bounds: {:?} ≤ (31, 31, 63)",
greedy_size - 1
);
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
fn generate_mesh(
self,
(greedy, opaque_mesh, vertical_stripes): Self::Supplement,
) -> MeshGen<SpritePipeline, &'b mut GreedyMesh<'a>, Self> {
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 16);
let (flat, flat_get) = {
let (w, h, d) = (greedy_size + 2).into_tuple();
let flat = {
let mut flat = vec![Cell::empty(); (w * h * d) as usize];
let mut i = 0;
for x in -1..greedy_size.x + 1 {
for y in -1..greedy_size.y + 1 {
for z in -1..greedy_size.z + 1 {
let wpos = lower_bound + Vec3::new(x, y, z);
let block = vol.get(wpos).map(|b| *b).unwrap_or(Cell::empty());
flat[i] = block;
i += 1;
let lower_bound = self.lower_bound();
let upper_bound = self.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
// Lower bound coordinates must fit in an i16 (which means upper bound
// coordinates fit as integers in a f23).
assert!(
i16::try_from(lower_bound.x).is_ok()
&& i16::try_from(lower_bound.y).is_ok()
&& i16::try_from(lower_bound.z).is_ok(),
"Sprite offsets should fit in i16",
);
let greedy_size = upper_bound - lower_bound + 1;
// TODO: Should this be 16, 16, 64?
assert!(
greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 64,
"Sprite size out of bounds: {:?} ≤ (31, 31, 63)",
greedy_size - 1
);
let (flat, flat_get) = {
let (w, h, d) = (greedy_size + 2).into_tuple();
let flat = {
let vol = self;
let mut flat = vec![Cell::empty(); (w * h * d) as usize];
let mut i = 0;
for x in -1..greedy_size.x + 1 {
for y in -1..greedy_size.y + 1 {
for z in -1..greedy_size.z + 1 {
let wpos = lower_bound + Vec3::new(x, y, z);
let block = vol.get(wpos).map(|b| *b).unwrap_or(Cell::empty());
flat[i] = block;
i += 1;
}
}
}
flat
};
let flat_get = move |flat: &Vec<Cell>, Vec3 { x, y, z }| match flat
.get((x * h * d + y * d + z) as usize)
.copied()
{
Some(b) => b,
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
};
(flat, flat_get)
};
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = greedy_size;
let draw_delta = Vec3::new(1, 1, 1);
let get_light = move |flat: &mut _, pos: Vec3<i32>| {
if flat_get(flat, pos).is_empty() {
1.0
} else {
0.0
}
flat
};
let get_glow = |_flat: &mut _, _pos: Vec3<i32>| 0.0;
let get_color = 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 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))
};
// NOTE: Fits in i16 (much lower actually) so f32 is no problem (and the final
// position, pos + mesh_delta, is guaranteed to fit in an f32).
let mesh_delta = lower_bound.as_::<f32>();
let create_opaque = |atlas_pos, pos: Vec3<f32>, norm, _meta| {
SpriteVertex::new(atlas_pos, pos + mesh_delta, norm)
};
let flat_get = move |flat: &Vec<Cell>, Vec3 { x, y, z }| match flat
.get((x * h * d + y * d + z) as usize)
.copied()
{
Some(b) => b,
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
};
greedy.push(GreedyConfig {
data: flat,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
));
},
make_face_texel: move |flat: &mut _, pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(flat, pos))
},
});
(flat, flat_get)
};
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = greedy_size;
let draw_delta = Vec3::new(1, 1, 1);
let get_light = move |flat: &mut _, pos: Vec3<i32>| {
if flat_get(flat, pos).is_empty() {
1.0
} else {
0.0
}
};
let get_glow = |_flat: &mut _, _pos: Vec3<i32>| 0.0;
let get_color =
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 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))
};
// NOTE: Fits in i16 (much lower actually) so f32 is no problem (and the final
// position, pos + mesh_delta, is guaranteed to fit in an f32).
let mesh_delta = lower_bound.as_::<f32>();
let create_opaque = |atlas_pos, pos: Vec3<f32>, norm, _meta| {
SpriteVertex::new(atlas_pos, pos + mesh_delta, norm)
};
greedy.push(GreedyConfig {
data: flat,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &bool| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
));
},
make_face_texel: move |flat: &mut _, pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(flat, pos))
},
});
(Mesh::new(), Mesh::new(), Mesh::new(), ())
(Mesh::new(), Mesh::new(), Mesh::new(), ())
}
}
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
pub fn generate_mesh_base_vol_particle<'a: 'b, 'b, V: 'a>(
vol: V,
greedy: &'b mut GreedyMesh<'a>,
) -> MeshGen<ParticleVertex, ParticleVertex, TerrainVertex, ()>
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>, */
{
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 16);
type Pipeline = ParticlePipeline;
type Result = ();
type ShadowPipeline = ShadowPipeline;
type Supplement = &'b mut GreedyMesh<'a>;
type TranslucentPipeline = ParticlePipeline;
let lower_bound = vol.lower_bound();
let upper_bound = vol.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
let greedy_size = upper_bound - lower_bound + 1;
assert!(
greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
"Particle size out of bounds: {:?} ≤ (15, 15, 63)",
greedy_size - 1
);
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
fn generate_mesh(
self,
greedy: Self::Supplement,
) -> MeshGen<ParticlePipeline, &'b mut GreedyMesh<'a>, Self> {
let max_size = greedy.max_size();
// 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
// of the atlas coordinates, which is why we "only" allow 1 << 15 per
// coordinate instead of 1 << 16.
assert!(max_size.width.max(max_size.height) < 1 << 16);
let greedy_size_cross = greedy_size;
let draw_delta = lower_bound;
let lower_bound = self.lower_bound();
let upper_bound = self.upper_bound();
assert!(
lower_bound.x <= upper_bound.x
&& lower_bound.y <= upper_bound.y
&& lower_bound.z <= upper_bound.z
);
let greedy_size = upper_bound - lower_bound + 1;
assert!(
greedy_size.x <= 16 && greedy_size.y <= 16 && greedy_size.z <= 64,
"Particle size out of bounds: {:?} ≤ (15, 15, 63)",
greedy_size - 1
);
// NOTE: Cast to usize is safe because of previous check, since all values fit
// into u16 which is safe to cast to usize.
let greedy_size = greedy_size.as_::<usize>();
let get_light = |vol: &mut V, pos: Vec3<i32>| {
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
1.0
} else {
0.0
}
};
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
let get_color = |vol: &mut V, pos: Vec3<i32>| {
vol.get(pos)
.ok()
.and_then(|vox| vox.get_color())
.unwrap_or(Rgb::zero())
};
let get_opacity = |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| {
should_draw_greedy(pos, delta, uv, |vox| {
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
})
};
let create_opaque = |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(pos, norm);
let greedy_size_cross = greedy_size;
let draw_delta = lower_bound;
let mut opaque_mesh = Mesh::new();
greedy.push(GreedyConfig {
data: vol,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
));
},
make_face_texel: move |vol: &mut V, pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(vol, pos))
},
});
let get_light = |vol: &mut V, pos: Vec3<i32>| {
if vol.get(pos).map(|vox| vox.is_empty()).unwrap_or(true) {
1.0
} else {
0.0
}
};
let get_glow = |_vol: &mut V, _pos: Vec3<i32>| 0.0;
let get_color = |vol: &mut V, pos: Vec3<i32>| {
vol.get(pos)
.ok()
.and_then(|vox| vox.get_color())
.unwrap_or(Rgb::zero())
};
let get_opacity =
|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| {
should_draw_greedy(pos, delta, uv, |vox| {
vol.get(vox).map(|vox| *vox).unwrap_or(Cell::empty())
})
};
let create_opaque = |_atlas_pos, pos: Vec3<f32>, norm| ParticleVertex::new(pos, norm);
(opaque_mesh, Mesh::new(), Mesh::new(), ())
let mut opaque_mesh = Mesh::new();
greedy.push(GreedyConfig {
data: self,
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &()| {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &_meta| create_opaque(atlas_pos, pos, norm),
));
},
make_face_texel: move |vol: &mut V, pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(vol, pos))
},
});
(opaque_mesh, Mesh::new(), Mesh::new(), ())
}
}
fn should_draw_greedy(

View File

@ -3,9 +3,9 @@
use crate::{
mesh::{
greedy::{self, GreedyConfig, GreedyMesh},
MeshGen,
MeshGen, Meshable,
},
render::{ColLightInfo, FluidVertex, Mesh, TerrainVertex},
render::{self, ColLightInfo, FluidPipeline, Mesh, ShadowPipeline, TerrainPipeline},
scene::terrain::BlocksOfInterest,
};
use common::{
@ -19,6 +19,9 @@ use std::{collections::VecDeque, fmt::Debug, sync::Arc};
use tracing::error;
use vek::*;
type TerrainVertex = <TerrainPipeline as render::Pipeline>::Vertex;
type FluidVertex = <FluidPipeline as render::Pipeline>::Vertex;
#[derive(Clone, Copy, PartialEq)]
enum FaceKind {
/// Opaque face that is facing something non-opaque; either
@ -224,234 +227,243 @@ fn calc_light<V: RectRasterableVol<Vox = Block> + ReadVol + Debug>(
}
}
#[allow(clippy::collapsible_if)]
#[allow(clippy::many_single_char_names)]
#[allow(clippy::type_complexity)]
#[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,
(
impl<'a, V: RectRasterableVol<Vox = Block> + ReadVol + Debug + 'static>
Meshable<TerrainPipeline, FluidPipeline> for &'a VolGrid2d<V>
{
type Pipeline = TerrainPipeline;
#[allow(clippy::type_complexity)]
type Result = (
Aabb<f32>,
ColLightInfo,
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
Arc<dyn Fn(Vec3<i32>) -> f32 + Send + Sync>,
),
> {
span!(
_guard,
"generate_mesh",
"<&VolGrid2d as Meshable<_, _>>::generate_mesh"
);
type ShadowPipeline = ShadowPipeline;
type Supplement = (Aabb<i32>, Vec2<u16>, &'a BlocksOfInterest);
type TranslucentPipeline = FluidPipeline;
// Find blocks that should glow
// TODO: Search neighbouring chunks too!
// let glow_blocks = boi.lights
// .iter()
// .map(|(pos, glow)| (*pos + range.min.xy(), *glow));
/* DefaultVolIterator::new(vol, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST)
.filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */
#[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!(
_guard,
"generate_mesh",
"<&VolGrid2d as Meshable<_, _>>::generate_mesh"
);
let mut glow_blocks = Vec::new();
// Find blocks that should glow
// TODO: Search neighbouring chunks too!
// let glow_blocks = boi.lights
// .iter()
// .map(|(pos, glow)| (*pos + range.min.xy(), *glow));
/* DefaultVolIterator::new(self, range.min - MAX_LIGHT_DIST, range.max + MAX_LIGHT_DIST)
.filter_map(|(pos, block)| block.get_glow().map(|glow| (pos, glow))); */
// TODO: This expensive, use BlocksOfInterest instead
let mut volume = vol.cached();
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 z in -1..range.size().d + 1 {
let wpos = range.min + Vec3::new(x, y, z);
volume
.get(wpos)
.ok()
.and_then(|b| b.get_glow())
.map(|glow| glow_blocks.push((wpos, glow)));
}
}
}
let mut glow_blocks = Vec::new();
// Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0)
let light = calc_light(true, SUNLIGHT, range, vol, core::iter::empty());
let glow = calc_light(false, 0, range, vol, glow_blocks.into_iter());
let mut opaque_limits = None::<Limits>;
let mut fluid_limits = None::<Limits>;
let mut air_limits = None::<Limits>;
let flat_get = {
span!(_guard, "copy to flat array");
let (w, h, d) = range.size().into_tuple();
// z can range from -1..range.size().d + 1
let d = d + 2;
let flat = {
let mut volume = vol.cached();
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty);
// TODO: Once we can manage it sensibly, consider using something like
// Option<Block> instead of just assuming air.
let mut flat = vec![AIR; (w * h * d) as usize];
let mut i = 0;
for x in 0..range.size().w {
for y in 0..range.size().h {
for z in -1..range.size().d + 1 {
let wpos = range.min + Vec3::new(x, y, z);
let block = volume
.get(wpos)
.map(|b| *b)
// TODO: Replace with None or some other more reasonable value,
// since it's not clear this will work properly with liquid.
.unwrap_or(AIR);
if block.is_opaque() {
opaque_limits = opaque_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
} else if block.is_liquid() {
fluid_limits = fluid_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
} else {
// Assume air
air_limits = air_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
};
flat[i] = block;
i += 1;
}
// TODO: This expensive, use BlocksOfInterest instead
let mut volume = self.cached();
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 z in -1..range.size().d + 1 {
let wpos = range.min + Vec3::new(x, y, z);
volume
.get(wpos)
.ok()
.and_then(|b| b.get_glow())
.map(|glow| glow_blocks.push((wpos, glow)));
}
}
}
// Calculate chunk lighting (sunlight defaults to 1.0, glow to 0.0)
let light = calc_light(true, SUNLIGHT, range, self, core::iter::empty());
let glow = calc_light(false, 0, range, self, glow_blocks.into_iter());
let mut opaque_limits = None::<Limits>;
let mut fluid_limits = None::<Limits>;
let mut air_limits = None::<Limits>;
let flat_get = {
span!(_guard, "copy to flat array");
let (w, h, d) = range.size().into_tuple();
// z can range from -1..range.size().d + 1
let d = d + 2;
let flat = {
let mut volume = self.cached();
const AIR: Block = Block::air(common::terrain::sprite::SpriteKind::Empty);
// TODO: Once we can manage it sensibly, consider using something like
// Option<Block> instead of just assuming air.
let mut flat = vec![AIR; (w * h * d) as usize];
let mut i = 0;
for x in 0..range.size().w {
for y in 0..range.size().h {
for z in -1..range.size().d + 1 {
let wpos = range.min + Vec3::new(x, y, z);
let block = volume
.get(wpos)
.map(|b| *b)
// TODO: Replace with None or some other more reasonable value,
// since it's not clear this will work properly with liquid.
.unwrap_or(AIR);
if block.is_opaque() {
opaque_limits = opaque_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
} else if block.is_liquid() {
fluid_limits = fluid_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
} else {
// Assume air
air_limits = air_limits
.map(|l| l.including(z))
.or_else(|| Some(Limits::from_value(z)));
};
flat[i] = block;
i += 1;
}
}
}
flat
};
move |Vec3 { x, y, z }| {
// z can range from -1..range.size().d + 1
let z = z + 1;
match flat.get((x * h * d + y * d + z) as usize).copied() {
Some(b) => b,
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
}
}
flat
};
move |Vec3 { x, y, z }| {
// z can range from -1..range.size().d + 1
let z = z + 1;
match flat.get((x * h * d + y * d + z) as usize).copied() {
Some(b) => b,
None => panic!("x {} y {} z {} d {} h {}", x, y, z, d, h),
// Constrain iterated area
let (z_start, z_end) = match (air_limits, fluid_limits, opaque_limits) {
(Some(air), Some(fluid), Some(opaque)) => air.three_way_intersection(fluid, opaque),
(Some(air), Some(fluid), None) => air.intersection(fluid),
(Some(air), None, Some(opaque)) => air.intersection(opaque),
(None, Some(fluid), Some(opaque)) => fluid.intersection(opaque),
// No interfaces (Note: if there are multiple fluid types this could change)
(Some(_), None, None) | (None, Some(_), None) | (None, None, Some(_)) => None,
(None, None, None) => {
error!("Impossible unless given an input AABB that has a height of zero");
None
},
}
.map_or((0, 0), |limits| {
let (start, end) = limits.into_tuple();
let start = start.max(0);
let end = end.min(range.size().d - 1).max(start);
(start, end)
});
let max_size =
guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y));
assert!(z_end >= z_start);
let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1);
// NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5
// + 14). FIXME: Make this function fallible, since the terrain
// information might be dynamically generated which would make this hard
// to enforce.
assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384);
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
// which always fits into a f32.
let max_bounds: Vec3<f32> = greedy_size.as_::<f32>();
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
// which always fits into a usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z);
let draw_delta = Vec3::new(1, 1, z_start);
let get_light = |_: &mut (), pos: Vec3<i32>| {
if flat_get(pos).is_opaque() {
0.0
} else {
light(pos + range.min)
}
}
};
};
let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min);
let get_color =
|_: &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 flat_get = |pos| flat_get(pos);
let should_draw = |_: &mut (), pos: Vec3<i32>, delta: Vec3<i32>, _uv| {
should_draw_greedy(pos, delta, flat_get)
};
// 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 create_opaque = |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);
// Constrain iterated area
let (z_start, z_end) = match (air_limits, fluid_limits, opaque_limits) {
(Some(air), Some(fluid), Some(opaque)) => air.three_way_intersection(fluid, opaque),
(Some(air), Some(fluid), None) => air.intersection(fluid),
(Some(air), None, Some(opaque)) => air.intersection(opaque),
(None, Some(fluid), Some(opaque)) => fluid.intersection(opaque),
// No interfaces (Note: if there are multiple fluid types this could change)
(Some(_), None, None) | (None, Some(_), None) | (None, None, Some(_)) => None,
(None, None, None) => {
error!("Impossible unless given an input AABB that has a height of zero");
None
},
}
.map_or((0, 0), |limits| {
let (start, end) = limits.into_tuple();
let start = start.max(0);
let end = end.min(range.size().d - 1).max(start);
(start, end)
});
let max_size =
guillotiere::Size::new(i32::from(max_texture_size.x), i32::from(max_texture_size.y));
assert!(z_end >= z_start);
let greedy_size = Vec3::new(range.size().w - 2, range.size().h - 2, z_end - z_start + 1);
// NOTE: Terrain sizes are limited to 32 x 32 x 16384 (to fit in 24 bits: 5 + 5
// + 14). FIXME: Make this function fallible, since the terrain
// information might be dynamically generated which would make this hard
// to enforce.
assert!(greedy_size.x <= 32 && greedy_size.y <= 32 && greedy_size.z <= 16384);
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
// which always fits into a f32.
let max_bounds: Vec3<f32> = greedy_size.as_::<f32>();
// NOTE: Cast is safe by prior assertion on greedy_size; it fits into a u16,
// which always fits into a usize.
let greedy_size = greedy_size.as_::<usize>();
let greedy_size_cross = Vec3::new(greedy_size.x - 1, greedy_size.y - 1, greedy_size.z);
let draw_delta = Vec3::new(1, 1, z_start);
let get_light = |_: &mut (), pos: Vec3<i32>| {
if flat_get(pos).is_opaque() {
0.0
} else {
light(pos + range.min)
}
};
let get_glow = |_: &mut (), pos: Vec3<i32>| glow(pos + range.min);
let get_color = |_: &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 flat_get = |pos| flat_get(pos);
let should_draw = |_: &mut (), pos: Vec3<i32>, delta: Vec3<i32>, _uv| {
should_draw_greedy(pos, delta, flat_get)
};
// 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 create_opaque =
|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 mut greedy = GreedyMesh::new(max_size);
let mut opaque_mesh = Mesh::new();
let mut fluid_mesh = Mesh::new();
greedy.push(GreedyConfig {
data: (),
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &FaceKind| match meta {
FaceKind::Opaque(meta) => {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
));
let mut greedy = GreedyMesh::new(max_size);
let mut opaque_mesh = Mesh::new();
let mut fluid_mesh = Mesh::new();
greedy.push(GreedyConfig {
data: (),
draw_delta,
greedy_size,
greedy_size_cross,
get_light,
get_glow,
get_opacity,
should_draw,
push_quad: |atlas_origin, dim, origin, draw_dim, norm, meta: &FaceKind| match meta {
FaceKind::Opaque(meta) => {
opaque_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
meta,
|atlas_pos, pos, norm, &meta| create_opaque(atlas_pos, pos, norm, meta),
));
},
FaceKind::Fluid => {
fluid_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
&(),
|atlas_pos, pos, norm, &_meta| create_transparent(atlas_pos, pos, norm),
));
},
},
FaceKind::Fluid => {
fluid_mesh.push_quad(greedy::create_quad(
atlas_origin,
dim,
origin,
draw_dim,
norm,
&(),
|atlas_pos, pos, norm, &_meta| create_transparent(atlas_pos, pos, norm),
));
make_face_texel: |data: &mut (), pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(data, pos))
},
},
make_face_texel: |data: &mut (), pos, light, glow| {
TerrainVertex::make_col_light(light, glow, get_color(data, pos))
},
});
});
let min_bounds = mesh_delta;
let bounds = Aabb {
min: min_bounds,
max: max_bounds + min_bounds,
};
let (col_lights, col_lights_size) = greedy.finalize();
let min_bounds = mesh_delta;
let bounds = Aabb {
min: min_bounds,
max: max_bounds + min_bounds,
};
let (col_lights, col_lights_size) = greedy.finalize();
(
opaque_mesh,
fluid_mesh,
Mesh::new(),
(
bounds,
(col_lights, col_lights_size),
Arc::new(light),
Arc::new(glow),
),
)
opaque_mesh,
fluid_mesh,
Mesh::new(),
(
bounds,
(col_lights, col_lights_size),
Arc::new(light),
Arc::new(glow),
),
)
}
}
/// NOTE: Make sure to reflect any changes to how meshing is performanced in

View File

@ -1,14 +0,0 @@
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

@ -1,63 +0,0 @@
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,26 +1,36 @@
use super::buffer::DynamicBuffer;
use bytemuck::Pod;
use super::{gfx_backend, RenderError};
use gfx::{self, traits::FactoryExt};
/// 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
/// single render pass.
pub struct Consts<T: Copy + Pod> {
buf: DynamicBuffer<T>,
#[derive(Clone)]
pub struct Consts<T: Copy + gfx::traits::Pod> {
pub buf: gfx::handle::Buffer<gfx_backend::Resources, T>,
}
impl<T: Copy + Pod> Consts<T> {
impl<T: Copy + gfx::traits::Pod> Consts<T> {
/// Create a new `Const<T>`.
pub fn new(device: &wgpu::Device, len: usize) -> Self {
pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Self {
Self {
// TODO: examine if all our consts need to be updateable
buf: DynamicBuffer::new(device, len, wgpu::BufferUsage::UNIFORM),
buf: factory.create_constant_buffer(len),
}
}
/// 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 buf(&self) -> &wgpu::Buffer { &self.buf.buf }
pub fn update(
&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,54 +1,74 @@
/// Used to represent one of many possible errors that may be omitted by the
/// rendering subsystem.
#[derive(Debug)]
pub enum RenderError {
RequestDeviceError(wgpu::RequestDeviceError),
MappingError(wgpu::BufferAsyncError),
SwapChainError(wgpu::SwapChainError),
PipelineError(gfx::PipelineStateError<String>),
UpdateError(gfx::UpdateError<usize>),
TexUpdateError(gfx::UpdateError<[u16; 3]>),
CombinedError(gfx::CombinedError),
BufferCreationError(gfx::buffer::CreationError),
IncludeError(glsl_include::Error),
MappingError(gfx::mapping::Error),
CopyError(gfx::CopyError<[u16; 3], usize>),
CustomError(String),
CouldNotFindAdapter,
ErrorInitializingCompiler,
ShaderError(String, shaderc::Error),
}
use std::fmt;
impl fmt::Debug for RenderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::RequestDeviceError(err) => {
f.debug_tuple("RequestDeviceError").field(err).finish()
impl From<gfx::PipelineStateError<String>> for RenderError {
fn from(err: gfx::PipelineStateError<String>) -> Self { Self::PipelineError(err) }
}
impl From<gfx::PipelineStateError<&str>> for RenderError {
fn from(err: gfx::PipelineStateError<&str>) -> Self {
match err {
gfx::PipelineStateError::DescriptorInit(err) => {
gfx::PipelineStateError::DescriptorInit(err)
},
Self::MappingError(err) => f.debug_tuple("MappingError").field(err).finish(),
Self::SwapChainError(err) => f
.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
),
err => err,
}
.into()
}
}
impl From<wgpu::RequestDeviceError> for RenderError {
fn from(err: wgpu::RequestDeviceError) -> Self { Self::RequestDeviceError(err) }
}
impl From<wgpu::BufferAsyncError> for RenderError {
fn from(err: wgpu::BufferAsyncError) -> Self { Self::MappingError(err) }
}
impl From<wgpu::SwapChainError> for RenderError {
fn from(err: wgpu::SwapChainError) -> Self { Self::SwapChainError(err) }
}
impl From<(&str, shaderc::Error)> for RenderError {
fn from((shader_name, err): (&str, shaderc::Error)) -> Self {
Self::ShaderError(shader_name.into(), 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 {
fn from(err: gfx::UpdateError<[u16; 3]>) -> Self { Self::TexUpdateError(err) }
}
impl From<gfx::CombinedError> for RenderError {
fn from(err: gfx::CombinedError) -> Self { Self::CombinedError(err) }
}
impl From<gfx::TargetViewError> for RenderError {
fn from(err: gfx::TargetViewError) -> Self { Self::CombinedError(err.into()) }
}
impl From<gfx::ResourceViewError> for RenderError {
fn from(err: gfx::ResourceViewError) -> Self { Self::CombinedError(err.into()) }
}
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,26 +1,34 @@
use super::buffer::DynamicBuffer;
use bytemuck::Pod;
use super::{gfx_backend, RenderError};
use gfx::{
self,
buffer::Role,
memory::{Bind, Usage},
Factory,
};
/// Represents a mesh that has been sent to the GPU.
pub struct Instances<T: Copy + Pod> {
buf: DynamicBuffer<T>,
pub struct Instances<T: Copy + gfx::traits::Pod> {
pub ibuf: gfx::handle::Buffer<gfx_backend::Resources, T>,
}
impl<T: Copy + Pod> Instances<T> {
pub fn new(device: &wgpu::Device, len: usize) -> Self {
Self {
// TODO: examine if we have Instances that are not updated (e.g. sprites) and if there
// would be any gains from separating those out
buf: DynamicBuffer::new(device, len, wgpu::BufferUsage::VERTEX),
}
impl<T: Copy + gfx::traits::Pod> Instances<T> {
pub fn new(factory: &mut gfx_backend::Factory, len: usize) -> Result<Self, RenderError> {
Ok(Self {
ibuf: factory
.create_buffer(len, Role::Vertex, Usage::Dynamic, Bind::empty())
.map_err(RenderError::BufferCreationError)?,
})
}
// TODO: count vs len naming scheme??
pub fn count(&self) -> usize { self.buf.len() }
pub fn count(&self) -> usize { self.ibuf.len() }
pub fn update(&mut self, queue: &wgpu::Queue, vals: &[T], offset: usize) {
self.buf.update(queue, vals, offset)
pub fn update(
&mut self,
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,12 +1,15 @@
use super::Vertex;
use super::Pipeline;
use core::{iter::FromIterator, ops::Range};
/// A `Vec`-based mesh structure used to store mesh data on the CPU.
pub struct Mesh<V: Vertex> {
verts: Vec<V>,
pub struct Mesh<P: Pipeline> {
verts: Vec<P::Vertex>,
}
impl<V: Vertex> Clone for Mesh<V> {
impl<P: Pipeline> Clone for Mesh<P>
where
P::Vertex: Clone,
{
fn clone(&self) -> Self {
Self {
verts: self.verts.clone(),
@ -14,7 +17,7 @@ impl<V: Vertex> Clone for Mesh<V> {
}
}
impl<V: Vertex> Mesh<V> {
impl<P: Pipeline> Mesh<P> {
/// Create a new `Mesh`.
#[allow(clippy::new_without_default)] // TODO: Pending review in #587
pub fn new() -> Self { Self { verts: Vec::new() } }
@ -23,103 +26,83 @@ impl<V: Vertex> Mesh<V> {
pub fn clear(&mut self) { self.verts.clear(); }
/// Get a slice referencing the vertices of this mesh.
pub fn vertices(&self) -> &[V] { &self.verts }
pub fn vertices(&self) -> &[P::Vertex] { &self.verts }
/// Get a mutable slice referencing the vertices of this mesh.
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 }
pub fn vertices_mut(&mut self) -> &mut [P::Vertex] { &mut self.verts }
/// Push a new vertex onto the end of this mesh.
pub fn push(&mut self, vert: V) { self.verts.push(vert); }
pub fn push(&mut self, vert: P::Vertex) { self.verts.push(vert); }
/// Push a new polygon onto the end of this mesh.
pub fn push_tri(&mut self, tri: Tri<V>) {
pub fn push_tri(&mut self, tri: Tri<P>) {
self.verts.push(tri.a);
self.verts.push(tri.b);
self.verts.push(tri.c);
}
/// Push a new quad onto the end of this mesh.
pub fn push_quad(&mut self, quad: Quad<V>) {
pub fn push_quad(&mut self, quad: Quad<P>) {
// A quad is composed of two triangles. The code below converts the former to
// the latter.
if V::QUADS_INDEX.is_some() {
// 0, 1, 2, 2, 1, 3
// b, c, a, a, c, d
self.verts.push(quad.b);
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
self.verts.push(quad.c);
self.verts.push(quad.d);
self.verts.push(quad.a);
}
// Tri 1
self.verts.push(quad.a.clone());
self.verts.push(quad.b);
self.verts.push(quad.c.clone());
// Tri 2
self.verts.push(quad.c);
self.verts.push(quad.d);
self.verts.push(quad.a);
}
/// Overwrite a quad
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);
assert!(index + 5 < self.verts.len());
// Tri 1
self.verts[index] = quad.a;
self.verts[index + 1] = quad.b;
self.verts[index + 2] = quad.c;
pub fn replace_quad(&mut self, index: usize, quad: Quad<P>) {
debug_assert!(index % 3 == 0);
assert!(index + 5 < self.verts.len());
// Tri 1
self.verts[index] = quad.a.clone();
self.verts[index + 1] = quad.b;
self.verts[index + 2] = quad.c.clone();
// Tri 2
self.verts[index + 3] = quad.c;
self.verts[index + 4] = quad.d;
self.verts[index + 5] = quad.a;
}
// Tri 2
self.verts[index + 3] = quad.c;
self.verts[index + 4] = quad.d;
self.verts[index + 5] = quad.a;
}
/// Push the vertices of another mesh onto the end of this mesh.
pub fn push_mesh(&mut self, other: &Mesh<V>) { self.verts.extend_from_slice(other.vertices()); }
pub fn push_mesh(&mut self, other: &Mesh<P>) { self.verts.extend_from_slice(other.vertices()); }
/// Map and push the vertices of another mesh onto the end of this mesh.
pub fn push_mesh_map<F: FnMut(V) -> V>(&mut self, other: &Mesh<V>, mut f: F) {
pub fn push_mesh_map<F: FnMut(P::Vertex) -> P::Vertex>(&mut self, other: &Mesh<P>, mut f: F) {
// Reserve enough space in our Vec. This isn't necessary, but it tends to reduce
// the number of required (re)allocations.
self.verts.reserve(other.vertices().len());
for vert in other.vertices() {
self.verts.push(f(*vert));
self.verts.push(f(vert.clone()));
}
}
pub fn iter(&self) -> std::slice::Iter<V> { self.verts.iter() }
pub fn iter(&self) -> std::slice::Iter<P::Vertex> { self.verts.iter() }
/// NOTE: Panics if vertex_range is out of bounds of vertices.
pub fn iter_mut(&mut self, vertex_range: Range<usize>) -> std::slice::IterMut<V> {
pub fn iter_mut(&mut self, vertex_range: Range<usize>) -> std::slice::IterMut<P::Vertex> {
self.verts[vertex_range].iter_mut()
}
}
impl<V: Vertex> IntoIterator for Mesh<V> {
type IntoIter = std::vec::IntoIter<V>;
type Item = V;
impl<P: Pipeline> IntoIterator for Mesh<P> {
type IntoIter = std::vec::IntoIter<P::Vertex>;
type Item = P::Vertex;
fn into_iter(self) -> Self::IntoIter { self.verts.into_iter() }
}
impl<V: Vertex> FromIterator<Tri<V>> for Mesh<V> {
fn from_iter<I: IntoIterator<Item = Tri<V>>>(tris: I) -> Self {
impl<P: Pipeline> FromIterator<Tri<P>> for Mesh<P> {
fn from_iter<I: IntoIterator<Item = Tri<P>>>(tris: I) -> Self {
tris.into_iter().fold(Self::new(), |mut this, tri| {
this.push_tri(tri);
this
@ -127,8 +110,8 @@ impl<V: Vertex> FromIterator<Tri<V>> for Mesh<V> {
}
}
impl<V: Vertex> FromIterator<Quad<V>> for Mesh<V> {
fn from_iter<I: IntoIterator<Item = Quad<V>>>(quads: I) -> Self {
impl<P: Pipeline> FromIterator<Quad<P>> for Mesh<P> {
fn from_iter<I: IntoIterator<Item = Quad<P>>>(quads: I) -> Self {
quads.into_iter().fold(Self::new(), |mut this, quad| {
this.push_quad(quad);
this
@ -137,35 +120,40 @@ impl<V: Vertex> FromIterator<Quad<V>> for Mesh<V> {
}
/// Represents a triangle stored on the CPU.
pub struct Tri<V: Vertex> {
a: V,
b: V,
c: V,
pub struct Tri<P: Pipeline> {
a: P::Vertex,
b: P::Vertex,
c: P::Vertex,
}
impl<V: Vertex> Tri<V> {
pub fn new(a: V, b: V, c: V) -> Self { Self { a, b, c } }
impl<P: Pipeline> Tri<P> {
pub fn new(a: P::Vertex, b: P::Vertex, c: P::Vertex) -> Self { Self { a, b, c } }
}
/// Represents a quad stored on the CPU.
pub struct Quad<V: Vertex> {
a: V,
b: V,
c: V,
d: V,
pub struct Quad<P: Pipeline> {
a: P::Vertex,
b: P::Vertex,
c: P::Vertex,
d: P::Vertex,
}
impl<V: Vertex> Quad<V> {
pub fn new(a: V, b: V, c: V, d: V) -> Self { Self { a, b, c, d } }
impl<P: Pipeline> Quad<P> {
pub fn new(a: P::Vertex, b: P::Vertex, c: P::Vertex, d: P::Vertex) -> Self {
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];
Self {
a: verts[n % 4],
b: verts[(1 + n) % 4],
c: verts[(2 + n) % 4],
d: verts[(3 + n) % 4],
a: verts[n % 4].clone(),
b: verts[(1 + n) % 4].clone(),
c: verts[(2 + n) % 4].clone(),
d: verts[(3 + n) % 4].clone(),
}
}
}

View File

@ -1,5 +1,3 @@
pub mod bound;
mod buffer;
#[allow(clippy::single_component_path_imports)] // TODO: Pending review in #587
pub mod consts;
mod error;
@ -12,55 +10,58 @@ pub mod texture;
// Reexports
pub use self::{
bound::Bound,
buffer::Buffer,
consts::Consts,
error::RenderError,
instances::Instances,
mesh::{Mesh, Quad, Tri},
model::{DynamicModel, Model, SubModel},
model::{DynamicModel, Model},
pipelines::{
clouds::Locals as CloudsLocals,
debug::{DebugPipeline, Locals as DebugLocals, Vertex as DebugVertex},
clouds::{create_mesh as create_clouds_mesh, CloudsPipeline, Locals as CloudsLocals},
figure::{
BoneData as FigureBoneData, BoneMeshes, FigureLayout, FigureModel,
BoneData as FigureBoneData, BoneMeshes, FigureModel, FigurePipeline,
Locals as FigureLocals,
},
fluid::Vertex as FluidVertex,
lod_terrain::{LodData, Vertex as LodTerrainVertex},
particle::{Instance as ParticleInstance, Vertex as ParticleVertex},
postprocess::Locals as PostProcessLocals,
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,
fluid::FluidPipeline,
lod_terrain::{Locals as LodTerrainLocals, LodData, LodTerrainPipeline},
particle::{Instance as ParticleInstance, ParticlePipeline},
postprocess::{
create_mesh as create_pp_mesh, Locals as PostProcessLocals, PostProcessPipeline,
},
terrain::{Locals as TerrainLocals, TerrainLayout, Vertex as TerrainVertex},
shadow::{Locals as ShadowLocals, ShadowPipeline},
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::{
create_quad as create_ui_quad,
create_quad_vert_gradient as create_ui_quad_vert_gradient, create_tri as create_ui_tri,
BoundLocals as UiBoundLocals, Locals as UiLocals, Mode as UiMode,
TextureBindGroup as UiTextureBindGroup, Vertex as UiVertex,
Locals as UiLocals, Mode as UiMode, UiPipeline,
},
GlobalModel, Globals, GlobalsBindGroup, GlobalsLayouts, Light, Shadow,
GlobalModel, Globals, Light, Shadow,
},
renderer::{
drawer::{
DebugDrawer, Drawer, FigureDrawer, FigureShadowDrawer, FirstPassDrawer, ParticleDrawer,
PreparedUiDrawer, SecondPassDrawer, ShadowPassDrawer, SpriteDrawer, TerrainDrawer,
TerrainShadowDrawer, ThirdPassDrawer, UiDrawer,
},
ColLightInfo, Renderer,
ColLightFmt, ColLightInfo, LodAltFmt, LodColorFmt, LodTextureFmt, Renderer,
ShadowDepthStencilFmt, TgtColorFmt, TgtDepthStencilFmt, WinColorFmt, WinDepthFmt,
},
texture::Texture,
};
pub use wgpu::{AddressMode, FilterMode};
pub use gfx::texture::{FilterMethod, WrapMode};
pub trait Vertex: Clone + bytemuck::Pod {
const STRIDE: wgpu::BufferAddress;
// Whether these types of verts use the quad index buffer for drawing them
const QUADS_INDEX: Option<wgpu::IndexFormat>;
#[cfg(feature = "gl")]
use gfx_device_gl as gfx_backend;
/// Used to represent a specific rendering configuration.
///
/// 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};
@ -242,30 +243,6 @@ impl Default for UpscaleMode {
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
#[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
#[serde(default)]
@ -276,6 +253,4 @@ pub struct RenderMode {
pub lighting: LightingMode,
pub shadow: ShadowMode,
pub upscale_mode: UpscaleMode,
pub present_mode: PresentMode,
pub profiler_enabled: bool,
}

View File

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

@ -1,123 +0,0 @@
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,15 +1,40 @@
use super::{
super::{AaMode, Consts},
GlobalsLayouts,
super::{Mesh, Pipeline, TgtColorFmt, TgtDepthStencilFmt, Tri},
Globals,
};
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::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
proj_mat_inv: [[f32; 4]; 4],
view_mat_inv: [[f32; 4]; 4],
gfx_defines! {
vertex Vertex {
pos: [f32; 2] = "v_pos",
}
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 {
@ -25,180 +50,28 @@ impl Locals {
}
}
pub struct BindGroup {
pub(in super::super) bind_group: wgpu::BindGroup,
pub struct CloudsPipeline;
impl Pipeline for CloudsPipeline {
type Vertex = Vertex;
}
pub struct CloudsLayout {
pub layout: wgpu::BindGroupLayout,
}
impl CloudsLayout {
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,
},
// 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,
}
}
pub fn create_mesh() -> Mesh<CloudsPipeline> {
let mut mesh = Mesh::new();
#[rustfmt::skip]
mesh.push_tri(Tri::new(
Vertex { pos: [ 1.0, -1.0] },
Vertex { pos: [-1.0, 1.0] },
Vertex { pos: [-1.0, -1.0] },
));
#[rustfmt::skip]
mesh.push_tri(Tri::new(
Vertex { pos: [1.0, -1.0] },
Vertex { pos: [1.0, 1.0] },
Vertex { pos: [-1.0, 1.0] },
));
mesh
}

View File

@ -1,172 +0,0 @@
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,31 +1,57 @@
use super::{
super::{AaMode, Bound, Consts, GlobalsLayouts, Mesh, Model},
terrain::Vertex,
super::{Mesh, Model, Pipeline, TerrainPipeline, TgtColorFmt, TgtDepthStencilFmt},
shadow, Globals, Light, Shadow,
};
use crate::mesh::greedy::GreedyMesh;
use bytemuck::{Pod, Zeroable};
use gfx::{
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
gfx_pipeline_inner, state::ColorMask,
};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
model_mat: [[f32; 4]; 4],
highlight_col: [f32; 4],
model_light: [f32; 4],
model_glow: [f32; 4],
atlas_offs: [i32; 4],
model_pos: [f32; 3],
flags: u32,
}
gfx_defines! {
constant Locals {
model_mat: [[f32; 4]; 4] = "model_mat",
highlight_col: [f32; 4] = "highlight_col",
model_light: [f32; 4] = "model_light",
model_glow: [f32; 4] = "model_glow",
atlas_offs: [i32; 4] = "atlas_offs",
model_pos: [f32; 3] = "model_pos",
flags: u32 = "flags",
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct BoneData {
bone_mat: [[f32; 4]; 4],
normals_mat: [[f32; 4]; 4],
}
constant BoneData {
bone_mat: [[f32; 4]; 4] = "bone_mat",
normals_mat: [[f32; 4]; 4] = "normals_mat",
}
pub type BoundLocals = Bound<(Consts<Locals>, Consts<BoneData>)>;
pipeline pipe {
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 {
pub fn new(
@ -79,8 +105,14 @@ impl Default for BoneData {
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 opaque: Model<Vertex>,
pub opaque: Model<TerrainPipeline>,
/* TODO: Consider using mipmaps instead of storing multiple texture atlases for different
* LOD levels. */
}
@ -97,157 +129,4 @@ impl FigureModel {
}
}
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,
}
}
}
pub type BoneMeshes = (Mesh<TerrainPipeline>, anim::vek::Aabb<f32>);

View File

@ -1,12 +1,42 @@
use super::super::{AaMode, GlobalsLayouts, TerrainLayout, Vertex as VertexTrait};
use bytemuck::{Pod, Zeroable};
use std::mem;
use super::{
super::{Pipeline, TerrainLocals, TgtColorFmt, TgtDepthStencilFmt},
shadow, Globals, Light, Shadow,
};
use gfx::{
self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner,
gfx_vertex_struct_meta, state::ColorMask,
};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos_norm: u32,
gfx_defines! {
vertex Vertex {
pos_norm: u32 = "v_pos_norm",
}
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 {
@ -31,116 +61,10 @@ impl Vertex {
| (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,
}
}
}
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 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,
}
}
pub struct FluidPipeline;
impl Pipeline for FluidPipeline {
type Vertex = Vertex;
}

View File

@ -1,12 +1,40 @@
use super::super::{AaMode, GlobalsLayouts, Renderer, Texture, Vertex as VertexTrait};
use bytemuck::{Pod, Zeroable};
use std::mem;
use super::{
super::{
LodAltFmt, LodColorFmt, LodTextureFmt, Pipeline, Renderer, Texture, TgtColorFmt,
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::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos: [f32; 2],
gfx_defines! {
vertex Vertex {
pos: [f32; 2] = "v_pos",
}
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 {
@ -15,210 +43,75 @@ impl Vertex {
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 VertexTrait for Vertex {
const QUADS_INDEX: Option<wgpu::IndexFormat> = Some(wgpu::IndexFormat::Uint32);
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
impl Locals {
pub fn default() -> Self { Self { nul: [0.0; 4] } }
}
pub struct LodTerrainPipeline;
impl Pipeline for LodTerrainPipeline {
type Vertex = Vertex;
}
pub struct LodData {
pub map: Texture,
pub alt: Texture,
pub horizon: Texture,
pub map: Texture<LodColorFmt>,
pub alt: Texture<LodAltFmt>,
pub horizon: Texture<LodTextureFmt>,
pub tgt_detail: u32,
}
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(
renderer: &mut Renderer,
map_size: Vec2<u32>,
map_size: Vec2<u16>,
lod_base: &[u32],
lod_alt: &[u32],
lod_horizon: &[u32],
tgt_detail: u32,
//border_color: gfx::texture::PackedColor,
border_color: gfx::texture::PackedColor,
) -> Self {
let mut create_texture = |format, data, filter| {
let texture_info = wgpu::TextureDescriptor {
label: None,
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,
let kind = gfx::texture::Kind::D2(map_size.x, map_size.y, gfx::texture::AaMode::Single);
let info = gfx::texture::SamplerInfo::new(
gfx::texture::FilterMethod::Bilinear,
gfx::texture::WrapMode::Border,
);
// 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 {
map,
alt,
horizon,
map: renderer
.create_texture_immutable_raw(
kind,
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,
}
}
}
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,6 +1,4 @@
pub mod blit;
pub mod clouds;
pub mod debug;
pub mod figure;
pub mod fluid;
pub mod lod_terrain;
@ -12,70 +10,56 @@ pub mod sprite;
pub mod terrain;
pub mod ui;
use super::{Consts, Texture};
use super::Consts;
use crate::scene::camera::CameraMode;
use bytemuck::{Pod, Zeroable};
use common::terrain::BlockKind;
use gfx::{self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta};
use vek::*;
// TODO: auto insert these into shaders
pub const MAX_POINT_LIGHT_COUNT: usize = 20;
pub const MAX_POINT_LIGHT_COUNT: usize = 31;
pub const MAX_FIGURE_SHADOW_COUNT: usize = 24;
pub const MAX_DIRECTED_LIGHT_COUNT: usize = 6;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Globals {
/// Transformation from world coordinate space (with focus_off as the
/// origin) to the camera space
view_mat: [[f32; 4]; 4],
proj_mat: [[f32; 4]; 4],
/// proj_mat * view_mat
all_mat: [[f32; 4]; 4],
/// Offset of the camera from the focus position
cam_pos: [f32; 4],
/// 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.
view_distance: [f32; 4],
time_of_day: [f32; 4], // TODO: Make this f64.
sun_dir: [f32; 4],
moon_dir: [f32; 4],
tick: [f32; 4],
/// x, y represent the resolution of the screen;
/// w, z represent the near and far planes of the shadow map.
screen_res: [f32; 4],
light_shadow_count: [u32; 4],
shadow_proj_factors: [f32; 4],
medium: [u32; 4],
select_pos: [i32; 4],
gamma_exposure: [f32; 4],
ambiance: f32,
cam_mode: u32,
sprite_render_distance: f32,
/// To keep 16-byte-aligned.
globals_dummy: f32,
}
gfx_defines! {
constant Globals {
view_mat: [[f32; 4]; 4] = "view_mat",
proj_mat: [[f32; 4]; 4] = "proj_mat",
all_mat: [[f32; 4]; 4] = "all_mat",
cam_pos: [f32; 4] = "cam_pos",
focus_off: [f32; 4] = "focus_off",
focus_pos: [f32; 4] = "focus_pos",
/// 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.
view_distance: [f32; 4] = "view_distance",
time_of_day: [f32; 4] = "time_of_day", // TODO: Make this f64.
sun_dir: [f32; 4] = "sun_dir",
moon_dir: [f32; 4] = "moon_dir",
tick: [f32; 4] = "tick",
/// x, y represent the resolution of the screen;
/// w, z represent the near and far planes of the shadow map.
screen_res: [f32; 4] = "screen_res",
light_shadow_count: [u32; 4] = "light_shadow_count",
shadow_proj_factors: [f32; 4] = "shadow_proj_factors",
medium: [u32; 4] = "medium",
select_pos: [i32; 4] = "select_pos",
gamma_exposure: [f32; 4] = "gamma_exposure",
ambiance: f32 = "ambiance",
cam_mode: u32 = "cam_mode",
sprite_render_distance: f32 = "sprite_render_distance",
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Light {
pub pos: [f32; 4],
pub col: [f32; 4],
}
constant Light {
pos: [f32; 4] = "light_pos",
col: [f32; 4] = "light_col",
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Shadow {
pos_radius: [f32; 4],
constant Shadow {
pos_radius: [f32; 4] = "shadow_pos_radius",
}
}
impl Globals {
@ -124,16 +108,15 @@ impl Globals {
shadow_planes.x,
shadow_planes.y,
],
// TODO: why do we accept values greater than the max?
light_shadow_count: [
usize::min(light_count, MAX_POINT_LIGHT_COUNT) as u32,
usize::min(shadow_count, MAX_FIGURE_SHADOW_COUNT) as u32,
usize::min(directed_light_count, MAX_DIRECTED_LIGHT_COUNT) as u32,
(light_count % (MAX_POINT_LIGHT_COUNT + 1)) as u32,
(shadow_count % (MAX_FIGURE_SHADOW_COUNT + 1)) as u32,
(directed_light_count % (MAX_DIRECTED_LIGHT_COUNT + 1)) as u32,
0,
],
shadow_proj_factors: [
shadow_planes.y / (shadow_planes.y - shadow_planes.x),
shadow_planes.y * shadow_planes.x / (shadow_planes.y - shadow_planes.x),
(shadow_planes.y + shadow_planes.x) / (shadow_planes.y - shadow_planes.x),
(2.0 * shadow_planes.y * shadow_planes.x) / (shadow_planes.y - shadow_planes.x),
0.0,
0.0,
],
@ -146,7 +129,6 @@ impl Globals {
ambiance,
cam_mode: cam_mode as u32,
sprite_render_distance,
globals_dummy: 0.0,
}
}
@ -232,388 +214,8 @@ impl Default for Shadow {
// Global scene data spread across several arrays.
pub struct GlobalModel {
// TODO: enforce that these are the lengths in the shaders??
pub globals: Consts<Globals>,
pub lights: Consts<Light>,
pub shadows: Consts<Shadow>,
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,
}
}
pub shadow_mats: Consts<shadow::Locals>,
}

View File

@ -1,18 +1,79 @@
use super::super::{AaMode, GlobalsLayouts, Vertex as VertexTrait};
use bytemuck::{Pod, Zeroable};
use std::mem;
use super::{
super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt},
shadow, Globals, Light, Shadow,
};
use gfx::{
self, gfx_defines, gfx_impl_struct_meta, gfx_pipeline, gfx_pipeline_inner,
gfx_vertex_struct_meta, state::ColorMask,
};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pub pos: [f32; 3],
// ____BBBBBBBBGGGGGGGGRRRRRRRR
// col: u32 = "v_col",
// ...AANNN
// A = AO
// N = Normal
norm_ao: u32,
gfx_defines! {
vertex Vertex {
pos: [f32; 3] = "v_pos",
// ____BBBBBBBBGGGGGGGGRRRRRRRR
// col: u32 = "v_col",
// ...AANNN
// A = AO
// N = Normal
norm_ao: u32 = "v_norm_ao",
}
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 {
@ -31,21 +92,6 @@ impl Vertex {
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)]
@ -88,40 +134,6 @@ impl ParticleMode {
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 {
pub fn new(
inst_time: f64,
@ -157,111 +169,14 @@ impl Instance {
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 {
fn default() -> Self { Self::new(0.0, 0.0, ParticleMode::CampfireSmoke, Vec3::zero()) }
}
pub struct ParticlePipeline {
pub pipeline: wgpu::RenderPipeline,
}
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,
}
}
pub struct ParticlePipeline;
impl Pipeline for ParticlePipeline {
type Vertex = Vertex;
}

View File

@ -1,12 +1,40 @@
use super::super::{Consts, GlobalsLayouts};
use bytemuck::{Pod, Zeroable};
use super::{
super::{Mesh, Pipeline, TgtColorFmt, TgtDepthStencilFmt, Tri, WinColorFmt},
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::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
proj_mat_inv: [[f32; 4]; 4],
view_mat_inv: [[f32; 4]; 4],
gfx_defines! {
vertex Vertex {
pos: [f32; 2] = "v_pos",
}
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 {
@ -22,143 +50,28 @@ impl Locals {
}
}
pub struct BindGroup {
pub(in super::super) bind_group: wgpu::BindGroup,
pub struct PostProcessPipeline;
impl Pipeline for PostProcessPipeline {
type Vertex = Vertex;
}
pub struct PostProcessLayout {
pub layout: wgpu::BindGroupLayout,
}
impl PostProcessLayout {
pub fn new(device: &wgpu::Device) -> Self {
Self {
layout: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[
// src color
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,
},
// 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,
}
}
pub fn create_mesh() -> Mesh<PostProcessPipeline> {
let mut mesh = Mesh::new();
#[rustfmt::skip]
mesh.push_tri(Tri::new(
Vertex { pos: [ 1.0, -1.0] },
Vertex { pos: [-1.0, 1.0] },
Vertex { pos: [-1.0, -1.0] },
));
#[rustfmt::skip]
mesh.push_tri(Tri::new(
Vertex { pos: [1.0, -1.0] },
Vertex { pos: [1.0, 1.0] },
Vertex { pos: [-1.0, 1.0] },
));
mesh
}

View File

@ -1,15 +1,54 @@
use super::super::{
AaMode, Bound, ColLightInfo, Consts, FigureLayout, GlobalsLayouts, Renderer, TerrainLayout,
TerrainVertex, Texture,
use super::{
super::{
ColLightFmt, ColLightInfo, Pipeline, RenderError, Renderer, ShadowDepthStencilFmt,
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::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
shadow_matrices: [[f32; 4]; 4],
texture_mats: [[f32; 4]; 4],
gfx_defines! {
constant Locals {
shadow_matrices: [[f32; 4]; 4] = "shadowMatrices",
texture_mats: [[f32; 4]; 4] = "texture_mat",
}
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 {
@ -23,326 +62,29 @@ impl Locals {
pub fn default() -> Self { Self::new(Mat4::identity(), Mat4::identity()) }
}
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()) }
}
pub fn create_col_lights(
renderer: &mut Renderer,
(col_lights, col_lights_size): &ColLightInfo,
) -> Texture {
let texture_info = wgpu::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: u32::from(col_lights_size.x),
height: u32::from(col_lights_size.y),
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
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,
}
}
}
pub struct ShadowPipeline {
pub pipeline: wgpu::RenderPipeline,
}
pub struct ShadowPipeline;
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 fn create_col_lights(
renderer: &mut Renderer,
(col_lights, col_lights_size): &ColLightInfo,
) -> Result<Texture<ColLightFmt>, RenderError> {
renderer.create_texture_immutable_raw(
gfx::texture::Kind::D2(
col_lights_size.x,
col_lights_size.y,
gfx::texture::AaMode::Single,
),
gfx::texture::Mipmap::Provided,
&[col_lights],
gfx::texture::SamplerInfo::new(
gfx::texture::FilterMethod::Bilinear,
gfx::texture::WrapMode::Clamp,
),
)
}
}
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,
}
}
impl Pipeline for ShadowPipeline {
type Vertex = terrain::Vertex;
}

View File

@ -1,120 +1,53 @@
use super::super::{AaMode, GlobalsLayouts, Mesh, Quad, Vertex as VertexTrait};
use bytemuck::{Pod, Zeroable};
use std::mem;
use super::{
super::{Mesh, Pipeline, Quad, TgtColorFmt, TgtDepthStencilFmt},
Globals,
};
use gfx::{
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
gfx_pipeline_inner, gfx_vertex_struct_meta,
};
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pub pos: [f32; 3],
}
gfx_defines! {
vertex Vertex {
pos: [f32; 3] = "v_pos",
}
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,
}],
}
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 VertexTrait for Vertex {
const QUADS_INDEX: Option<wgpu::IndexFormat> = None;
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
impl Locals {
pub fn default() -> Self { Self { nul: [0.0; 4] } }
}
// TODO: does skybox still do anything with new cloud shaders?
pub struct SkyboxPipeline {
pub pipeline: wgpu::RenderPipeline,
pub struct SkyboxPipeline;
impl Pipeline for SkyboxPipeline {
type Vertex = Vertex;
}
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,
};
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Skybox 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,
}
}
}
#[rustfmt::skip]
pub fn create_mesh() -> Mesh<Vertex> {
pub fn create_mesh() -> Mesh<SkyboxPipeline> {
let mut mesh = Mesh::new();
// -x
#[rustfmt::skip]
mesh.push_quad(Quad::new(
Vertex { pos: [-1.0, -1.0, -1.0] },
Vertex { pos: [-1.0, 1.0, -1.0] },
@ -122,6 +55,7 @@ pub fn create_mesh() -> Mesh<Vertex> {
Vertex { pos: [-1.0, -1.0, 1.0] },
));
// +x
#[rustfmt::skip]
mesh.push_quad(Quad::new(
Vertex { pos: [ 1.0, -1.0, 1.0] },
Vertex { pos: [ 1.0, 1.0, 1.0] },
@ -129,6 +63,7 @@ pub fn create_mesh() -> Mesh<Vertex> {
Vertex { pos: [ 1.0, -1.0, -1.0] },
));
// -y
#[rustfmt::skip]
mesh.push_quad(Quad::new(
Vertex { pos: [ 1.0, -1.0, -1.0] },
Vertex { pos: [-1.0, -1.0, -1.0] },
@ -136,6 +71,7 @@ pub fn create_mesh() -> Mesh<Vertex> {
Vertex { pos: [ 1.0, -1.0, 1.0] },
));
// +y
#[rustfmt::skip]
mesh.push_quad(Quad::new(
Vertex { pos: [ 1.0, 1.0, 1.0] },
Vertex { pos: [-1.0, 1.0, 1.0] },
@ -143,6 +79,7 @@ pub fn create_mesh() -> Mesh<Vertex> {
Vertex { pos: [ 1.0, 1.0, -1.0] },
));
// -z
#[rustfmt::skip]
mesh.push_quad(Quad::new(
Vertex { pos: [-1.0, -1.0, -1.0] },
Vertex { pos: [ 1.0, -1.0, -1.0] },
@ -150,6 +87,7 @@ pub fn create_mesh() -> Mesh<Vertex> {
Vertex { pos: [-1.0, 1.0, -1.0] },
));
// +z
#[rustfmt::skip]
mesh.push_quad(Quad::new(
Vertex { pos: [-1.0, 1.0, 1.0] },
Vertex { pos: [ 1.0, 1.0, 1.0] },

View File

@ -1,49 +1,98 @@
use super::{
super::{
buffer::Buffer, AaMode, GlobalsLayouts, Mesh, TerrainLayout, Texture, Vertex as VertexTrait,
},
lod_terrain, GlobalModel,
super::{Pipeline, TgtColorFmt, TgtDepthStencilFmt},
shadow, terrain, Globals, Light, Shadow,
};
use core::fmt;
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::*;
pub const VERT_PAGE_SIZE: u32 = 256;
gfx_defines! {
vertex Vertex {
pos: [f32; 3] = "v_pos",
// Because we try to restrict terrain sprite data to a 128×128 block
// we need an offset into the texture atlas.
atlas_pos: u32 = "v_atlas_pos",
// ____BBBBBBBBGGGGGGGGRRRRRRRR
// col: u32 = "v_col",
// ...AANNN
// A = AO
// N = Normal
norm_ao: u32 = "v_norm_ao",
}
#[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
// we need an offset into the texture atlas.
atlas_pos: u32,
/* ____BBBBBBBBGGGGGGGGRRRRRRRR
* col: u32 = "v_col",
* .....NNN
* A = AO
* N = Normal
*norm: u32, */
constant Locals {
// Each matrix performs rotatation, translation, and scaling, relative to the sprite
// 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))),
}
}
// TODO: fix?
/*impl fmt::Display for Vertex {
impl fmt::Display for Vertex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Vertex")
.field("pos_norm", &Vec3::<f32>::from(self.pos))
.field("pos", &Vec3::<f32>::from(self.pos))
.field(
"atlas_pos",
&Vec2::new(self.atlas_pos & 0xFFFF, (self.atlas_pos >> 16) & 0xFFFF),
)
.field("norm_ao", &self.norm_ao)
.finish()
}
}*/
}
impl Vertex {
// NOTE: Limit to 16 (x) × 16 (y) × 32 (z).
#[allow(clippy::collapsible_else_if)]
pub fn new(atlas_pos: Vec2<u16>, pos: Vec3<f32>, norm: Vec3<f32>) -> Self {
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)
pub fn new(
atlas_pos: Vec2<u16>,
pos: Vec3<f32>,
norm: Vec3<f32>, /* , col: Rgb<f32>, ao: f32 */
) -> Self {
let norm_bits = if norm.x != 0.0 {
if norm.x < 0.0 { 0 } else { 1 }
} else if norm.y != 0.0 {
@ -58,287 +107,60 @@ impl Vertex {
// | (((pos + EXTRA_NEG_Z).z.max(0.0).min((1 << 16) as f32) as u32) & 0xFFFF) << 12
// | if meta { 1 } else { 0 } << 28
// | (norm_bits & 0x7) << 29,
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,
pos: pos.into_array(),
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 {
pub fn new(
mat: Mat4<f32>,
wind_sway: f32,
z_scale: f32,
pos: Vec3<i32>,
ori_bits: u8,
light: f32,
glow: f32,
vert_page: u32,
) -> Self {
const EXTRA_NEG_Z: i32 = 32768;
let mat_arr = mat.into_col_arrays();
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_mat1: mat_arr[1],
inst_mat2: mat_arr[2],
inst_mat3: mat_arr[3],
pos_ori: ((pos.x as u32) & 0x003F)
| ((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,
inst_light: [light, glow, 1.0, 1.0],
inst_wind_sway: wind_sway,
}
}
}
impl Default for Instance {
fn default() -> Self { Self::new(Mat4::identity(), 0.0, 0.0, Vec3::zero(), 0, 1.0, 0.0, 0) }
fn default() -> Self { Self::new(Mat4::identity(), 0.0, Vec3::zero(), 0, 1.0, 0.0) }
}
// TODO: ColLightsWrapper instead?
pub struct Locals;
pub struct SpriteGlobalsBindGroup {
pub(in super::super) bind_group: wgpu::BindGroup,
impl Default for Locals {
fn default() -> Self { Self::new(Mat4::identity(), Vec3::one(), Vec3::zero(), 0.0) }
}
pub struct SpriteLayout {
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,
},
]);
impl Locals {
pub fn new(mat: Mat4<f32>, scale: Vec3<f32>, offs: Vec3<f32>, wind_sway: f32) -> Self {
Self {
globals: device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &entries,
}),
}
}
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);
entries.extend_from_slice(&[
// 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,
mat: mat.into_col_arrays(),
wind_sway: [scale.x, scale.y, scale.z, wind_sway],
offs: [offs.x, offs.y, offs.z, 0.0],
}
}
}
pub struct SpritePipeline;
impl Pipeline for SpritePipeline {
type Vertex = Vertex;
}

View File

@ -1,13 +1,49 @@
use super::super::{AaMode, Bound, Consts, GlobalsLayouts, Vertex as VertexTrait};
use bytemuck::{Pod, Zeroable};
use std::mem;
use super::{
super::{ColLightFmt, Pipeline, TgtColorFmt, TgtDepthStencilFmt},
shadow, Globals, Light, Shadow,
};
use gfx::{
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
gfx_pipeline_inner, gfx_vertex_struct_meta,
};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos_norm: u32,
atlas_pos: u32,
gfx_defines! {
vertex Vertex {
pos_norm: u32 = "v_pos_norm",
atlas_pos: u32 = "v_atlas_pos",
}
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 {
@ -68,7 +104,8 @@ impl Vertex {
// 0 to 31
glow: u8,
col: Rgb<u8>,
) -> [u8; 4] {
) -> <<ColLightFmt as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType
{
//[col.r, col.g, col.b, light]
// 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,
@ -104,7 +141,8 @@ impl Vertex {
glowy: bool,
shiny: bool,
col: Rgb<u8>,
) -> [u8; 4] {
) -> <<ColLightFmt as gfx::format::Formatted>::Surface as gfx::format::SurfaceTyped>::DataType
{
let attr = 0 | ((glowy as u8) << 0) | ((shiny as u8) << 1);
[
(light.min(31) << 3) | ((col.r >> 1) & 0b111),
@ -113,48 +151,9 @@ impl Vertex {
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 {
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 {
Self {
model_offs: [0.0; 3],
@ -164,135 +163,8 @@ impl Locals {
}
}
pub type BoundLocals = Bound<Consts<Locals>>;
pub struct TerrainPipeline;
pub struct TerrainLayout {
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,
}
}
impl Pipeline for TerrainPipeline {
type Vertex = Vertex;
}

View File

@ -1,38 +1,41 @@
use super::super::{Bound, Consts, GlobalsLayouts, Quad, Texture, Tri, Vertex as VertexTrait};
use bytemuck::{Pod, Zeroable};
use std::mem;
use super::super::{Globals, Pipeline, Quad, Tri, WinColorFmt, WinDepthFmt};
use gfx::{
self, gfx_constant_struct_meta, gfx_defines, gfx_impl_struct_meta, gfx_pipeline,
gfx_pipeline_inner, gfx_vertex_struct_meta,
};
use vek::*;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Vertex {
pos: [f32; 2],
uv: [f32; 2],
color: [f32; 4],
center: [f32; 2],
mode: u32,
}
gfx_defines! {
vertex Vertex {
pos: [f32; 2] = "v_pos",
uv: [f32; 2] = "v_uv",
color: [f32; 4] = "v_color",
center: [f32; 2] = "v_center",
mode: u32 = "v_mode",
}
impl Vertex {
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,
}
constant Locals {
pos: [f32; 4] = "w_pos",
}
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,
}
}
impl VertexTrait for Vertex {
const QUADS_INDEX: Option<wgpu::IndexFormat> = None;
const STRIDE: wgpu::BufferAddress = mem::size_of::<Self>() as wgpu::BufferAddress;
}
pub struct UiPipeline;
#[repr(C)]
#[derive(Copy, Clone, Debug, Zeroable, Pod)]
pub struct Locals {
pos: [f32; 4],
impl Pipeline for UiPipeline {
type Vertex = Vertex;
}
impl From<Vec4<f32>> for Locals {
@ -84,177 +87,12 @@ 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(
rect: Aabr<f32>,
uv_rect: Aabr<f32>,
color: Rgba<f32>,
mode: Mode,
) -> Quad<Vertex> {
) -> Quad<UiPipeline> {
create_quad_vert_gradient(rect, uv_rect, color, color, mode)
}
@ -265,7 +103,7 @@ pub fn create_quad_vert_gradient(
top_color: Rgba<f32>,
bottom_color: Rgba<f32>,
mode: Mode,
) -> Quad<Vertex> {
) -> Quad<UiPipeline> {
let top_color = top_color.into_array();
let bottom_color = bottom_color.into_array();
@ -314,7 +152,7 @@ pub fn create_tri(
uv_tri: [[f32; 2]; 3],
color: Rgba<f32>,
mode: Mode,
) -> Tri<Vertex> {
) -> Tri<UiPipeline> {
let center = [0.0, 0.0];
let mode_val = mode.value();
let v = |pos, uv| Vertex {

File diff suppressed because it is too large Load Diff

View File

@ -1,88 +0,0 @@
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

@ -1,869 +0,0 @@
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

@ -1,75 +0,0 @@
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

@ -1,914 +0,0 @@
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

@ -1,185 +0,0 @@
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

@ -1,96 +0,0 @@
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